thoth-agents 0.1.18 → 0.2.0

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.
Files changed (37) hide show
  1. package/README.md +55 -12
  2. package/dist/agents/prompt-dialects.d.ts +9 -0
  3. package/dist/{chunk-6K3ZXIMC.js → chunk-3NOVCFN7.js} +88 -29
  4. package/dist/chunk-4WYCZ5Z7.js +698 -0
  5. package/dist/{chunk-SOT5ZY53.js → chunk-WH3F3GWE.js} +1498 -350
  6. package/dist/cli/claude-code-install.d.ts +53 -0
  7. package/dist/cli/claude-code-paths.d.ts +31 -0
  8. package/dist/cli/codex-install.d.ts +1 -5
  9. package/dist/cli/commands.d.ts +1 -1
  10. package/dist/cli/index.js +85 -27
  11. package/dist/cli/managed-state-io.d.ts +16 -0
  12. package/dist/cli/operations/claude-code.d.ts +21 -0
  13. package/dist/cli/tui/index.js +87 -9
  14. package/dist/cli/tui/operations.d.ts +2 -0
  15. package/dist/cli/types.d.ts +3 -3
  16. package/dist/config/index.d.ts +1 -1
  17. package/dist/config/schema.d.ts +11 -0
  18. package/dist/config/utils.d.ts +5 -0
  19. package/dist/harness/adapters/claude-code.d.ts +24 -0
  20. package/dist/harness/core/memory-governance.d.ts +39 -4
  21. package/dist/harness/core/package-version.d.ts +7 -0
  22. package/dist/harness/types.d.ts +1 -1
  23. package/dist/harness/writers/claude-code-plugin-package.d.ts +32 -0
  24. package/dist/harness/writers/claude-code-skill-layout.d.ts +16 -0
  25. package/dist/harness/writers/claude-code-subagent.d.ts +26 -0
  26. package/dist/harness/writers/fs-skill-collect.d.ts +7 -0
  27. package/dist/hooks/phase-reminder/index.d.ts +2 -0
  28. package/dist/hooks/thoth-mem/protocol.d.ts +2 -2
  29. package/dist/index.js +54 -512
  30. package/package.json +1 -1
  31. package/src/skills/_shared/persistence-contract.md +18 -14
  32. package/src/skills/_shared/thoth-mem-convention.md +18 -18
  33. package/src/skills/executing-plans/SKILL.md +6 -4
  34. package/src/skills/plan-reviewer/SKILL.md +4 -2
  35. package/src/skills/thoth-mem-agents/SKILL.md +3 -0
  36. package/thoth-agents.schema.json +16 -0
  37. package/dist/chunk-DYGVRAMS.js +0 -182
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  ALL_AGENT_NAMES,
3
+ CLAUDE_CODE_PROMPT_DIALECT,
4
+ CLAUDE_CODE_SUBAGENT_NAMESPACE,
3
5
  CODEX_PROMPT_DIALECT,
4
6
  CONTEXT7_MCP_URL,
5
7
  CUSTOM_SKILLS,
