Skip to content

Commit 820bedf

Browse files
committed
feat(cli): refresh initial map metadata during init
1 parent b89e2fd commit 820bedf

4 files changed

Lines changed: 298 additions & 253 deletions

File tree

apps/cli/package.json

Lines changed: 89 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,91 @@
11
{
2-
"name": "offworld",
3-
"version": "0.3.2",
4-
"description": "Offworld CLI - Repository reference generation for AI coding agents",
5-
"keywords": [
6-
"ai",
7-
"ai-agents",
8-
"amp",
9-
"anthropic",
10-
"claude",
11-
"claude-code",
12-
"cli",
13-
"codex",
14-
"coding-agent",
15-
"context",
16-
"cursor",
17-
"dependencies",
18-
"developer-tools",
19-
"documentation",
20-
"git",
21-
"llm",
22-
"monorepo",
23-
"offworld",
24-
"opencode",
25-
"reference",
26-
"repository",
27-
"skill",
28-
"typescript"
29-
],
30-
"homepage": "https://offworld.sh",
31-
"bugs": {
32-
"url": "https://github.com/oscabriel/offworld/issues"
33-
},
34-
"license": "MIT",
35-
"author": "Oscar Gabriel",
36-
"repository": {
37-
"type": "git",
38-
"url": "https://github.com/oscabriel/offworld.git",
39-
"directory": "apps/cli"
40-
},
41-
"bin": {
42-
"offworld": "./dist/cli.mjs",
43-
"ow": "./dist/cli.mjs"
44-
},
45-
"files": [
46-
"dist",
47-
"LICENSE",
48-
"README.md"
49-
],
50-
"type": "module",
51-
"exports": {
52-
".": {
53-
"types": "./dist/index.d.mts",
54-
"import": "./dist/index.mjs"
55-
}
56-
},
57-
"publishConfig": {
58-
"access": "public"
59-
},
60-
"scripts": {
61-
"build": "tsdown && chmod +x ./dist/cli.mjs",
62-
"dev": "tsdown --watch",
63-
"ow:dev": "bun run src/cli.ts",
64-
"typecheck": "tsc --noEmit",
65-
"test": "vitest run",
66-
"test:watch": "vitest",
67-
"prepublishOnly": "npm run build",
68-
"check": "oxlint && oxfmt --write"
69-
},
70-
"dependencies": {
71-
"@clack/prompts": "^1.0.0",
72-
"@offworld/sdk": "workspace:*",
73-
"@offworld/types": "workspace:*",
74-
"@opencode-ai/sdk": "^1.1.51",
75-
"@orpc/server": "^1.13.4",
76-
"convex": "catalog:",
77-
"open": "^11.0.0",
78-
"picocolors": "^1.1.1",
79-
"trpc-cli": "^0.12.2",
80-
"zod": "catalog:"
81-
},
82-
"devDependencies": {
83-
"@types/node": "catalog:",
84-
"tsdown": "catalog:",
85-
"typescript": "catalog:",
86-
"vitest": "catalog:"
87-
},
88-
"engines": {
89-
"node": ">=18"
90-
}
2+
"name": "offworld",
3+
"version": "0.3.2",
4+
"description": "Offworld CLI - Repository reference generation for AI coding agents",
5+
"keywords": [
6+
"ai",
7+
"ai-agents",
8+
"amp",
9+
"anthropic",
10+
"claude",
11+
"claude-code",
12+
"cli",
13+
"codex",
14+
"coding-agent",
15+
"context",
16+
"cursor",
17+
"dependencies",
18+
"developer-tools",
19+
"documentation",
20+
"git",
21+
"llm",
22+
"monorepo",
23+
"offworld",
24+
"opencode",
25+
"reference",
26+
"repository",
27+
"skill",
28+
"typescript"
29+
],
30+
"homepage": "https://offworld.sh",
31+
"bugs": {
32+
"url": "https://github.com/oscabriel/offworld/issues"
33+
},
34+
"license": "MIT",
35+
"author": "Oscar Gabriel",
36+
"repository": {
37+
"type": "git",
38+
"url": "https://github.com/oscabriel/offworld.git",
39+
"directory": "apps/cli"
40+
},
41+
"bin": {
42+
"offworld": "./dist/cli.mjs",
43+
"ow": "./dist/cli.mjs"
44+
},
45+
"files": [
46+
"dist",
47+
"LICENSE",
48+
"README.md"
49+
],
50+
"type": "module",
51+
"exports": {
52+
".": {
53+
"types": "./dist/index.d.mts",
54+
"import": "./dist/index.mjs"
55+
}
56+
},
57+
"publishConfig": {
58+
"access": "public"
59+
},
60+
"scripts": {
61+
"build": "tsdown && chmod +x ./dist/cli.mjs",
62+
"dev": "tsdown --watch",
63+
"ow:dev": "bun run src/cli.ts",
64+
"typecheck": "tsc --noEmit",
65+
"test": "vitest run",
66+
"test:watch": "vitest",
67+
"prepublishOnly": "npm run build",
68+
"check": "oxlint && oxfmt --write"
69+
},
70+
"dependencies": {
71+
"@clack/prompts": "^1.0.0",
72+
"@offworld/sdk": "workspace:*",
73+
"@offworld/types": "workspace:*",
74+
"@opencode-ai/sdk": "^1.1.51",
75+
"@orpc/server": "^1.13.4",
76+
"convex": "catalog:",
77+
"open": "^11.0.0",
78+
"picocolors": "^1.1.1",
79+
"trpc-cli": "^0.12.2",
80+
"zod": "catalog:"
81+
},
82+
"devDependencies": {
83+
"@types/node": "catalog:",
84+
"tsdown": "catalog:",
85+
"typescript": "catalog:",
86+
"vitest": "catalog:"
87+
},
88+
"engines": {
89+
"node": ">=18"
90+
}
9191
}

