mancode 0.3.10 → 0.3.12

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.
@@ -0,0 +1,1290 @@
1
+ // src/installers/v3-adapter.ts
2
+ import {
3
+ lstat,
4
+ mkdir,
5
+ readFile,
6
+ rename,
7
+ rm,
8
+ rmdir,
9
+ writeFile
10
+ } from "fs/promises";
11
+ import path from "path";
12
+
13
+ // src/installers/managed-block.ts
14
+ var DEFAULT_MANCODE_START_MARKER = "<!-- mancode:start -->";
15
+ var DEFAULT_MANCODE_END_MARKER = "<!-- mancode:end -->";
16
+ function removeManagedBlock(existing, startMarker = DEFAULT_MANCODE_START_MARKER, endMarker = DEFAULT_MANCODE_END_MARKER) {
17
+ const start = findMarkerLine(existing, startMarker);
18
+ const end = findMarkerLine(existing, endMarker);
19
+ if (start === null && end === null) return existing;
20
+ if (start === null || end === null) return existing;
21
+ if (end.start < start.start) return existing;
22
+ const before = existing.slice(0, start.start);
23
+ const after = existing.slice(end.end);
24
+ const merged = `${before}${after}`;
25
+ return cleanUpOrphanedNewlines(merged);
26
+ }
27
+ function hasManagedBlock(existing, startMarker = DEFAULT_MANCODE_START_MARKER, endMarker = DEFAULT_MANCODE_END_MARKER) {
28
+ const start = findMarkerLine(existing, startMarker);
29
+ const end = findMarkerLine(existing, endMarker);
30
+ return start !== null && end !== null && end.start > start.start;
31
+ }
32
+ function cleanUpOrphanedNewlines(content) {
33
+ const trimmed = content.replace(/\n{3,}/gu, "\n\n").replace(/\n+$/u, "\n");
34
+ return trimmed || "";
35
+ }
36
+ function replaceManagedBlock(existing, block, startMarker = DEFAULT_MANCODE_START_MARKER, endMarker = DEFAULT_MANCODE_END_MARKER) {
37
+ const normalizedBlock = normalizeManagedBlock(block, startMarker, endMarker);
38
+ const start = findMarkerLine(existing, startMarker);
39
+ const end = findMarkerLine(existing, endMarker);
40
+ if (start === null !== (end === null)) {
41
+ throw new Error("managed block is malformed: missing start or end marker");
42
+ }
43
+ if (start === null && end === null) {
44
+ const trimmedExisting = trimTrailingNewlines(existing);
45
+ if (!trimmedExisting) return `${normalizedBlock}
46
+ `;
47
+ return `${trimmedExisting}
48
+
49
+ ${normalizedBlock}
50
+ `;
51
+ }
52
+ if (!start || !end) {
53
+ throw new Error("managed block is malformed: missing start or end marker");
54
+ }
55
+ if (end.start < start.start) {
56
+ throw new Error("managed block is malformed: end marker precedes start");
57
+ }
58
+ return `${existing.slice(0, start.start)}${normalizedBlock}${existing.slice(
59
+ end.end
60
+ )}`;
61
+ }
62
+ function normalizeManagedBlock(block, startMarker, endMarker) {
63
+ const trimmedBlock = block.trim();
64
+ const hasStart = trimmedBlock.startsWith(startMarker);
65
+ const hasEnd = trimmedBlock.endsWith(endMarker);
66
+ if (hasStart && hasEnd) return trimmedBlock;
67
+ if (hasStart || hasEnd) {
68
+ throw new Error("managed block content includes only one marker");
69
+ }
70
+ return `${startMarker}
71
+ ${trimmedBlock}
72
+ ${endMarker}`;
73
+ }
74
+ function trimTrailingNewlines(value) {
75
+ return value.replace(/\n+$/u, "");
76
+ }
77
+ function findMarkerLine(content, marker) {
78
+ let offset = 0;
79
+ let inFence = null;
80
+ for (const lineWithBreak of content.matchAll(/[^\n]*(?:\n|$)/gu)) {
81
+ const rawLine = lineWithBreak[0];
82
+ if (!rawLine) break;
83
+ const line = rawLine.replace(/\n$/u, "").replace(/\r$/u, "");
84
+ const fence = line.match(/^(`{3,}|~{3,})/u)?.[1];
85
+ if (fence) {
86
+ const char = fence[0];
87
+ if (!inFence) {
88
+ inFence = { char, length: fence.length };
89
+ } else if (inFence.char === char && fence.length >= inFence.length) {
90
+ inFence = null;
91
+ }
92
+ } else if (!inFence && line === marker) {
93
+ return {
94
+ start: offset,
95
+ end: offset + marker.length
96
+ };
97
+ }
98
+ offset += rawLine.length;
99
+ }
100
+ return null;
101
+ }
102
+
103
+ // src/installers/v3-adapter.ts
104
+ var V3_ADAPTER_VERSION = "3";
105
+ var V3_ADAPTER_MANAGED_MARKER = "<!-- Managed by mancode:v3-adapter. Do not edit this marker. -->";
106
+ var V3_MODE_ENTRY_MANAGED_MARKER = "<!-- Managed by mancode:v3-mode-entry. Do not edit this marker. -->";
107
+ var V3_MODE_NAMES = [
108
+ "manba",
109
+ "man",
110
+ "manteam",
111
+ "manps",
112
+ "mansolo"
113
+ ];
114
+ var LEGACY_MODE_ENTRY_MANAGED_MARKERS = [
115
+ "<!-- Managed by mancode:claude-skill. Do not edit this marker. -->",
116
+ "<!-- Managed by mancode:codex-skill. Do not edit this file manually. -->",
117
+ "<!-- Managed by mancode:zcode-skill. Do not edit this file manually. -->",
118
+ "<!-- Managed by mancode:mode-file. Do not edit this file manually. -->"
119
+ ];
120
+ var GENERATED_CLAUDE_HOOK_COMMANDS = /* @__PURE__ */ new Set([
121
+ 'node ".mancode/hooks/session-start.mjs"',
122
+ 'node ".mancode/hooks/user-prompt-submit.mjs"',
123
+ "bash .mancode/hooks/session-start.sh",
124
+ "bash .mancode/hooks/user-prompt-submit.sh"
125
+ ]);
126
+ var LEGACY_CLAUDE_SKILL_PATHS = /* @__PURE__ */ new Set([
127
+ ".claude/skills/mancode-solo.md",
128
+ ".claude/skills/mancode-man8.md",
129
+ ".claude/skills/mancode-man.md",
130
+ ".claude/skills/mancode-mansolo.md"
131
+ ]);
132
+ var LEGACY_CLAUDE_SETTINGS_RAW_HINTS = [
133
+ ".mancode/hooks/session-start.",
134
+ ".mancode/hooks/user-prompt-submit.",
135
+ ...LEGACY_CLAUDE_SKILL_PATHS
136
+ ];
137
+ var V3_CODEX_START_MARKER = "<!-- mancode:v3:codex:start -->";
138
+ var V3_CODEX_END_MARKER = "<!-- mancode:v3:codex:end -->";
139
+ var V3_ZCODE_START_MARKER = "<!-- mancode:v3:zcode:start -->";
140
+ var V3_ZCODE_END_MARKER = "<!-- mancode:v3:zcode:end -->";
141
+ var V3_COPILOT_START_MARKER = "<!-- mancode:v3:copilot:start -->";
142
+ var V3_COPILOT_END_MARKER = "<!-- mancode:v3:copilot:end -->";
143
+ var LEGACY_CODEX_START_MARKER = "<!-- mancode:start -->";
144
+ var LEGACY_CODEX_END_MARKER = "<!-- mancode:end -->";
145
+ var LEGACY_ZCODE_START_MARKER = "<!-- mancode:zcode:start -->";
146
+ var LEGACY_ZCODE_END_MARKER = "<!-- mancode:zcode:end -->";
147
+ var RETRIABLE_ADAPTER_READ_CODES = /* @__PURE__ */ new Set(["EACCES", "EBUSY", "EPERM"]);
148
+ var ADAPTER_READ_MAX_ATTEMPTS = 4;
149
+ var ADAPTER_READ_RETRY_DELAY_MS = 25;
150
+ var V3_MODE_ENTRY_FILE_TARGETS = V3_MODE_NAMES.flatMap((mode) => [
151
+ `claude-mode-${mode}`,
152
+ `agents-mode-${mode}`,
153
+ `cursor-mode-${mode}`,
154
+ `copilot-mode-${mode}`
155
+ ]);
156
+ var V3_LEGACY_ADAPTER_FILE_TARGETS = [
157
+ "claude-settings",
158
+ "claude-legacy-solo",
159
+ "cursor-legacy-context",
160
+ "cursor-legacy-practice",
161
+ "cursor-legacy-solo",
162
+ "cursor-legacy-manba",
163
+ "cursor-legacy-man",
164
+ "cursor-legacy-manteam",
165
+ "cursor-legacy-manps",
166
+ "cursor-legacy-mamba",
167
+ "cursor-legacy-man8",
168
+ ...["mamba", "man8"].flatMap((mode) => [
169
+ `claude-alias-${mode}`,
170
+ `agents-alias-${mode}`,
171
+ `cursor-alias-${mode}`,
172
+ `copilot-alias-${mode}`
173
+ ])
174
+ ];
175
+ var V3_ADAPTER_FILE_TARGETS = [
176
+ "claude-skill",
177
+ "cursor-rule",
178
+ "agents",
179
+ "copilot-instructions",
180
+ ...V3_MODE_ENTRY_FILE_TARGETS,
181
+ ...V3_LEGACY_ADAPTER_FILE_TARGETS
182
+ ];
183
+ async function planV3AdapterFiles(projectRoot) {
184
+ const root = path.resolve(projectRoot);
185
+ const existing = /* @__PURE__ */ new Map();
186
+ for (const target of V3_ADAPTER_FILE_TARGETS) {
187
+ existing.set(target, await readAdapterTarget(root, target));
188
+ }
189
+ const agents = removeLegacyAgentsBlocks(existing.get("agents") ?? "");
190
+ const nextAgents = replaceManagedV3BlockText(
191
+ replaceManagedV3BlockText(
192
+ agents,
193
+ V3_CODEX_START_MARKER,
194
+ V3_CODEX_END_MARKER,
195
+ renderV3Bootstrap("codex")
196
+ ),
197
+ V3_ZCODE_START_MARKER,
198
+ V3_ZCODE_END_MARKER,
199
+ renderV3Bootstrap("zcode")
200
+ );
201
+ const legacyAdapterPlans = planLegacyAdapterRetirement(existing);
202
+ const plans = [
203
+ managedFilePlan(
204
+ "claude-skill",
205
+ existing.get("claude-skill") ?? null,
206
+ renderClaudeSkill(renderV3Bootstrap("claude-code"))
207
+ ),
208
+ managedFilePlan(
209
+ "cursor-rule",
210
+ existing.get("cursor-rule") ?? null,
211
+ renderCursorRule(renderV3Bootstrap("cursor"))
212
+ ),
213
+ {
214
+ target: "agents",
215
+ beforeContent: existing.get("agents") ?? null,
216
+ targetContent: nextAgents
217
+ },
218
+ {
219
+ target: "copilot-instructions",
220
+ beforeContent: existing.get("copilot-instructions") ?? null,
221
+ targetContent: replaceManagedV3BlockText(
222
+ removeManagedBlock(existing.get("copilot-instructions") ?? ""),
223
+ V3_COPILOT_START_MARKER,
224
+ V3_COPILOT_END_MARKER,
225
+ renderV3Bootstrap("copilot")
226
+ )
227
+ },
228
+ ...V3_MODE_ENTRY_FILE_TARGETS.map(
229
+ (target) => managedModeEntryPlan(
230
+ target,
231
+ existing.get(target) ?? null,
232
+ renderModeEntryForFileTarget(target)
233
+ )
234
+ ),
235
+ ...legacyAdapterPlans
236
+ ];
237
+ return plans;
238
+ }
239
+ async function applyV3AdapterFilePlan(projectRoot, plan) {
240
+ const root = path.resolve(projectRoot);
241
+ if (!V3_ADAPTER_FILE_TARGETS.includes(plan.target)) {
242
+ throw new Error("MANCODE_V3_ADAPTER_TARGET_INVALID");
243
+ }
244
+ if (typeof plan.targetContent !== "string" || !plan.targetContent.trim()) {
245
+ throw new Error("MANCODE_V3_ADAPTER_TARGET_INVALID");
246
+ }
247
+ const target = v3AdapterTargetPath(root, plan.target);
248
+ await assertAdapterPathSafe(root, target);
249
+ await mkdir(path.dirname(target), { recursive: true });
250
+ await atomicWrite(target, plan.targetContent);
251
+ }
252
+ async function stageV3Adapter(projectRoot, platform) {
253
+ const root = path.resolve(projectRoot);
254
+ await assertPlatformAdapterPathsSafe(root, platform);
255
+ const target = targetFor(platform);
256
+ const content = await renderV3AdapterCandidate(root, platform);
257
+ const stagingTarget = path.join(
258
+ ".mancode",
259
+ "staging",
260
+ "adapters",
261
+ "v3",
262
+ platform,
263
+ target
264
+ );
265
+ const destination = path.join(root, stagingTarget);
266
+ await assertAdapterPathSafe(root, destination);
267
+ await mkdir(path.dirname(destination), { recursive: true });
268
+ await atomicWrite(destination, content);
269
+ const modeEntries = [];
270
+ for (const mode of V3_MODE_NAMES) {
271
+ const modeTarget = path.relative(
272
+ root,
273
+ v3ModeEntryPath(root, platform, mode)
274
+ );
275
+ const modeStagingTarget = path.join(
276
+ ".mancode",
277
+ "staging",
278
+ "adapters",
279
+ "v3",
280
+ platform,
281
+ modeTarget
282
+ );
283
+ const modeDestination = path.join(root, modeStagingTarget);
284
+ await assertAdapterPathSafe(root, modeDestination);
285
+ await mkdir(path.dirname(modeDestination), { recursive: true });
286
+ await atomicWrite(modeDestination, renderV3ModeEntry(mode, platform));
287
+ modeEntries.push({
288
+ mode,
289
+ target: modeTarget,
290
+ stagingTarget: modeStagingTarget
291
+ });
292
+ }
293
+ return { platform, target, stagingTarget, modeEntries };
294
+ }
295
+ function v3AdapterTargetPath(projectRoot, target) {
296
+ const root = path.resolve(projectRoot);
297
+ const modeTarget = parseModeEntryFileTarget(target);
298
+ if (modeTarget !== null) {
299
+ return v3ModeEntryPath(root, modeTarget.platform, modeTarget.mode);
300
+ }
301
+ const legacyTarget = legacyAdapterRelativePath(target);
302
+ if (legacyTarget !== null) return path.join(root, legacyTarget);
303
+ switch (target) {
304
+ case "claude-skill":
305
+ return path.join(root, ".claude", "skills", "mancode-v3", "SKILL.md");
306
+ case "cursor-rule":
307
+ return path.join(root, ".cursor", "rules", "mancode-v3.mdc");
308
+ case "agents":
309
+ return path.join(root, "AGENTS.md");
310
+ case "copilot-instructions":
311
+ return path.join(root, ".github", "copilot-instructions.md");
312
+ }
313
+ throw new Error("MANCODE_V3_ADAPTER_TARGET_INVALID");
314
+ }
315
+ async function assertV3AdapterTargetSafe(projectRoot, target) {
316
+ const root = path.resolve(projectRoot);
317
+ await assertAdapterPathSafe(root, v3AdapterTargetPath(root, target));
318
+ }
319
+ function v3ModeEntryPath(projectRoot, platform, mode) {
320
+ const root = path.resolve(projectRoot);
321
+ switch (platform) {
322
+ case "claude-code":
323
+ return path.join(root, ".claude", "skills", mode, "SKILL.md");
324
+ case "codex":
325
+ case "zcode":
326
+ return path.join(root, ".agents", "skills", mode, "SKILL.md");
327
+ case "cursor":
328
+ return path.join(root, ".cursor", "commands", `${mode}.md`);
329
+ case "copilot":
330
+ return path.join(root, ".github", "prompts", `${mode}.prompt.md`);
331
+ }
332
+ }
333
+ async function installV3Adapter(projectRoot, platform) {
334
+ const root = path.resolve(projectRoot);
335
+ await assertV3AdapterInstallable(root, platform);
336
+ const content = renderV3Bootstrap(platform);
337
+ switch (platform) {
338
+ case "claude-code":
339
+ await writeManagedFile(
340
+ path.join(root, ".claude", "skills", "mancode-v3", "SKILL.md"),
341
+ renderClaudeSkill(content)
342
+ );
343
+ break;
344
+ case "cursor":
345
+ await writeManagedFile(
346
+ path.join(root, ".cursor", "rules", "mancode-v3.mdc"),
347
+ renderCursorRule(content)
348
+ );
349
+ break;
350
+ case "codex":
351
+ await replaceManagedV3Block(
352
+ path.join(root, "AGENTS.md"),
353
+ V3_CODEX_START_MARKER,
354
+ V3_CODEX_END_MARKER,
355
+ content,
356
+ [
357
+ [LEGACY_CODEX_START_MARKER, LEGACY_CODEX_END_MARKER],
358
+ [LEGACY_ZCODE_START_MARKER, LEGACY_ZCODE_END_MARKER]
359
+ ]
360
+ );
361
+ break;
362
+ case "copilot":
363
+ await replaceManagedV3Block(
364
+ path.join(root, ".github", "copilot-instructions.md"),
365
+ V3_COPILOT_START_MARKER,
366
+ V3_COPILOT_END_MARKER,
367
+ content,
368
+ [[LEGACY_CODEX_START_MARKER, LEGACY_CODEX_END_MARKER]]
369
+ );
370
+ break;
371
+ case "zcode":
372
+ await replaceManagedV3Block(
373
+ path.join(root, "AGENTS.md"),
374
+ V3_ZCODE_START_MARKER,
375
+ V3_ZCODE_END_MARKER,
376
+ content,
377
+ [
378
+ [LEGACY_CODEX_START_MARKER, LEGACY_CODEX_END_MARKER],
379
+ [LEGACY_ZCODE_START_MARKER, LEGACY_ZCODE_END_MARKER]
380
+ ]
381
+ );
382
+ break;
383
+ }
384
+ await retireLegacyPlatformFiles(root, platform);
385
+ for (const mode of V3_MODE_NAMES) {
386
+ await writeV3ModeEntry(
387
+ v3ModeEntryPath(root, platform, mode),
388
+ renderV3ModeEntry(mode, platform)
389
+ );
390
+ }
391
+ return inspectV3Adapter(root, platform);
392
+ }
393
+ async function assertV3AdapterInstallable(projectRoot, platform) {
394
+ const root = path.resolve(projectRoot);
395
+ await assertPlatformAdapterPathsSafe(root, platform);
396
+ await assertV3ModeEntriesWritable(root, platform);
397
+ await renderV3AdapterCandidate(root, platform);
398
+ const existing = /* @__PURE__ */ new Map();
399
+ for (const target of legacyAdapterTargetsForPlatform(platform, true)) {
400
+ existing.set(target, await readAdapterTarget(root, target));
401
+ }
402
+ planLegacyAdapterRetirement(existing);
403
+ }
404
+ async function inspectV3Adapter(projectRoot, platform) {
405
+ const root = path.resolve(projectRoot);
406
+ await assertPlatformAdapterPathsSafe(root, platform);
407
+ const target = targetFor(platform);
408
+ const bootstrapInstalled = await adapterTargetPresent(root, platform);
409
+ const modeEntriesInstalled = (await Promise.all(
410
+ V3_MODE_NAMES.map(
411
+ (mode) => v3ModeEntryPresent(v3ModeEntryPath(root, platform, mode))
412
+ )
413
+ )).every(Boolean);
414
+ const installed = bootstrapInstalled && modeEntriesInstalled;
415
+ return {
416
+ version: V3_ADAPTER_VERSION,
417
+ installed,
418
+ ready: installed,
419
+ target,
420
+ detail: installed ? "V3 bootstrap and original mode entries are present; session identity is explicit-required." : "V3 bootstrap or one of its original mode entries is not installed.",
421
+ capabilities: capabilitiesFor(platform)
422
+ };
423
+ }
424
+ async function inspectV3AdapterVersions(projectRoot) {
425
+ const platforms = [
426
+ "claude-code",
427
+ "codex",
428
+ "cursor",
429
+ "copilot",
430
+ "zcode"
431
+ ];
432
+ const entries = await Promise.all(
433
+ platforms.map(async (platform) => {
434
+ const status = await inspectV3Adapter(projectRoot, platform);
435
+ return [platform, status.ready ? status.version : "missing"];
436
+ })
437
+ );
438
+ return Object.fromEntries(entries);
439
+ }
440
+ async function removeV3Adapter(projectRoot, platform) {
441
+ const root = path.resolve(projectRoot);
442
+ await assertPlatformAdapterPathsSafe(root, platform);
443
+ let preserveSharedModeEntries = false;
444
+ switch (platform) {
445
+ case "claude-code":
446
+ await removeManagedFile(
447
+ path.join(root, ".claude", "skills", "mancode-v3", "SKILL.md")
448
+ );
449
+ break;
450
+ case "cursor":
451
+ await removeManagedFile(
452
+ path.join(root, ".cursor", "rules", "mancode-v3.mdc")
453
+ );
454
+ break;
455
+ case "codex":
456
+ await removeManagedV3Block(
457
+ path.join(root, "AGENTS.md"),
458
+ V3_CODEX_START_MARKER,
459
+ V3_CODEX_END_MARKER
460
+ );
461
+ preserveSharedModeEntries = await managedBlockPresent(
462
+ path.join(root, "AGENTS.md"),
463
+ V3_ZCODE_START_MARKER,
464
+ V3_ZCODE_END_MARKER
465
+ );
466
+ break;
467
+ case "copilot":
468
+ await removeManagedV3Block(
469
+ path.join(root, ".github", "copilot-instructions.md"),
470
+ V3_COPILOT_START_MARKER,
471
+ V3_COPILOT_END_MARKER
472
+ );
473
+ break;
474
+ case "zcode":
475
+ await removeManagedV3Block(
476
+ path.join(root, "AGENTS.md"),
477
+ V3_ZCODE_START_MARKER,
478
+ V3_ZCODE_END_MARKER
479
+ );
480
+ preserveSharedModeEntries = await managedBlockPresent(
481
+ path.join(root, "AGENTS.md"),
482
+ V3_CODEX_START_MARKER,
483
+ V3_CODEX_END_MARKER
484
+ );
485
+ break;
486
+ }
487
+ if (!preserveSharedModeEntries) {
488
+ for (const mode of V3_MODE_NAMES) {
489
+ await removeV3ModeEntry(v3ModeEntryPath(root, platform, mode));
490
+ }
491
+ }
492
+ await removeRetiredLegacyPlatformFiles(root, platform);
493
+ }
494
+ function renderV3Bootstrap(platform) {
495
+ const platformLabel = platformLabelFor(platform);
496
+ const sessionCreationGuidance = platform === "codex" || platform === "zcode" ? "When status has no `currentSession`, first reuse any explicit session ID already returned in this conversation. Only when neither exists, create one once with `mancode context session new --client codex` in Codex or `mancode context session new --client zcode` in ZCode." : `When status has no \`currentSession\`, first reuse any explicit session ID already returned in this conversation. Only when neither exists, create one once with \`mancode context session new --client ${platform}\`.`;
497
+ const spikePlatform = platform === "codex" || platform === "zcode" ? "the active Codex or ZCode host" : platform;
498
+ const modeEntry = capabilitiesFor(platform).nativeModeEntry ? "Use the platform mode entry only as a shortcut; resolve a Context Pack first." : "This platform has no native V3 mode entry; use the CLI commands explicitly.";
499
+ return [
500
+ "# mancode V3 bootstrap",
501
+ "",
502
+ V3_ADAPTER_MANAGED_MARKER,
503
+ "",
504
+ `- Platform: ${platformLabel}. This file is a non-authoritative bootstrap.`,
505
+ "- Locate the project root before running mancode commands.",
506
+ "- First run `mancode status --json` from the project root.",
507
+ "- An explicitly invoked original `man`, `manba`, `manteam`, `manps`, or `mansolo` entry supplies its authorized action. Its mode-specific steps override conflicting generic no-task or mutation guidance below.",
508
+ "- In particular, `manps` may run local health scans without an actor, session, or TaskRef. `mansolo` needs them only for an explicit governed handoff.",
509
+ '- If status has no `localIdentity.actorId`, ask for a display name and run `mancode team identity create --name "<display name>"` before creating a session.',
510
+ "- If status reports `currentSession`, reuse it. `currentTask: null` and `MANCODE_TASK_REQUIRED` do not make a session stale.",
511
+ `- ${sessionCreationGuidance} Pass its returned \`sessionId\` as \`--session <id>\` to later commands; an \`export\` inside one command tool does not persist to later command tools.`,
512
+ '- Outside an invoked original mode entry, if no current task and no task is explicitly supplied, report "no task bound" and stop. Do not probe workflow subcommands to work around `MANCODE_TASK_REQUIRED`.',
513
+ "- Bootstrap discovery is read-only: before the operator explicitly requests task work, do not run `mancode init`, `mancode migrate`, `mancode workflow`, or inspect mancode installed package/source.",
514
+ "- With an existing or supplied task, read its Context Pack with `mancode context show --purpose orient --session <id>`; for anonymous diagnosis, include an explicit `--task <namespace:id>`.",
515
+ "- After an operator explicitly requests task work, perform mutations only through `mancode workflow`, `mancode team`, and `mancode context` commands with their required revision and session arguments.",
516
+ "- For a mode entry, request the matching Context Pack purpose: `plan`, `implement`, `review`, `verify`, or `handoff`.",
517
+ "- Do not persist task, mode, or session state in this adapter file or any legacy state file.",
518
+ `- ${modeEntry}`,
519
+ `- No approved session or prompt hook is assumed. After a real-host spike is recorded for ${spikePlatform}, a verified host may provide MANCODE_HOST_SESSION_KEY; otherwise mutations require an explicit \`--session\`.`
520
+ ].join("\n");
521
+ }
522
+ function renderV3ModeEntry(mode, platform) {
523
+ const definition = V3_MODE_DEFINITIONS[mode];
524
+ const sessionCreationGuidance = platform === "codex" || platform === "zcode" ? "If status has no current session, reuse an explicit session ID already retained in this conversation. Only if neither exists, run `mancode context session new --client codex` in Codex or `mancode context session new --client zcode` in ZCode exactly once, then retain the returned session ID." : `If status has no current session, reuse an explicit session ID already retained in this conversation. Only if neither exists, run \`mancode context session new --client ${platform}\` exactly once and retain the returned session ID.`;
525
+ const authoritySteps = mode === "manps" ? [
526
+ "1. Run `mancode status --json` from the project root and require active V3 authority.",
527
+ "2. Run the health action below directly. A local scan needs no TaskRef, actor identity, or explicit session.",
528
+ "3. Never read or write legacy mode authority before or after the scan."
529
+ ] : mode === "mansolo" ? [
530
+ "1. Run `mancode status --json` from the project root. Never read or write legacy mode authority.",
531
+ "2. If no governed task is being handed off, continue with focused solo work without creating a persistent mode, actor, session, or TaskRef.",
532
+ "3. For an explicit governed handoff, ensure `localIdentity.actorId`, reuse or create the current session, and bind the existing TaskRef before running the handoff action below.",
533
+ "4. For that governed task only, read `mancode context show --purpose implement --session <id>` using the bound or explicit TaskRef."
534
+ ] : [
535
+ "1. Run `mancode status --json` from the project root. Never read or write the legacy authority file.",
536
+ '2. If `localIdentity.actorId` is absent, ask for a display name and run `mancode team identity create --name "<display name>"`.',
537
+ `3. Reuse \`currentSession.sessionId\` when present. ${sessionCreationGuidance}`,
538
+ "4. Reuse the current TaskRef. To bind a supplied existing task, run `mancode context resume <namespace:ULID> --session <id>`.",
539
+ `5. For an existing task, read only the needed Context Pack with \`mancode context show --purpose ${definition.contextPurpose} --session <id>\`; include \`--task <namespace:ULID>\` when it is not yet bound. For a new task, create it through the mode action first, then read the returned TaskRef's Context Pack.`
540
+ ];
541
+ const frontmatter = [
542
+ "---",
543
+ ...platform === "claude-code" || platform === "codex" || platform === "zcode" ? [`name: ${mode}`] : [],
544
+ ...platform === "copilot" ? ["agent: 'agent'"] : [],
545
+ `description: ${JSON.stringify(definition.description)}`,
546
+ "---"
547
+ ];
548
+ const mutationGuidance = mode === "manps" ? "The local scan and an explicitly requested remediation do not require a TaskRef, workflow revision, actor, or session. Never turn their report files into workflow authority." : mode === "mansolo" ? "Only an explicit governed handoff mutation requires the bound TaskRef, explicit session, and latest expected revision. Ordinary focused solo work persists no mode state." : "For every mutation, use the TaskRef, explicit session, and latest expected revision reported by V3. Do not emulate the legacy `--step` protocol or persist mode state in an adapter file.";
549
+ return [
550
+ ...frontmatter,
551
+ "",
552
+ V3_MODE_ENTRY_MANAGED_MARKER,
553
+ "",
554
+ `# mancode V3 mode: ${mode}`,
555
+ "",
556
+ `Purpose: ${definition.purpose}.`,
557
+ "",
558
+ "## Enter through V3 authority",
559
+ "",
560
+ ...authoritySteps,
561
+ "",
562
+ "## Mode action",
563
+ "",
564
+ ...definition.actions,
565
+ "",
566
+ mutationGuidance,
567
+ ""
568
+ ].join("\n");
569
+ }
570
+ var V3_MODE_DEFINITIONS = {
571
+ man: {
572
+ description: "Plan and execute governed work through mancode V3.",
573
+ purpose: "clarify, plan, implement, verify, and review governed work",
574
+ contextPurpose: "plan",
575
+ actions: [
576
+ '- For a new task, run `mancode workflow create man "<task>" --session <id>`.',
577
+ "- Finalize requirements with `mancode workflow requirements <namespace:ULID> finalize --file <requirements.json> --expected-revision <n> --session <id>`.",
578
+ "- Revise or confirm the plan with `mancode workflow plan <namespace:ULID> revise|confirm --expected-revision <n> ... --session <id>`.",
579
+ "- Apply verification and review ledgers with their V3 `apply --file` commands, then use `mancode workflow complete <namespace:ULID> --expected-revision <n> --session <id>`."
580
+ ]
581
+ },
582
+ manba: {
583
+ description: "Diagnose and verify a bug through mancode V3.",
584
+ purpose: "reproduce, diagnose, fix, and verify a regression",
585
+ contextPurpose: "implement",
586
+ actions: [
587
+ '- For a new diagnostic task, run `mancode workflow create manba "<task>" --session <id>`.',
588
+ "- When this is a child investigation, add `--parent <namespace:ULID>`; report and merge the typed outcome through the V3 child commands.",
589
+ "- Change lifecycle only with `mancode workflow update <namespace:ULID> --status <status> --expected-revision <n> --session <id>` and finish with `workflow complete` plus the typed `--outcome`."
590
+ ]
591
+ },
592
+ manteam: {
593
+ description: "Coordinate shared governed work through mancode V3.",
594
+ purpose: "plan and execute work with explicit team ownership and handoff",
595
+ contextPurpose: "plan",
596
+ actions: [
597
+ "- Confirm team membership with `mancode team status`; join invited participants before assigning shared work.",
598
+ '- For a new shared task, run `mancode workflow create manteam "<task>" --visibility shared --coordination team --confirm-shared --session <id>`.',
599
+ "- Use claims, checkpoints, sync, and handoffs through `mancode team`; never infer ownership from an adapter prompt.",
600
+ "- Use the same V3 requirements, plan, verification, review, and completion commands as `man`, adding `--sync` whenever the active transport requires it."
601
+ ]
602
+ },
603
+ manps: {
604
+ description: "Inspect project health through the existing manps command.",
605
+ purpose: "scan project health and review bounded remediation",
606
+ contextPurpose: "review",
607
+ actions: [
608
+ "- Run `mancode manps [area]` through the same public command entry (`all`, `deps`, `security`, `dead-code`, or `config`).",
609
+ "- Add `--remediate` only when the operator explicitly requests an interactive remediation review."
610
+ ]
611
+ },
612
+ mansolo: {
613
+ description: "Return to focused solo execution under V3 authority.",
614
+ purpose: "perform a small focused task or accept an explicit solo handoff",
615
+ contextPurpose: "implement",
616
+ actions: [
617
+ "- Do not create or persist a legacy solo mode. Ordinary focused work needs no TaskRef; if the operator expects a governed task, use its bound TaskRef or report that none is bound.",
618
+ "- For a governed-to-solo transition, use `mancode workflow handoff <namespace:ULID> --to solo --expected-revision <n> --session <id>`."
619
+ ]
620
+ }
621
+ };
622
+ async function renderV3AdapterCandidate(root, platform) {
623
+ switch (platform) {
624
+ case "claude-code": {
625
+ const existing = await readAdapterTarget(root, "claude-skill");
626
+ return managedFilePlan(
627
+ "claude-skill",
628
+ existing,
629
+ renderClaudeSkill(renderV3Bootstrap(platform))
630
+ ).targetContent;
631
+ }
632
+ case "cursor": {
633
+ const existing = await readAdapterTarget(root, "cursor-rule");
634
+ return managedFilePlan(
635
+ "cursor-rule",
636
+ existing,
637
+ renderCursorRule(renderV3Bootstrap(platform))
638
+ ).targetContent;
639
+ }
640
+ case "codex": {
641
+ const existing = await readAdapterTarget(root, "agents") ?? "";
642
+ return replaceManagedV3BlockText(
643
+ removeLegacyAgentsBlocks(existing),
644
+ V3_CODEX_START_MARKER,
645
+ V3_CODEX_END_MARKER,
646
+ renderV3Bootstrap(platform)
647
+ );
648
+ }
649
+ case "copilot": {
650
+ const existing = await readAdapterTarget(root, "copilot-instructions") ?? "";
651
+ return replaceManagedV3BlockText(
652
+ removeManagedBlock(existing),
653
+ V3_COPILOT_START_MARKER,
654
+ V3_COPILOT_END_MARKER,
655
+ renderV3Bootstrap(platform)
656
+ );
657
+ }
658
+ case "zcode": {
659
+ const existing = await readAdapterTarget(root, "agents") ?? "";
660
+ return replaceManagedV3BlockText(
661
+ removeLegacyAgentsBlocks(existing),
662
+ V3_ZCODE_START_MARKER,
663
+ V3_ZCODE_END_MARKER,
664
+ renderV3Bootstrap(platform)
665
+ );
666
+ }
667
+ }
668
+ }
669
+ function renderClaudeSkill(content) {
670
+ return [
671
+ "---",
672
+ "name: mancode-v3",
673
+ 'description: "Internal bootstrap for original mancode mode entries under V3 authority."',
674
+ "user-invocable: false",
675
+ "---",
676
+ "",
677
+ content,
678
+ ""
679
+ ].join("\n");
680
+ }
681
+ function renderCursorRule(content) {
682
+ return [
683
+ "---",
684
+ 'description: "Stable bootstrap for mancode V3 context and workflow commands."',
685
+ "alwaysApply: true",
686
+ 'globs: "**/*"',
687
+ "---",
688
+ "",
689
+ content,
690
+ ""
691
+ ].join("\n");
692
+ }
693
+ async function adapterTargetPresent(root, platform) {
694
+ switch (platform) {
695
+ case "claude-code":
696
+ return managedFilePresent(
697
+ path.join(root, ".claude", "skills", "mancode-v3", "SKILL.md")
698
+ );
699
+ case "cursor":
700
+ return managedFilePresent(
701
+ path.join(root, ".cursor", "rules", "mancode-v3.mdc")
702
+ );
703
+ case "codex":
704
+ return managedBlockPresent(
705
+ path.join(root, "AGENTS.md"),
706
+ V3_CODEX_START_MARKER,
707
+ V3_CODEX_END_MARKER
708
+ );
709
+ case "copilot":
710
+ return managedBlockPresent(
711
+ path.join(root, ".github", "copilot-instructions.md"),
712
+ V3_COPILOT_START_MARKER,
713
+ V3_COPILOT_END_MARKER
714
+ );
715
+ case "zcode":
716
+ return managedBlockPresent(
717
+ path.join(root, "AGENTS.md"),
718
+ V3_ZCODE_START_MARKER,
719
+ V3_ZCODE_END_MARKER
720
+ );
721
+ }
722
+ }
723
+ async function writeManagedFile(filePath, content) {
724
+ const existing = await readTextIfExists(filePath);
725
+ if (existing !== null && !existing.includes(V3_ADAPTER_MANAGED_MARKER)) {
726
+ throw new Error("MANCODE_V3_ADAPTER_TARGET_USER_AUTHORED");
727
+ }
728
+ await mkdir(path.dirname(filePath), { recursive: true });
729
+ await atomicWrite(filePath, content);
730
+ }
731
+ async function writeV3ModeEntry(filePath, content) {
732
+ const existing = await readTextIfExists(filePath);
733
+ const managed = existing === null || existing.includes(V3_MODE_ENTRY_MANAGED_MARKER) || LEGACY_MODE_ENTRY_MANAGED_MARKERS.some(
734
+ (marker) => existing.includes(marker)
735
+ );
736
+ if (!managed) {
737
+ throw new Error("MANCODE_V3_MODE_ENTRY_USER_AUTHORED");
738
+ }
739
+ await mkdir(path.dirname(filePath), { recursive: true });
740
+ await atomicWrite(filePath, content);
741
+ }
742
+ async function assertV3ModeEntriesWritable(root, platform) {
743
+ for (const mode of V3_MODE_NAMES) {
744
+ const existing = await readTextIfExists(
745
+ v3ModeEntryPath(root, platform, mode)
746
+ );
747
+ if (existing !== null && !existing.includes(V3_MODE_ENTRY_MANAGED_MARKER) && !LEGACY_MODE_ENTRY_MANAGED_MARKERS.some(
748
+ (marker) => existing.includes(marker)
749
+ )) {
750
+ throw new Error("MANCODE_V3_MODE_ENTRY_USER_AUTHORED");
751
+ }
752
+ }
753
+ }
754
+ async function removeV3ModeEntry(filePath) {
755
+ const existing = await readTextIfExists(filePath);
756
+ if (existing?.includes(V3_MODE_ENTRY_MANAGED_MARKER)) {
757
+ await rm(filePath, { force: true });
758
+ await removeDirectoryIfEmpty(path.dirname(filePath));
759
+ }
760
+ }
761
+ async function v3ModeEntryPresent(filePath) {
762
+ const existing = await readTextIfExists(filePath);
763
+ return existing?.includes(V3_MODE_ENTRY_MANAGED_MARKER) ?? false;
764
+ }
765
+ async function removeDirectoryIfEmpty(directory) {
766
+ try {
767
+ await rmdir(directory);
768
+ } catch (error) {
769
+ if (isNodeError(error) && (error.code === "ENOENT" || error.code === "ENOTEMPTY" || error.code === "EEXIST")) {
770
+ return;
771
+ }
772
+ throw error;
773
+ }
774
+ }
775
+ async function removeManagedFile(filePath) {
776
+ const existing = await readTextIfExists(filePath);
777
+ if (existing?.includes(V3_ADAPTER_MANAGED_MARKER)) {
778
+ await rm(filePath, { force: true });
779
+ }
780
+ }
781
+ async function replaceManagedV3Block(filePath, startMarker, endMarker, content, legacyMarkers) {
782
+ const current = await readTextIfExists(filePath) ?? "";
783
+ const existing = (legacyMarkers ?? []).reduce(
784
+ (contentWithoutLegacy, [legacyStart, legacyEnd]) => removeManagedBlock(contentWithoutLegacy, legacyStart, legacyEnd),
785
+ current
786
+ );
787
+ const block = [startMarker, content, endMarker].join("\n");
788
+ await mkdir(path.dirname(filePath), { recursive: true });
789
+ await atomicWrite(
790
+ filePath,
791
+ replaceManagedBlock(existing, block, startMarker, endMarker)
792
+ );
793
+ }
794
+ function removeLegacyAgentsBlocks(existing) {
795
+ return removeManagedBlock(
796
+ removeManagedBlock(
797
+ existing,
798
+ LEGACY_CODEX_START_MARKER,
799
+ LEGACY_CODEX_END_MARKER
800
+ ),
801
+ LEGACY_ZCODE_START_MARKER,
802
+ LEGACY_ZCODE_END_MARKER
803
+ );
804
+ }
805
+ function replaceManagedV3BlockText(existing, startMarker, endMarker, content) {
806
+ return replaceManagedBlock(
807
+ existing,
808
+ [startMarker, content, endMarker].join("\n"),
809
+ startMarker,
810
+ endMarker
811
+ );
812
+ }
813
+ function managedFilePlan(target, beforeContent, targetContent) {
814
+ if (beforeContent !== null && !beforeContent.includes(V3_ADAPTER_MANAGED_MARKER)) {
815
+ throw new Error("MANCODE_V3_ADAPTER_TARGET_USER_AUTHORED");
816
+ }
817
+ return { target, beforeContent, targetContent };
818
+ }
819
+ function managedModeEntryPlan(target, beforeContent, targetContent) {
820
+ if (beforeContent !== null && !beforeContent.includes(V3_MODE_ENTRY_MANAGED_MARKER) && !LEGACY_MODE_ENTRY_MANAGED_MARKERS.some(
821
+ (marker) => beforeContent.includes(marker)
822
+ )) {
823
+ throw new Error("MANCODE_V3_MODE_ENTRY_USER_AUTHORED");
824
+ }
825
+ return { target, beforeContent, targetContent };
826
+ }
827
+ function planLegacyAdapterRetirement(existing) {
828
+ const plans = [];
829
+ const settings = existing.get("claude-settings") ?? null;
830
+ if (settings !== null) {
831
+ const cleaned = removeLegacyClaudeSettings(settings);
832
+ if (cleaned !== settings) {
833
+ plans.push({
834
+ target: "claude-settings",
835
+ beforeContent: settings,
836
+ targetContent: cleaned
837
+ });
838
+ }
839
+ }
840
+ const legacySolo = existing.get("claude-legacy-solo") ?? null;
841
+ if (legacySolo?.includes(
842
+ "<!-- Managed by mancode:claude-skill. Do not edit this marker. -->"
843
+ )) {
844
+ plans.push({
845
+ target: "claude-legacy-solo",
846
+ beforeContent: legacySolo,
847
+ targetContent: renderRetiredClaudeSoloEntry()
848
+ });
849
+ }
850
+ for (const target of V3_LEGACY_ADAPTER_FILE_TARGETS) {
851
+ if (!target.startsWith("cursor-legacy-")) continue;
852
+ const beforeContent = existing.get(target) ?? null;
853
+ if (beforeContent?.includes(
854
+ "<!-- Managed by mancode:cursor-rule. Do not edit this marker. -->"
855
+ )) {
856
+ plans.push({
857
+ target,
858
+ beforeContent,
859
+ targetContent: renderRetiredCursorRule(target)
860
+ });
861
+ }
862
+ }
863
+ for (const target of V3_LEGACY_ADAPTER_FILE_TARGETS) {
864
+ const alias = parseLegacyModeAliasTarget(target);
865
+ if (alias === null) continue;
866
+ const beforeContent = existing.get(target) ?? null;
867
+ if (beforeContent !== null && isGeneratedLegacyModeAlias(beforeContent, alias.alias)) {
868
+ plans.push({
869
+ target,
870
+ beforeContent,
871
+ targetContent: renderRetiredModeAlias(target)
872
+ });
873
+ }
874
+ }
875
+ return plans;
876
+ }
877
+ async function retireLegacyPlatformFiles(root, platform) {
878
+ const targets = legacyAdapterTargetsForPlatform(platform, true);
879
+ if (targets.length === 0) return;
880
+ const existing = /* @__PURE__ */ new Map();
881
+ for (const target of targets) {
882
+ existing.set(target, await readAdapterTarget(root, target));
883
+ }
884
+ for (const plan of planLegacyAdapterRetirement(existing)) {
885
+ await applyV3AdapterFilePlan(root, plan);
886
+ }
887
+ }
888
+ async function removeRetiredLegacyPlatformFiles(root, platform) {
889
+ const targets = legacyAdapterTargetsForPlatform(platform, false);
890
+ for (const target of targets) {
891
+ const filePath = v3AdapterTargetPath(root, target);
892
+ const content = await readTextIfExists(filePath);
893
+ const retiredModeEntry = target === "claude-legacy-solo" || parseLegacyModeAliasTarget(target) !== null;
894
+ const retired = content?.includes(
895
+ retiredModeEntry ? V3_MODE_ENTRY_MANAGED_MARKER : V3_ADAPTER_MANAGED_MARKER
896
+ );
897
+ if (retired) {
898
+ await rm(filePath, { force: true });
899
+ if (target === "claude-legacy-solo" || target.startsWith("claude-alias-") || target.startsWith("agents-alias-")) {
900
+ await removeDirectoryIfEmpty(path.dirname(filePath));
901
+ }
902
+ }
903
+ }
904
+ }
905
+ function legacyAdapterTargetsForPlatform(platform, includeSettings) {
906
+ if (platform === "claude-code") {
907
+ return [
908
+ ...includeSettings ? ["claude-settings"] : [],
909
+ "claude-legacy-solo",
910
+ ...V3_LEGACY_ADAPTER_FILE_TARGETS.filter(
911
+ (target) => target.startsWith("claude-alias-")
912
+ )
913
+ ];
914
+ }
915
+ if (platform === "cursor") {
916
+ return V3_LEGACY_ADAPTER_FILE_TARGETS.filter(
917
+ (target) => /^(cursor-legacy|cursor-alias)-/u.test(target)
918
+ );
919
+ }
920
+ if (platform === "codex" || platform === "zcode") {
921
+ return V3_LEGACY_ADAPTER_FILE_TARGETS.filter(
922
+ (target) => target.startsWith("agents-alias-")
923
+ );
924
+ }
925
+ return V3_LEGACY_ADAPTER_FILE_TARGETS.filter(
926
+ (target) => target.startsWith("copilot-alias-")
927
+ );
928
+ }
929
+ function removeLegacyClaudeSettings(content) {
930
+ const hasKnownLegacyReference = LEGACY_CLAUDE_SETTINGS_RAW_HINTS.some(
931
+ (value) => content.includes(value)
932
+ );
933
+ if (!hasKnownLegacyReference) return content;
934
+ let parsed;
935
+ try {
936
+ parsed = JSON.parse(content);
937
+ } catch {
938
+ throw new Error("MANCODE_V3_CLAUDE_SETTINGS_INVALID");
939
+ }
940
+ if (!isRecord(parsed)) {
941
+ throw new Error("MANCODE_V3_CLAUDE_SETTINGS_INVALID");
942
+ }
943
+ const hasGeneratedHook = containsGeneratedClaudeHookValue(parsed.hooks);
944
+ const hasGeneratedSkill = isRecord(parsed.skills) && Object.values(parsed.skills).some(
945
+ (value) => typeof value === "string" && LEGACY_CLAUDE_SKILL_PATHS.has(value)
946
+ );
947
+ if (!hasGeneratedHook && !hasGeneratedSkill) return content;
948
+ const settings = { ...parsed };
949
+ if ("hooks" in settings) {
950
+ const hooks = removeGeneratedClaudeHookValue(settings.hooks);
951
+ if (hooks === void 0) settings.hooks = void 0;
952
+ else settings.hooks = hooks;
953
+ }
954
+ if (isRecord(settings.skills)) {
955
+ const skills = Object.fromEntries(
956
+ Object.entries(settings.skills).filter(
957
+ ([, value]) => typeof value !== "string" || !LEGACY_CLAUDE_SKILL_PATHS.has(value)
958
+ )
959
+ );
960
+ if (Object.keys(skills).length === 0) settings.skills = void 0;
961
+ else settings.skills = skills;
962
+ }
963
+ return `${JSON.stringify(settings, null, 2)}
964
+ `;
965
+ }
966
+ function containsGeneratedClaudeHookValue(value) {
967
+ if (Array.isArray(value)) {
968
+ return value.some(containsGeneratedClaudeHookValue);
969
+ }
970
+ if (!isRecord(value)) return false;
971
+ if (typeof value.command === "string" && isGeneratedClaudeHookCommand(value.command)) {
972
+ return true;
973
+ }
974
+ return Object.values(value).some(containsGeneratedClaudeHookValue);
975
+ }
976
+ function removeGeneratedClaudeHookValue(value) {
977
+ if (Array.isArray(value)) {
978
+ const cleaned2 = value.map(removeGeneratedClaudeHookValue).filter((entry) => entry !== void 0);
979
+ return cleaned2.length > 0 ? cleaned2 : void 0;
980
+ }
981
+ if (!isRecord(value)) return value;
982
+ if (typeof value.command === "string" && isGeneratedClaudeHookCommand(value.command)) {
983
+ return void 0;
984
+ }
985
+ if (Array.isArray(value.hooks)) {
986
+ const hooks = value.hooks.map(removeGeneratedClaudeHookValue).filter((entry) => entry !== void 0);
987
+ return hooks.length > 0 ? { ...value, hooks } : void 0;
988
+ }
989
+ const cleaned = Object.entries(value).flatMap(([key, entry]) => {
990
+ const next = removeGeneratedClaudeHookValue(entry);
991
+ return next === void 0 ? [] : [[key, next]];
992
+ });
993
+ return cleaned.length > 0 ? Object.fromEntries(cleaned) : void 0;
994
+ }
995
+ function isGeneratedClaudeHookCommand(command) {
996
+ return GENERATED_CLAUDE_HOOK_COMMANDS.has(command.trim());
997
+ }
998
+ function renderRetiredClaudeSoloEntry() {
999
+ return [
1000
+ "---",
1001
+ "name: solo",
1002
+ 'description: "Compatibility alias for the mancode V3 mansolo entry."',
1003
+ "user-invocable: false",
1004
+ "---",
1005
+ "",
1006
+ V3_MODE_ENTRY_MANAGED_MARKER,
1007
+ "",
1008
+ "# mancode V3 mode compatibility alias",
1009
+ "",
1010
+ "Use the `mansolo` mode entry. Resolve V3 status, identity, session, TaskRef, and Context Pack there; do not use legacy mode persistence.",
1011
+ ""
1012
+ ].join("\n");
1013
+ }
1014
+ function renderRetiredCursorRule(target) {
1015
+ const legacyName = target.replace("cursor-legacy-", "");
1016
+ const mode = legacyName === "solo" ? "mansolo" : legacyName === "mamba" ? "manba" : legacyName === "man8" ? "man" : legacyName;
1017
+ const guidance = mode === "context" || mode === "practice" ? "Run `mancode status --json`, then use the matching V3 mode command." : `Use the \`/${mode}\` V3 mode command.`;
1018
+ return [
1019
+ "---",
1020
+ 'description: "Retired legacy mancode rule; V3 mode entries are authoritative."',
1021
+ "alwaysApply: false",
1022
+ 'globs: "__mancode_v3_retired_rule__"',
1023
+ "---",
1024
+ "",
1025
+ V3_ADAPTER_MANAGED_MARKER,
1026
+ "",
1027
+ "# mancode V3 compatibility redirect",
1028
+ "",
1029
+ guidance,
1030
+ "Do not use legacy mode persistence or legacy workflow paths.",
1031
+ ""
1032
+ ].join("\n");
1033
+ }
1034
+ function renderRetiredModeAlias(target) {
1035
+ const parsed = parseLegacyModeAliasTarget(target);
1036
+ if (parsed === null) {
1037
+ throw new Error("MANCODE_V3_ADAPTER_TARGET_INVALID");
1038
+ }
1039
+ const publicMode = parsed.alias === "mamba" ? "manba" : "man";
1040
+ const frontmatter = [
1041
+ "---",
1042
+ ...parsed.family === "claude" || parsed.family === "agents" ? [`name: ${parsed.alias}`] : [],
1043
+ ...parsed.family === "copilot" ? ["agent: 'agent'"] : [],
1044
+ ...parsed.family === "claude" ? ["user-invocable: false"] : [],
1045
+ `description: ${JSON.stringify(`Compatibility alias for the mancode V3 ${publicMode} entry.`)}`,
1046
+ "---"
1047
+ ];
1048
+ return [
1049
+ ...frontmatter,
1050
+ "",
1051
+ V3_MODE_ENTRY_MANAGED_MARKER,
1052
+ "",
1053
+ "# mancode V3 mode compatibility alias",
1054
+ "",
1055
+ `The legacy name \`${parsed.alias}\` maps to the public V3 mode \`${publicMode}\`. Use that original V3 mode entry instead.`,
1056
+ "",
1057
+ "Resolve status, identity, session, TaskRef, and Context Pack through V3 authority there. Do not use legacy mode persistence or the legacy workflow protocol.",
1058
+ ""
1059
+ ].join("\n");
1060
+ }
1061
+ function isGeneratedLegacyModeAlias(content, alias) {
1062
+ if (content.includes(V3_MODE_ENTRY_MANAGED_MARKER) || LEGACY_MODE_ENTRY_MANAGED_MARKERS.some((marker) => content.includes(marker))) {
1063
+ return true;
1064
+ }
1065
+ if (alias !== "man8") return false;
1066
+ return content.includes("# mancode \xB7 /man8 (4 AM Warmup)") || content.includes("# mancode man8 \u2014 Investigate and Plan") && content.includes("## Mode Persistence");
1067
+ }
1068
+ function legacyAdapterRelativePath(target) {
1069
+ if (target === "claude-settings")
1070
+ return path.join(".claude", "settings.json");
1071
+ if (target === "claude-legacy-solo") {
1072
+ return path.join(".claude", "skills", "solo", "SKILL.md");
1073
+ }
1074
+ if (target.startsWith("cursor-legacy-")) {
1075
+ const name = target.slice("cursor-legacy-".length);
1076
+ return path.join(".cursor", "rules", `mancode-${name}.mdc`);
1077
+ }
1078
+ const alias = parseLegacyModeAliasTarget(target);
1079
+ if (alias === null) return null;
1080
+ switch (alias.family) {
1081
+ case "claude":
1082
+ return path.join(".claude", "skills", alias.alias, "SKILL.md");
1083
+ case "agents":
1084
+ return path.join(".agents", "skills", alias.alias, "SKILL.md");
1085
+ case "cursor":
1086
+ return path.join(".cursor", "commands", `${alias.alias}.md`);
1087
+ case "copilot":
1088
+ return path.join(".github", "prompts", `${alias.alias}.prompt.md`);
1089
+ }
1090
+ }
1091
+ function parseLegacyModeAliasTarget(target) {
1092
+ const match = /^(claude|agents|cursor|copilot)-alias-(mamba|man8)$/u.exec(
1093
+ target
1094
+ );
1095
+ if (!match) return null;
1096
+ return {
1097
+ family: match[1],
1098
+ alias: match[2]
1099
+ };
1100
+ }
1101
+ function renderModeEntryForFileTarget(target) {
1102
+ const parsed = parseModeEntryFileTarget(target);
1103
+ if (parsed === null) {
1104
+ throw new Error("MANCODE_V3_ADAPTER_TARGET_INVALID");
1105
+ }
1106
+ return renderV3ModeEntry(parsed.mode, parsed.platform);
1107
+ }
1108
+ function parseModeEntryFileTarget(target) {
1109
+ const match = /^(claude|agents|cursor|copilot)-mode-(manba|man|manteam|manps|mansolo)$/u.exec(
1110
+ target
1111
+ );
1112
+ if (!match) return null;
1113
+ const family = match[1];
1114
+ const mode = match[2];
1115
+ const platform = family === "claude" ? "claude-code" : family === "agents" ? "codex" : family === "cursor" ? "cursor" : "copilot";
1116
+ return { platform, mode };
1117
+ }
1118
+ function isRecord(value) {
1119
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1120
+ }
1121
+ async function readAdapterTarget(root, target) {
1122
+ const filePath = v3AdapterTargetPath(root, target);
1123
+ await assertAdapterPathSafe(root, filePath);
1124
+ try {
1125
+ const entry = await lstat(filePath);
1126
+ if (!entry.isFile() || entry.isSymbolicLink()) {
1127
+ throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
1128
+ }
1129
+ } catch (error) {
1130
+ if (isNodeError(error) && error.code === "ENOENT") return null;
1131
+ throw error;
1132
+ }
1133
+ return readFile(filePath, "utf8");
1134
+ }
1135
+ async function assertPlatformAdapterPathsSafe(root, platform) {
1136
+ const targets = /* @__PURE__ */ new Set([
1137
+ path.join(root, targetFor(platform)),
1138
+ ...V3_MODE_NAMES.map((mode) => v3ModeEntryPath(root, platform, mode)),
1139
+ ...legacyAdapterTargetsForPlatform(platform, true).map(
1140
+ (target) => v3AdapterTargetPath(root, target)
1141
+ )
1142
+ ]);
1143
+ for (const target of targets) {
1144
+ await assertAdapterPathSafe(root, target);
1145
+ }
1146
+ }
1147
+ async function assertAdapterPathSafe(root, target) {
1148
+ const relative = path.relative(root, target);
1149
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
1150
+ throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
1151
+ }
1152
+ const rootEntry = await lstat(root);
1153
+ if (!rootEntry.isDirectory() || rootEntry.isSymbolicLink()) {
1154
+ throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
1155
+ }
1156
+ const segments = relative.split(path.sep);
1157
+ let current = root;
1158
+ for (let index = 0; index < segments.length; index += 1) {
1159
+ current = path.join(current, segments[index] ?? "");
1160
+ try {
1161
+ const entry = await lstat(current);
1162
+ if (entry.isSymbolicLink() || index < segments.length - 1 && !entry.isDirectory()) {
1163
+ throw new Error("MANCODE_ARTIFACT_PATH_UNSAFE");
1164
+ }
1165
+ } catch (error) {
1166
+ if (isNodeError(error) && error.code === "ENOENT") return;
1167
+ throw error;
1168
+ }
1169
+ }
1170
+ }
1171
+ async function removeManagedV3Block(filePath, startMarker, endMarker) {
1172
+ const existing = await readTextIfExists(filePath);
1173
+ if (existing === null || !hasManagedBlock(existing, startMarker, endMarker)) {
1174
+ return;
1175
+ }
1176
+ const cleaned = removeManagedBlock(existing, startMarker, endMarker);
1177
+ if (cleaned.trim()) {
1178
+ await atomicWrite(filePath, `${cleaned.trimEnd()}
1179
+ `);
1180
+ } else {
1181
+ await rm(filePath, { force: true });
1182
+ }
1183
+ }
1184
+ async function managedFilePresent(filePath) {
1185
+ const content = await readTextIfExists(filePath);
1186
+ return content?.includes(V3_ADAPTER_MANAGED_MARKER) ?? false;
1187
+ }
1188
+ async function managedBlockPresent(filePath, startMarker, endMarker) {
1189
+ const content = await readTextIfExists(filePath);
1190
+ return content !== null && hasManagedBlock(content, startMarker, endMarker);
1191
+ }
1192
+ async function readTextIfExists(filePath) {
1193
+ for (let attempt = 1; attempt <= ADAPTER_READ_MAX_ATTEMPTS; attempt += 1) {
1194
+ try {
1195
+ return await readFile(filePath, "utf8");
1196
+ } catch (error) {
1197
+ if (isNodeError(error) && error.code === "ENOENT") return null;
1198
+ if (!isRetriableAdapterReadError(error) || attempt === ADAPTER_READ_MAX_ATTEMPTS) {
1199
+ throw error;
1200
+ }
1201
+ await delay(ADAPTER_READ_RETRY_DELAY_MS * attempt);
1202
+ }
1203
+ }
1204
+ throw new Error("MANCODE_V3_ADAPTER_READ_RETRY_EXHAUSTED");
1205
+ }
1206
+ async function atomicWrite(filePath, content) {
1207
+ const temporary = path.join(
1208
+ path.dirname(filePath),
1209
+ `.${path.basename(filePath)}.${process.pid}.${Date.now()}.tmp`
1210
+ );
1211
+ try {
1212
+ await writeFile(temporary, content, { encoding: "utf8", flag: "wx" });
1213
+ await rename(temporary, filePath);
1214
+ } finally {
1215
+ await rm(temporary, { force: true }).catch(() => void 0);
1216
+ }
1217
+ }
1218
+ function targetFor(platform) {
1219
+ switch (platform) {
1220
+ case "claude-code":
1221
+ return ".claude/skills/mancode-v3/SKILL.md";
1222
+ case "cursor":
1223
+ return ".cursor/rules/mancode-v3.mdc";
1224
+ case "codex":
1225
+ case "zcode":
1226
+ return "AGENTS.md";
1227
+ case "copilot":
1228
+ return ".github/copilot-instructions.md";
1229
+ }
1230
+ }
1231
+ function platformLabelFor(platform) {
1232
+ switch (platform) {
1233
+ case "claude-code":
1234
+ return "Claude Code";
1235
+ case "cursor":
1236
+ return "Cursor";
1237
+ case "codex":
1238
+ return "Codex or ZCode (shared AGENTS.md bootstrap)";
1239
+ case "copilot":
1240
+ return "GitHub Copilot";
1241
+ case "zcode":
1242
+ return "Codex or ZCode (shared AGENTS.md bootstrap)";
1243
+ }
1244
+ }
1245
+ function capabilitiesFor(_platform) {
1246
+ return {
1247
+ nativeModeEntry: true,
1248
+ sessionHook: false,
1249
+ promptHook: false,
1250
+ sessionIdentity: "explicit-required"
1251
+ };
1252
+ }
1253
+ function isNodeError(error) {
1254
+ return typeof error === "object" && error !== null && "code" in error;
1255
+ }
1256
+ function isRetriableAdapterReadError(error) {
1257
+ return isNodeError(error) && RETRIABLE_ADAPTER_READ_CODES.has(error.code ?? "");
1258
+ }
1259
+ async function delay(milliseconds) {
1260
+ await new Promise((resolve) => {
1261
+ setTimeout(resolve, milliseconds);
1262
+ });
1263
+ }
1264
+
1265
+ export {
1266
+ DEFAULT_MANCODE_START_MARKER,
1267
+ DEFAULT_MANCODE_END_MARKER,
1268
+ removeManagedBlock,
1269
+ hasManagedBlock,
1270
+ replaceManagedBlock,
1271
+ V3_ADAPTER_VERSION,
1272
+ V3_ADAPTER_MANAGED_MARKER,
1273
+ V3_MODE_ENTRY_MANAGED_MARKER,
1274
+ V3_MODE_NAMES,
1275
+ V3_ADAPTER_FILE_TARGETS,
1276
+ planV3AdapterFiles,
1277
+ applyV3AdapterFilePlan,
1278
+ stageV3Adapter,
1279
+ v3AdapterTargetPath,
1280
+ assertV3AdapterTargetSafe,
1281
+ v3ModeEntryPath,
1282
+ installV3Adapter,
1283
+ assertV3AdapterInstallable,
1284
+ inspectV3Adapter,
1285
+ inspectV3AdapterVersions,
1286
+ removeV3Adapter,
1287
+ renderV3Bootstrap,
1288
+ renderV3ModeEntry
1289
+ };
1290
+ //# sourceMappingURL=chunk-CFC3HBNA.js.map