@cleocode/core 2026.3.47 → 2026.3.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/init.d.ts CHANGED
@@ -16,7 +16,7 @@
16
16
  * 9. Core skill installation via CAAMP
17
17
  * 10. NEXUS project registration
18
18
  * 11. Project type detection (--detect)
19
- * 12. Injection refresh (--update-docs)
19
+ * 12. Injection refresh
20
20
  * 13. Git hook installation (commit-msg, pre-commit)
21
21
  * 14. GitHub issue/PR templates (.github/ directory)
22
22
  *
@@ -39,8 +39,6 @@ export interface InitOptions {
39
39
  force?: boolean;
40
40
  /** Auto-detect project configuration. */
41
41
  detect?: boolean;
42
- /** Update agent documentation injections only. */
43
- updateDocs?: boolean;
44
42
  /** Run codebase analysis and store findings to brain.db. */
45
43
  mapCodebase?: boolean;
46
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AA8BH,sCAAsC;AACtC,MAAM,WAAW,WAAW;IAC1B,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,yCAAyC;IACzC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,oCAAoC;AACpC,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAaD;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA8D9F;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,IAAI,CAAC,CA0Cf;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+FzF;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAID;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CAiDf;AAID;;;;;GAKG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC,CAwBtD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,WAAW,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAgS7E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,CAiB/F;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAyBnF"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AA8BH,sCAAsC;AACtC,MAAM,WAAW,WAAW;IAC1B,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,yCAAyC;IACzC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,oCAAoC;AACpC,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAaD;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqE9F;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,IAAI,CAAC,CA0Cf;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+FzF;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAID;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CAiDf;AAID;;;;;GAKG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC,CAwBtD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,WAAW,CAAC,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CA2R7E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,CAiB/F;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAyBnF"}
@@ -32,4 +32,9 @@ export declare function getProjectInfo(cwd?: string): Promise<ProjectInfo>;
32
32
  * Returns null if the file is missing or unparseable.
33
33
  */
34
34
  export declare function getProjectInfoSync(cwd?: string): ProjectInfo | null;
35
+ /**
36
+ * Update the project name in project-info.json.
37
+ * Used by `cleo upgrade --name` and programmatic consumers.
38
+ */
39
+ export declare function updateProjectName(cwd: string, name: string): void;
35
40
  //# sourceMappingURL=project-info.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"project-info.d.ts","sourceRoot":"","sources":["../src/project-info.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH,qEAAqE;AACrE,MAAM,WAAW,WAAW;IAC1B,iFAAiF;IACjF,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;CACrB;AAID;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAqBvE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CA2BnE"}