apps/cli/src/handlers/init.ts

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import * as p from "@clack/prompts";
22
import { existsSync } from "node:fs";
33
import { homedir } from "node:os";
4-
import { resolve, join, dirname } from "node:path";
4+
import { resolve, join, dirname, basename } from "node:path";
55
import {
66
loadConfig,
77
saveConfig,
88
getConfigPath,
99
getMetaRoot,
10+
getReferencePath,
1011
Paths,
1112
detectInstalledAgents,
1213
getAllAgentConfigs,
@@ -16,6 +17,9 @@ import {
1617
getProvider,
1718
validateProviderModel,
1819
discoverRepos,
20+
readGlobalMap,
21+
writeGlobalMap,
22+
resolveReferenceKeywords,
1923
type ProviderInfo,
2024
type ModelInfo,
2125
} from "@offworld/sdk/internal";
@@ -85,18 +89,19 @@ function detectProjectRoot(): string | null {
8589
}
8690

8791
const BANNER = `
88-
██████╗ ███████╗███████╗██╗ ██╗ ██████╗ ██████╗ ██╗ ██████╗
92+
██████╗ ███████╗███████╗██╗ ██╗ ██████╗ ██████╗ ██╗ ██████╗
8993
██╔═══██╗██╔════╝██╔════╝██║ ██║██╔═══██╗██╔══██╗██║ ██╔══██╗
9094
██║ ██║█████╗ █████╗ ██║ █╗ ██║██║ ██║██████╔╝██║ ██║ ██║
9195
██║ ██║██╔══╝ ██╔══╝ ██║███╗██║██║ ██║██╔══██╗██║ ██║ ██║
9296
╚██████╔╝██║ ██║ ╚███╔███╔╝╚██████╔╝██║ ██║███████╗██████╔╝
93-
╚═════╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═════╝
97+
╚═════╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═════╝
9498
`;
9599

96100
export async function initHandler(options: InitOptions = {}): Promise<InitResult> {
97101
const configPath = getConfigPath();
98102
const existingConfig = loadConfig();
99103
const configExists = existsSync(configPath);
104+
const mapExistedBeforeInit = existsSync(Paths.offworldGlobalMapPath);
100105
const projectRoot = detectProjectRoot();
101106

102107
console.log(BANNER);
@@ -338,7 +343,7 @@ export async function initHandler(options: InitOptions = {}): Promise<InitResult
338343
let shouldDiscover = options.yes;
339344
if (!options.yes) {
340345
const confirmDiscover = await p.confirm({
341-
message: "Add them to your clone map? (they will be marked as not referenced)",
346+
message: "Add them to your clone map?",
342347
initialValue: true,
343348
});
344349
if (!p.isCancel(confirmDiscover)) {
@@ -349,6 +354,46 @@ export async function initHandler(options: InitOptions = {}): Promise<InitResult
349354
if (shouldDiscover) {
350355
const result = await discoverRepos({ repoRoot: expandedRepoRoot });
351356
p.log.success(`Added ${result.discovered.length} repos to clone map`);
357+
358+
if (!mapExistedBeforeInit && result.discovered.length > 0) {
359+
const refreshSpinner = p.spinner();
360+
refreshSpinner.start("Building map.json...");
361+
362+
const map = readGlobalMap();
363+
let refreshedCount = 0;
364+
let withReferenceCount = 0;
365+
366+
for (const repo of result.discovered) {
367+
const existingEntry = map.repos[repo.qualifiedName];
368+
if (!existingEntry) continue;
369+
370+
const keywords = await resolveReferenceKeywords(repo.fullName, repo.localPath);
371+
const referencePath = getReferencePath(repo.fullName);
372+
const referenceFile = existsSync(referencePath) ? basename(referencePath) : "";
373+
const references = referenceFile
374+
? Array.from(new Set([...(existingEntry.references ?? []), referenceFile]))
375+
: existingEntry.references;
376+
377+
if (referenceFile) {
378+
withReferenceCount++;
379+
}
380+
381+
map.repos[repo.qualifiedName] = {
382+
...existingEntry,
383+
references,
384+
primary: referenceFile || existingEntry.primary,
385+
keywords,
386+
updatedAt: new Date().toISOString(),
387+
};
388+
389+
refreshedCount++;
390+
}
391+
392+
writeGlobalMap(map);
393+
refreshSpinner.stop(
394+
`Refreshed ${refreshedCount} map entries (${withReferenceCount} with references)`,
395+
);
396+
}
352397
}
353398
}
354399
}

0 commit comments

Comments
 (0)