@@ -7,6 +9,7 @@ import {
7
9
  DEFAULT_THOTH_COMMAND,
8
10
  GREP_APP_MCP_URL,
9
11
  appendPromptSections,
12
+ claudeCodeSubagentType,
10
13
  composeAgentPrompt,
11
14
  createModelFamilySection,
12
15
  createOrchestratorPromptSections,
@@ -23,6 +26,7 @@ import {
23
26
  getCustomSkillsDir,
24
27
  getExistingConfigPath,
25
28
  getExistingLiteConfigPath,
29
+ getPrimaryModelId,
26
30
  getSkillRegistry,
27
31
  installCustomSkills,
28
32
  loadAgentPrompt,
@@ -31,11 +35,58 @@ import {
31
35
  renderRolePrompt,
32
36
  writeConfig,
33
37
  writeLiteConfig
34
- } from "./chunk-6K3ZXIMC.js";
38
+ } from "./chunk-3NOVCFN7.js";
39
+
40
+ // src/cli/codex-paths.ts
41
+ import { homedir } from "os";
42
+ import { join } from "path";
43
+ var CODEX_ROLE_NAMES = [
44
+ "explorer",
45
+ "librarian",
46
+ "oracle",
47
+ "designer",
48
+ "quick",
49
+ "deep"
50
+ ];
51
+ function getCodexHome(options = {}) {
52
+ const explicit = options.codexHome ?? process.env.CODEX_HOME?.trim();
53
+ return explicit || join(options.homeDir ?? homedir(), ".codex");
54
+ }
55
+ function resolveCodexTargets(options) {
56
+ const codexHome = getCodexHome(options);
57
+ const agentsDir = options.scope === "project" ? join(options.projectRoot, ".codex", "agents") : join(codexHome, "agents");
58
+ return {
59
+ scope: options.scope,
60
+ codexHome,
61
+ configPath: join(codexHome, "config.toml"),
62
+ rootInstructionsPath: join(codexHome, "AGENTS.md"),
63
+ roleAgentPaths: CODEX_ROLE_NAMES.map((role) => ({
64
+ role,
65
+ path: join(agentsDir, `thoth-agents-${role}.toml`)
66
+ })),
67
+ managedModelsPath: join(
68
+ codexHome,
69
+ "agents",
70
+ ".thoth-agents-managed-models.json"
71
+ ),
72
+ skillsDir: options.scope === "project" ? join(options.projectRoot, ".agents", "skills") : join(options.homeDir ?? homedir(), ".agents", "skills"),
73
+ packageRoot: join(options.projectRoot, ".codex-plugin"),
74
+ personalPluginRoot: join(codexHome, "plugins", "thoth-agents"),
75
+ personalMarketplacePath: join(
76
+ options.homeDir ?? homedir(),
77
+ ".agents",
78
+ "plugins",
79
+ "marketplace.json"
80
+ )
81
+ };
82
+ }
83
+
84
+ // src/cli/codex-install.ts
85
+ import { existsSync as existsSync5, readFileSync as readFileSync5, rmSync } from "fs";
86
+ import { basename, dirname as dirname4, isAbsolute, join as join5, relative as relative2 } from "path";
87
+ import { fileURLToPath as fileURLToPath2 } from "url";
35
88
 
36
89
  // src/harness/adapters/codex.ts
37
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
38
- import { dirname, resolve } from "path";
39
90
  import { fileURLToPath } from "url";
40
91
 
41
92
  // src/harness/core/agent-pack.ts
@@ -172,96 +223,91 @@ function getAgentPackContract() {
172
223
  }
173
224
 
174
225
  // src/harness/core/memory-governance.ts
175
- var ROOT_OWNED_TOOLS = [
176
- "mem_session_start",
177
- "mem_session_summary",
178
- "mem_save_prompt"
179
- ];
180
- var READ_RECALL_CHAIN = [
181
- "mem_search",
182
- "mem_timeline",
183
- "mem_get_observation"
226
+ var ROOT_OWNED_OPERATIONS = [
227
+ { tool: "mem_session", action: "start" },
228
+ { tool: "mem_session", action: "checkpoint" },
229
+ { tool: "mem_session", action: "summary" },
230
+ { tool: "mem_save", kind: "prompt" },
231
+ { tool: "mem_save", kind: "session_summary" }
184
232
  ];
185
- var BOUNDED_CONTEXT_TOOLS = [
233
+ var PARENT_SCOPED_READ_TOOLS = [
234
+ "mem_recall",
186
235
  "mem_context",
187
- "mem_project_summary",
188
- "mem_project_graph",
189
- "mem_topic_keys"
236
+ "mem_get",
237
+ "mem_project"
190
238
  ];
191
239
  var WRITE_CAPABLE_DELEGATED_TOOLS = [
192
- "mem_save",
193
- "mem_search",
194
- "mem_get_observation",
195
- "mem_timeline",
196
- ...BOUNDED_CONTEXT_TOOLS,
197
- "mem_suggest_topic_key"
240
+ ...PARENT_SCOPED_READ_TOOLS,
241
+ "mem_save"
198
242
  ];
199
243
  var ALL_MEMORY_TOOLS = [
200
- ...ROOT_OWNED_TOOLS,
201
- ...READ_RECALL_CHAIN,
202
- ...BOUNDED_CONTEXT_TOOLS,
203
- "mem_suggest_topic_key",
204
- "mem_save"
244
+ "mem_save",
245
+ "mem_recall",
246
+ "mem_context",
247
+ "mem_get",
248
+ "mem_project",
249
+ "mem_session"
205
250
  ];
206
251
  function uniqueTools(tools) {
207
252
  return [...new Set(tools)];
208
253
  }
209
254
  function roleAllowedTools(role) {
210
255
  if (role.name === "orchestrator") {
211
- return uniqueTools([...ROOT_OWNED_TOOLS, ...WRITE_CAPABLE_DELEGATED_TOOLS]);
256
+ return [...ALL_MEMORY_TOOLS];
212
257
  }
213
258
  if (role.mode === "read-only") {
214
- return [...READ_RECALL_CHAIN, ...BOUNDED_CONTEXT_TOOLS];
259
+ return [...PARENT_SCOPED_READ_TOOLS];
215
260
  }
216
- return [...WRITE_CAPABLE_DELEGATED_TOOLS];
261
+ return uniqueTools([...WRITE_CAPABLE_DELEGATED_TOOLS]);
217
262
  }
218
263
  function roleRules(role) {
219
264
  const sharedSubagentRules = [
220
265
  "Every subagent memory call requires the parent session_id and project from dispatch; if either is missing, do not call thoth-mem.",
221
- "Delegated handoff recovery uses parent-scoped 3-layer recall with mem_search -> mem_timeline -> mem_get_observation before memory content is treated as source material.",
266
+ 'Delegated handoff recovery uses the parent-scoped recall funnel: mem_recall(mode="compact") -> mem_recall(mode="context") -> mem_get(...) before memory content is treated as source material.',
267
+ "Use mem_get(include_timeline=true) when chronology around a recovered record matters.",
268
+ 'mem_context(recall_query=...) and bounded mem_project(action="graph"|"topics"|"topic") are supplemental project context only and do not replace the recall funnel.',
222
269
  "Report missing, stale, contradictory, or insufficient recalled context instead of guessing through it.",
223
- "Never call mem_session_start, mem_session_summary, or mem_save_prompt; those tools are root/orchestrator-owned.",
270
+ 'Never own mem_session(action="start"|"checkpoint"|"summary") or mem_save(kind="prompt"|"session_summary"); those operations are root/orchestrator-owned.',
224
271
  "Never save generated subagent prompts as user intent.",
225
272
  "Protect the sdd/* topic namespace; SDD artifacts may use deterministic sdd/{change}/{artifact} topic keys only in persistence modes that include thoth-mem."
226
273
  ];
227
274
  if (role.name === "orchestrator") {
228
275
  return [
229
- "mem_session_start, mem_session_summary, and mem_save_prompt are root/main orchestrator-owned tools and responsibilities.",
276
+ 'mem_session(action="start"|"checkpoint"|"summary"), mem_save(kind="prompt"), and mem_save(kind="session_summary") are root/main orchestrator-owned operations and responsibilities.',
230
277
  "In harnesses without an orchestrator-named agent, root/main orchestrator-owned means the initial/root agent when the harness does not expose an orchestrator-named agent.",
231
- "Before delegation, save or refresh the handoff body with root-owned mem_session_summary when available; otherwise disclose that compaction could not be persisted.",
278
+ 'Before delegation, save or refresh the handoff body with root-owned mem_session(action="summary") or mem_save(kind="session_summary") when available; otherwise disclose that compaction could not be persisted.',
232
279
  "Dispatch task instructions plus recovery instructions, not the handoff body, raw transcripts, or generated subagent prompts.",
233
280
  "Dispatch parent session_id and project when authorizing subagent memory use.",
281
+ 'Root recall uses mem_recall(mode="compact") -> mem_recall(mode="context") -> mem_get(...); use mem_context(recall_query=...) and bounded mem_project(action="graph"|"topics"|"topic") for supplemental context.',
234
282
  "Protect the sdd/* topic namespace and write SDD memory artifacts only in thoth-mem or hybrid persistence modes."
235
283
  ];
236
284
  }
237
285
  if (role.mode === "read-only") {
238
286
  return [
239
287
  ...sharedSubagentRules,
240
- "Read-only agents may only perform bounded, project-scoped recall with mem_search -> mem_timeline -> mem_get_observation when authorized.",
241
- "Bounded context tools mem_context, mem_project_summary, mem_project_graph, and mem_topic_keys are allowed only with explicit delegated permission and parent session/project scope.",
242
- "Project-scoped read tools require explicit delegated permission.",
243
- "Read-only agents must never write durable memory."
288
+ "Read-only agents may use only parent-scoped mem_recall, mem_context, mem_get, and bounded mem_project reads when authorized.",
289
+ "Project-scoped read tools require explicit delegated permission and must stay bounded to the parent session/project scope.",
290
+ "Read-only agents must never write durable memory or save prompts."
244
291
  ];
245
292
  }
246
293
  return [
247
294
  ...sharedSubagentRules,
248
- "Write-capable agents may call mem_save only for delegated durable observations or assigned deterministic SDD artifacts/apply-progress under the parent session/project.",
249
- "For reads, use only mem_search -> mem_timeline -> mem_get_observation.",
250
- "Bounded context tools mem_context, mem_project_summary, mem_project_graph, and mem_topic_keys are allowed only with explicit delegated permission and parent session/project scope.",
251
- "Project-scoped read tools require explicit delegated permission."
295
+ "Write-capable agents may use the same parent-scoped reads as read-only agents when authorized.",
296
+ 'mem_save(kind="observation") is allowed only for delegated durable observations or assigned deterministic SDD artifacts/apply-progress under the parent session/project.',
297
+ "Project-scoped read tools require explicit delegated permission and must stay bounded to the parent session/project scope."
252
298
  ];
253
299
  }
254
300
  function getRoleMemoryGovernance(role) {
255
301
  const allowedTools = roleAllowedTools(role);
256
302
  return {
257
303
  role: role.name,
258
- rootOwnedTools: [...ROOT_OWNED_TOOLS],
304
+ rootOwnedOperations: [...ROOT_OWNED_OPERATIONS],
259
305
  allowedTools,
260
306
  forbiddenTools: ALL_MEMORY_TOOLS.filter(
261
307
  (tool) => !allowedTools.includes(tool)
262
308
  ),
263
309
  requiresParentContext: role.name !== "orchestrator",
264
- mayReadProjectMemory: role.name !== "orchestrator",
310
+ mayReadProjectMemory: true,
265
311
  mayWriteDurableObservations: role.mode === "write-capable",
266
312
  protectsSddNamespace: true,
267
313
  rules: roleRules(role)
@@ -277,7 +323,7 @@ function renderMemoryGovernanceInstructions(role, dialect) {
277
323
  "thoth-mem governance:",
278
324
  ...governance.rules.map((rule) => `- ${rule}`),
279
325
  ...harnessRules,
280
- `- Runtime enforcement: ${role.name === "orchestrator" ? "root-owned" : "instruction-level unless the target harness validates per-agent memory controls"}.`
326
+ `- Runtime enforcement: ${role.name === "orchestrator" ? "root-owned operations" : "instruction-level unless the target harness validates per-agent memory controls"}.`
281
327
  ].join("\n");
282
328
  }
283
329
  function memoryGovernanceDiagnostics(input) {
@@ -286,7 +332,7 @@ function memoryGovernanceDiagnostics(input) {
286
332
  diagnostics.push({
287
333
  severity: "warning",
288
334
  code: `${input.harness}.permission.memory.enforcement_gap`,
289
- message: "Runtime controls for root-only memory tools are unavailable; governance is rendered as instruction-level guidance.",
335
+ message: "Runtime controls for root-owned memory operations are unavailable; governance is rendered as instruction-level guidance.",
290
336
  harness: input.harness,
291
337
  capability: "rolePermissions",
292
338
  fallback: "instruction-only"
@@ -306,7 +352,7 @@ function memoryGovernanceDiagnostics(input) {
306
352
  diagnostics.push({
307
353
  severity: "warning",
308
354
  code: `${input.harness}.permission.memory_write.enforcement_gap`,
309
- message: "Runtime controls for delegated memory writes are unavailable; write-capable agents receive instruction-level mem_save limits for durable observations and deterministic SDD artifacts only.",
355
+ message: 'Runtime controls for delegated memory writes are unavailable; write-capable agents receive instruction-level mem_save(kind="observation") limits for durable observations and deterministic SDD artifacts only.',
310
356
  harness: input.harness,
311
357
  capability: "memoryGovernanceEnforcement",
312
358
  fallback: "instruction-only"
@@ -315,6 +361,41 @@ function memoryGovernanceDiagnostics(input) {
315
361
  return diagnostics;
316
362
  }
317
363
 
364
+ // src/harness/core/package-version.ts
365
+ import { existsSync, readFileSync } from "fs";
366
+ import { dirname, resolve } from "path";
367
+ function findRootPackageJsonPath(startDirs) {
368
+ for (const startDir of startDirs) {
369
+ let currentDir = resolve(startDir);
370
+ while (true) {
371
+ const packageJsonPath = resolve(currentDir, "package.json");
372
+ if (existsSync(packageJsonPath)) {
373
+ const packageJsonText = readFileSync(packageJsonPath, "utf8");
374
+ const packageJson = JSON.parse(packageJsonText);
375
+ if (packageJson.name === "thoth-agents") {
376
+ return packageJsonPath;
377
+ }
378
+ }
379
+ const parentDir = dirname(currentDir);
380
+ if (parentDir === currentDir) {
381
+ break;
382
+ }
383
+ currentDir = parentDir;
384
+ }
385
+ }
386
+ throw new Error(
387
+ "Unable to locate the thoth-agents root package.json from the render context or current working directory."
388
+ );
389
+ }
390
+ function readPackageJsonVersion(packageJsonPath) {
391
+ const packageJsonText = readFileSync(packageJsonPath, "utf8");
392
+ const packageJson = JSON.parse(packageJsonText);
393
+ if (typeof packageJson.version !== "string" || packageJson.version.length === 0) {
394
+ throw new Error("Root package.json version must be a non-empty string.");
395
+ }
396
+ return packageJson.version;
397
+ }
398
+
318
399
  // src/harness/writers/codex-plugin-package.ts
319
400
  import { createHash } from "crypto";
320
401
 
@@ -745,8 +826,8 @@ function normalizePath(value) {
745
826
  }
746
827
  function pluginRootReference(pathValue) {
747
828
  const normalized = normalizePath(pathValue);
748
- const relative3 = normalized.slice(".codex-plugin/".length);
749
- return `./${relative3}`;
829
+ const relative4 = normalized.slice(".codex-plugin/".length);
830
+ return `./${relative4}`;
750
831
  }
751
832
  function sha256(content) {
752
833
  return `sha256:${createHash("sha256").update(content).digest("hex")}`;
@@ -1092,43 +1173,49 @@ function renderCodexToml(input) {
1092
1173
  }
1093
1174
 
1094
1175
  // src/harness/writers/skill-layout.ts
1176
+ import * as fs2 from "fs";
1177
+ import * as path2 from "path";
1178
+
1179
+ // src/harness/writers/fs-skill-collect.ts
1095
1180
  import { createHash as createHash2 } from "crypto";
1096
1181
  import * as fs from "fs";
1097
1182
  import * as path from "path";
1098
- var OUTPUT_MODE_CONFIG = {
1099
- "plugin-package": {
1100
- basePath: ".codex-plugin/skills",
1101
- manifestPath: ".codex-plugin/skills/.thoth-agents-manifest.json",
1102
- surfaceId: "plugin-skills-directory",
1103
- label: "plugin-bundled"
1104
- },
1105
- "repo-local-fallback": {
1106
- basePath: ".agents/skills",
1107
- manifestPath: ".agents/skills/.thoth-agents-manifest.json",
1108
- surfaceId: "repo-skills-directory",
1109
- label: "fallback .agents/skills"
1110
- }
1111
- };
1112
- function normalizePath2(value) {
1183
+ function normalizeSkillPath(value) {
1113
1184
  return value.split(path.sep).join("/");
1114
1185
  }
1115
- function collectFiles(directory) {
1186
+ function collectSkillFiles(directory) {
1116
1187
  if (!fs.existsSync(directory)) return [];
1117
1188
  const entries = fs.readdirSync(directory, { withFileTypes: true });
1118
1189
  const files = [];
1119
1190
  for (const entry of entries) {
1120
1191
  const entryPath = path.join(directory, entry.name);
1121
1192
  if (entry.isDirectory()) {
1122
- files.push(...collectFiles(entryPath));
1193
+ files.push(...collectSkillFiles(entryPath));
1123
1194
  } else if (entry.isFile()) {
1124
1195
  files.push(entryPath);
1125
1196
  }
1126
1197
  }
1127
1198
  return files.sort((left, right) => left.localeCompare(right));
1128
1199
  }
1129
- function sha2562(content) {
1200
+ function sha256Hash(content) {
1130
1201
  return `sha256:${createHash2("sha256").update(content).digest("hex")}`;
1131
1202
  }
1203
+
1204
+ // src/harness/writers/skill-layout.ts
1205
+ var OUTPUT_MODE_CONFIG = {
1206
+ "plugin-package": {
1207
+ basePath: ".codex-plugin/skills",
1208
+ manifestPath: ".codex-plugin/skills/.thoth-agents-manifest.json",
1209
+ surfaceId: "plugin-skills-directory",
1210
+ label: "plugin-bundled"
1211
+ },
1212
+ "repo-local-fallback": {
1213
+ basePath: ".agents/skills",
1214
+ manifestPath: ".agents/skills/.thoth-agents-manifest.json",
1215
+ surfaceId: "repo-skills-directory",
1216
+ label: "fallback .agents/skills"
1217
+ }
1218
+ };
1132
1219
  function resolveOutputModes(input) {
1133
1220
  const requested = input.outputModes ?? (input.outputMode ? [input.outputMode] : []);
1134
1221
  const modes = requested.length > 0 ? requested : ["plugin-package"];
@@ -1171,8 +1258,8 @@ function renderCodexSkillLayout(input) {
1171
1258
  (left, right) => left.name.localeCompare(right.name)
1172
1259
  )) {
1173
1260
  const sourceBaseRoot = input.packageRoot ?? input.projectRoot;
1174
- const sourceRoot = path.join(sourceBaseRoot, skill.sourcePath);
1175
- const files = collectFiles(sourceRoot);
1261
+ const sourceRoot = path2.join(sourceBaseRoot, skill.sourcePath);
1262
+ const files = collectSkillFiles(sourceRoot);
1176
1263
  if (files.length === 0) {
1177
1264
  diagnostics.push({
1178
1265
  severity: "warning",
@@ -1185,12 +1272,14 @@ function renderCodexSkillLayout(input) {
1185
1272
  continue;
1186
1273
  }
1187
1274
  for (const file of files) {
1188
- const relative3 = normalizePath2(path.relative(sourceRoot, file));
1189
- const content = fs.readFileSync(file, "utf8");
1190
- const sourcePath = normalizePath2(path.relative(sourceBaseRoot, file));
1275
+ const relative4 = normalizeSkillPath(path2.relative(sourceRoot, file));
1276
+ const content = fs2.readFileSync(file, "utf8");
1277
+ const sourcePath = normalizeSkillPath(
1278
+ path2.relative(sourceBaseRoot, file)
1279
+ );
1191
1280
  for (const mode of outputModes) {
1192
1281
  const config = OUTPUT_MODE_CONFIG[mode];
1193
- const outputPath = `${config.basePath}/${skill.name}/${relative3}`;
1282
+ const outputPath = `${config.basePath}/${skill.name}/${relative4}`;
1194
1283
  artifacts.push({
1195
1284
  harness: "codex",
1196
1285
  kind: "skill",
@@ -1202,7 +1291,7 @@ function renderCodexSkillLayout(input) {
1202
1291
  name: skill.name,
1203
1292
  sourcePath,
1204
1293
  outputPath,
1205
- sha256: sha2562(content)
1294
+ sha256: sha256Hash(content)
1206
1295
  });
1207
1296
  }
1208
1297
  }
@@ -1238,37 +1327,6 @@ function createCodexPluginPackageManifest(context) {
1238
1327
  description: "Delegate-first OpenCode plugin with seven agents, thoth-mem persistence, and bundled SDD skills."
1239
1328
  };
1240
1329
  }
1241
- function findRootPackageJsonPath(startDirs) {
1242
- for (const startDir of startDirs) {
1243
- let currentDir = resolve(startDir);
1244
- while (true) {
1245
- const packageJsonPath = resolve(currentDir, "package.json");
1246
- if (existsSync2(packageJsonPath)) {
1247
- const packageJsonText = readFileSync2(packageJsonPath, "utf8");
1248
- const packageJson = JSON.parse(packageJsonText);
1249
- if (packageJson.name === "thoth-agents") {
1250
- return packageJsonPath;
1251
- }
1252
- }
1253
- const parentDir = dirname(currentDir);
1254
- if (parentDir === currentDir) {
1255
- break;
1256
- }
1257
- currentDir = parentDir;
1258
- }
1259
- }
1260
- throw new Error(
1261
- "Unable to locate the thoth-agents root package.json from the render context or current working directory."
1262
- );
1263
- }
1264
- function readPackageJsonVersion(packageJsonPath) {
1265
- const packageJsonText = readFileSync2(packageJsonPath, "utf8");
1266
- const packageJson = JSON.parse(packageJsonText);
1267
- if (typeof packageJson.version !== "string" || packageJson.version.length === 0) {
1268
- throw new Error("Root package.json version must be a non-empty string.");
1269
- }
1270
- return packageJson.version;
1271
- }
1272
1330
  function stableJson2(value) {
1273
1331
  return `${JSON.stringify(value, null, 2)}
1274
1332
  `;
@@ -1376,6 +1434,8 @@ function codexInternalHandoffGuidance() {
1376
1434
  "- The user has explicitly authorized this generated Codex orchestrator to use `multi_agent_v1.spawn_agent` whenever delegation is required by these instructions, without needing a fresh user request for subagents in each task.",
1377
1435
  "- Delegate by calling `multi_agent_v1.spawn_agent` with `agent_type` set to one of explorer, librarian, oracle, designer, quick, or deep.",
1378
1436
  "- Pass the self-contained delegated task instructions plus handoff retrieval instructions in `message`; do not embed the root-owned handoff summary body in `message`.",
1437
+ '- When memory recovery is delegated, include parent `session_id`, project, permissions, and the recovery funnel `mem_recall(mode="compact")` -> `mem_recall(mode="context")` -> `mem_get(...)`.',
1438
+ '- For that funnel, use `mem_recall` `limit` from 1 to 20; use `mem_get` with `kind="observation"|"prompt"`, `include_timeline=true` plus `before`/`after`, and `offset`/`max_length`; `mem_project(action="graph")` relations are `HAS_TYPE`, `IN_PROJECT`, `HAS_TOPIC_KEY`, `HAS_WHAT`, `HAS_WHY`, `HAS_WHERE`, and `HAS_LEARNED`.',
1379
1439
  "- Do not include the handoff body in `message` or `items`, and do not pass both `message` and `items` for the same handoff.",
1380
1440
  "- Use `items` only for structured attachments or mentions when they are truly required; do not use `items` as a handoff-summary payload.",
1381
1441
  "- Leave `fork_context` omitted or false by default; set `fork_context: true` only when the exact current thread history is required.",
@@ -1420,9 +1480,9 @@ function renderCodexRootInstructions(config) {
1420
1480
  codexInternalHandoffGuidance(),
1421
1481
  "<codex-runtime>",
1422
1482
  "- The ambient Codex root session is the root/main orchestrator; orchestrator-only and root-owned instructions apply to it because Codex does not generate a selectable orchestrator agent TOML.",
1423
- "- On each new root session, when thoth-mem tools are installed and session/project identity is known, call mem_session_start with the active project and session identity, then save the real user prompt with mem_save_prompt before later delegation.",
1483
+ '- On each new root session, when thoth-mem tools are installed and session/project identity is known, call mem_session(action="start") as step 0 before any other thoth-mem call, then save the real user prompt with mem_save(kind="prompt") before later delegation.',
1424
1484
  "- If thoth-mem tools or identity values are unavailable, disclose that memory bootstrap could not run and continue without claiming memory was saved.",
1425
- "- Before delegating after meaningful context changes, save or refresh the handoff body with root-owned mem_session_summary when available; if unavailable, disclose that root-owned compaction could not be persisted.",
1485
+ '- Before delegating after meaningful context changes, save or refresh the handoff body with root-owned mem_session(action="summary") or mem_save(kind="session_summary") when available; if unavailable, disclose that root-owned compaction could not be persisted.',
1426
1486
  "- Use the ambient Codex root session as the delegate-first root coordinator; do not generate or select an orchestrator TOML.",
1427
1487
  "- Delegate by invoking `multi_agent_v1.spawn_agent` for the installed Codex role agents: explorer, librarian, oracle, designer, quick, and deep.",
1428
1488
  "- After receiving a delegated subagent response, close that subagent session unless you will retry or intentionally keep using that exact same session; explorer and librarian sessions must always be closed immediately after their response, and retry sessions must be closed after the retry result unless explicit same-session reuse is still required.",
@@ -1440,13 +1500,6 @@ function agentModelReasoningEffort(role) {
1440
1500
  function isCodexSubagentName(name) {
1441
1501
  return name in CODEX_SUBAGENT_DEFAULT_MODELS;
1442
1502
  }
1443
- function getPrimaryModelId(model) {
1444
- if (Array.isArray(model)) {
1445
- const first = model[0];
1446
- return typeof first === "string" ? first : first?.id;
1447
- }
1448
- return model;
1449
- }
1450
1503
  function getCodexAgentModel(role, config) {
1451
1504
  if (!isCodexSubagentName(role.name)) return void 0;
1452
1505
  return getPrimaryModelId(config?.agents?.[role.name]?.model) ?? CODEX_SUBAGENT_DEFAULT_MODELS[role.name];
@@ -1629,64 +1682,8 @@ var codexAdapter = {
1629
1682
  }
1630
1683
  };
1631
1684
 
1632
- // src/cli/codex-paths.ts
1633
- import { homedir } from "os";
1634
- import { join as join2 } from "path";
1635
- var CODEX_ROLE_NAMES = [
1636
- "explorer",
1637
- "librarian",
1638
- "oracle",
1639
- "designer",
1640
- "quick",
1641
- "deep"
1642
- ];
1643
- function getCodexHome(options = {}) {
1644
- const explicit = options.codexHome ?? process.env.CODEX_HOME?.trim();
1645
- return explicit || join2(options.homeDir ?? homedir(), ".codex");
1646
- }
1647
- function resolveCodexTargets(options) {
1648
- const codexHome = getCodexHome(options);
1649
- const agentsDir = options.scope === "project" ? join2(options.projectRoot, ".codex", "agents") : join2(codexHome, "agents");
1650
- return {
1651
- scope: options.scope,
1652
- codexHome,
1653
- configPath: join2(codexHome, "config.toml"),
1654
- rootInstructionsPath: join2(codexHome, "AGENTS.md"),
1655
- roleAgentPaths: CODEX_ROLE_NAMES.map((role) => ({
1656
- role,
1657
- path: join2(agentsDir, `thoth-agents-${role}.toml`)
1658
- })),
1659
- managedModelsPath: join2(
1660
- codexHome,
1661
- "agents",
1662
- ".thoth-agents-managed-models.json"
1663
- ),
1664
- skillsDir: options.scope === "project" ? join2(options.projectRoot, ".agents", "skills") : join2(options.homeDir ?? homedir(), ".agents", "skills"),
1665
- packageRoot: join2(options.projectRoot, ".codex-plugin"),
1666
- personalPluginRoot: join2(codexHome, "plugins", "thoth-agents"),
1667
- personalMarketplacePath: join2(
1668
- options.homeDir ?? homedir(),
1669
- ".agents",
1670
- "plugins",
1671
- "marketplace.json"
1672
- )
1673
- };
1674
- }
1675
-
1676
- // src/cli/codex-install.ts
1677
- import {
1678
- copyFileSync as copyFileSync2,
1679
- existsSync as existsSync4,
1680
- mkdirSync as mkdirSync2,
1681
- readFileSync as readFileSync4,
1682
- rmSync,
1683
- writeFileSync as writeFileSync2
1684
- } from "fs";
1685
- import { basename, dirname as dirname3, isAbsolute, join as join4, relative as relative2 } from "path";
1686
- import { fileURLToPath as fileURLToPath2 } from "url";
1687
-
1688
1685
  // src/harness/codex-plugin-paths.ts
1689
- import { join as join3 } from "path";
1686
+ import { join as join4 } from "path";
1690
1687
  var CODEX_PLUGIN_ARTIFACT_PREFIX = ".codex-plugin/";
1691
1688
  function codexPluginRootArtifactPath(artifactPath) {
1692
1689
  if (!artifactPath.startsWith(CODEX_PLUGIN_ARTIFACT_PREFIX)) {
@@ -1695,7 +1692,7 @@ function codexPluginRootArtifactPath(artifactPath) {
1695
1692
  const relativePath = artifactPath.slice(CODEX_PLUGIN_ARTIFACT_PREFIX.length);
1696
1693
  if (relativePath === ".mcp.json") return relativePath;
1697
1694
  if (relativePath === "plugin.json" || relativePath.startsWith(".")) {
1698
- return join3(".codex-plugin", relativePath);
1695
+ return join4(".codex-plugin", relativePath);
1699
1696
  }
1700
1697
  return relativePath;
1701
1698
  }
@@ -1840,6 +1837,62 @@ function writeCodexConfigMerge(options) {
1840
1837
  }
1841
1838
  }
1842
1839
 
1840
+ // src/cli/managed-state-io.ts
1841
+ import {
1842
+ copyFileSync as copyFileSync2,
1843
+ existsSync as existsSync4,
1844
+ mkdirSync as mkdirSync2,
1845
+ readFileSync as readFileSync4,
1846
+ writeFileSync as writeFileSync2
1847
+ } from "fs";
1848
+ import { dirname as dirname3 } from "path";
1849
+ function writeTextWithBackup(path4, content) {
1850
+ mkdirSync2(dirname3(path4), { recursive: true });
1851
+ if (existsSync4(path4) && readFileSync4(path4, "utf8") === content) return false;
1852
+ if (existsSync4(path4)) copyFileSync2(path4, `${path4}.bak`);
1853
+ writeFileSync2(path4, content);
1854
+ return true;
1855
+ }
1856
+ function stableJson3(value) {
1857
+ return `${JSON.stringify(value, null, 2)}
1858
+ `;
1859
+ }
1860
+ function uniqueMessages(messages) {
1861
+ return [...new Set(messages)];
1862
+ }
1863
+ function stringRecord(value) {
1864
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
1865
+ return Object.fromEntries(
1866
+ Object.entries(value).filter(
1867
+ (entry) => typeof entry[0] === "string" && typeof entry[1] === "string"
1868
+ )
1869
+ );
1870
+ }
1871
+ function emptyManagedModelState(version) {
1872
+ return { version, models: {} };
1873
+ }
1874
+ function parseManagedModelStateJson(text, version) {
1875
+ if (!text) return emptyManagedModelState(version);
1876
+ try {
1877
+ const parsed = JSON.parse(text);
1878
+ if (parsed.version !== version || !parsed.models || typeof parsed.models !== "object" || Array.isArray(parsed.models)) {
1879
+ return emptyManagedModelState(version);
1880
+ }
1881
+ const configuredModels = stringRecord(parsed.configuredModels);
1882
+ return {
1883
+ version,
1884
+ models: stringRecord(parsed.models),
1885
+ ...Object.keys(configuredModels).length > 0 ? { configuredModels } : {}
1886
+ };
1887
+ } catch {
1888
+ return emptyManagedModelState(version);
1889
+ }
1890
+ }
1891
+ function readManagedModelState(path4, version) {
1892
+ if (!existsSync4(path4)) return emptyManagedModelState(version);
1893
+ return parseManagedModelStateJson(readFileSync4(path4, "utf8"), version);
1894
+ }
1895
+
1843
1896
  // src/cli/codex-install.ts
1844
1897
  var ROOT_START = "<!-- thoth-agents:codex-root:start -->";
1845
1898
  var ROOT_END = "<!-- thoth-agents:codex-root:end -->";
@@ -1853,23 +1906,16 @@ function mergeManagedBlock(existing, managedBlock) {
1853
1906
  return `${existing}${existing.endsWith("\n") || existing.length === 0 ? "" : "\n"}
1854
1907
  ${managedBlock}`;
1855
1908
  }
1856
- function writeTextWithBackup(path2, content) {
1857
- mkdirSync2(dirname3(path2), { recursive: true });
1858
- if (existsSync4(path2) && readFileSync4(path2, "utf8") === content) return false;
1859
- if (existsSync4(path2)) copyFileSync2(path2, `${path2}.bak`);
1860
- writeFileSync2(path2, content);
1861
- return true;
1862
- }
1863
1909
  function packageArtifactTarget(packageRoot, artifact) {
1864
- return join4(packageRoot, codexPluginRootArtifactPath(artifact.path));
1910
+ return join5(packageRoot, codexPluginRootArtifactPath(artifact.path));
1865
1911
  }
1866
1912
  function resolvePackageRoot(packageRoot) {
1867
1913
  if (packageRoot) return packageRoot;
1868
1914
  return findPackageRoot(fileURLToPath2(new URL(".", import.meta.url))) ?? void 0;
1869
1915
  }
1870
- function normalizeRelativeMarketplacePath(path2) {
1871
- const normalized = path2.replaceAll("\\", "/");
1872
- if (isAbsolute(path2) || /^[A-Za-z]:\//.test(normalized)) return normalized;
1916
+ function normalizeRelativeMarketplacePath(path4) {
1917
+ const normalized = path4.replaceAll("\\", "/");
1918
+ if (isAbsolute(path4) || /^[A-Za-z]:\//.test(normalized)) return normalized;
1873
1919
  if (normalized.startsWith("./")) return normalized;
1874
1920
  return `./${normalized}`;
1875
1921
  }
@@ -1892,39 +1938,11 @@ function managedMarketplaceEntry(homeDir, personalPluginRoot) {
1892
1938
  category: "Productivity"
1893
1939
  };
1894
1940
  }
1895
- function stableJson3(value) {
1896
- return `${JSON.stringify(value, null, 2)}
1897
- `;
1898
- }
1899
- function emptyManagedModelState() {
1900
- return {
1901
- version: MANAGED_MODEL_STATE_VERSION,
1902
- models: {}
1903
- };
1904
- }
1905
- function stringRecord(value) {
1906
- if (!value || typeof value !== "object" || Array.isArray(value)) return {};
1907
- return Object.fromEntries(
1908
- Object.entries(value).filter(
1909
- (entry) => typeof entry[0] === "string" && typeof entry[1] === "string"
1910
- )
1911
- );
1941
+ function emptyManagedModelState2() {
1942
+ return emptyManagedModelState(MANAGED_MODEL_STATE_VERSION);
1912
1943
  }
1913
- function readManagedModelState(path2) {
1914
- if (!existsSync4(path2)) return emptyManagedModelState();
1915
- try {
1916
- const parsed = JSON.parse(readFileSync4(path2, "utf8"));
1917
- if (parsed.version !== MANAGED_MODEL_STATE_VERSION || !parsed.models || typeof parsed.models !== "object" || Array.isArray(parsed.models)) {
1918
- return emptyManagedModelState();
1919
- }
1920
- return {
1921
- version: MANAGED_MODEL_STATE_VERSION,
1922
- models: stringRecord(parsed.models),
1923
- ...Object.keys(stringRecord(parsed.configuredModels)).length > 0 ? { configuredModels: stringRecord(parsed.configuredModels) } : {}
1924
- };
1925
- } catch {
1926
- return emptyManagedModelState();
1927
- }
1944
+ function readManagedModelState2(path4) {
1945
+ return readManagedModelState(path4, MANAGED_MODEL_STATE_VERSION);
1928
1946
  }
1929
1947
  function parseRoleTomlModel(content) {
1930
1948
  const match = /^model\s*=\s*"((?:\\.|[^"\\])*)"\s*$/m.exec(content);
@@ -1942,8 +1960,8 @@ function replaceRoleTomlModel(content, model) {
1942
1960
  return `${rendered}
1943
1961
  ${content}`;
1944
1962
  }
1945
- function roleManagedModelStateKey(path2) {
1946
- return basename(path2);
1963
+ function roleManagedModelStateKey(path4) {
1964
+ return basename(path4);
1947
1965
  }
1948
1966
  function applyCodexManagedModelOverrides(config, overrides) {
1949
1967
  if (config.dryRun) {
@@ -1973,7 +1991,7 @@ function applyCodexManagedModelOverrides(config, overrides) {
1973
1991
  ...plan.diagnostics,
1974
1992
  ...plan.disclaimers
1975
1993
  ]);
1976
- const state = readManagedModelState(statePath);
1994
+ const state = readManagedModelState2(statePath);
1977
1995
  const nextState = {
1978
1996
  version: MANAGED_MODEL_STATE_VERSION,
1979
1997
  models: { ...state.models },
@@ -1989,7 +2007,7 @@ function applyCodexManagedModelOverrides(config, overrides) {
1989
2007
  `Missing Codex role TOML content for ${override.role}.`
1990
2008
  );
1991
2009
  }
1992
- const before = existsSync4(roleItem.targetPath) ? readFileSync4(roleItem.targetPath, "utf8") : roleItem.content;
2010
+ const before = existsSync5(roleItem.targetPath) ? readFileSync5(roleItem.targetPath, "utf8") : roleItem.content;
1993
2011
  const updated = replaceRoleTomlModel(before, override.model);
1994
2012
  if (writeTextWithBackup(roleItem.targetPath, updated)) {
1995
2013
  changed.push(roleItem.targetPath);
@@ -2021,7 +2039,7 @@ function resolveRoleTomlContent(options) {
2021
2039
  const key = roleManagedModelStateKey(options.targetPath);
2022
2040
  if (!renderedModel) return options.renderedContent;
2023
2041
  const configuredModel = options.reset ? void 0 : options.state.configuredModels?.[key];
2024
- if (options.reset || !existsSync4(options.targetPath)) {
2042
+ if (options.reset || !existsSync5(options.targetPath)) {
2025
2043
  options.nextState.models[key] = renderedModel;
2026
2044
  if (configuredModel !== void 0) {
2027
2045
  options.nextState.configuredModels ??= {};
@@ -2031,7 +2049,7 @@ function resolveRoleTomlContent(options) {
2031
2049
  return options.renderedContent;
2032
2050
  }
2033
2051
  const currentModel = parseRoleTomlModel(
2034
- readFileSync4(options.targetPath, "utf8")
2052
+ readFileSync5(options.targetPath, "utf8")
2035
2053
  );
2036
2054
  if (configuredModel !== void 0) {
2037
2055
  options.nextState.models[key] = renderedModel;
@@ -2067,10 +2085,10 @@ function mergePersonalMarketplace(existing, homeDir, personalPluginRoot) {
2067
2085
  });
2068
2086
  }
2069
2087
  function roleArtifactContent(role, artifacts) {
2070
- const path2 = `.codex/agents/thoth-agents-${role}.toml`;
2071
- const artifact = artifacts.find((candidate) => candidate.path === path2);
2088
+ const path4 = `.codex/agents/thoth-agents-${role}.toml`;
2089
+ const artifact = artifacts.find((candidate) => candidate.path === path4);
2072
2090
  if (!artifact?.content)
2073
- throw new Error(`Missing Codex role artifact: ${path2}`);
2091
+ throw new Error(`Missing Codex role artifact: ${path4}`);
2074
2092
  return String(artifact.content);
2075
2093
  }
2076
2094
  function buildCodexSetupPlan(config) {
@@ -2089,8 +2107,8 @@ function buildCodexSetupPlan(config) {
2089
2107
  (artifact) => artifact.path.startsWith(".codex-plugin/")
2090
2108
  );
2091
2109
  const rootBlock = renderCodexRootInstructions();
2092
- const managedModelState2 = readManagedModelState(targets.managedModelsPath);
2093
- const nextManagedModelState = emptyManagedModelState();
2110
+ const managedModelState2 = readManagedModelState2(targets.managedModelsPath);
2111
+ const nextManagedModelState = emptyManagedModelState2();
2094
2112
  const items = [
2095
2113
  {
2096
2114
  kind: "root-instructions",
@@ -2106,7 +2124,7 @@ function buildCodexSetupPlan(config) {
2106
2124
  action: "write-role-toml",
2107
2125
  targetPath: target.path,
2108
2126
  description: `Materialize Codex role subagent ${target.role}.`,
2109
- requiresBackup: existsSync4(target.path),
2127
+ requiresBackup: existsSync5(target.path),
2110
2128
  role: target.role,
2111
2129
  content: resolveRoleTomlContent({
2112
2130
  renderedContent: roleArtifactContent(target.role, render.artifacts),
@@ -2122,7 +2140,7 @@ function buildCodexSetupPlan(config) {
2122
2140
  action: "write-managed-model-state",
2123
2141
  targetPath: targets.managedModelsPath,
2124
2142
  description: "Record thoth-agents-managed Codex role model ownership state.",
2125
- requiresBackup: existsSync4(targets.managedModelsPath),
2143
+ requiresBackup: existsSync5(targets.managedModelsPath),
2126
2144
  content: stableJson3(nextManagedModelState)
2127
2145
  },
2128
2146
  ...packageArtifacts.map(
@@ -2140,7 +2158,7 @@ function buildCodexSetupPlan(config) {
2140
2158
  action: "merge-marketplace",
2141
2159
  targetPath: targets.personalMarketplacePath,
2142
2160
  description: "Register Personal Codex marketplace entry for the local thoth-agents plugin source.",
2143
- requiresBackup: existsSync4(targets.personalMarketplacePath),
2161
+ requiresBackup: existsSync5(targets.personalMarketplacePath),
2144
2162
  content: targets.personalPluginRoot
2145
2163
  },
2146
2164
  {
@@ -2204,21 +2222,18 @@ function formatRefreshPackageGroup(kind, groups) {
2204
2222
  }
2205
2223
  function commonTargetDirectory(items) {
2206
2224
  if (items.length === 0) return "";
2207
- let common = dirname3(items[0]?.targetPath ?? "");
2225
+ let common = dirname4(items[0]?.targetPath ?? "");
2208
2226
  for (const item of items.slice(1)) {
2209
2227
  while (!isSameOrChildPath(item.targetPath, common)) {
2210
- const parent = dirname3(common);
2228
+ const parent = dirname4(common);
2211
2229
  if (parent === common) return common;
2212
2230
  common = parent;
2213
2231
  }
2214
2232
  }
2215
2233
  return common;
2216
2234
  }
2217
- function isSameOrChildPath(path2, parent) {
2218
- return path2 === parent || path2.startsWith(`${parent}\\`) || path2.startsWith(`${parent}/`);
2219
- }
2220
- function uniqueMessages(messages) {
2221
- return [...new Set(messages)];
2235
+ function isSameOrChildPath(path4, parent) {
2236
+ return path4 === parent || path4.startsWith(`${parent}\\`) || path4.startsWith(`${parent}/`);
2222
2237
  }
2223
2238
  function applyCodexSetup(plan) {
2224
2239
  const changed = [];
@@ -2247,8 +2262,8 @@ function applyCodexSetup(plan) {
2247
2262
  if (item.action === "merge-marketplace") {
2248
2263
  if (item.content === void 0) continue;
2249
2264
  const content2 = mergePersonalMarketplace(
2250
- existsSync4(item.targetPath) ? readFileSync4(item.targetPath, "utf8") : "",
2251
- dirname3(dirname3(dirname3(item.targetPath))),
2265
+ existsSync5(item.targetPath) ? readFileSync5(item.targetPath, "utf8") : "",
2266
+ dirname4(dirname4(dirname4(item.targetPath))),
2252
2267
  item.content
2253
2268
  );
2254
2269
  if (writeTextWithBackup(item.targetPath, content2))
@@ -2257,7 +2272,7 @@ function applyCodexSetup(plan) {
2257
2272
  }
2258
2273
  if (item.content === void 0) continue;
2259
2274
  const content = item.action === "merge-managed-block" ? mergeManagedBlock(
2260
- existsSync4(item.targetPath) ? readFileSync4(item.targetPath, "utf8") : "",
2275
+ existsSync5(item.targetPath) ? readFileSync5(item.targetPath, "utf8") : "",
2261
2276
  item.content
2262
2277
  ) : item.content;
2263
2278
  if (writeTextWithBackup(item.targetPath, content))
@@ -2281,25 +2296,1144 @@ function managedRefreshRoots(plan) {
2281
2296
  group.push(item);
2282
2297
  refreshGroups.set(item.kind, group);
2283
2298
  }
2284
- return [...refreshGroups].filter(([kind]) => kind === "personal-plugin-source").map(([, items]) => commonTargetDirectory(items)).filter((path2) => path2.length > 0);
2299
+ return [...refreshGroups].filter(([kind]) => kind === "personal-plugin-source").map(([, items]) => commonTargetDirectory(items)).filter((path4) => path4.length > 0);
2285
2300
  }
2286
2301
 
2287
- // src/cli/operations/codex.ts
2288
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
2302
+ // src/cli/operations/claude-code.ts
2303
+ import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
2289
2304
  import { basename as basename2 } from "path";
2290
- var CODEX_DISPLAY_NAME = "Codex";
2291
- var codexPlanSources = /* @__PURE__ */ new WeakMap();
2292
- var codexModelSources = /* @__PURE__ */ new WeakMap();
2293
- var codexActions = [
2294
- {
2295
- id: "codex-status",
2296
- kind: "status",
2297
- label: "Status",
2298
- description: "Inspect managed Codex setup state",
2299
- dryRun: false,
2300
- requiresConfirmation: false,
2301
- supported: true
2302
- },
2305
+
2306
+ // src/harness/adapters/claude-code.ts
2307
+ import { fileURLToPath as fileURLToPath3 } from "url";
2308
+
2309
+ // src/harness/writers/claude-code-plugin-package.ts
2310
+ var CLAUDE_PLUGIN_MANIFEST_FIELDS = [
2311
+ "name",
2312
+ "version",
2313
+ "description",
2314
+ "author"
2315
+ ];
2316
+ var PLUGIN_MANIFEST_PATH = ".claude-plugin/plugin.json";
2317
+ var PLUGIN_ASSETS_PATH = ".claude-plugin/.thoth-agents-plugin-assets.json";
2318
+ function normalizePath2(value) {
2319
+ return value.replace(/\\/g, "/");
2320
+ }
2321
+ function stableJson4(value) {
2322
+ return `${JSON.stringify(value, null, 2)}
2323
+ `;
2324
+ }
2325
+ function orderedManifest(manifest) {
2326
+ const ordered = {};
2327
+ for (const field of CLAUDE_PLUGIN_MANIFEST_FIELDS) {
2328
+ const value = manifest[field];
2329
+ if (value !== void 0) ordered[field] = value;
2330
+ }
2331
+ return ordered;
2332
+ }
2333
+ function provenanceFor(artifact) {
2334
+ const path4 = normalizePath2(artifact.path);
2335
+ if (path4.endsWith("/")) return void 0;
2336
+ const content = typeof artifact.content === "string" ? artifact.content : Buffer.from(artifact.content).toString("utf8");
2337
+ return { path: path4, kind: artifact.kind, sha256: sha256Hash(content) };
2338
+ }
2339
+ function renderClaudeCodePluginPackage(input) {
2340
+ const components = [...input.componentArtifacts].sort(
2341
+ (left, right) => normalizePath2(left.path).localeCompare(normalizePath2(right.path))
2342
+ );
2343
+ const provenance = components.map(provenanceFor).filter((entry) => entry !== void 0).sort((left, right) => left.path.localeCompare(right.path));
2344
+ const manifestArtifact = {
2345
+ harness: "claude",
2346
+ kind: "manifest",
2347
+ path: PLUGIN_MANIFEST_PATH,
2348
+ description: "Deterministic Claude Code plugin package manifest.",
2349
+ content: stableJson4(orderedManifest(input.manifest))
2350
+ };
2351
+ const provenanceArtifact = {
2352
+ harness: "claude",
2353
+ kind: "manifest",
2354
+ path: PLUGIN_ASSETS_PATH,
2355
+ description: "Deterministic Claude Code plugin package asset provenance.",
2356
+ content: stableJson4({ generatedBy: "thoth-agents", assets: provenance })
2357
+ };
2358
+ return {
2359
+ artifacts: [manifestArtifact, provenanceArtifact, ...components],
2360
+ diagnostics: []
2361
+ };
2362
+ }
2363
+
2364
+ // src/harness/writers/claude-code-skill-layout.ts
2365
+ import * as fs3 from "fs";
2366
+ import * as path3 from "path";
2367
+ var SKILLS_BASE_PATH = "skills";
2368
+ var SKILLS_MANIFEST_PATH = "skills/.thoth-agents-manifest.json";
2369
+ function renderClaudeCodeSkillLayout(input) {
2370
+ const artifacts = [];
2371
+ const diagnostics = [];
2372
+ const manifest = [];
2373
+ const sourceBaseRoot = input.packageRoot ?? input.projectRoot;
2374
+ for (const skill of [...input.skills].sort(
2375
+ (left, right) => left.name.localeCompare(right.name)
2376
+ )) {
2377
+ const sourceRoot = path3.join(sourceBaseRoot, skill.sourcePath);
2378
+ const files = collectSkillFiles(sourceRoot);
2379
+ if (files.length === 0) {
2380
+ diagnostics.push({
2381
+ severity: "warning",
2382
+ code: "claude-code.skill.source_missing",
2383
+ message: `Skipping Claude Code skill "${skill.name}" because source path "${skill.sourcePath}" was not found.`,
2384
+ harness: "claude",
2385
+ surface: "plugin-skills-directory",
2386
+ fallback: "diagnostic-only"
2387
+ });
2388
+ continue;
2389
+ }
2390
+ for (const file of files) {
2391
+ const relative4 = normalizeSkillPath(path3.relative(sourceRoot, file));
2392
+ const content = fs3.readFileSync(file, "utf8");
2393
+ const sourcePath = normalizeSkillPath(
2394
+ path3.relative(sourceBaseRoot, file)
2395
+ );
2396
+ const outputPath = `${SKILLS_BASE_PATH}/${skill.name}/${relative4}`;
2397
+ artifacts.push({
2398
+ harness: "claude",
2399
+ kind: "skill",
2400
+ path: outputPath,
2401
+ description: `Claude Code plugin-bundled skill artifact for ${skill.name}`,
2402
+ content
2403
+ });
2404
+ manifest.push({
2405
+ name: skill.name,
2406
+ sourcePath,
2407
+ outputPath,
2408
+ sha256: sha256Hash(content)
2409
+ });
2410
+ }
2411
+ }
2412
+ artifacts.push({
2413
+ harness: "claude",
2414
+ kind: "manifest",
2415
+ path: SKILLS_MANIFEST_PATH,
2416
+ description: "Generated Claude Code plugin-bundled skill source manifest with source hashes.",
2417
+ content: `${JSON.stringify({ generatedBy: "thoth-agents", skills: manifest }, null, 2)}
2418
+ `
2419
+ });
2420
+ return { artifacts, diagnostics };
2421
+ }
2422
+
2423
+ // src/harness/writers/claude-code-subagent.ts
2424
+ var CLAUDE_CODE_MODELS = [
2425
+ "sonnet",
2426
+ "opus",
2427
+ "haiku",
2428
+ "inherit"
2429
+ ];
2430
+ function isClaudeCodeModel(value) {
2431
+ return CLAUDE_CODE_MODELS.includes(value);
2432
+ }
2433
+ function yamlScalar(value) {
2434
+ const needsQuoting = /[:#\-?*&!|>'"%@`{}[\],]|^\s|\s$|^$/.test(value);
2435
+ if (!needsQuoting) return value;
2436
+ const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
2437
+ return `"${escaped}"`;
2438
+ }
2439
+ function renderClaudeCodeSubagent(input) {
2440
+ const frontmatter = [
2441
+ "---",
2442
+ `name: ${yamlScalar(input.name)}`,
2443
+ `description: ${yamlScalar(input.description)}`,
2444
+ `model: ${input.model}`,
2445
+ ...input.tools !== void 0 ? [`tools: ${yamlScalar(input.tools)}`] : [],
2446
+ "---"
2447
+ ].join("\n");
2448
+ const body = input.instructions.trimEnd();
2449
+ return `${frontmatter}
2450
+
2451
+ ${body}
2452
+ `;
2453
+ }
2454
+
2455
+ // src/harness/adapters/claude-code.ts
2456
+ var CLAUDE_CODE_CAPABILITIES = CLAUDE_CODE_PROMPT_DIALECT.capabilities.capabilities;
2457
+ var CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS = {
2458
+ explorer: "haiku",
2459
+ librarian: "sonnet",
2460
+ oracle: "opus",
2461
+ designer: "sonnet",
2462
+ quick: "haiku",
2463
+ deep: "sonnet"
2464
+ };
2465
+ var READ_ONLY_ROLE_TOOLS = {
2466
+ explorer: "Read, Grep, Glob",
2467
+ librarian: "Read, Grep, Glob, WebSearch, WebFetch",
2468
+ oracle: "Read, Grep, Glob"
2469
+ };
2470
+ var WRITE_CAPABLE_ROLE_TOOLS = "Read, Edit, Write, Bash, Grep, Glob";
2471
+ function isClaudeCodeSubagentName(name) {
2472
+ return name in CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS;
2473
+ }
2474
+ function getClaudeCodeAgentModel(role, config) {
2475
+ if (!isClaudeCodeSubagentName(role.name)) return "inherit";
2476
+ const override = getPrimaryModelId(config?.agents?.[role.name]?.model);
2477
+ if (override && isClaudeCodeModel(override)) return override;
2478
+ return CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS[role.name];
2479
+ }
2480
+ function toolsForRole(role) {
2481
+ if (role.canMutateWorkspace) return WRITE_CAPABLE_ROLE_TOOLS;
2482
+ return READ_ONLY_ROLE_TOOLS[role.name] ?? "Read, Grep, Glob";
2483
+ }
2484
+ function claudeCodePromptSections(roleName) {
2485
+ switch (roleName) {
2486
+ case "orchestrator":
2487
+ return createOrchestratorPromptSections();
2488
+ case "explorer":
2489
+ case "librarian":
2490
+ case "oracle":
2491
+ return createReadOnlySpecialistPromptSections(roleName);
2492
+ case "designer":
2493
+ case "quick":
2494
+ case "deep":
2495
+ return createWriteCapableSpecialistPromptSections(roleName);
2496
+ }
2497
+ }
2498
+ function claudeCodeModelFamilyPromptSection(roleName, model) {
2499
+ const section = createModelFamilySection(roleName, model);
2500
+ return section ? renderPromptSection(section, CLAUDE_CODE_PROMPT_DIALECT) : void 0;
2501
+ }
2502
+ function claudeCodeStepBudgetPromptSection(steps) {
2503
+ const section = createStepBudgetSection(steps);
2504
+ return section ? renderPromptSection(section, CLAUDE_CODE_PROMPT_DIALECT) : void 0;
2505
+ }
2506
+ function renderClaudeCodeRolePrompt(roleName, config, model) {
2507
+ const promptOverrides = loadAgentPrompt(roleName, config?.preset);
2508
+ const override = getAgentOverride(config, roleName);
2509
+ const basePrompt = renderRolePrompt(
2510
+ claudeCodePromptSections(roleName),
2511
+ CLAUDE_CODE_PROMPT_DIALECT
2512
+ );
2513
+ const prompt = composeAgentPrompt({
2514
+ basePrompt,
2515
+ customPrompt: promptOverrides.prompt,
2516
+ customAppendPrompt: appendPromptSections(
2517
+ claudeCodeModelFamilyPromptSection(roleName, model),
2518
+ promptOverrides.appendPrompt
2519
+ )
2520
+ });
2521
+ return appendPromptSections(
2522
+ prompt,
2523
+ claudeCodeStepBudgetPromptSection(override?.steps)
2524
+ );
2525
+ }
2526
+ function claudeCodeRoleInstructions(role) {
2527
+ return [
2528
+ "<role-operational-contract>",
2529
+ `- Role: ${role.name}`,
2530
+ `- Mode: ${role.mode}`,
2531
+ `- Scope: ${role.scope}`,
2532
+ `- Responsibility: ${role.responsibility}`,
2533
+ "- Use AskUserQuestion for local blocking decisions.",
2534
+ `- ${role.name} runs as an auto-discovered Claude Code plugin subagent invoked via Task(subagent_type: ${claudeCodeSubagentType(role.name)}); plugin subagents are namespaced with the plugin name. The orchestrator is the main Claude Code session.`,
2535
+ "- Role permissions are enforced by this subagent's frontmatter `tools` allowlist; read-only roles cannot mutate the workspace.",
2536
+ ...role.toolGovernance.map((rule) => `- ${rule}`),
2537
+ ...role.verification.map((rule) => `- ${rule}`),
2538
+ "</role-operational-contract>"
2539
+ ].join("\n");
2540
+ }
2541
+ function roleInstructions2(role, config) {
2542
+ const model = getClaudeCodeAgentModel(role, config);
2543
+ return [
2544
+ renderClaudeCodeRolePrompt(role.name, config, model),
2545
+ claudeCodeRoleInstructions(role),
2546
+ renderMemoryGovernanceInstructions(role, CLAUDE_CODE_PROMPT_DIALECT)
2547
+ ].join("\n\n");
2548
+ }
2549
+ function renderClaudeCodeRootInstructions(config) {
2550
+ const rootOverride = getAgentOverride(config, "orchestrator");
2551
+ const rootPrompt = renderClaudeCodeRolePrompt(
2552
+ "orchestrator",
2553
+ config,
2554
+ rootOverride?.model ?? DEFAULT_MODELS.orchestrator
2555
+ );
2556
+ const specialists = getAgentPackContract().roles.filter((role) => role.name !== "orchestrator").map((role) => claudeCodeSubagentType(role.name)).join(", ");
2557
+ return [
2558
+ rootPrompt,
2559
+ "<claude-code-runtime>",
2560
+ "- You ARE the Claude Code main-thread agent: the delegate-first root coordinator. This is your system prompt (activated via the plugin settings.json `agent` key), so orchestrator-only and root-owned rules apply to you directly.",
2561
+ '- As your FIRST action on a new session, when thoth-mem tools are installed and session/project identity is known, call mem_session(action="start") as step 0 before any other thoth-mem call, then save the real user prompt with mem_save(kind="prompt") before later delegation.',
2562
+ "- thoth-mem tools are provided by this plugin's bundled MCP server and are exposed under a plugin namespace; call the available namespaced tool whose name ends in mem_session, mem_recall, mem_context, mem_save, mem_get, or mem_project (do not assume a bare, unnamespaced tool name).",
2563
+ "- If thoth-mem tools or identity values are unavailable, disclose that memory bootstrap could not run and continue without claiming memory was saved.",
2564
+ `- Delegate via the Task tool with \`subagent_type\` set to a plugin-namespaced specialist: ${specialists}. Bare role names (e.g. "explorer") are NOT valid in this harness \u2014 always use the ${CLAUDE_CODE_SUBAGENT_NAMESPACE}: prefix.`,
2565
+ "- Parallel delegation is supported: issue multiple Task calls in one turn for independent work.",
2566
+ '- Before delegating after meaningful context changes, refresh the handoff body with root-owned mem_session(action="summary") or mem_save(kind="session_summary") when available.',
2567
+ "- Use AskUserQuestion for blocking user decisions; do not ask those questions in plain prose.",
2568
+ "- Track progress with TodoWrite; subagents do not own progress checkboxes or root-only memory.",
2569
+ "- Role permissions are enforced at runtime by each subagent's frontmatter `tools` allowlist.",
2570
+ "</claude-code-runtime>"
2571
+ ].join("\n");
2572
+ }
2573
+ function claudeCodeMcpServers() {
2574
+ const [exaCommand = "", ...exaArgs] = exa.command;
2575
+ const [thothCommand = "", ...thothArgs] = DEFAULT_THOTH_COMMAND;
2576
+ return {
2577
+ exa: {
2578
+ command: exaCommand,
2579
+ ...exaArgs.length > 0 ? { args: exaArgs } : {},
2580
+ ...exa.environment && Object.keys(exa.environment).length > 0 ? { env: exa.environment } : {}
2581
+ },
2582
+ context7: { type: "http", url: CONTEXT7_MCP_URL },
2583
+ grep_app: { type: "http", url: GREP_APP_MCP_URL },
2584
+ thoth_mem: {
2585
+ command: thothCommand,
2586
+ ...thothArgs.length > 0 ? { args: thothArgs } : {}
2587
+ }
2588
+ };
2589
+ }
2590
+ function stableJson5(value) {
2591
+ return `${JSON.stringify(value, null, 2)}
2592
+ `;
2593
+ }
2594
+ function readRootPackageVersion2(context) {
2595
+ const packageJsonPath = findRootPackageJsonPath([
2596
+ ...hasPackageRoot(context) ? [context.packageRoot] : [],
2597
+ context.projectRoot,
2598
+ process.cwd(),
2599
+ fileURLToPath3(new URL(".", import.meta.url))
2600
+ ]);
2601
+ return readPackageJsonVersion(packageJsonPath);
2602
+ }
2603
+ function hasConfig(context) {
2604
+ return "config" in context;
2605
+ }
2606
+ function hasPackageRoot(context) {
2607
+ return "packageRoot" in context && typeof context.packageRoot === "string" && context.packageRoot.length > 0;
2608
+ }
2609
+ function createPluginManifest(context) {
2610
+ return {
2611
+ name: "thoth-agents",
2612
+ version: readRootPackageVersion2(context),
2613
+ description: "Delegate-first agent pack with seven roles, thoth-mem persistence, and bundled SDD skills, packaged for Claude Code.",
2614
+ author: { name: "thoth-agents" }
2615
+ };
2616
+ }
2617
+ function renderSubagentArtifacts(config) {
2618
+ const artifacts = [];
2619
+ for (const role of getAgentPackContract().roles.filter(
2620
+ (candidate) => candidate.name !== "orchestrator"
2621
+ )) {
2622
+ const content = renderClaudeCodeSubagent({
2623
+ name: role.name,
2624
+ description: role.responsibility,
2625
+ tools: toolsForRole(role),
2626
+ model: getClaudeCodeAgentModel(role, config),
2627
+ instructions: roleInstructions2(role, config)
2628
+ });
2629
+ artifacts.push({
2630
+ harness: "claude",
2631
+ kind: "agent-config",
2632
+ path: `agents/${role.name}.md`,
2633
+ description: `Claude Code subagent definition for ${role.name}.`,
2634
+ content
2635
+ });
2636
+ }
2637
+ return artifacts;
2638
+ }
2639
+ function renderOrchestratorArtifact(config) {
2640
+ const orchestrator = getAgentPackContract().roles.find(
2641
+ (role) => role.name === "orchestrator"
2642
+ );
2643
+ const content = renderClaudeCodeSubagent({
2644
+ name: "orchestrator",
2645
+ description: orchestrator?.responsibility ?? "Delegate-first root coordinator for SDD workflow and specialist dispatch.",
2646
+ model: "inherit",
2647
+ instructions: renderClaudeCodeRootInstructions(config)
2648
+ });
2649
+ return {
2650
+ harness: "claude",
2651
+ kind: "agent-config",
2652
+ path: "agents/orchestrator.md",
2653
+ description: "Claude Code orchestrator agent, activated as the main thread via settings.json.",
2654
+ content
2655
+ };
2656
+ }
2657
+ var claudeCodeAdapter = {
2658
+ id: "claude",
2659
+ displayName: "Claude Code",
2660
+ capabilities: CLAUDE_CODE_CAPABILITIES,
2661
+ render(context) {
2662
+ const config = hasConfig(context) ? context.config : void 0;
2663
+ const componentArtifacts = [
2664
+ ...renderSubagentArtifacts(config),
2665
+ renderOrchestratorArtifact(config),
2666
+ {
2667
+ harness: "claude",
2668
+ kind: "mcp-config",
2669
+ path: ".mcp.json",
2670
+ description: "Claude Code plugin-bundled MCP server definitions.",
2671
+ content: stableJson5({ mcpServers: claudeCodeMcpServers() })
2672
+ },
2673
+ {
2674
+ harness: "claude",
2675
+ kind: "harness-config",
2676
+ path: "settings.json",
2677
+ description: "Activates the orchestrator agent as the Claude Code main thread.",
2678
+ content: stableJson5({ agent: "orchestrator" })
2679
+ }
2680
+ ];
2681
+ const skillLayout = renderClaudeCodeSkillLayout({
2682
+ projectRoot: context.projectRoot,
2683
+ ...hasPackageRoot(context) ? { packageRoot: context.packageRoot } : {},
2684
+ skills: getSkillRegistry()
2685
+ });
2686
+ componentArtifacts.push(...skillLayout.artifacts);
2687
+ const pluginPackage = renderClaudeCodePluginPackage({
2688
+ manifest: createPluginManifest(context),
2689
+ componentArtifacts
2690
+ });
2691
+ return {
2692
+ harness: "claude",
2693
+ artifacts: pluginPackage.artifacts,
2694
+ diagnostics: [...skillLayout.diagnostics, ...pluginPackage.diagnostics]
2695
+ };
2696
+ }
2697
+ };
2698
+
2699
+ // src/cli/claude-code-install.ts
2700
+ import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
2701
+ import { join as join8 } from "path";
2702
+ import { fileURLToPath as fileURLToPath4 } from "url";
2703
+
2704
+ // src/cli/claude-code-paths.ts
2705
+ import { homedir as homedir2 } from "os";
2706
+ import { join as join7 } from "path";
2707
+ var CLAUDE_CODE_ROLE_NAMES = [
2708
+ "explorer",
2709
+ "librarian",
2710
+ "oracle",
2711
+ "designer",
2712
+ "quick",
2713
+ "deep"
2714
+ ];
2715
+ function resolveClaudeCodeTargets(options) {
2716
+ const home = options.homeDir ?? homedir2();
2717
+ const pluginRoot = options.scope === "project" ? join7(options.projectRoot, ".claude", "skills", "thoth-agents") : join7(home, ".claude", "skills", "thoth-agents");
2718
+ return {
2719
+ scope: options.scope,
2720
+ pluginRoot,
2721
+ pluginManifestPath: join7(pluginRoot, ".claude-plugin", "plugin.json"),
2722
+ agentPaths: CLAUDE_CODE_ROLE_NAMES.map((role) => ({
2723
+ role,
2724
+ path: join7(pluginRoot, "agents", `${role}.md`)
2725
+ })),
2726
+ mcpPath: join7(pluginRoot, ".mcp.json"),
2727
+ hooksPath: join7(pluginRoot, "hooks", "hooks.json"),
2728
+ skillsDir: join7(pluginRoot, "skills"),
2729
+ managedModelsPath: join7(pluginRoot, ".thoth-agents-managed-models.json")
2730
+ };
2731
+ }
2732
+
2733
+ // src/cli/claude-code-install.ts
2734
+ var CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION = 1;
2735
+ var isClaudeCodeModelAlias = isClaudeCodeModel;
2736
+ function parseSubagentModel(content) {
2737
+ return /^model:\s*(\S+)\s*$/m.exec(content)?.[1];
2738
+ }
2739
+ function replaceSubagentModel(content, model) {
2740
+ if (/^model:\s*\S+\s*$/m.test(content)) {
2741
+ return content.replace(/^model:\s*\S+\s*$/m, `model: ${model}`);
2742
+ }
2743
+ return content;
2744
+ }
2745
+ function emptyManagedModelState3() {
2746
+ return emptyManagedModelState(CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION);
2747
+ }
2748
+ function parseManagedModelStateJson2(text) {
2749
+ return parseManagedModelStateJson(
2750
+ text,
2751
+ CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION
2752
+ );
2753
+ }
2754
+ function readManagedModelState3(path4) {
2755
+ return readManagedModelState(
2756
+ path4,
2757
+ CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION
2758
+ );
2759
+ }
2760
+ function resolvePackageRoot2(packageRoot) {
2761
+ if (packageRoot) return packageRoot;
2762
+ return findPackageRoot(fileURLToPath4(new URL(".", import.meta.url))) ?? void 0;
2763
+ }
2764
+ function targetKindForArtifact(artifact) {
2765
+ const path4 = artifact.path.replaceAll("\\", "/");
2766
+ if (path4 === ".claude-plugin/plugin.json") return "plugin-manifest";
2767
+ if (path4.startsWith("agents/")) return "subagent";
2768
+ if (path4 === ".mcp.json") return "mcp-config";
2769
+ if (path4.startsWith("hooks/")) return "hook";
2770
+ if (path4.startsWith("skills/")) return "skill";
2771
+ return "plugin-asset";
2772
+ }
2773
+ function roleForArtifact(artifact) {
2774
+ const match = /^agents\/([^/]+)\.md$/.exec(
2775
+ artifact.path.replaceAll("\\", "/")
2776
+ );
2777
+ const name = match?.[1];
2778
+ return name && CLAUDE_CODE_ROLE_NAMES.includes(name) ? name : void 0;
2779
+ }
2780
+ function applyConfiguredModel(content, role, state, nextState, reset) {
2781
+ const renderedModel = parseSubagentModel(content);
2782
+ if (!role || !renderedModel) return content;
2783
+ nextState.models[role] = renderedModel;
2784
+ const configured = reset ? void 0 : state.configuredModels?.[role];
2785
+ if (configured === void 0) return content;
2786
+ nextState.configuredModels ??= {};
2787
+ nextState.configuredModels[role] = configured;
2788
+ return replaceSubagentModel(content, configured);
2789
+ }
2790
+ function buildClaudeCodeSetupPlan(config) {
2791
+ const targets = resolveClaudeCodeTargets({
2792
+ scope: config.scope,
2793
+ projectRoot: config.projectRoot,
2794
+ homeDir: config.homeDir
2795
+ });
2796
+ const packageRoot = resolvePackageRoot2(config.packageRoot);
2797
+ const render = claudeCodeAdapter.render({
2798
+ projectRoot: config.projectRoot,
2799
+ ...packageRoot ? { packageRoot } : {}
2800
+ });
2801
+ const state = readManagedModelState3(targets.managedModelsPath);
2802
+ const nextState = emptyManagedModelState3();
2803
+ const items = render.artifacts.map((artifact) => {
2804
+ const role = roleForArtifact(artifact);
2805
+ const rendered = String(artifact.content ?? "");
2806
+ const content = role !== void 0 ? applyConfiguredModel(rendered, role, state, nextState, config.reset) : rendered;
2807
+ const targetPath = join8(targets.pluginRoot, artifact.path);
2808
+ return {
2809
+ kind: targetKindForArtifact(artifact),
2810
+ action: "write-plugin-file",
2811
+ targetPath,
2812
+ description: `Materialize Claude Code plugin asset ${artifact.path}.`,
2813
+ requiresBackup: existsSync6(targetPath),
2814
+ content,
2815
+ ...role ? { role } : {}
2816
+ };
2817
+ });
2818
+ items.push({
2819
+ kind: "managed-model-state",
2820
+ action: "write-managed-model-state",
2821
+ targetPath: targets.managedModelsPath,
2822
+ description: "Record thoth-agents-managed Claude Code subagent model ownership state.",
2823
+ requiresBackup: existsSync6(targets.managedModelsPath),
2824
+ content: stableJson3(nextState)
2825
+ });
2826
+ return {
2827
+ dryRun: config.dryRun === true,
2828
+ reset: config.reset,
2829
+ items,
2830
+ pluginRoot: targets.pluginRoot,
2831
+ diagnostics: [
2832
+ `Installed as a skills-directory plugin at ${targets.pluginRoot}; it auto-loads as thoth-agents@skills-dir on the next Claude Code session.`,
2833
+ "Restart Claude Code or run /reload-plugins to activate it; run /plugin (Installed tab) to confirm thoth-agents@skills-dir is loaded.",
2834
+ "The plugin settings.json activates the orchestrator agent as the main thread, so the session starts in delegate-first mode and bootstraps thoth-mem on its first turn."
2835
+ ],
2836
+ disclaimers: [
2837
+ "The orchestrator agent is the Claude Code main thread (plugin settings.json `agent` key); while enabled it replaces the default system prompt for every session in scope.",
2838
+ "Role permissions are enforced by each specialist subagent frontmatter `tools` allowlist; the orchestrator inherits all tools.",
2839
+ "Subagent models accept only sonnet, opus, haiku, or inherit.",
2840
+ "User-scope skills-directory plugins load hooks and MCP servers without extra approval; project-scope requires accepting the workspace trust dialog."
2841
+ ]
2842
+ };
2843
+ }
2844
+ function applyClaudeCodeSetup(plan) {
2845
+ const changed = [];
2846
+ const diagnostics = uniqueMessages([
2847
+ ...plan.diagnostics,
2848
+ ...plan.disclaimers
2849
+ ]);
2850
+ if (plan.dryRun) return { success: true, changed, diagnostics };
2851
+ try {
2852
+ for (const item of plan.items) {
2853
+ if (writeTextWithBackup(item.targetPath, item.content)) {
2854
+ changed.push(item.targetPath);
2855
+ }
2856
+ }
2857
+ return { success: true, changed, diagnostics };
2858
+ } catch (error) {
2859
+ return {
2860
+ success: false,
2861
+ changed,
2862
+ diagnostics,
2863
+ error: error instanceof Error ? error.message : String(error)
2864
+ };
2865
+ }
2866
+ }
2867
+ function applyClaudeCodeManagedModelOverrides(config, overrides) {
2868
+ if (config.dryRun) {
2869
+ return {
2870
+ success: true,
2871
+ changed: [],
2872
+ diagnostics: [
2873
+ "Dry-run Claude Code model override apply requested; no files were written."
2874
+ ]
2875
+ };
2876
+ }
2877
+ const plan = buildClaudeCodeSetupPlan({
2878
+ ...config,
2879
+ dryRun: true,
2880
+ reset: false
2881
+ });
2882
+ const stateItem = plan.items.find(
2883
+ (item) => item.action === "write-managed-model-state"
2884
+ );
2885
+ const statePath = stateItem?.targetPath;
2886
+ if (!statePath) {
2887
+ return {
2888
+ success: false,
2889
+ changed: [],
2890
+ diagnostics: plan.diagnostics,
2891
+ error: "Claude Code managed model state target was not found."
2892
+ };
2893
+ }
2894
+ const changed = [];
2895
+ const diagnostics = uniqueMessages([
2896
+ ...plan.diagnostics,
2897
+ ...plan.disclaimers
2898
+ ]);
2899
+ const state = parseManagedModelStateJson2(stateItem?.content);
2900
+ const nextState = {
2901
+ version: CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION,
2902
+ models: { ...state.models },
2903
+ ...state.configuredModels ? { configuredModels: { ...state.configuredModels } } : {}
2904
+ };
2905
+ try {
2906
+ for (const override of overrides) {
2907
+ if (!isClaudeCodeModelAlias(override.model)) {
2908
+ throw new Error(
2909
+ `Unsupported Claude Code model "${override.model}" for ${override.role}; use sonnet, opus, haiku, or inherit.`
2910
+ );
2911
+ }
2912
+ const roleItem = plan.items.find(
2913
+ (item) => item.kind === "subagent" && item.role === override.role
2914
+ );
2915
+ if (!roleItem) {
2916
+ throw new Error(`Missing Claude Code subagent for ${override.role}.`);
2917
+ }
2918
+ const before = existsSync6(roleItem.targetPath) ? readFileSync7(roleItem.targetPath, "utf8") : roleItem.content;
2919
+ const updated = replaceSubagentModel(before, override.model);
2920
+ if (writeTextWithBackup(roleItem.targetPath, updated)) {
2921
+ changed.push(roleItem.targetPath);
2922
+ }
2923
+ nextState.configuredModels ??= {};
2924
+ nextState.configuredModels[override.role] = override.model;
2925
+ }
2926
+ if (writeTextWithBackup(statePath, stableJson3(nextState))) {
2927
+ changed.push(statePath);
2928
+ }
2929
+ return { success: true, changed, diagnostics };
2930
+ } catch (error) {
2931
+ return {
2932
+ success: false,
2933
+ changed,
2934
+ diagnostics,
2935
+ error: error instanceof Error ? error.message : String(error)
2936
+ };
2937
+ }
2938
+ }
2939
+ function formatClaudeCodeSetupPlan(plan) {
2940
+ const lines = plan.items.map(
2941
+ (item) => `- ${item.action}: ${item.targetPath} (${item.description})`
2942
+ );
2943
+ return ["Claude Code setup plan:", ...lines].join("\n");
2944
+ }
2945
+
2946
+ // src/cli/operations/claude-code.ts
2947
+ var CLAUDE_CODE_DISPLAY_NAME = "Claude Code";
2948
+ var claudeCodePlanSources = /* @__PURE__ */ new WeakMap();
2949
+ var claudeCodeModelSources = /* @__PURE__ */ new WeakMap();
2950
+ var claudeCodeActions = [
2951
+ {
2952
+ id: "claude-code-status",
2953
+ kind: "status",
2954
+ label: "Status",
2955
+ description: "Inspect managed Claude Code plugin state",
2956
+ dryRun: false,
2957
+ requiresConfirmation: false,
2958
+ supported: true
2959
+ },
2960
+ {
2961
+ id: "claude-code-list",
2962
+ kind: "list",
2963
+ label: "List",
2964
+ description: "List managed Claude Code surfaces and actions",
2965
+ dryRun: false,
2966
+ requiresConfirmation: false,
2967
+ supported: true
2968
+ },
2969
+ {
2970
+ id: "claude-code-install",
2971
+ kind: "install",
2972
+ label: "Install",
2973
+ description: "Preview Claude Code plugin package install",
2974
+ dryRun: true,
2975
+ requiresConfirmation: true,
2976
+ supported: true
2977
+ },
2978
+ {
2979
+ id: "claude-code-update",
2980
+ kind: "update",
2981
+ label: "Update",
2982
+ description: "Preview Claude Code managed plugin refresh",
2983
+ dryRun: true,
2984
+ requiresConfirmation: true,
2985
+ supported: true
2986
+ },
2987
+ {
2988
+ id: "claude-code-sync",
2989
+ kind: "sync",
2990
+ label: "Sync",
2991
+ description: "Preview Claude Code managed plugin sync",
2992
+ dryRun: true,
2993
+ requiresConfirmation: true,
2994
+ supported: true
2995
+ },
2996
+ {
2997
+ id: "claude-code-model-config",
2998
+ kind: "model-config",
2999
+ label: "Model",
3000
+ description: "Preview supported Claude Code subagent model line changes",
3001
+ dryRun: true,
3002
+ requiresConfirmation: true,
3003
+ supported: true
3004
+ }
3005
+ ];
3006
+ var claudeCodeOperationAdapter = {
3007
+ id: "claude",
3008
+ displayName: CLAUDE_CODE_DISPLAY_NAME,
3009
+ available: true,
3010
+ description: "Claude Code plugin package and managed subagent surfaces.",
3011
+ actions: claudeCodeActions
3012
+ };
3013
+ function claudeCodeConfig(context = { cwd: process.cwd() }, dryRun) {
3014
+ return {
3015
+ dryRun,
3016
+ reset: false,
3017
+ // Match the installer (install.ts uses 'user') so post-install status,
3018
+ // update, sync, and model commands resolve the same plugin root.
3019
+ scope: context.scope ?? "user",
3020
+ projectRoot: context.cwd,
3021
+ homeDir: context.homeDir,
3022
+ packageRoot: context.packageRoot
3023
+ };
3024
+ }
3025
+ function claudeCodeDisclaimers() {
3026
+ return [
3027
+ {
3028
+ message: "Role permissions are enforced by subagent frontmatter tools; the orchestrator is the main session, injected via the SessionStart hook.",
3029
+ code: "claude-code-first-class"
3030
+ },
3031
+ {
3032
+ message: "Subagent models accept only sonnet, opus, haiku, or inherit.",
3033
+ code: "claude-code-model-aliases"
3034
+ }
3035
+ ];
3036
+ }
3037
+ function warning(message, code) {
3038
+ return { severity: "important", message, code };
3039
+ }
3040
+ function targetForItem(item, state, observed) {
3041
+ return {
3042
+ kind: item.kind === "managed-model-state" ? "memory-state" : "generated-artifact",
3043
+ path: item.targetPath,
3044
+ label: item.role ? `Claude Code ${item.role} subagent` : item.kind.replaceAll("-", " "),
3045
+ state,
3046
+ expected: item.action,
3047
+ observed,
3048
+ description: item.description
3049
+ };
3050
+ }
3051
+ function surfaceForItem(item) {
3052
+ return {
3053
+ id: `${item.kind}:${item.role ?? basename2(item.targetPath)}`,
3054
+ label: item.role ? `Claude Code ${item.role} subagent` : item.kind.replaceAll("-", " "),
3055
+ path: item.targetPath,
3056
+ description: item.description
3057
+ };
3058
+ }
3059
+ function backupForItem(item) {
3060
+ return {
3061
+ required: item.requiresBackup,
3062
+ strategy: item.requiresBackup ? "managed-backup-file" : "none",
3063
+ destinations: item.requiresBackup ? [{ path: `${item.targetPath}.bak`, label: "managed backup" }] : []
3064
+ };
3065
+ }
3066
+ function manifestState(item) {
3067
+ if (!existsSync7(item.targetPath))
3068
+ return { state: "missing", observed: "absent" };
3069
+ const observed = readFileSync8(item.targetPath, "utf8");
3070
+ if (observed === item.content)
3071
+ return { state: "installed", observed: "current" };
3072
+ try {
3073
+ const expectedVersion = JSON.parse(item.content).version;
3074
+ const observedVersion = JSON.parse(observed).version;
3075
+ if (typeof expectedVersion === "string" && typeof observedVersion === "string" && expectedVersion !== observedVersion) {
3076
+ return {
3077
+ state: "outdated",
3078
+ observed: `version ${observedVersion}; expected ${expectedVersion}`
3079
+ };
3080
+ }
3081
+ } catch {
3082
+ return { state: "unknown", observed: "unparseable plugin manifest" };
3083
+ }
3084
+ return { state: "drift", observed: "content differs" };
3085
+ }
3086
+ function contentState(item) {
3087
+ if (!existsSync7(item.targetPath))
3088
+ return { state: "missing", observed: "absent" };
3089
+ return readFileSync8(item.targetPath, "utf8") === item.content ? { state: "installed", observed: "current" } : { state: "drift", observed: "content differs" };
3090
+ }
3091
+ function classifyItem(item) {
3092
+ if (item.kind === "plugin-manifest") return manifestState(item);
3093
+ return contentState(item);
3094
+ }
3095
+ function aggregateState(states) {
3096
+ if (states.includes("unknown")) return "unknown";
3097
+ if (states.includes("drift")) return "drift";
3098
+ if (states.includes("outdated")) return "outdated";
3099
+ if (states.includes("missing")) return "missing";
3100
+ return "installed";
3101
+ }
3102
+ function statusSummary(state) {
3103
+ switch (state) {
3104
+ case "installed":
3105
+ return "Claude Code managed plugin is installed and current.";
3106
+ case "missing":
3107
+ return "Claude Code managed plugin is missing.";
3108
+ case "drift":
3109
+ return "Claude Code managed plugin exists but differs from expected output.";
3110
+ case "outdated":
3111
+ return "Claude Code managed plugin includes an older generated manifest.";
3112
+ case "unknown":
3113
+ return "Claude Code managed plugin could not be classified safely.";
3114
+ }
3115
+ }
3116
+ function statusFromSetupPlan(plan) {
3117
+ const classified = plan.items.map((item) => ({
3118
+ item,
3119
+ ...classifyItem(item)
3120
+ }));
3121
+ const state = aggregateState(classified.map((entry) => entry.state));
3122
+ return {
3123
+ harness: "claude",
3124
+ displayName: CLAUDE_CODE_DISPLAY_NAME,
3125
+ state,
3126
+ summary: statusSummary(state),
3127
+ targets: classified.map(
3128
+ ({ item, state: state2, observed }) => targetForItem(item, state2, observed)
3129
+ ),
3130
+ diagnostics: plan.diagnostics.map((message) => ({
3131
+ severity: "minor",
3132
+ message,
3133
+ code: "claude-code-diagnostic"
3134
+ })),
3135
+ actions: claudeCodeActions,
3136
+ disclaimers: [
3137
+ ...claudeCodeDisclaimers(),
3138
+ ...plan.disclaimers.map((message) => ({ message }))
3139
+ ]
3140
+ };
3141
+ }
3142
+ function getClaudeCodeStatus(context = { cwd: process.cwd() }) {
3143
+ let plan;
3144
+ try {
3145
+ plan = buildClaudeCodeSetupPlan(claudeCodeConfig(context, true));
3146
+ } catch (error) {
3147
+ const message = error instanceof Error ? error.message : String(error);
3148
+ return {
3149
+ harness: "claude",
3150
+ displayName: CLAUDE_CODE_DISPLAY_NAME,
3151
+ state: "unknown",
3152
+ summary: `Claude Code setup plan could not be built: ${message}`,
3153
+ targets: [],
3154
+ diagnostics: [
3155
+ {
3156
+ severity: "critical",
3157
+ message,
3158
+ code: "claude-code-plan-build-failed"
3159
+ }
3160
+ ],
3161
+ actions: claudeCodeActions,
3162
+ disclaimers: claudeCodeDisclaimers()
3163
+ };
3164
+ }
3165
+ return statusFromSetupPlan(plan);
3166
+ }
3167
+ function planItemFromSetup(item) {
3168
+ return {
3169
+ title: item.description,
3170
+ target: targetForItem(item),
3171
+ preview: item.content,
3172
+ backup: backupForItem(item)
3173
+ };
3174
+ }
3175
+ function planFromSetup(id, action, title, summary, setupPlan) {
3176
+ const status = statusFromSetupPlan(setupPlan);
3177
+ const canApply = status.state === "installed" || status.state === "missing" || status.state === "outdated" || status.state === "drift";
3178
+ const plan = {
3179
+ id,
3180
+ harness: "claude",
3181
+ action,
3182
+ title,
3183
+ summary,
3184
+ dryRun: true,
3185
+ canApply,
3186
+ targets: status.targets,
3187
+ surfaces: setupPlan.items.map(surfaceForItem),
3188
+ backup: {
3189
+ required: setupPlan.items.some((item) => item.requiresBackup),
3190
+ strategy: "managed-backup-file",
3191
+ description: "Existing Claude Code plugin files are backed up before being overwritten."
3192
+ },
3193
+ items: setupPlan.items.map(planItemFromSetup),
3194
+ warnings: status.diagnostics,
3195
+ disclaimers: [
3196
+ ...claudeCodeDisclaimers(),
3197
+ ...setupPlan.disclaimers.map((message) => ({ message }))
3198
+ ]
3199
+ };
3200
+ claudeCodePlanSources.set(plan, setupPlan);
3201
+ return plan;
3202
+ }
3203
+ function buildClaudeCodeInstallPlan(context = { cwd: process.cwd() }) {
3204
+ return planFromSetup(
3205
+ "claude-code-install-preview",
3206
+ "install",
3207
+ "Install Claude Code plugin package",
3208
+ "Preview Claude Code plugin package install using buildClaudeCodeSetupPlan().",
3209
+ buildClaudeCodeSetupPlan(claudeCodeConfig(context, true))
3210
+ );
3211
+ }
3212
+ function buildClaudeCodeUpdatePlan(context = { cwd: process.cwd() }) {
3213
+ return planFromSetup(
3214
+ "claude-code-update-preview",
3215
+ "update",
3216
+ "Update Claude Code plugin package",
3217
+ "Preview Claude Code managed plugin refresh using buildClaudeCodeSetupPlan().",
3218
+ buildClaudeCodeSetupPlan(claudeCodeConfig(context, true))
3219
+ );
3220
+ }
3221
+ function buildClaudeCodeSyncPlan(context = { cwd: process.cwd() }) {
3222
+ return planFromSetup(
3223
+ "claude-code-sync-preview",
3224
+ "sync",
3225
+ "Sync Claude Code plugin package",
3226
+ "Preview Claude Code managed plugin subagents, MCP, hooks, and skills.",
3227
+ buildClaudeCodeSetupPlan(claudeCodeConfig(context, true))
3228
+ );
3229
+ }
3230
+ function isClaudeCodeRole(role) {
3231
+ return CLAUDE_CODE_ROLE_NAMES.includes(role);
3232
+ }
3233
+ function buildClaudeCodeModelPlan(input, context = { cwd: process.cwd() }) {
3234
+ const status = getClaudeCodeStatus(context);
3235
+ const supportedRoles = input.roles.filter((role) => isClaudeCodeRole(role.role)).filter((role) => isClaudeCodeModelAlias(role.model)).map((role) => ({
3236
+ role: role.role,
3237
+ model: role.model
3238
+ }));
3239
+ const rejectedRoles = input.roles.filter(
3240
+ (role) => !isClaudeCodeRole(role.role) || !isClaudeCodeModelAlias(role.model)
3241
+ );
3242
+ const warnings = [
3243
+ ...status.diagnostics,
3244
+ ...input.warnings ?? [],
3245
+ ...rejectedRoles.map(
3246
+ (role) => warning(
3247
+ `Claude Code does not accept role "${role.role}" with model "${role.model}"; roles must be one of ${CLAUDE_CODE_ROLE_NAMES.join(", ")} and models must be sonnet, opus, haiku, or inherit.`,
3248
+ "claude-code-unsupported-model-role"
3249
+ )
3250
+ )
3251
+ ];
3252
+ if (input.harness !== "claude") {
3253
+ warnings.push(
3254
+ warning(
3255
+ "Model plan target harness must be claude.",
3256
+ "claude-code-model-harness-mismatch"
3257
+ )
3258
+ );
3259
+ }
3260
+ const targets = supportedRoles.map(({ role }) => {
3261
+ const target = status.targets.find(
3262
+ (candidate) => candidate.path?.endsWith(`agents${pathSep()}${role}.md`)
3263
+ );
3264
+ return target ?? {
3265
+ kind: "generated-artifact",
3266
+ label: `Claude Code ${role} subagent`,
3267
+ state: "missing"
3268
+ };
3269
+ });
3270
+ const stateTarget = status.targets.find(
3271
+ (target) => target.path?.endsWith(".thoth-agents-managed-models.json")
3272
+ );
3273
+ const plan = {
3274
+ id: "claude-code-model-config-preview",
3275
+ harness: "claude",
3276
+ action: "model-config",
3277
+ title: "Configure Claude Code subagent model lines",
3278
+ summary: "Preview model changes for generated Claude Code subagent files and managed model state only.",
3279
+ dryRun: true,
3280
+ canApply: input.harness === "claude" && supportedRoles.length > 0 && status.state !== "unknown",
3281
+ targets: [...targets, ...stateTarget ? [stateTarget] : []],
3282
+ surfaces: targets.map((target) => ({
3283
+ id: `claude-code-model:${target.label}`,
3284
+ label: target.label ?? "Claude Code subagent",
3285
+ path: target.path,
3286
+ state: target.state
3287
+ })),
3288
+ backup: {
3289
+ required: true,
3290
+ strategy: "managed-backup-file",
3291
+ description: "Existing subagent files and managed model state are backed up by the managed write helper."
3292
+ },
3293
+ items: supportedRoles.map(({ role, model }) => ({
3294
+ title: `Set ${role} Claude Code subagent model line`,
3295
+ target: targets.find(
3296
+ (target) => target.path?.endsWith(`agents${pathSep()}${role}.md`)
3297
+ ) ?? {
3298
+ kind: "generated-artifact",
3299
+ label: `Claude Code ${role} subagent`
3300
+ },
3301
+ preview: JSON.stringify({ role, model }),
3302
+ backup: { required: true, strategy: "managed-backup-file" }
3303
+ })),
3304
+ warnings,
3305
+ disclaimers: [
3306
+ ...claudeCodeDisclaimers(),
3307
+ ...input.disclaimers ?? [],
3308
+ {
3309
+ message: "Claude Code model configuration writes only generated subagent frontmatter model lines and the managed model state JSON.",
3310
+ code: "claude-code-model-supported-surface"
3311
+ }
3312
+ ]
3313
+ };
3314
+ claudeCodeModelSources.set(plan, {
3315
+ config: claudeCodeConfig(context, false),
3316
+ roles: supportedRoles
3317
+ });
3318
+ return plan;
3319
+ }
3320
+ function pathSep() {
3321
+ return process.platform === "win32" ? "\\" : "/";
3322
+ }
3323
+ function rejectPlan(plan, message, severity = "critical") {
3324
+ return {
3325
+ harness: plan.harness,
3326
+ action: plan.action,
3327
+ applied: false,
3328
+ summary: message,
3329
+ changedTargets: [],
3330
+ backups: [],
3331
+ warnings: [{ severity, message }],
3332
+ disclaimers: claudeCodeDisclaimers()
3333
+ };
3334
+ }
3335
+ function validateClaudeCodePlan(plan) {
3336
+ if (plan.harness !== "claude") {
3337
+ return rejectPlan(plan, "Only Claude Code operation plans can be applied.");
3338
+ }
3339
+ if (!plan.canApply) {
3340
+ return rejectPlan(
3341
+ plan,
3342
+ "Claude Code plan cannot be applied because canApply is false."
3343
+ );
3344
+ }
3345
+ if (!["install", "update", "sync", "model-config"].includes(plan.action)) {
3346
+ return rejectPlan(
3347
+ plan,
3348
+ `Unsupported Claude Code apply action: ${plan.action}.`
3349
+ );
3350
+ }
3351
+ if (plan.items.length === 0) {
3352
+ return rejectPlan(plan, "Claude Code plan has no items to apply.");
3353
+ }
3354
+ return null;
3355
+ }
3356
+ function applyClaudeCodePlan(plan) {
3357
+ const rejection = validateClaudeCodePlan(plan);
3358
+ if (rejection) return rejection;
3359
+ if (plan.action === "model-config") {
3360
+ const source = claudeCodeModelSources.get(plan);
3361
+ if (!source) {
3362
+ return rejectPlan(
3363
+ plan,
3364
+ "Claude Code model plan was not produced by buildClaudeCodeModelPlan in this process."
3365
+ );
3366
+ }
3367
+ const result2 = applyClaudeCodeManagedModelOverrides(
3368
+ source.config,
3369
+ source.roles.map((role) => ({
3370
+ role: role.role,
3371
+ model: role.model
3372
+ }))
3373
+ );
3374
+ return {
3375
+ harness: "claude",
3376
+ action: "model-config",
3377
+ applied: result2.success,
3378
+ summary: result2.success ? "Applied Claude Code subagent model overrides." : result2.error ?? "Failed to apply Claude Code subagent model overrides.",
3379
+ changedTargets: result2.changed.map((path4) => ({
3380
+ kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
3381
+ path: path4,
3382
+ label: basename2(path4),
3383
+ state: "installed"
3384
+ })),
3385
+ backups: result2.changed.filter((path4) => existsSync7(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
3386
+ warnings: result2.success ? [] : [{ severity: "critical", message: result2.error ?? "apply failed." }],
3387
+ disclaimers: claudeCodeDisclaimers()
3388
+ };
3389
+ }
3390
+ const setupPlan = claudeCodePlanSources.get(plan);
3391
+ if (!setupPlan) {
3392
+ return rejectPlan(
3393
+ plan,
3394
+ "Claude Code setup plan was not produced by a Claude Code operation plan builder in this process."
3395
+ );
3396
+ }
3397
+ const result = applyClaudeCodeSetup({ ...setupPlan, dryRun: false });
3398
+ return {
3399
+ harness: "claude",
3400
+ action: plan.action,
3401
+ applied: result.success,
3402
+ summary: result.success ? `Applied Claude Code managed ${plan.action} plan.` : result.error ?? `Failed to apply Claude Code ${plan.action} plan.`,
3403
+ changedTargets: result.changed.map((path4) => ({
3404
+ kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
3405
+ path: path4,
3406
+ label: basename2(path4),
3407
+ state: "installed"
3408
+ })),
3409
+ backups: result.changed.filter((path4) => existsSync7(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
3410
+ warnings: result.success ? [] : [{ severity: "critical", message: result.error ?? "apply failed." }],
3411
+ disclaimers: claudeCodeDisclaimers()
3412
+ };
3413
+ }
3414
+ function defaultClaudeCodeModelRoles() {
3415
+ return CLAUDE_CODE_ROLE_NAMES.map((role) => ({
3416
+ role,
3417
+ model: CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS[role]
3418
+ }));
3419
+ }
3420
+
3421
+ // src/cli/operations/codex.ts
3422
+ import { existsSync as existsSync8, readFileSync as readFileSync9 } from "fs";
3423
+ import { basename as basename3 } from "path";
3424
+ var CODEX_DISPLAY_NAME = "Codex";
3425
+ var codexPlanSources = /* @__PURE__ */ new WeakMap();
3426
+ var codexModelSources = /* @__PURE__ */ new WeakMap();
3427
+ var codexActions = [
3428
+ {
3429
+ id: "codex-status",
3430
+ kind: "status",
3431
+ label: "Status",
3432
+ description: "Inspect managed Codex setup state",
3433
+ dryRun: false,
3434
+ requiresConfirmation: false,
3435
+ supported: true
3436
+ },
2303
3437
  {
2304
3438
  id: "codex-list",
2305
3439
  kind: "list",
@@ -2377,10 +3511,10 @@ function codexDisclaimers() {
2377
3511
  }
2378
3512
  ];
2379
3513
  }
2380
- function warning(message, code) {
3514
+ function warning2(message, code) {
2381
3515
  return { severity: "important", message, code };
2382
3516
  }
2383
- function targetForItem(item, state, observed) {
3517
+ function targetForItem2(item, state, observed) {
2384
3518
  return {
2385
3519
  kind: item.kind === "managed-model-state" ? "memory-state" : "generated-artifact",
2386
3520
  path: item.targetPath,
@@ -2391,15 +3525,15 @@ function targetForItem(item, state, observed) {
2391
3525
  description: item.description
2392
3526
  };
2393
3527
  }
2394
- function surfaceForItem(item) {
3528
+ function surfaceForItem2(item) {
2395
3529
  return {
2396
- id: `${item.kind}:${item.role ?? basename2(item.targetPath)}`,
3530
+ id: `${item.kind}:${item.role ?? basename3(item.targetPath)}`,
2397
3531
  label: item.role ? `Codex ${item.role} subagent TOML` : item.kind.replaceAll("-", " "),
2398
3532
  path: item.targetPath,
2399
3533
  description: item.description
2400
3534
  };
2401
3535
  }
2402
- function backupForItem(item) {
3536
+ function backupForItem2(item) {
2403
3537
  return {
2404
3538
  required: item.requiresBackup,
2405
3539
  strategy: item.requiresBackup ? "managed-backup-file" : "none",
@@ -2407,9 +3541,9 @@ function backupForItem(item) {
2407
3541
  };
2408
3542
  }
2409
3543
  function rootBlockState(item) {
2410
- if (!existsSync5(item.targetPath))
3544
+ if (!existsSync8(item.targetPath))
2411
3545
  return { state: "missing", observed: "absent" };
2412
- const content = readFileSync5(item.targetPath, "utf8");
3546
+ const content = readFileSync9(item.targetPath, "utf8");
2413
3547
  if (item.content && content.includes(item.content)) {
2414
3548
  return { state: "installed", observed: "managed root block present" };
2415
3549
  }
@@ -2419,22 +3553,22 @@ function rootBlockState(item) {
2419
3553
  return { state: "missing", observed: "managed root block absent" };
2420
3554
  }
2421
3555
  function managedModelState(item) {
2422
- if (!existsSync5(item.targetPath))
3556
+ if (!existsSync8(item.targetPath))
2423
3557
  return { state: "missing", observed: "absent" };
2424
3558
  try {
2425
- const parsed = JSON.parse(readFileSync5(item.targetPath, "utf8"));
3559
+ const parsed = JSON.parse(readFileSync9(item.targetPath, "utf8"));
2426
3560
  if (parsed.version !== MANAGED_MODEL_STATE_VERSION || !parsed.models || typeof parsed.models !== "object" || Array.isArray(parsed.models)) {
2427
3561
  return { state: "unknown", observed: "invalid managed model state" };
2428
3562
  }
2429
- return item.content === readFileSync5(item.targetPath, "utf8") ? { state: "installed", observed: "managed model state current" } : { state: "drift", observed: "managed model state differs" };
3563
+ return item.content === readFileSync9(item.targetPath, "utf8") ? { state: "installed", observed: "managed model state current" } : { state: "drift", observed: "managed model state differs" };
2430
3564
  } catch {
2431
3565
  return { state: "unknown", observed: "unparseable managed model state" };
2432
3566
  }
2433
3567
  }
2434
3568
  function userConfigState(item) {
2435
- if (!existsSync5(item.targetPath))
3569
+ if (!existsSync8(item.targetPath))
2436
3570
  return { state: "missing", observed: "absent" };
2437
- const content = readFileSync5(item.targetPath, "utf8");
3571
+ const content = readFileSync9(item.targetPath, "utf8");
2438
3572
  if (/^\s*default_mode_request_user_input\s*=\s*true\b/m.test(content)) {
2439
3573
  return {
2440
3574
  state: "installed",
@@ -2447,10 +3581,10 @@ function userConfigState(item) {
2447
3581
  return { state: "missing", observed: "managed feature flag absent" };
2448
3582
  }
2449
3583
  function marketplaceState(item) {
2450
- if (!existsSync5(item.targetPath))
3584
+ if (!existsSync8(item.targetPath))
2451
3585
  return { state: "missing", observed: "absent" };
2452
3586
  try {
2453
- const parsed = JSON.parse(readFileSync5(item.targetPath, "utf8"));
3587
+ const parsed = JSON.parse(readFileSync9(item.targetPath, "utf8"));
2454
3588
  const plugins = Array.isArray(parsed.plugins) ? parsed.plugins : [];
2455
3589
  const entry = plugins.find(
2456
3590
  (plugin) => plugin && typeof plugin === "object" && "name" in plugin && plugin.name === "thoth-agents"
@@ -2463,11 +3597,11 @@ function marketplaceState(item) {
2463
3597
  return { state: "unknown", observed: "unparseable marketplace JSON" };
2464
3598
  }
2465
3599
  }
2466
- function contentState(item) {
2467
- if (!existsSync5(item.targetPath))
3600
+ function contentState2(item) {
3601
+ if (!existsSync8(item.targetPath))
2468
3602
  return { state: "missing", observed: "absent" };
2469
- const observed = readFileSync5(item.targetPath, "utf8");
2470
- if (basename2(item.targetPath) === "plugin.json" && item.targetPath.replaceAll("\\", "/").includes("/.codex-plugin/")) {
3603
+ const observed = readFileSync9(item.targetPath, "utf8");
3604
+ if (basename3(item.targetPath) === "plugin.json" && item.targetPath.replaceAll("\\", "/").includes("/.codex-plugin/")) {
2471
3605
  try {
2472
3606
  const expectedVersion = JSON.parse(item.content ?? "{}").version;
2473
3607
  const observedVersion = JSON.parse(observed).version;
@@ -2495,7 +3629,7 @@ function contentState(item) {
2495
3629
  }
2496
3630
  return { state: "drift", observed: "content differs" };
2497
3631
  }
2498
- function classifyItem(item) {
3632
+ function classifyItem2(item) {
2499
3633
  if (item.action === "diagnose-only") {
2500
3634
  return { state: "unknown", observed: "diagnostic guidance only" };
2501
3635
  }
@@ -2505,16 +3639,16 @@ function classifyItem(item) {
2505
3639
  }
2506
3640
  if (item.action === "merge-toml") return userConfigState(item);
2507
3641
  if (item.action === "merge-marketplace") return marketplaceState(item);
2508
- return contentState(item);
3642
+ return contentState2(item);
2509
3643
  }
2510
- function aggregateState(states) {
3644
+ function aggregateState2(states) {
2511
3645
  if (states.includes("unknown")) return "unknown";
2512
3646
  if (states.includes("drift")) return "drift";
2513
3647
  if (states.includes("outdated")) return "outdated";
2514
3648
  if (states.includes("missing")) return "missing";
2515
3649
  return "installed";
2516
3650
  }
2517
- function statusSummary(state) {
3651
+ function statusSummary2(state) {
2518
3652
  switch (state) {
2519
3653
  case "installed":
2520
3654
  return "Codex managed setup surfaces are installed and current.";
@@ -2551,8 +3685,8 @@ function getCodexStatus(context = { cwd: process.cwd() }) {
2551
3685
  disclaimers: codexDisclaimers()
2552
3686
  };
2553
3687
  }
2554
- const classified = plan.items.filter((item) => item.action !== "diagnose-only").map((item) => ({ item, ...classifyItem(item) }));
2555
- const state = aggregateState(classified.map((item) => item.state));
3688
+ const classified = plan.items.filter((item) => item.action !== "diagnose-only").map((item) => ({ item, ...classifyItem2(item) }));
3689
+ const state = aggregateState2(classified.map((item) => item.state));
2556
3690
  const diagnostics = plan.diagnostics.map((message) => ({
2557
3691
  severity: "minor",
2558
3692
  message,
@@ -2562,9 +3696,9 @@ function getCodexStatus(context = { cwd: process.cwd() }) {
2562
3696
  harness: "codex",
2563
3697
  displayName: CODEX_DISPLAY_NAME,
2564
3698
  state,
2565
- summary: statusSummary(state),
3699
+ summary: statusSummary2(state),
2566
3700
  targets: classified.map(
2567
- ({ item, state: state2, observed }) => targetForItem(item, state2, observed)
3701
+ ({ item, state: state2, observed }) => targetForItem2(item, state2, observed)
2568
3702
  ),
2569
3703
  diagnostics,
2570
3704
  actions: codexActions,
@@ -2574,15 +3708,15 @@ function getCodexStatus(context = { cwd: process.cwd() }) {
2574
3708
  ]
2575
3709
  };
2576
3710
  }
2577
- function planItemFromSetup(item) {
3711
+ function planItemFromSetup2(item) {
2578
3712
  return {
2579
3713
  title: item.description,
2580
- target: targetForItem(item),
3714
+ target: targetForItem2(item),
2581
3715
  preview: item.content,
2582
- backup: backupForItem(item)
3716
+ backup: backupForItem2(item)
2583
3717
  };
2584
3718
  }
2585
- function planFromSetup(id, action, title, summary, setupPlan, context) {
3719
+ function planFromSetup2(id, action, title, summary, setupPlan, context) {
2586
3720
  const status = getCodexStatus(context);
2587
3721
  const canApply = status.state === "installed" || status.state === "missing" || status.state === "outdated";
2588
3722
  const plan = {
@@ -2594,17 +3728,17 @@ function planFromSetup(id, action, title, summary, setupPlan, context) {
2594
3728
  dryRun: true,
2595
3729
  canApply,
2596
3730
  targets: status.targets,
2597
- surfaces: setupPlan.items.filter((item) => item.action !== "diagnose-only").map(surfaceForItem),
3731
+ surfaces: setupPlan.items.filter((item) => item.action !== "diagnose-only").map(surfaceForItem2),
2598
3732
  backup: {
2599
3733
  required: setupPlan.items.some((item) => item.requiresBackup),
2600
3734
  strategy: "existing-helper",
2601
3735
  description: "Codex setup apply uses the existing installer backup behavior for files that already exist."
2602
3736
  },
2603
- items: setupPlan.items.map(planItemFromSetup),
3737
+ items: setupPlan.items.map(planItemFromSetup2),
2604
3738
  warnings: [
2605
3739
  ...status.diagnostics,
2606
3740
  ...canApply ? [] : [
2607
- warning(
3741
+ warning2(
2608
3742
  `Codex state is ${status.state}; apply is disabled until the state is safely classified or repaired.`,
2609
3743
  "codex-unsafe-state"
2610
3744
  )
@@ -2620,7 +3754,7 @@ function planFromSetup(id, action, title, summary, setupPlan, context) {
2620
3754
  }
2621
3755
  function buildCodexUpdatePlan(context = { cwd: process.cwd() }) {
2622
3756
  const setupPlan = buildCodexSetupPlan(codexConfig(context, true));
2623
- return planFromSetup(
3757
+ return planFromSetup2(
2624
3758
  "codex-update-preview",
2625
3759
  "update",
2626
3760
  "Update Codex managed setup",
@@ -2631,7 +3765,7 @@ function buildCodexUpdatePlan(context = { cwd: process.cwd() }) {
2631
3765
  }
2632
3766
  function buildCodexSyncPlan(context = { cwd: process.cwd() }) {
2633
3767
  const setupPlan = buildCodexSetupPlan(codexConfig(context, true));
2634
- return planFromSetup(
3768
+ return planFromSetup2(
2635
3769
  "codex-sync-preview",
2636
3770
  "sync",
2637
3771
  "Sync Codex managed configuration",
@@ -2642,7 +3776,7 @@ function buildCodexSyncPlan(context = { cwd: process.cwd() }) {
2642
3776
  }
2643
3777
  function buildCodexInstallPlan(context = { cwd: process.cwd() }) {
2644
3778
  const setupPlan = buildCodexSetupPlan(codexConfig(context, true));
2645
- return planFromSetup(
3779
+ return planFromSetup2(
2646
3780
  "codex-install-preview",
2647
3781
  "install",
2648
3782
  "Install Codex managed setup",
@@ -2673,7 +3807,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
2673
3807
  ...status.diagnostics,
2674
3808
  ...input.warnings ?? [],
2675
3809
  ...unsupportedRoles.map(
2676
- (role) => warning(
3810
+ (role) => warning2(
2677
3811
  `Codex does not expose a supported generated model surface for ${role.role}; this plan will not write that role.`,
2678
3812
  "codex-unsupported-model-role"
2679
3813
  )
@@ -2681,7 +3815,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
2681
3815
  ];
2682
3816
  if (input.harness !== "codex") {
2683
3817
  warnings.push(
2684
- warning(
3818
+ warning2(
2685
3819
  "Model plan target harness must be codex.",
2686
3820
  "codex-model-harness-mismatch"
2687
3821
  )
@@ -2748,7 +3882,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
2748
3882
  codexModelSources.set(plan, { config, roles: supportedRoles });
2749
3883
  return plan;
2750
3884
  }
2751
- function rejectPlan(plan, message, severity = "critical") {
3885
+ function rejectPlan2(plan, message, severity = "critical") {
2752
3886
  return {
2753
3887
  harness: plan.harness,
2754
3888
  action: plan.action,
@@ -2762,19 +3896,19 @@ function rejectPlan(plan, message, severity = "critical") {
2762
3896
  }
2763
3897
  function validateCodexPlan(plan) {
2764
3898
  if (plan.harness !== "codex") {
2765
- return rejectPlan(plan, "Only Codex operation plans can be applied.");
3899
+ return rejectPlan2(plan, "Only Codex operation plans can be applied.");
2766
3900
  }
2767
3901
  if (!plan.canApply) {
2768
- return rejectPlan(
3902
+ return rejectPlan2(
2769
3903
  plan,
2770
3904
  "Codex plan cannot be applied because canApply is false."
2771
3905
  );
2772
3906
  }
2773
3907
  if (!["install", "update", "sync", "model-config"].includes(plan.action)) {
2774
- return rejectPlan(plan, `Unsupported Codex apply action: ${plan.action}.`);
3908
+ return rejectPlan2(plan, `Unsupported Codex apply action: ${plan.action}.`);
2775
3909
  }
2776
3910
  if (plan.items.length === 0) {
2777
- return rejectPlan(plan, "Codex plan has no items to apply.");
3911
+ return rejectPlan2(plan, "Codex plan has no items to apply.");
2778
3912
  }
2779
3913
  return null;
2780
3914
  }
@@ -2784,7 +3918,7 @@ function applyCodexPlan(plan) {
2784
3918
  if (plan.action === "model-config") {
2785
3919
  const source = codexModelSources.get(plan);
2786
3920
  if (!source) {
2787
- return rejectPlan(
3921
+ return rejectPlan2(
2788
3922
  plan,
2789
3923
  "Codex model plan was not produced by buildCodexModelPlan in this process."
2790
3924
  );
@@ -2795,13 +3929,13 @@ function applyCodexPlan(plan) {
2795
3929
  action: "model-config",
2796
3930
  applied: result2.success,
2797
3931
  summary: result2.success ? "Applied Codex subagent model overrides." : result2.error ?? "Failed to apply Codex subagent model overrides.",
2798
- changedTargets: result2.changed.map((path2) => ({
2799
- kind: path2.endsWith(".json") ? "memory-state" : "generated-artifact",
2800
- path: path2,
2801
- label: basename2(path2),
3932
+ changedTargets: result2.changed.map((path4) => ({
3933
+ kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
3934
+ path: path4,
3935
+ label: basename3(path4),
2802
3936
  state: "installed"
2803
3937
  })),
2804
- backups: result2.changed.filter((path2) => existsSync5(`${path2}.bak`)).map((path2) => ({ path: `${path2}.bak`, label: "managed backup" })),
3938
+ backups: result2.changed.filter((path4) => existsSync8(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
2805
3939
  warnings: result2.success ? [] : [
2806
3940
  {
2807
3941
  severity: "critical",
@@ -2813,7 +3947,7 @@ function applyCodexPlan(plan) {
2813
3947
  }
2814
3948
  const setupPlan = codexPlanSources.get(plan);
2815
3949
  if (!setupPlan) {
2816
- return rejectPlan(
3950
+ return rejectPlan2(
2817
3951
  plan,
2818
3952
  "Codex setup plan was not produced by a Codex operation plan builder in this process."
2819
3953
  );
@@ -2824,13 +3958,13 @@ function applyCodexPlan(plan) {
2824
3958
  action: plan.action,
2825
3959
  applied: result.success,
2826
3960
  summary: result.success ? `Applied Codex managed ${plan.action} plan.` : result.error ?? `Failed to apply Codex ${plan.action} plan.`,
2827
- changedTargets: result.changed.map((path2) => ({
2828
- kind: path2.endsWith(".json") ? "memory-state" : "generated-artifact",
2829
- path: path2,
2830
- label: basename2(path2),
3961
+ changedTargets: result.changed.map((path4) => ({
3962
+ kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
3963
+ path: path4,
3964
+ label: basename3(path4),
2831
3965
  state: "installed"
2832
3966
  })),
2833
- backups: result.changed.filter((path2) => existsSync5(`${path2}.bak`)).map((path2) => ({ path: `${path2}.bak`, label: "managed backup" })),
3967
+ backups: result.changed.filter((path4) => existsSync8(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
2834
3968
  warnings: result.success ? [] : [
2835
3969
  {
2836
3970
  severity: "critical",
@@ -2842,14 +3976,14 @@ function applyCodexPlan(plan) {
2842
3976
  }
2843
3977
 
2844
3978
  // src/cli/operations/opencode.ts
2845
- import { existsSync as existsSync7 } from "fs";
2846
- import { join as join6 } from "path";
3979
+ import { existsSync as existsSync10 } from "fs";
3980
+ import { join as join10 } from "path";
2847
3981
 
2848
3982
  // src/cli/skills.ts
2849
3983
  import { spawnSync } from "child_process";
2850
- import { existsSync as existsSync6 } from "fs";
2851
- import { homedir as homedir2 } from "os";
2852
- import { join as join5 } from "path";
3984
+ import { existsSync as existsSync9 } from "fs";
3985
+ import { homedir as homedir3 } from "os";
3986
+ import { join as join9 } from "path";
2853
3987
  var RECOMMENDED_SKILLS = [
2854
3988
  {
2855
3989
  name: "simplify",
@@ -2864,11 +3998,11 @@ var RECOMMENDED_SKILLS = [
2864
3998
  description: "Browser automation for visual checks and testing"
2865
3999
  }
2866
4000
  ];
2867
- function getRecommendedSkillPath(skill, homeDir = homedir2()) {
2868
- return join5(homeDir, ".agents", "skills", skill.skillName, "SKILL.md");
4001
+ function getRecommendedSkillPath(skill, homeDir = homedir3()) {
4002
+ return join9(homeDir, ".agents", "skills", skill.skillName, "SKILL.md");
2869
4003
  }
2870
4004
  function isRecommendedSkillInstalled(skill, options = {}) {
2871
- return existsSync6(getRecommendedSkillPath(skill, options.homeDir));
4005
+ return existsSync9(getRecommendedSkillPath(skill, options.homeDir));
2872
4006
  }
2873
4007
  function runSkillInstallCommand(skill) {
2874
4008
  const args = [
@@ -3022,11 +4156,11 @@ function openCodeSkillTargets(context) {
3022
4156
  const homeDir = homeDirFromContext(context);
3023
4157
  const recommendedTargets = RECOMMENDED_SKILLS.map(
3024
4158
  (skill) => {
3025
- const path2 = getRecommendedSkillPath(skill, homeDir);
3026
- const installed = existsSync7(path2);
4159
+ const path4 = getRecommendedSkillPath(skill, homeDir);
4160
+ const installed = existsSync10(path4);
3027
4161
  return {
3028
4162
  kind: "skill",
3029
- path: path2,
4163
+ path: path4,
3030
4164
  label: titleCaseSkillName(skill.skillName),
3031
4165
  state: installed ? "installed" : "missing",
3032
4166
  expected: "recommended global OpenCode skill",
@@ -3035,11 +4169,11 @@ function openCodeSkillTargets(context) {
3035
4169
  }
3036
4170
  );
3037
4171
  const bundledTargets = CUSTOM_SKILLS.map((skill) => {
3038
- const path2 = join6(getCustomSkillsDir(), skill.name, "SKILL.md");
3039
- const installed = existsSync7(path2);
4172
+ const path4 = join10(getCustomSkillsDir(), skill.name, "SKILL.md");
4173
+ const installed = existsSync10(path4);
3040
4174
  return {
3041
4175
  kind: "skill",
3042
- path: path2,
4176
+ path: path4,
3043
4177
  label: titleCaseSkillName(skill.name),
3044
4178
  state: installed ? "installed" : "missing",
3045
4179
  expected: "bundled thoth-agents OpenCode skill",
@@ -3142,8 +4276,8 @@ function getOpenCodeStatus(context = { cwd: process.cwd() }) {
3142
4276
  actions: openCodeActions
3143
4277
  };
3144
4278
  }
3145
- const mainExists = existsSync7(mainPath);
3146
- const liteExists = existsSync7(litePath);
4279
+ const mainExists = existsSync10(mainPath);
4280
+ const liteExists = existsSync10(litePath);
3147
4281
  const mainTarget = {
3148
4282
  ...targetForMainConfig(),
3149
4283
  observed: configPluginMarker(main.config)
@@ -3240,11 +4374,11 @@ function getOpenCodeStatus(context = { cwd: process.cwd() }) {
3240
4374
  actions: openCodeActions
3241
4375
  };
3242
4376
  }
3243
- function defaultBackup(path2) {
4377
+ function defaultBackup(path4) {
3244
4378
  return {
3245
4379
  required: true,
3246
4380
  strategy: "managed-backup-file",
3247
- destinations: [{ path: `${path2}.bak`, label: "managed backup" }]
4381
+ destinations: [{ path: `${path4}.bak`, label: "managed backup" }]
3248
4382
  };
3249
4383
  }
3250
4384
  function defaultDisclaimers() {
@@ -3296,7 +4430,7 @@ function planFromItems(id, action, title, summary, items) {
3296
4430
  };
3297
4431
  }
3298
4432
  function buildOpenCodeUpdatePlan(_context = { cwd: process.cwd() }) {
3299
- const path2 = getExistingConfigPath();
4433
+ const path4 = getExistingConfigPath();
3300
4434
  return planFromItems(
3301
4435
  "opencode-update-preview",
3302
4436
  "update",
@@ -3308,7 +4442,7 @@ function buildOpenCodeUpdatePlan(_context = { cwd: process.cwd() }) {
3308
4442
  target: targetForMainConfig(),
3309
4443
  state: getOpenCodeStatus().state,
3310
4444
  preview: `plugin: ["${EXPECTED_PLUGIN}"]`,
3311
- backup: defaultBackup(path2)
4445
+ backup: defaultBackup(path4)
3312
4446
  }
3313
4447
  ]
3314
4448
  );
@@ -3475,7 +4609,7 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
3475
4609
  }
3476
4610
  return plan;
3477
4611
  }
3478
- function rejectPlan2(plan, message, severity = "critical") {
4612
+ function rejectPlan3(plan, message, severity = "critical") {
3479
4613
  return {
3480
4614
  harness: plan.harness,
3481
4615
  action: plan.action,
@@ -3519,27 +4653,27 @@ function ensureLatestPluginEntry() {
3519
4653
  }
3520
4654
  function validateApplyPlan(plan) {
3521
4655
  if (plan.harness !== "opencode") {
3522
- return rejectPlan2(plan, "Only OpenCode operation plans can be applied.");
4656
+ return rejectPlan3(plan, "Only OpenCode operation plans can be applied.");
3523
4657
  }
3524
4658
  if (!plan.canApply) {
3525
- return rejectPlan2(
4659
+ return rejectPlan3(
3526
4660
  plan,
3527
4661
  "OpenCode plan cannot be applied because canApply is false."
3528
4662
  );
3529
4663
  }
3530
4664
  if (!["install", "update", "sync", "model-config"].includes(plan.action)) {
3531
- return rejectPlan2(
4665
+ return rejectPlan3(
3532
4666
  plan,
3533
4667
  `Unsupported OpenCode apply action: ${plan.action}.`
3534
4668
  );
3535
4669
  }
3536
4670
  if (plan.items.length === 0) {
3537
- return rejectPlan2(plan, "OpenCode plan has no items to apply.");
4671
+ return rejectPlan3(plan, "OpenCode plan has no items to apply.");
3538
4672
  }
3539
4673
  if (plan.items.some(
3540
4674
  (item) => !item.title || !item.target.path && item.target.kind !== "skill" || item.state === "drift" || item.state === "unknown"
3541
4675
  )) {
3542
- return rejectPlan2(
4676
+ return rejectPlan3(
3543
4677
  plan,
3544
4678
  "OpenCode plan contains malformed or unsafe items."
3545
4679
  );
@@ -3551,7 +4685,7 @@ function applyModelPlan(plan) {
3551
4685
  for (const item of plan.items) {
3552
4686
  const match = /^Set (.+) OpenCode model override$/.exec(item.title);
3553
4687
  if (!match) {
3554
- return rejectPlan2(
4688
+ return rejectPlan3(
3555
4689
  plan,
3556
4690
  "OpenCode model plan contains an unrecognized item."
3557
4691
  );
@@ -3560,7 +4694,7 @@ function applyModelPlan(plan) {
3560
4694
  try {
3561
4695
  parsed2 = item.preview ? JSON.parse(item.preview) : {};
3562
4696
  } catch {
3563
- return rejectPlan2(
4697
+ return rejectPlan3(
3564
4698
  plan,
3565
4699
  "OpenCode model plan contains malformed preview JSON."
3566
4700
  );
@@ -3568,7 +4702,7 @@ function applyModelPlan(plan) {
3568
4702
  const role = match[1] ?? "";
3569
4703
  const model = parsed2[role]?.model;
3570
4704
  if (!ROLE_NAMES.includes(role) || typeof model !== "string") {
3571
- return rejectPlan2(
4705
+ return rejectPlan3(
3572
4706
  plan,
3573
4707
  "OpenCode model plan contains an invalid role or model."
3574
4708
  );
@@ -3576,7 +4710,7 @@ function applyModelPlan(plan) {
3576
4710
  roleModels.set(role, model);
3577
4711
  }
3578
4712
  if (roleModels.size === 0) {
3579
- return rejectPlan2(
4713
+ return rejectPlan3(
3580
4714
  plan,
3581
4715
  "OpenCode model plan does not contain any role overrides."
3582
4716
  );
@@ -3612,7 +4746,7 @@ function applyModelPlan(plan) {
3612
4746
  observed: "agents role overrides updated"
3613
4747
  }
3614
4748
  ],
3615
- backups: existsSync7(`${targetPath}.bak`) ? [{ path: `${targetPath}.bak`, label: "managed backup" }] : [],
4749
+ backups: existsSync10(`${targetPath}.bak`) ? [{ path: `${targetPath}.bak`, label: "managed backup" }] : [],
3616
4750
  warnings: [],
3617
4751
  disclaimers: defaultDisclaimers()
3618
4752
  };
@@ -3621,7 +4755,7 @@ function applyInstallSkills(plan) {
3621
4755
  for (const skill of RECOMMENDED_SKILLS) {
3622
4756
  const result = installRecommendedSkill(skill);
3623
4757
  if (result.status === "failed") {
3624
- return rejectPlan2(
4758
+ return rejectPlan3(
3625
4759
  plan,
3626
4760
  `Failed to install recommended OpenCode skill: ${skill.name}.`
3627
4761
  );
@@ -3629,7 +4763,7 @@ function applyInstallSkills(plan) {
3629
4763
  }
3630
4764
  const bundled = installCustomSkills();
3631
4765
  if (!bundled.success) {
3632
- return rejectPlan2(
4766
+ return rejectPlan3(
3633
4767
  plan,
3634
4768
  "Failed to install bundled thoth-agents OpenCode skills."
3635
4769
  );
@@ -3641,7 +4775,7 @@ function applyOpenCodePlan(plan) {
3641
4775
  if (rejection) return rejection;
3642
4776
  const status = getOpenCodeStatus();
3643
4777
  if (!classifyApplySafety(status.state)) {
3644
- return rejectPlan2(
4778
+ return rejectPlan3(
3645
4779
  plan,
3646
4780
  `OpenCode state is ${status.state}; refusing to apply plan without a safe status.`
3647
4781
  );
@@ -3651,7 +4785,7 @@ function applyOpenCodePlan(plan) {
3651
4785
  const backups = [];
3652
4786
  const pluginResult = ensureLatestPluginEntry();
3653
4787
  if (!pluginResult.success) {
3654
- return rejectPlan2(
4788
+ return rejectPlan3(
3655
4789
  plan,
3656
4790
  pluginResult.error ?? "Failed to update OpenCode plugin config."
3657
4791
  );
@@ -3660,7 +4794,7 @@ function applyOpenCodePlan(plan) {
3660
4794
  ...targetForMainConfig("installed"),
3661
4795
  observed: `plugin includes ${EXPECTED_PLUGIN}`
3662
4796
  });
3663
- if (existsSync7(`${pluginResult.configPath}.bak`)) {
4797
+ if (existsSync10(`${pluginResult.configPath}.bak`)) {
3664
4798
  backups.push({
3665
4799
  path: `${pluginResult.configPath}.bak`,
3666
4800
  label: "OpenCode config backup"
@@ -3669,7 +4803,7 @@ function applyOpenCodePlan(plan) {
3669
4803
  if (plan.action === "sync" || plan.action === "install") {
3670
4804
  const defaultAgentResult = disableDefaultAgents();
3671
4805
  if (!defaultAgentResult.success) {
3672
- return rejectPlan2(
4806
+ return rejectPlan3(
3673
4807
  plan,
3674
4808
  defaultAgentResult.error ?? "Failed to disable OpenCode default agents."
3675
4809
  );
@@ -3686,7 +4820,7 @@ function applyOpenCodePlan(plan) {
3686
4820
  getExistingLiteConfigPath()
3687
4821
  );
3688
4822
  if (!liteResult.success) {
3689
- return rejectPlan2(
4823
+ return rejectPlan3(
3690
4824
  plan,
3691
4825
  liteResult.error ?? "Failed to write thoth-agents config."
3692
4826
  );
@@ -3695,7 +4829,7 @@ function applyOpenCodePlan(plan) {
3695
4829
  ...targetForLiteConfig("installed"),
3696
4830
  observed: "seven-agent roster written"
3697
4831
  });
3698
- if (existsSync7(`${liteResult.configPath}.bak`)) {
4832
+ if (existsSync10(`${liteResult.configPath}.bak`)) {
3699
4833
  backups.push({
3700
4834
  path: `${liteResult.configPath}.bak`,
3701
4835
  label: "thoth-agents config backup"
@@ -3727,7 +4861,8 @@ function applyOpenCodePlan(plan) {
3727
4861
  // src/cli/operations/index.ts
3728
4862
  var OPERATION_HARNESSES = {
3729
4863
  opencode: opencodeOperationAdapter,
3730
- codex: codexOperationAdapter
4864
+ codex: codexOperationAdapter,
4865
+ claude: claudeCodeOperationAdapter
3731
4866
  };
3732
4867
  var SUPPORTED_OPERATION_HARNESSES = Object.keys(
3733
4868
  OPERATION_HARNESSES
@@ -3742,14 +4877,27 @@ function getOperationHarness(harness) {
3742
4877
  }
3743
4878
 
3744
4879
  export {
4880
+ claudeCodeAdapter,
3745
4881
  codexAdapter,
3746
4882
  CODEX_ROLE_NAMES,
3747
4883
  parseRoleTomlModel,
3748
4884
  buildCodexSetupPlan,
3749
4885
  formatCodexSetupPlan,
3750
4886
  applyCodexSetup,
4887
+ CLAUDE_CODE_ROLE_NAMES,
4888
+ parseSubagentModel,
4889
+ buildClaudeCodeSetupPlan,
4890
+ applyClaudeCodeSetup,
4891
+ formatClaudeCodeSetupPlan,
3751
4892
  RECOMMENDED_SKILLS,
3752
4893
  installRecommendedSkill,
4894
+ getClaudeCodeStatus,
4895
+ buildClaudeCodeInstallPlan,
4896
+ buildClaudeCodeUpdatePlan,
4897
+ buildClaudeCodeSyncPlan,
4898
+ buildClaudeCodeModelPlan,
4899
+ applyClaudeCodePlan,
4900
+ defaultClaudeCodeModelRoles,
3753
4901
  getCodexStatus,
3754
4902
  buildCodexUpdatePlan,
3755
4903
  buildCodexSyncPlan,