1
+ {"version":3,"file":"project-info.d.ts","sourceRoot":"","sources":["../src/project-info.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH,qEAAqE;AACrE,MAAM,WAAW,WAAW;IAC1B,iFAAiF;IACjF,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;CACrB;AAID;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAqBvE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CA2BnE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CASjE"}
package/dist/upgrade.d.ts CHANGED
@@ -48,12 +48,18 @@ export interface UpgradeResult {
48
48
  * @param options.dryRun Preview changes without applying
49
49
  * @param options.includeGlobal Also check global ~/.cleo
50
50
  * @param options.autoMigrate Auto-migrate storage if needed (default: true)
51
+ * @param options.forceDetect Force re-detection of project type (ignore staleness)
52
+ * @param options.mapCodebase Run full codebase analysis and store to brain.db
53
+ * @param options.projectName Update project name in project-info and nexus
51
54
  * @param options.cwd Project directory override
52
55
  */
53
56
  export declare function runUpgrade(options?: {
54
57
  dryRun?: boolean;
55
58
  includeGlobal?: boolean;
56
59
  autoMigrate?: boolean;
60
+ forceDetect?: boolean;
61
+ mapCodebase?: boolean;
62
+ projectName?: string;
57
63
  cwd?: string;
58
64
  }): Promise<UpgradeResult>;
59
65
  //# sourceMappingURL=upgrade.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../src/upgrade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAoCH,2CAA2C;AAC3C,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,2BAA2B;AAC3B,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,iEAAiE;IACjE,gBAAgB,CAAC,EAAE;QACjB,QAAQ,EAAE,OAAO,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;QACzB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAC9B,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/F,OAAO,CAAC,aAAa,CAAC,CA41BxB"}
1
+ {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../src/upgrade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAoCH,2CAA2C;AAC3C,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,2BAA2B;AAC3B,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,iEAAiE;IACjE,gBAAgB,CAAC,EAAE;QACjB,QAAQ,EAAE,OAAO,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;QACzB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,UAAU,CAC9B,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;CACT,GACL,OAAO,CAAC,aAAa,CAAC,CAg4BxB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleocode/core",
3
- "version": "2026.3.47",
3
+ "version": "2026.3.49",
4
4
  "description": "CLEO core business logic kernel — tasks, sessions, memory, orchestration, lifecycle, with bundled SQLite store",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -36,10 +36,10 @@
36
36
  "write-file-atomic": "^6.0.0",
37
37
  "yaml": "^2.8.2",
38
38
  "zod": "^3.25.76",
39
- "@cleocode/adapters": "2026.3.47",
40
- "@cleocode/contracts": "2026.3.47",
41
- "@cleocode/skills": "2026.3.47",
42
- "@cleocode/agents": "2026.3.47"
39
+ "@cleocode/contracts": "2026.3.49",
40
+ "@cleocode/adapters": "2026.3.49",
41
+ "@cleocode/agents": "2026.3.49",
42
+ "@cleocode/skills": "2026.3.49"
43
43
  },
44
44
  "engines": {
45
45
  "node": ">=24.0.0"
@@ -257,8 +257,9 @@ describe('E2E: injection chain validation (T4694)', () => {
257
257
  expect(existsSync(join(testDir, 'CLAUDE.md'))).toBe(true);
258
258
  expect(existsSync(join(testDir, 'AGENTS.md'))).toBe(true);
259
259
 
260
- // Run updateDocs mode
261
- const result = await initProject({ updateDocs: true });
260
+ // Run updateDocs directly (no longer available via initProject flag — use upgrade path)
261
+ const { updateDocs } = await import('../init.js');
262
+ const result = await updateDocs();
262
263
  expect(result.updateDocsOnly).toBe(true);
263
264
  expect(result.initialized).toBe(true);
264
265
  });
package/src/bootstrap.ts CHANGED
@@ -53,6 +53,14 @@ export async function bootstrapGlobalCleo(options?: BootstrapOptions): Promise<B
53
53
  isDryRun: options?.dryRun ?? false,
54
54
  };
55
55
 
56
+ // Step 0: Ensure global home structure and clean stale artifacts
57
+ try {
58
+ const { ensureGlobalHome } = await import('./scaffold.js');
59
+ await ensureGlobalHome();
60
+ } catch {
61
+ // Best-effort — don't fail bootstrap if cleanup fails
62
+ }
63
+
56
64
  // Step 1: Ensure global templates
57
65
  await ensureGlobalTemplatesBootstrap(ctx, options?.packageRoot);
58
66
 
@@ -135,19 +143,20 @@ async function injectAgentsHub(ctx: BootstrapContext): Promise<void> {
135
143
  if (!ctx.isDryRun) {
136
144
  await mkdir(globalAgentsDir, { recursive: true });
137
145
 
138
- // Strip any legacy CLEO blocks first (handles bare and versioned markers)
146
+ // Consolidate: strip ALL legacy CLEO blocks AND duplicate CAAMP blocks,
147
+ // then inject a single clean block. CAAMP 1.8.0 idempotency doesn't
148
+ // consolidate pre-existing duplicates — it only prevents new ones.
139
149
  if (existsSync(globalAgentsMd)) {
140
150
  const content = await readFile(globalAgentsMd, 'utf8');
141
- const stripped = content.replace(
142
- /\n?<!-- CLEO:START[^>]*-->[\s\S]*?<!-- CLEO:END -->\n?/g,
143
- '',
144
- );
145
- if (stripped !== content) {
146
- await writeFile(globalAgentsMd, stripped, 'utf8');
147
- }
151
+ const stripped = content
152
+ .replace(/\n?<!-- CLEO:START[^>]*-->[\s\S]*?<!-- CLEO:END -->\n?/g, '')
153
+ .replace(/\n?<!-- CAAMP:START -->[\s\S]*?<!-- CAAMP:END -->\n?/g, '')
154
+ .trim();
155
+ // Write clean file (empty or user content only)
156
+ await writeFile(globalAgentsMd, stripped ? `${stripped}\n` : '', 'utf8');
148
157
  }
149
158
 
150
- // Direct call — CAAMP 1.8.0 handles idempotency
159
+ // Single inject call on a clean file no duplicates possible
151
160
  const expectedContent = '@~/.cleo/templates/CLEO-INJECTION.md';
152
161
  const action = await inject(globalAgentsMd, expectedContent);
153
162
  ctx.created.push(`~/.agents/AGENTS.md (${action})`);
package/src/init.ts CHANGED
@@ -16,7 +16,7 @@
16
16
  * 9. Core skill installation via CAAMP
17
17
  * 10. NEXUS project registration
18
18
  * 11. Project type detection (--detect)
19
- * 12. Injection refresh (--update-docs)
19
+ * 12. Injection refresh
20
20
  * 13. Git hook installation (commit-msg, pre-commit)
21
21
  * 14. GitHub issue/PR templates (.github/ directory)
22
22
  *
@@ -68,8 +68,6 @@ export interface InitOptions {
68
68
  force?: boolean;
69
69
  /** Auto-detect project configuration. */
70
70
  detect?: boolean;
71
- /** Update agent documentation injections only. */
72
- updateDocs?: boolean;
73
71
  /** Run codebase analysis and store findings to brain.db. */
74
72
  mapCodebase?: boolean;
75
73
  }
@@ -132,12 +130,19 @@ export async function initAgentDefinition(created: string[], warnings: string[])
132
130
  await mkdir(dirname(globalAgentsDir), { recursive: true });
133
131
 
134
132
  try {
135
- // Check if symlink already exists
133
+ // Check if symlink already exists and points to correct target
136
134
  try {
137
135
  const stat = await lstat(globalAgentsDir);
138
- if (stat.isSymbolicLink() || stat.isDirectory()) {
139
- // Already installed
140
- return;
136
+ if (stat.isSymbolicLink()) {
137
+ const { readlink } = await import('node:fs/promises');
138
+ const currentTarget = await readlink(globalAgentsDir);
139
+ if (currentTarget === agentSourceDir) {
140
+ return; // Symlink intact and pointing to correct location
141
+ }
142
+ // Stale symlink — remove and recreate
143
+ await unlink(globalAgentsDir);
144
+ } else if (stat.isDirectory()) {
145
+ return; // Copied dir, leave as-is
141
146
  }
142
147
  } catch {
143
148
  // Doesn't exist, proceed to create
@@ -472,11 +477,6 @@ export async function updateDocs(): Promise<InitResult> {
472
477
  * @task T4707
473
478
  */
474
479
  export async function initProject(opts: InitOptions = {}): Promise<InitResult> {
475
- // Handle --update-docs (T4686)
476
- if (opts.updateDocs) {
477
- return updateDocs();
478
- }
479
-
480
480
  const cleoDir = getCleoDirAbsolute();
481
481
  const projRoot = getProjectRoot();
482
482
 
package/src/injection.ts CHANGED
@@ -281,7 +281,7 @@ export function checkInjection(projectRoot: string): InjectionCheckResult {
281
281
  status: 'warning',
282
282
  message: 'AGENTS.md not found in project root',
283
283
  details: { path: agentsMdPath, exists: false },
284
- fix: 'cleo init --update-docs',
284
+ fix: 'cleo upgrade',
285
285
  };
286
286
  }
287
287
 
@@ -311,7 +311,7 @@ export function checkInjection(projectRoot: string): InjectionCheckResult {
311
311
  status: 'warning',
312
312
  message: 'AGENTS.md exists but has no CAAMP markers',
313
313
  details: { path: agentsMdPath, hasCaampMarker: false },
314
- fix: 'cleo init --update-docs',
314
+ fix: 'cleo upgrade',
315
315
  };
316
316
  }
317
317
 
@@ -323,7 +323,7 @@ export function checkInjection(projectRoot: string): InjectionCheckResult {
323
323
  status: 'warning',
324
324
  message: `CAAMP markers unbalanced: ${startCount} START vs ${endCount} END`,
325
325
  details: { path: agentsMdPath, startCount, endCount },
326
- fix: 'cleo init --update-docs',
326
+ fix: 'cleo upgrade',
327
327
  };
328
328
  }
329
329
 
@@ -352,7 +352,7 @@ export function checkInjection(projectRoot: string): InjectionCheckResult {
352
352
  status: 'warning',
353
353
  message: `Missing @ reference targets: ${missing.join(', ')}`,
354
354
  details: { path: agentsMdPath, missing, totalRefs: refs.length },
355
- fix: 'cleo init --update-docs',
355
+ fix: 'cleo upgrade',
356
356
  };
357
357
  }
358
358
  }
@@ -372,7 +372,7 @@ export function checkInjection(projectRoot: string): InjectionCheckResult {
372
372
  status: 'warning',
373
373
  message: `CLAUDE.md CAAMP markers unbalanced: ${cStartCount} START vs ${cEndCount} END`,
374
374
  details: { file: 'CLAUDE.md', startCount: cStartCount, endCount: cEndCount },
375
- fix: 'cleo init --update-docs',
375
+ fix: 'cleo upgrade',
376
376
  };
377
377
  }
378
378
 
@@ -383,7 +383,7 @@ export function checkInjection(projectRoot: string): InjectionCheckResult {
383
383
  status: 'warning',
384
384
  message: 'CLAUDE.md has no CAAMP markers',
385
385
  details: { file: 'CLAUDE.md', hasCaampMarker: false },
386
- fix: 'cleo init --update-docs',
386
+ fix: 'cleo upgrade',
387
387
  };
388
388
  }
389
389
  } catch {
@@ -8,7 +8,7 @@
8
8
  * @task T5333
9
9
  */
10
10
 
11
- import { existsSync, readFileSync } from 'node:fs';
11
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
12
12
  import { readFile } from 'node:fs/promises';
13
13
  import { join } from 'node:path';
14
14
  import { getCleoDirAbsolute } from './paths.js';
@@ -92,3 +92,18 @@ export function getProjectInfoSync(cwd?: string): ProjectInfo | null {
92
92
  return null;
93
93
  }
94
94
  }
95
+
96
+ /**
97
+ * Update the project name in project-info.json.
98
+ * Used by `cleo upgrade --name` and programmatic consumers.
99
+ */
100
+ export function updateProjectName(cwd: string, name: string): void {
101
+ const cleoDir = getCleoDirAbsolute(cwd);
102
+ const infoPath = join(cleoDir, 'project-info.json');
103
+ if (!existsSync(infoPath)) return;
104
+
105
+ const data = JSON.parse(readFileSync(infoPath, 'utf-8')) as Record<string, string>;
106
+ data.projectName = name;
107
+ data.lastUpdated = new Date().toISOString();
108
+ writeFileSync(infoPath, `${JSON.stringify(data, null, 2)}\n`);
109
+ }
package/src/upgrade.ts CHANGED
@@ -85,10 +85,21 @@ export interface UpgradeResult {
85
85
  * @param options.dryRun Preview changes without applying
86
86
  * @param options.includeGlobal Also check global ~/.cleo
87
87
  * @param options.autoMigrate Auto-migrate storage if needed (default: true)
88
+ * @param options.forceDetect Force re-detection of project type (ignore staleness)
89
+ * @param options.mapCodebase Run full codebase analysis and store to brain.db
90
+ * @param options.projectName Update project name in project-info and nexus
88
91
  * @param options.cwd Project directory override
89
92
  */
90
93
  export async function runUpgrade(
91
- options: { dryRun?: boolean; includeGlobal?: boolean; autoMigrate?: boolean; cwd?: string } = {},
94
+ options: {
95
+ dryRun?: boolean;
96
+ includeGlobal?: boolean;
97
+ autoMigrate?: boolean;
98
+ forceDetect?: boolean;
99
+ mapCodebase?: boolean;
100
+ projectName?: string;
101
+ cwd?: string;
102
+ } = {},
92
103
  ): Promise<UpgradeResult> {
93
104
  const isDryRun = options.dryRun ?? false;
94
105
  const autoMigrate = options.autoMigrate ?? true;
@@ -635,7 +646,9 @@ export async function runUpgrade(
635
646
  }
636
647
  }
637
648
  } else {
638
- const contextResult = await ensureProjectContext(projectRootForContext, { staleDays: 30 });
649
+ const contextResult = await ensureProjectContext(projectRootForContext, {
650
+ staleDays: options.forceDetect ? 0 : 30,
651
+ });
639
652
  actions.push({
640
653
  action: 'project_context_detection',
641
654
  status: contextResult.action === 'skipped' ? 'skipped' : 'applied',
@@ -886,6 +899,40 @@ export async function runUpgrade(
886
899
  /* best-effort */
887
900
  }
888
901
 
902
+ // Run codebase mapping if requested (delegates to core mapCodebase)
903
+ if (options.mapCodebase) {
904
+ try {
905
+ const { mapCodebase } = await import('./codebase-map/index.js');
906
+ const mapResult = await mapCodebase(projectRootForMaint, { storeToBrain: true });
907
+ actions.push({
908
+ action: 'codebase_map',
909
+ status: 'applied',
910
+ details: `Analyzed: ${mapResult.stack?.languages?.length ?? 0} languages, ${mapResult.concerns?.todos?.length ?? 0} TODOs found`,
911
+ });
912
+ } catch (err) {
913
+ actions.push({
914
+ action: 'codebase_map',
915
+ status: 'error',
916
+ details: `Codebase mapping failed: ${err instanceof Error ? err.message : String(err)}`,
917
+ });
918
+ }
919
+ }
920
+
921
+ // Update project name if requested (delegates to core updateProjectName)
922
+ if (options.projectName) {
923
+ try {
924
+ const { updateProjectName } = await import('./project-info.js');
925
+ await updateProjectName(projectRootForMaint, options.projectName);
926
+ actions.push({
927
+ action: 'project_name_update',
928
+ status: 'applied',
929
+ details: `Project name set to "${options.projectName}"`,
930
+ });
931
+ } catch {
932
+ /* best-effort */
933
+ }
934
+ }
935
+
889
936
  // GitHub issue/PR templates — install missing ones, warn if absent
890
937
  try {
891
938
  const { existsSync: fsExistsSync } = await import('node:fs');
@@ -211,7 +211,7 @@ export function checkAgentsMdHub(projectRoot?: string): CheckResult {
211
211
  status: 'warning',
212
212
  message: 'AGENTS.md not found in project root',
213
213
  details: { path: agentsMdPath, exists: false },
214
- fix: 'cleo init --update-docs',
214
+ fix: 'cleo upgrade',
215
215
  };
216
216
  }
217
217
 
@@ -236,7 +236,7 @@ export function checkAgentsMdHub(projectRoot?: string): CheckResult {
236
236
  status: 'warning',
237
237
  message: 'AGENTS.md exists but has no CAAMP:START marker',
238
238
  details: { path: agentsMdPath, hasCaampMarker: false },
239
- fix: 'cleo init --update-docs',
239
+ fix: 'cleo upgrade',
240
240
  };
241
241
  }
242
242
 
@@ -709,7 +709,7 @@ export function checkCaampMarkerIntegrity(projectRoot?: string): CheckResult {
709
709
  status: 'warning',
710
710
  message: `CAAMP marker issues: ${issues.join('; ')}`,
711
711
  details: { issues },
712
- fix: 'cleo init --update-docs',
712
+ fix: 'cleo upgrade',
713
713
  };
714
714
  }
715
715
 
@@ -797,7 +797,7 @@ export function checkAtReferenceTargetExists(projectRoot?: string): CheckResult
797
797
  status: 'warning',
798
798
  message: `Missing @ reference targets: ${missing.join(', ')}`,
799
799
  details: { missing, totalRefs: refs.length },
800
- fix: 'cleo init --update-docs',
800
+ fix: 'cleo upgrade',
801
801
  };
802
802
  }
803
803