thoth-agents 0.1.19 → 0.2.1

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.
@@ -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-2SGSRR6L.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
@@ -310,6 +361,41 @@ function memoryGovernanceDiagnostics(input) {
310
361
  return diagnostics;
311
362
  }
312
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
+
313
399
  // src/harness/writers/codex-plugin-package.ts
314
400
  import { createHash } from "crypto";
315
401
 
@@ -740,8 +826,8 @@ function normalizePath(value) {
740
826
  }
741
827
  function pluginRootReference(pathValue) {
742
828
  const normalized = normalizePath(pathValue);
743
- const relative3 = normalized.slice(".codex-plugin/".length);
744
- return `./${relative3}`;
829
+ const relative4 = normalized.slice(".codex-plugin/".length);
830
+ return `./${relative4}`;
745
831
  }
746
832
  function sha256(content) {
747
833
  return `sha256:${createHash("sha256").update(content).digest("hex")}`;
@@ -1087,43 +1173,49 @@ function renderCodexToml(input) {
1087
1173
  }
1088
1174
 
1089
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
1090
1180
  import { createHash as createHash2 } from "crypto";
1091
1181
  import * as fs from "fs";
1092
1182
  import * as path from "path";
1093
- var OUTPUT_MODE_CONFIG = {
1094
- "plugin-package": {
1095
- basePath: ".codex-plugin/skills",
1096
- manifestPath: ".codex-plugin/skills/.thoth-agents-manifest.json",
1097
- surfaceId: "plugin-skills-directory",
1098
- label: "plugin-bundled"
1099
- },
1100
- "repo-local-fallback": {
1101
- basePath: ".agents/skills",
1102
- manifestPath: ".agents/skills/.thoth-agents-manifest.json",
1103
- surfaceId: "repo-skills-directory",
1104
- label: "fallback .agents/skills"
1105
- }
1106
- };
1107
- function normalizePath2(value) {
1183
+ function normalizeSkillPath(value) {
1108
1184
  return value.split(path.sep).join("/");
1109
1185
  }
1110
- function collectFiles(directory) {
1186
+ function collectSkillFiles(directory) {
1111
1187
  if (!fs.existsSync(directory)) return [];
1112
1188
  const entries = fs.readdirSync(directory, { withFileTypes: true });
1113
1189
  const files = [];
1114
1190
  for (const entry of entries) {
1115
1191
  const entryPath = path.join(directory, entry.name);
1116
1192
  if (entry.isDirectory()) {
1117
- files.push(...collectFiles(entryPath));
1193
+ files.push(...collectSkillFiles(entryPath));
1118
1194
  } else if (entry.isFile()) {
1119
1195
  files.push(entryPath);
1120
1196
  }
1121
1197
  }
1122
1198
  return files.sort((left, right) => left.localeCompare(right));
1123
1199
  }
1124
- function sha2562(content) {
1200
+ function sha256Hash(content) {
1125
1201
  return `sha256:${createHash2("sha256").update(content).digest("hex")}`;
1126
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
+ };
1127
1219
  function resolveOutputModes(input) {
1128
1220
  const requested = input.outputModes ?? (input.outputMode ? [input.outputMode] : []);
1129
1221
  const modes = requested.length > 0 ? requested : ["plugin-package"];
@@ -1166,8 +1258,8 @@ function renderCodexSkillLayout(input) {
1166
1258
  (left, right) => left.name.localeCompare(right.name)
1167
1259
  )) {
1168
1260
  const sourceBaseRoot = input.packageRoot ?? input.projectRoot;
1169
- const sourceRoot = path.join(sourceBaseRoot, skill.sourcePath);
1170
- const files = collectFiles(sourceRoot);
1261
+ const sourceRoot = path2.join(sourceBaseRoot, skill.sourcePath);
1262
+ const files = collectSkillFiles(sourceRoot);
1171
1263
  if (files.length === 0) {
1172
1264
  diagnostics.push({
1173
1265
  severity: "warning",
@@ -1180,12 +1272,14 @@ function renderCodexSkillLayout(input) {
1180
1272
  continue;
1181
1273
  }
1182
1274
  for (const file of files) {
1183
- const relative3 = normalizePath2(path.relative(sourceRoot, file));
1184
- const content = fs.readFileSync(file, "utf8");
1185
- 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
+ );
1186
1280
  for (const mode of outputModes) {
1187
1281
  const config = OUTPUT_MODE_CONFIG[mode];
1188
- const outputPath = `${config.basePath}/${skill.name}/${relative3}`;
1282
+ const outputPath = `${config.basePath}/${skill.name}/${relative4}`;
1189
1283
  artifacts.push({
1190
1284
  harness: "codex",
1191
1285
  kind: "skill",
@@ -1197,7 +1291,7 @@ function renderCodexSkillLayout(input) {
1197
1291
  name: skill.name,
1198
1292
  sourcePath,
1199
1293
  outputPath,
1200
- sha256: sha2562(content)
1294
+ sha256: sha256Hash(content)
1201
1295
  });
1202
1296
  }
1203
1297
  }
@@ -1233,37 +1327,6 @@ function createCodexPluginPackageManifest(context) {
1233
1327
  description: "Delegate-first OpenCode plugin with seven agents, thoth-mem persistence, and bundled SDD skills."
1234
1328
  };
1235
1329
  }
1236
- function findRootPackageJsonPath(startDirs) {
1237
- for (const startDir of startDirs) {
1238
- let currentDir = resolve(startDir);
1239
- while (true) {
1240
- const packageJsonPath = resolve(currentDir, "package.json");
1241
- if (existsSync2(packageJsonPath)) {
1242
- const packageJsonText = readFileSync2(packageJsonPath, "utf8");
1243
- const packageJson = JSON.parse(packageJsonText);
1244
- if (packageJson.name === "thoth-agents") {
1245
- return packageJsonPath;
1246
- }
1247
- }
1248
- const parentDir = dirname(currentDir);
1249
- if (parentDir === currentDir) {
1250
- break;
1251
- }
1252
- currentDir = parentDir;
1253
- }
1254
- }
1255
- throw new Error(
1256
- "Unable to locate the thoth-agents root package.json from the render context or current working directory."
1257
- );
1258
- }
1259
- function readPackageJsonVersion(packageJsonPath) {
1260
- const packageJsonText = readFileSync2(packageJsonPath, "utf8");
1261
- const packageJson = JSON.parse(packageJsonText);
1262
- if (typeof packageJson.version !== "string" || packageJson.version.length === 0) {
1263
- throw new Error("Root package.json version must be a non-empty string.");
1264
- }
1265
- return packageJson.version;
1266
- }
1267
1330
  function stableJson2(value) {
1268
1331
  return `${JSON.stringify(value, null, 2)}
1269
1332
  `;
@@ -1437,13 +1500,6 @@ function agentModelReasoningEffort(role) {
1437
1500
  function isCodexSubagentName(name) {
1438
1501
  return name in CODEX_SUBAGENT_DEFAULT_MODELS;
1439
1502
  }
1440
- function getPrimaryModelId(model) {
1441
- if (Array.isArray(model)) {
1442
- const first = model[0];
1443
- return typeof first === "string" ? first : first?.id;
1444
- }
1445
- return model;
1446
- }
1447
1503
  function getCodexAgentModel(role, config) {
1448
1504
  if (!isCodexSubagentName(role.name)) return void 0;
1449
1505
  return getPrimaryModelId(config?.agents?.[role.name]?.model) ?? CODEX_SUBAGENT_DEFAULT_MODELS[role.name];
@@ -1626,64 +1682,8 @@ var codexAdapter = {
1626
1682
  }
1627
1683
  };
1628
1684
 
1629
- // src/cli/codex-paths.ts
1630
- import { homedir } from "os";
1631
- import { join as join2 } from "path";
1632
- var CODEX_ROLE_NAMES = [
1633
- "explorer",
1634
- "librarian",
1635
- "oracle",
1636
- "designer",
1637
- "quick",
1638
- "deep"
1639
- ];
1640
- function getCodexHome(options = {}) {
1641
- const explicit = options.codexHome ?? process.env.CODEX_HOME?.trim();
1642
- return explicit || join2(options.homeDir ?? homedir(), ".codex");
1643
- }
1644
- function resolveCodexTargets(options) {
1645
- const codexHome = getCodexHome(options);
1646
- const agentsDir = options.scope === "project" ? join2(options.projectRoot, ".codex", "agents") : join2(codexHome, "agents");
1647
- return {
1648
- scope: options.scope,
1649
- codexHome,
1650
- configPath: join2(codexHome, "config.toml"),
1651
- rootInstructionsPath: join2(codexHome, "AGENTS.md"),
1652
- roleAgentPaths: CODEX_ROLE_NAMES.map((role) => ({
1653
- role,
1654
- path: join2(agentsDir, `thoth-agents-${role}.toml`)
1655
- })),
1656
- managedModelsPath: join2(
1657
- codexHome,
1658
- "agents",
1659
- ".thoth-agents-managed-models.json"
1660
- ),
1661
- skillsDir: options.scope === "project" ? join2(options.projectRoot, ".agents", "skills") : join2(options.homeDir ?? homedir(), ".agents", "skills"),
1662
- packageRoot: join2(options.projectRoot, ".codex-plugin"),
1663
- personalPluginRoot: join2(codexHome, "plugins", "thoth-agents"),
1664
- personalMarketplacePath: join2(
1665
- options.homeDir ?? homedir(),
1666
- ".agents",
1667
- "plugins",
1668
- "marketplace.json"
1669
- )
1670
- };
1671
- }
1672
-
1673
- // src/cli/codex-install.ts
1674
- import {
1675
- copyFileSync as copyFileSync2,
1676
- existsSync as existsSync4,
1677
- mkdirSync as mkdirSync2,
1678
- readFileSync as readFileSync4,
1679
- rmSync,
1680
- writeFileSync as writeFileSync2
1681
- } from "fs";
1682
- import { basename, dirname as dirname3, isAbsolute, join as join4, relative as relative2 } from "path";
1683
- import { fileURLToPath as fileURLToPath2 } from "url";
1684
-
1685
1685
  // src/harness/codex-plugin-paths.ts
1686
- import { join as join3 } from "path";
1686
+ import { join as join4 } from "path";
1687
1687
  var CODEX_PLUGIN_ARTIFACT_PREFIX = ".codex-plugin/";
1688
1688
  function codexPluginRootArtifactPath(artifactPath) {
1689
1689
  if (!artifactPath.startsWith(CODEX_PLUGIN_ARTIFACT_PREFIX)) {
@@ -1692,7 +1692,7 @@ function codexPluginRootArtifactPath(artifactPath) {
1692
1692
  const relativePath = artifactPath.slice(CODEX_PLUGIN_ARTIFACT_PREFIX.length);
1693
1693
  if (relativePath === ".mcp.json") return relativePath;
1694
1694
  if (relativePath === "plugin.json" || relativePath.startsWith(".")) {
1695
- return join3(".codex-plugin", relativePath);
1695
+ return join4(".codex-plugin", relativePath);
1696
1696
  }
1697
1697
  return relativePath;
1698
1698
  }
@@ -1837,6 +1837,62 @@ function writeCodexConfigMerge(options) {
1837
1837
  }
1838
1838
  }
1839
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
+
1840
1896
  // src/cli/codex-install.ts
1841
1897
  var ROOT_START = "<!-- thoth-agents:codex-root:start -->";
1842
1898
  var ROOT_END = "<!-- thoth-agents:codex-root:end -->";
@@ -1850,23 +1906,16 @@ function mergeManagedBlock(existing, managedBlock) {
1850
1906
  return `${existing}${existing.endsWith("\n") || existing.length === 0 ? "" : "\n"}
1851
1907
  ${managedBlock}`;
1852
1908
  }
1853
- function writeTextWithBackup(path2, content) {
1854
- mkdirSync2(dirname3(path2), { recursive: true });
1855
- if (existsSync4(path2) && readFileSync4(path2, "utf8") === content) return false;
1856
- if (existsSync4(path2)) copyFileSync2(path2, `${path2}.bak`);
1857
- writeFileSync2(path2, content);
1858
- return true;
1859
- }
1860
1909
  function packageArtifactTarget(packageRoot, artifact) {
1861
- return join4(packageRoot, codexPluginRootArtifactPath(artifact.path));
1910
+ return join5(packageRoot, codexPluginRootArtifactPath(artifact.path));
1862
1911
  }
1863
1912
  function resolvePackageRoot(packageRoot) {
1864
1913
  if (packageRoot) return packageRoot;
1865
1914
  return findPackageRoot(fileURLToPath2(new URL(".", import.meta.url))) ?? void 0;
1866
1915
  }
1867
- function normalizeRelativeMarketplacePath(path2) {
1868
- const normalized = path2.replaceAll("\\", "/");
1869
- 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;
1870
1919
  if (normalized.startsWith("./")) return normalized;
1871
1920
  return `./${normalized}`;
1872
1921
  }
@@ -1889,39 +1938,11 @@ function managedMarketplaceEntry(homeDir, personalPluginRoot) {
1889
1938
  category: "Productivity"
1890
1939
  };
1891
1940
  }
1892
- function stableJson3(value) {
1893
- return `${JSON.stringify(value, null, 2)}
1894
- `;
1895
- }
1896
- function emptyManagedModelState() {
1897
- return {
1898
- version: MANAGED_MODEL_STATE_VERSION,
1899
- models: {}
1900
- };
1901
- }
1902
- function stringRecord(value) {
1903
- if (!value || typeof value !== "object" || Array.isArray(value)) return {};
1904
- return Object.fromEntries(
1905
- Object.entries(value).filter(
1906
- (entry) => typeof entry[0] === "string" && typeof entry[1] === "string"
1907
- )
1908
- );
1941
+ function emptyManagedModelState2() {
1942
+ return emptyManagedModelState(MANAGED_MODEL_STATE_VERSION);
1909
1943
  }
1910
- function readManagedModelState(path2) {
1911
- if (!existsSync4(path2)) return emptyManagedModelState();
1912
- try {
1913
- const parsed = JSON.parse(readFileSync4(path2, "utf8"));
1914
- if (parsed.version !== MANAGED_MODEL_STATE_VERSION || !parsed.models || typeof parsed.models !== "object" || Array.isArray(parsed.models)) {
1915
- return emptyManagedModelState();
1916
- }
1917
- return {
1918
- version: MANAGED_MODEL_STATE_VERSION,
1919
- models: stringRecord(parsed.models),
1920
- ...Object.keys(stringRecord(parsed.configuredModels)).length > 0 ? { configuredModels: stringRecord(parsed.configuredModels) } : {}
1921
- };
1922
- } catch {
1923
- return emptyManagedModelState();
1924
- }
1944
+ function readManagedModelState2(path4) {
1945
+ return readManagedModelState(path4, MANAGED_MODEL_STATE_VERSION);
1925
1946
  }
1926
1947
  function parseRoleTomlModel(content) {
1927
1948
  const match = /^model\s*=\s*"((?:\\.|[^"\\])*)"\s*$/m.exec(content);
@@ -1939,8 +1960,8 @@ function replaceRoleTomlModel(content, model) {
1939
1960
  return `${rendered}
1940
1961
  ${content}`;
1941
1962
  }
1942
- function roleManagedModelStateKey(path2) {
1943
- return basename(path2);
1963
+ function roleManagedModelStateKey(path4) {
1964
+ return basename(path4);
1944
1965
  }
1945
1966
  function applyCodexManagedModelOverrides(config, overrides) {
1946
1967
  if (config.dryRun) {
@@ -1970,7 +1991,7 @@ function applyCodexManagedModelOverrides(config, overrides) {
1970
1991
  ...plan.diagnostics,
1971
1992
  ...plan.disclaimers
1972
1993
  ]);
1973
- const state = readManagedModelState(statePath);
1994
+ const state = readManagedModelState2(statePath);
1974
1995
  const nextState = {
1975
1996
  version: MANAGED_MODEL_STATE_VERSION,
1976
1997
  models: { ...state.models },
@@ -1986,7 +2007,7 @@ function applyCodexManagedModelOverrides(config, overrides) {
1986
2007
  `Missing Codex role TOML content for ${override.role}.`
1987
2008
  );
1988
2009
  }
1989
- const before = existsSync4(roleItem.targetPath) ? readFileSync4(roleItem.targetPath, "utf8") : roleItem.content;
2010
+ const before = existsSync5(roleItem.targetPath) ? readFileSync5(roleItem.targetPath, "utf8") : roleItem.content;
1990
2011
  const updated = replaceRoleTomlModel(before, override.model);
1991
2012
  if (writeTextWithBackup(roleItem.targetPath, updated)) {
1992
2013
  changed.push(roleItem.targetPath);
@@ -2018,7 +2039,7 @@ function resolveRoleTomlContent(options) {
2018
2039
  const key = roleManagedModelStateKey(options.targetPath);
2019
2040
  if (!renderedModel) return options.renderedContent;
2020
2041
  const configuredModel = options.reset ? void 0 : options.state.configuredModels?.[key];
2021
- if (options.reset || !existsSync4(options.targetPath)) {
2042
+ if (options.reset || !existsSync5(options.targetPath)) {
2022
2043
  options.nextState.models[key] = renderedModel;
2023
2044
  if (configuredModel !== void 0) {
2024
2045
  options.nextState.configuredModels ??= {};
@@ -2028,7 +2049,7 @@ function resolveRoleTomlContent(options) {
2028
2049
  return options.renderedContent;
2029
2050
  }
2030
2051
  const currentModel = parseRoleTomlModel(
2031
- readFileSync4(options.targetPath, "utf8")
2052
+ readFileSync5(options.targetPath, "utf8")
2032
2053
  );
2033
2054
  if (configuredModel !== void 0) {
2034
2055
  options.nextState.models[key] = renderedModel;
@@ -2064,10 +2085,10 @@ function mergePersonalMarketplace(existing, homeDir, personalPluginRoot) {
2064
2085
  });
2065
2086
  }
2066
2087
  function roleArtifactContent(role, artifacts) {
2067
- const path2 = `.codex/agents/thoth-agents-${role}.toml`;
2068
- 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);
2069
2090
  if (!artifact?.content)
2070
- throw new Error(`Missing Codex role artifact: ${path2}`);
2091
+ throw new Error(`Missing Codex role artifact: ${path4}`);
2071
2092
  return String(artifact.content);
2072
2093
  }
2073
2094
  function buildCodexSetupPlan(config) {
@@ -2086,8 +2107,8 @@ function buildCodexSetupPlan(config) {
2086
2107
  (artifact) => artifact.path.startsWith(".codex-plugin/")
2087
2108
  );
2088
2109
  const rootBlock = renderCodexRootInstructions();
2089
- const managedModelState2 = readManagedModelState(targets.managedModelsPath);
2090
- const nextManagedModelState = emptyManagedModelState();
2110
+ const managedModelState2 = readManagedModelState2(targets.managedModelsPath);
2111
+ const nextManagedModelState = emptyManagedModelState2();
2091
2112
  const items = [
2092
2113
  {
2093
2114
  kind: "root-instructions",
@@ -2103,7 +2124,7 @@ function buildCodexSetupPlan(config) {
2103
2124
  action: "write-role-toml",
2104
2125
  targetPath: target.path,
2105
2126
  description: `Materialize Codex role subagent ${target.role}.`,
2106
- requiresBackup: existsSync4(target.path),
2127
+ requiresBackup: existsSync5(target.path),
2107
2128
  role: target.role,
2108
2129
  content: resolveRoleTomlContent({
2109
2130
  renderedContent: roleArtifactContent(target.role, render.artifacts),
@@ -2119,7 +2140,7 @@ function buildCodexSetupPlan(config) {
2119
2140
  action: "write-managed-model-state",
2120
2141
  targetPath: targets.managedModelsPath,
2121
2142
  description: "Record thoth-agents-managed Codex role model ownership state.",
2122
- requiresBackup: existsSync4(targets.managedModelsPath),
2143
+ requiresBackup: existsSync5(targets.managedModelsPath),
2123
2144
  content: stableJson3(nextManagedModelState)
2124
2145
  },
2125
2146
  ...packageArtifacts.map(
@@ -2137,7 +2158,7 @@ function buildCodexSetupPlan(config) {
2137
2158
  action: "merge-marketplace",
2138
2159
  targetPath: targets.personalMarketplacePath,
2139
2160
  description: "Register Personal Codex marketplace entry for the local thoth-agents plugin source.",
2140
- requiresBackup: existsSync4(targets.personalMarketplacePath),
2161
+ requiresBackup: existsSync5(targets.personalMarketplacePath),
2141
2162
  content: targets.personalPluginRoot
2142
2163
  },
2143
2164
  {
@@ -2201,21 +2222,18 @@ function formatRefreshPackageGroup(kind, groups) {
2201
2222
  }
2202
2223
  function commonTargetDirectory(items) {
2203
2224
  if (items.length === 0) return "";
2204
- let common = dirname3(items[0]?.targetPath ?? "");
2225
+ let common = dirname4(items[0]?.targetPath ?? "");
2205
2226
  for (const item of items.slice(1)) {
2206
2227
  while (!isSameOrChildPath(item.targetPath, common)) {
2207
- const parent = dirname3(common);
2228
+ const parent = dirname4(common);
2208
2229
  if (parent === common) return common;
2209
2230
  common = parent;
2210
2231
  }
2211
2232
  }
2212
2233
  return common;
2213
2234
  }
2214
- function isSameOrChildPath(path2, parent) {
2215
- return path2 === parent || path2.startsWith(`${parent}\\`) || path2.startsWith(`${parent}/`);
2216
- }
2217
- function uniqueMessages(messages) {
2218
- return [...new Set(messages)];
2235
+ function isSameOrChildPath(path4, parent) {
2236
+ return path4 === parent || path4.startsWith(`${parent}\\`) || path4.startsWith(`${parent}/`);
2219
2237
  }
2220
2238
  function applyCodexSetup(plan) {
2221
2239
  const changed = [];
@@ -2244,8 +2262,8 @@ function applyCodexSetup(plan) {
2244
2262
  if (item.action === "merge-marketplace") {
2245
2263
  if (item.content === void 0) continue;
2246
2264
  const content2 = mergePersonalMarketplace(
2247
- existsSync4(item.targetPath) ? readFileSync4(item.targetPath, "utf8") : "",
2248
- dirname3(dirname3(dirname3(item.targetPath))),
2265
+ existsSync5(item.targetPath) ? readFileSync5(item.targetPath, "utf8") : "",
2266
+ dirname4(dirname4(dirname4(item.targetPath))),
2249
2267
  item.content
2250
2268
  );
2251
2269
  if (writeTextWithBackup(item.targetPath, content2))
@@ -2254,7 +2272,7 @@ function applyCodexSetup(plan) {
2254
2272
  }
2255
2273
  if (item.content === void 0) continue;
2256
2274
  const content = item.action === "merge-managed-block" ? mergeManagedBlock(
2257
- existsSync4(item.targetPath) ? readFileSync4(item.targetPath, "utf8") : "",
2275
+ existsSync5(item.targetPath) ? readFileSync5(item.targetPath, "utf8") : "",
2258
2276
  item.content
2259
2277
  ) : item.content;
2260
2278
  if (writeTextWithBackup(item.targetPath, content))
@@ -2278,31 +2296,1139 @@ function managedRefreshRoots(plan) {
2278
2296
  group.push(item);
2279
2297
  refreshGroups.set(item.kind, group);
2280
2298
  }
2281
- 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);
2282
2300
  }
2283
2301
 
2284
- // src/cli/operations/codex.ts
2285
- 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";
2286
2304
  import { basename as basename2 } from "path";
2287
- var CODEX_DISPLAY_NAME = "Codex";
2288
- var codexPlanSources = /* @__PURE__ */ new WeakMap();
2289
- var codexModelSources = /* @__PURE__ */ new WeakMap();
2290
- var codexActions = [
2291
- {
2292
- id: "codex-status",
2293
- kind: "status",
2294
- label: "Status",
2295
- description: "Inspect managed Codex setup state",
2296
- dryRun: false,
2297
- requiresConfirmation: false,
2298
- supported: true
2299
- },
2300
- {
2301
- id: "codex-list",
2302
- kind: "list",
2303
- label: "List",
2304
- description: "List managed Codex surfaces and actions",
2305
- dryRun: false,
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
+ function isClaudeCodeSubagentName(name) {
2466
+ return name in CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS;
2467
+ }
2468
+ function getClaudeCodeAgentModel(role, config) {
2469
+ if (!isClaudeCodeSubagentName(role.name)) return "inherit";
2470
+ const override = getPrimaryModelId(config?.agents?.[role.name]?.model);
2471
+ if (override && isClaudeCodeModel(override)) return override;
2472
+ return CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS[role.name];
2473
+ }
2474
+ function claudeCodePromptSections(roleName) {
2475
+ switch (roleName) {
2476
+ case "orchestrator":
2477
+ return createOrchestratorPromptSections();
2478
+ case "explorer":
2479
+ case "librarian":
2480
+ case "oracle":
2481
+ return createReadOnlySpecialistPromptSections(roleName);
2482
+ case "designer":
2483
+ case "quick":
2484
+ case "deep":
2485
+ return createWriteCapableSpecialistPromptSections(roleName);
2486
+ }
2487
+ }
2488
+ function claudeCodeModelFamilyPromptSection(roleName, model) {
2489
+ const section = createModelFamilySection(roleName, model);
2490
+ return section ? renderPromptSection(section, CLAUDE_CODE_PROMPT_DIALECT) : void 0;
2491
+ }
2492
+ function claudeCodeStepBudgetPromptSection(steps) {
2493
+ const section = createStepBudgetSection(steps);
2494
+ return section ? renderPromptSection(section, CLAUDE_CODE_PROMPT_DIALECT) : void 0;
2495
+ }
2496
+ function renderClaudeCodeRolePrompt(roleName, config, model) {
2497
+ const promptOverrides = loadAgentPrompt(roleName, config?.preset);
2498
+ const override = getAgentOverride(config, roleName);
2499
+ const basePrompt = renderRolePrompt(
2500
+ claudeCodePromptSections(roleName),
2501
+ CLAUDE_CODE_PROMPT_DIALECT
2502
+ );
2503
+ const prompt = composeAgentPrompt({
2504
+ basePrompt,
2505
+ customPrompt: promptOverrides.prompt,
2506
+ customAppendPrompt: appendPromptSections(
2507
+ claudeCodeModelFamilyPromptSection(roleName, model),
2508
+ promptOverrides.appendPrompt
2509
+ )
2510
+ });
2511
+ return appendPromptSections(
2512
+ prompt,
2513
+ claudeCodeStepBudgetPromptSection(override?.steps)
2514
+ );
2515
+ }
2516
+ function claudeCodeRoleInstructions(role) {
2517
+ return [
2518
+ "<role-operational-contract>",
2519
+ `- Role: ${role.name}`,
2520
+ `- Mode: ${role.mode}`,
2521
+ `- Scope: ${role.scope}`,
2522
+ `- Responsibility: ${role.responsibility}`,
2523
+ "- Use AskUserQuestion for local blocking decisions.",
2524
+ `- ${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.`,
2525
+ "- This subagent inherits ALL of the main thread's tools, including MCP servers (thoth-mem, context7, exa, grep_app); read-only roles (explorer, librarian, oracle) MUST NOT mutate the workspace per this operational contract (instruction-level, not tooling-enforced).",
2526
+ ...role.toolGovernance.map((rule) => `- ${rule}`),
2527
+ ...role.verification.map((rule) => `- ${rule}`),
2528
+ "</role-operational-contract>"
2529
+ ].join("\n");
2530
+ }
2531
+ function roleInstructions2(role, config) {
2532
+ const model = getClaudeCodeAgentModel(role, config);
2533
+ return [
2534
+ renderClaudeCodeRolePrompt(role.name, config, model),
2535
+ claudeCodeRoleInstructions(role),
2536
+ renderMemoryGovernanceInstructions(role, CLAUDE_CODE_PROMPT_DIALECT)
2537
+ ].join("\n\n");
2538
+ }
2539
+ function renderClaudeCodeRootInstructions(config) {
2540
+ const rootOverride = getAgentOverride(config, "orchestrator");
2541
+ const rootPrompt = renderClaudeCodeRolePrompt(
2542
+ "orchestrator",
2543
+ config,
2544
+ rootOverride?.model ?? DEFAULT_MODELS.orchestrator
2545
+ );
2546
+ const specialists = getAgentPackContract().roles.filter((role) => role.name !== "orchestrator").map((role) => claudeCodeSubagentType(role.name)).join(", ");
2547
+ return [
2548
+ rootPrompt,
2549
+ "<claude-code-runtime>",
2550
+ "- 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.",
2551
+ '- 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.',
2552
+ "- 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).",
2553
+ "- If thoth-mem tools or identity values are unavailable, disclose that memory bootstrap could not run and continue without claiming memory was saved.",
2554
+ `- 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.`,
2555
+ "- Parallel delegation is supported: issue multiple Task calls in one turn for independent work.",
2556
+ '- 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.',
2557
+ "- Use AskUserQuestion for blocking user decisions; do not ask those questions in plain prose.",
2558
+ "- Track progress with TodoWrite; subagents do not own progress checkboxes or root-only memory.",
2559
+ "- Subagents inherit all of your tools (including MCP servers); role permissions are instruction-level, so read-only roles (explorer, librarian, oracle) must not mutate the workspace per their operational contract.",
2560
+ "</claude-code-runtime>"
2561
+ ].join("\n");
2562
+ }
2563
+ function claudeCodeMcpServers() {
2564
+ const [exaCommand = "", ...exaArgs] = exa.command;
2565
+ const [thothCommand = "", ...thothArgs] = DEFAULT_THOTH_COMMAND;
2566
+ return {
2567
+ exa: {
2568
+ command: exaCommand,
2569
+ ...exaArgs.length > 0 ? { args: exaArgs } : {},
2570
+ ...exa.environment && Object.keys(exa.environment).length > 0 ? { env: exa.environment } : {}
2571
+ },
2572
+ context7: { type: "http", url: CONTEXT7_MCP_URL },
2573
+ grep_app: { type: "http", url: GREP_APP_MCP_URL },
2574
+ thoth_mem: {
2575
+ command: thothCommand,
2576
+ ...thothArgs.length > 0 ? { args: thothArgs } : {}
2577
+ }
2578
+ };
2579
+ }
2580
+ function stableJson5(value) {
2581
+ return `${JSON.stringify(value, null, 2)}
2582
+ `;
2583
+ }
2584
+ function readRootPackageVersion2(context) {
2585
+ const packageJsonPath = findRootPackageJsonPath([
2586
+ ...hasPackageRoot(context) ? [context.packageRoot] : [],
2587
+ context.projectRoot,
2588
+ process.cwd(),
2589
+ fileURLToPath3(new URL(".", import.meta.url))
2590
+ ]);
2591
+ return readPackageJsonVersion(packageJsonPath);
2592
+ }
2593
+ function hasConfig(context) {
2594
+ return "config" in context;
2595
+ }
2596
+ function hasPackageRoot(context) {
2597
+ return "packageRoot" in context && typeof context.packageRoot === "string" && context.packageRoot.length > 0;
2598
+ }
2599
+ function createPluginManifest(context) {
2600
+ return {
2601
+ name: "thoth-agents",
2602
+ version: readRootPackageVersion2(context),
2603
+ description: "Delegate-first agent pack with seven roles, thoth-mem persistence, and bundled SDD skills, packaged for Claude Code.",
2604
+ author: { name: "thoth-agents" }
2605
+ };
2606
+ }
2607
+ function renderSubagentArtifacts(config) {
2608
+ const artifacts = [];
2609
+ for (const role of getAgentPackContract().roles.filter(
2610
+ (candidate) => candidate.name !== "orchestrator"
2611
+ )) {
2612
+ const content = renderClaudeCodeSubagent({
2613
+ name: role.name,
2614
+ description: role.responsibility,
2615
+ model: getClaudeCodeAgentModel(role, config),
2616
+ instructions: roleInstructions2(role, config)
2617
+ });
2618
+ artifacts.push({
2619
+ harness: "claude",
2620
+ kind: "agent-config",
2621
+ path: `agents/${role.name}.md`,
2622
+ description: `Claude Code subagent definition for ${role.name}.`,
2623
+ content
2624
+ });
2625
+ }
2626
+ return artifacts;
2627
+ }
2628
+ function renderOrchestratorArtifact(config) {
2629
+ const orchestrator = getAgentPackContract().roles.find(
2630
+ (role) => role.name === "orchestrator"
2631
+ );
2632
+ const content = renderClaudeCodeSubagent({
2633
+ name: "orchestrator",
2634
+ description: orchestrator?.responsibility ?? "Delegate-first root coordinator for SDD workflow and specialist dispatch.",
2635
+ model: "inherit",
2636
+ instructions: renderClaudeCodeRootInstructions(config)
2637
+ });
2638
+ return {
2639
+ harness: "claude",
2640
+ kind: "agent-config",
2641
+ path: "agents/orchestrator.md",
2642
+ description: "Claude Code orchestrator agent, activated as the main thread via settings.json.",
2643
+ content
2644
+ };
2645
+ }
2646
+ var claudeCodeAdapter = {
2647
+ id: "claude",
2648
+ displayName: "Claude Code",
2649
+ capabilities: CLAUDE_CODE_CAPABILITIES,
2650
+ render(context) {
2651
+ const config = hasConfig(context) ? context.config : void 0;
2652
+ const componentArtifacts = [
2653
+ ...renderSubagentArtifacts(config),
2654
+ renderOrchestratorArtifact(config),
2655
+ {
2656
+ harness: "claude",
2657
+ kind: "mcp-config",
2658
+ path: ".mcp.json",
2659
+ description: "Claude Code plugin-bundled MCP server definitions.",
2660
+ content: stableJson5({ mcpServers: claudeCodeMcpServers() })
2661
+ },
2662
+ {
2663
+ harness: "claude",
2664
+ kind: "harness-config",
2665
+ path: "settings.json",
2666
+ description: "Activates the orchestrator agent as the Claude Code main thread.",
2667
+ content: stableJson5({ agent: "orchestrator" })
2668
+ }
2669
+ ];
2670
+ const skillLayout = renderClaudeCodeSkillLayout({
2671
+ projectRoot: context.projectRoot,
2672
+ ...hasPackageRoot(context) ? { packageRoot: context.packageRoot } : {},
2673
+ skills: getSkillRegistry()
2674
+ });
2675
+ componentArtifacts.push(...skillLayout.artifacts);
2676
+ const pluginPackage = renderClaudeCodePluginPackage({
2677
+ manifest: createPluginManifest(context),
2678
+ componentArtifacts
2679
+ });
2680
+ return {
2681
+ harness: "claude",
2682
+ artifacts: pluginPackage.artifacts,
2683
+ diagnostics: [...skillLayout.diagnostics, ...pluginPackage.diagnostics]
2684
+ };
2685
+ }
2686
+ };
2687
+
2688
+ // src/cli/claude-code-install.ts
2689
+ import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
2690
+ import { join as join8 } from "path";
2691
+ import { fileURLToPath as fileURLToPath4 } from "url";
2692
+
2693
+ // src/cli/claude-code-paths.ts
2694
+ import { homedir as homedir2 } from "os";
2695
+ import { join as join7 } from "path";
2696
+ var CLAUDE_CODE_ROLE_NAMES = [
2697
+ "explorer",
2698
+ "librarian",
2699
+ "oracle",
2700
+ "designer",
2701
+ "quick",
2702
+ "deep"
2703
+ ];
2704
+ function resolveClaudeCodeTargets(options) {
2705
+ const home = options.homeDir ?? homedir2();
2706
+ const pluginRoot = options.scope === "project" ? join7(options.projectRoot, ".claude", "skills", "thoth-agents") : join7(home, ".claude", "skills", "thoth-agents");
2707
+ return {
2708
+ scope: options.scope,
2709
+ pluginRoot,
2710
+ pluginManifestPath: join7(pluginRoot, ".claude-plugin", "plugin.json"),
2711
+ agentPaths: CLAUDE_CODE_ROLE_NAMES.map((role) => ({
2712
+ role,
2713
+ path: join7(pluginRoot, "agents", `${role}.md`)
2714
+ })),
2715
+ mcpPath: join7(pluginRoot, ".mcp.json"),
2716
+ hooksPath: join7(pluginRoot, "hooks", "hooks.json"),
2717
+ skillsDir: join7(pluginRoot, "skills"),
2718
+ managedModelsPath: join7(pluginRoot, ".thoth-agents-managed-models.json")
2719
+ };
2720
+ }
2721
+
2722
+ // src/cli/claude-code-install.ts
2723
+ var CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION = 1;
2724
+ var isClaudeCodeModelAlias = isClaudeCodeModel;
2725
+ function parseSubagentModel(content) {
2726
+ return /^model:\s*(\S+)\s*$/m.exec(content)?.[1];
2727
+ }
2728
+ function replaceSubagentModel(content, model) {
2729
+ if (/^model:\s*\S+\s*$/m.test(content)) {
2730
+ return content.replace(/^model:\s*\S+\s*$/m, `model: ${model}`);
2731
+ }
2732
+ return content;
2733
+ }
2734
+ function emptyManagedModelState3() {
2735
+ return emptyManagedModelState(CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION);
2736
+ }
2737
+ function parseManagedModelStateJson2(text) {
2738
+ return parseManagedModelStateJson(
2739
+ text,
2740
+ CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION
2741
+ );
2742
+ }
2743
+ function readManagedModelState3(path4) {
2744
+ return readManagedModelState(
2745
+ path4,
2746
+ CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION
2747
+ );
2748
+ }
2749
+ function resolvePackageRoot2(packageRoot) {
2750
+ if (packageRoot) return packageRoot;
2751
+ return findPackageRoot(fileURLToPath4(new URL(".", import.meta.url))) ?? void 0;
2752
+ }
2753
+ function targetKindForArtifact(artifact) {
2754
+ const path4 = artifact.path.replaceAll("\\", "/");
2755
+ if (path4 === ".claude-plugin/plugin.json") return "plugin-manifest";
2756
+ if (path4.startsWith("agents/")) return "subagent";
2757
+ if (path4 === ".mcp.json") return "mcp-config";
2758
+ if (path4.startsWith("hooks/")) return "hook";
2759
+ if (path4.startsWith("skills/")) return "skill";
2760
+ return "plugin-asset";
2761
+ }
2762
+ function roleForArtifact(artifact) {
2763
+ const match = /^agents\/([^/]+)\.md$/.exec(
2764
+ artifact.path.replaceAll("\\", "/")
2765
+ );
2766
+ const name = match?.[1];
2767
+ return name && CLAUDE_CODE_ROLE_NAMES.includes(name) ? name : void 0;
2768
+ }
2769
+ function applyConfiguredModel(content, role, state, nextState, reset) {
2770
+ const renderedModel = parseSubagentModel(content);
2771
+ if (!role || !renderedModel) return content;
2772
+ nextState.models[role] = renderedModel;
2773
+ const configured = reset ? void 0 : state.configuredModels?.[role];
2774
+ if (configured === void 0) return content;
2775
+ nextState.configuredModels ??= {};
2776
+ nextState.configuredModels[role] = configured;
2777
+ return replaceSubagentModel(content, configured);
2778
+ }
2779
+ function buildClaudeCodeSetupPlan(config) {
2780
+ const targets = resolveClaudeCodeTargets({
2781
+ scope: config.scope,
2782
+ projectRoot: config.projectRoot,
2783
+ homeDir: config.homeDir
2784
+ });
2785
+ const packageRoot = resolvePackageRoot2(config.packageRoot);
2786
+ const render = claudeCodeAdapter.render({
2787
+ projectRoot: config.projectRoot,
2788
+ ...packageRoot ? { packageRoot } : {}
2789
+ });
2790
+ const state = readManagedModelState3(targets.managedModelsPath);
2791
+ const nextState = emptyManagedModelState3();
2792
+ const items = render.artifacts.map((artifact) => {
2793
+ const role = roleForArtifact(artifact);
2794
+ const rendered = String(artifact.content ?? "");
2795
+ const content = role !== void 0 ? applyConfiguredModel(rendered, role, state, nextState, config.reset) : rendered;
2796
+ const targetPath = join8(targets.pluginRoot, artifact.path);
2797
+ return {
2798
+ kind: targetKindForArtifact(artifact),
2799
+ action: "write-plugin-file",
2800
+ targetPath,
2801
+ description: `Materialize Claude Code plugin asset ${artifact.path}.`,
2802
+ requiresBackup: existsSync6(targetPath),
2803
+ content,
2804
+ ...role ? { role } : {}
2805
+ };
2806
+ });
2807
+ items.push({
2808
+ kind: "managed-model-state",
2809
+ action: "write-managed-model-state",
2810
+ targetPath: targets.managedModelsPath,
2811
+ description: "Record thoth-agents-managed Claude Code subagent model ownership state.",
2812
+ requiresBackup: existsSync6(targets.managedModelsPath),
2813
+ content: stableJson3(nextState)
2814
+ });
2815
+ return {
2816
+ dryRun: config.dryRun === true,
2817
+ reset: config.reset,
2818
+ items,
2819
+ pluginRoot: targets.pluginRoot,
2820
+ diagnostics: [
2821
+ `Installed as a skills-directory plugin at ${targets.pluginRoot}; it auto-loads as thoth-agents@skills-dir on the next Claude Code session.`,
2822
+ "Restart Claude Code or run /reload-plugins to activate it; run /plugin (Installed tab) to confirm thoth-agents@skills-dir is loaded.",
2823
+ "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."
2824
+ ],
2825
+ disclaimers: [
2826
+ "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.",
2827
+ "Every subagent inherits all main-thread tools, including MCP servers; read-only roles must not mutate the workspace per their operational contract (instruction-level, not tooling-enforced).",
2828
+ "Subagent models accept only sonnet, opus, haiku, or inherit.",
2829
+ "User-scope skills-directory plugins load hooks and MCP servers without extra approval; project-scope requires accepting the workspace trust dialog."
2830
+ ]
2831
+ };
2832
+ }
2833
+ function applyClaudeCodeSetup(plan) {
2834
+ const changed = [];
2835
+ const diagnostics = uniqueMessages([
2836
+ ...plan.diagnostics,
2837
+ ...plan.disclaimers
2838
+ ]);
2839
+ if (plan.dryRun) return { success: true, changed, diagnostics };
2840
+ try {
2841
+ for (const item of plan.items) {
2842
+ if (writeTextWithBackup(item.targetPath, item.content)) {
2843
+ changed.push(item.targetPath);
2844
+ }
2845
+ }
2846
+ return { success: true, changed, diagnostics };
2847
+ } catch (error) {
2848
+ return {
2849
+ success: false,
2850
+ changed,
2851
+ diagnostics,
2852
+ error: error instanceof Error ? error.message : String(error)
2853
+ };
2854
+ }
2855
+ }
2856
+ function applyClaudeCodeManagedModelOverrides(config, overrides) {
2857
+ if (config.dryRun) {
2858
+ return {
2859
+ success: true,
2860
+ changed: [],
2861
+ diagnostics: [
2862
+ "Dry-run Claude Code model override apply requested; no files were written."
2863
+ ]
2864
+ };
2865
+ }
2866
+ const plan = buildClaudeCodeSetupPlan({
2867
+ ...config,
2868
+ dryRun: true,
2869
+ reset: false
2870
+ });
2871
+ const stateItem = plan.items.find(
2872
+ (item) => item.action === "write-managed-model-state"
2873
+ );
2874
+ const statePath = stateItem?.targetPath;
2875
+ if (!statePath) {
2876
+ return {
2877
+ success: false,
2878
+ changed: [],
2879
+ diagnostics: plan.diagnostics,
2880
+ error: "Claude Code managed model state target was not found."
2881
+ };
2882
+ }
2883
+ const changed = [];
2884
+ const diagnostics = uniqueMessages([
2885
+ ...plan.diagnostics,
2886
+ ...plan.disclaimers
2887
+ ]);
2888
+ const state = parseManagedModelStateJson2(stateItem?.content);
2889
+ const nextState = {
2890
+ version: CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION,
2891
+ models: { ...state.models },
2892
+ ...state.configuredModels ? { configuredModels: { ...state.configuredModels } } : {}
2893
+ };
2894
+ try {
2895
+ for (const override of overrides) {
2896
+ if (!isClaudeCodeModelAlias(override.model)) {
2897
+ throw new Error(
2898
+ `Unsupported Claude Code model "${override.model}" for ${override.role}; use sonnet, opus, haiku, or inherit.`
2899
+ );
2900
+ }
2901
+ const roleItem = plan.items.find(
2902
+ (item) => item.kind === "subagent" && item.role === override.role
2903
+ );
2904
+ if (!roleItem) {
2905
+ throw new Error(`Missing Claude Code subagent for ${override.role}.`);
2906
+ }
2907
+ const before = existsSync6(roleItem.targetPath) ? readFileSync7(roleItem.targetPath, "utf8") : roleItem.content;
2908
+ const updated = replaceSubagentModel(before, override.model);
2909
+ if (writeTextWithBackup(roleItem.targetPath, updated)) {
2910
+ changed.push(roleItem.targetPath);
2911
+ }
2912
+ nextState.configuredModels ??= {};
2913
+ nextState.configuredModels[override.role] = override.model;
2914
+ }
2915
+ if (writeTextWithBackup(statePath, stableJson3(nextState))) {
2916
+ changed.push(statePath);
2917
+ }
2918
+ return { success: true, changed, diagnostics };
2919
+ } catch (error) {
2920
+ return {
2921
+ success: false,
2922
+ changed,
2923
+ diagnostics,
2924
+ error: error instanceof Error ? error.message : String(error)
2925
+ };
2926
+ }
2927
+ }
2928
+ function formatClaudeCodeSetupPlan(plan) {
2929
+ const lines = plan.items.map(
2930
+ (item) => `- ${item.action}: ${item.targetPath} (${item.description})`
2931
+ );
2932
+ return ["Claude Code setup plan:", ...lines].join("\n");
2933
+ }
2934
+
2935
+ // src/cli/operations/claude-code.ts
2936
+ var CLAUDE_CODE_DISPLAY_NAME = "Claude Code";
2937
+ var claudeCodePlanSources = /* @__PURE__ */ new WeakMap();
2938
+ var claudeCodeModelSources = /* @__PURE__ */ new WeakMap();
2939
+ var claudeCodeActions = [
2940
+ {
2941
+ id: "claude-code-status",
2942
+ kind: "status",
2943
+ label: "Status",
2944
+ description: "Inspect managed Claude Code plugin state",
2945
+ dryRun: false,
2946
+ requiresConfirmation: false,
2947
+ supported: true
2948
+ },
2949
+ {
2950
+ id: "claude-code-list",
2951
+ kind: "list",
2952
+ label: "List",
2953
+ description: "List managed Claude Code surfaces and actions",
2954
+ dryRun: false,
2955
+ requiresConfirmation: false,
2956
+ supported: true
2957
+ },
2958
+ {
2959
+ id: "claude-code-install",
2960
+ kind: "install",
2961
+ label: "Install",
2962
+ description: "Preview Claude Code plugin package install",
2963
+ dryRun: true,
2964
+ requiresConfirmation: true,
2965
+ supported: true
2966
+ },
2967
+ {
2968
+ id: "claude-code-update",
2969
+ kind: "update",
2970
+ label: "Update",
2971
+ description: "Preview Claude Code managed plugin refresh",
2972
+ dryRun: true,
2973
+ requiresConfirmation: true,
2974
+ supported: true
2975
+ },
2976
+ {
2977
+ id: "claude-code-sync",
2978
+ kind: "sync",
2979
+ label: "Sync",
2980
+ description: "Preview Claude Code managed plugin sync",
2981
+ dryRun: true,
2982
+ requiresConfirmation: true,
2983
+ supported: true
2984
+ },
2985
+ {
2986
+ id: "claude-code-model-config",
2987
+ kind: "model-config",
2988
+ label: "Model",
2989
+ description: "Preview supported Claude Code subagent model line changes",
2990
+ dryRun: true,
2991
+ requiresConfirmation: true,
2992
+ supported: true
2993
+ }
2994
+ ];
2995
+ var claudeCodeOperationAdapter = {
2996
+ id: "claude",
2997
+ displayName: CLAUDE_CODE_DISPLAY_NAME,
2998
+ available: true,
2999
+ description: "Claude Code plugin package and managed subagent surfaces.",
3000
+ actions: claudeCodeActions
3001
+ };
3002
+ function claudeCodeConfig(context = { cwd: process.cwd() }, dryRun) {
3003
+ return {
3004
+ dryRun,
3005
+ reset: false,
3006
+ // Match the installer (install.ts uses 'user') so post-install status,
3007
+ // update, sync, and model commands resolve the same plugin root.
3008
+ scope: context.scope ?? "user",
3009
+ projectRoot: context.cwd,
3010
+ homeDir: context.homeDir,
3011
+ packageRoot: context.packageRoot
3012
+ };
3013
+ }
3014
+ function claudeCodeDisclaimers() {
3015
+ return [
3016
+ {
3017
+ message: "Subagents inherit all main-thread tools (including MCP servers); read-only roles must not mutate the workspace per their operational contract (instruction-level, not tooling-enforced). The orchestrator is the main session.",
3018
+ code: "claude-code-first-class"
3019
+ },
3020
+ {
3021
+ message: "Subagent models accept only sonnet, opus, haiku, or inherit.",
3022
+ code: "claude-code-model-aliases"
3023
+ }
3024
+ ];
3025
+ }
3026
+ function warning(message, code) {
3027
+ return { severity: "important", message, code };
3028
+ }
3029
+ function targetForItem(item, state, observed) {
3030
+ return {
3031
+ kind: item.kind === "managed-model-state" ? "memory-state" : "generated-artifact",
3032
+ path: item.targetPath,
3033
+ label: item.role ? `Claude Code ${item.role} subagent` : item.kind.replaceAll("-", " "),
3034
+ state,
3035
+ expected: item.action,
3036
+ observed,
3037
+ description: item.description
3038
+ };
3039
+ }
3040
+ function surfaceForItem(item) {
3041
+ return {
3042
+ id: `${item.kind}:${item.role ?? basename2(item.targetPath)}`,
3043
+ label: item.role ? `Claude Code ${item.role} subagent` : item.kind.replaceAll("-", " "),
3044
+ path: item.targetPath,
3045
+ description: item.description
3046
+ };
3047
+ }
3048
+ function backupForItem(item) {
3049
+ return {
3050
+ required: item.requiresBackup,
3051
+ strategy: item.requiresBackup ? "managed-backup-file" : "none",
3052
+ destinations: item.requiresBackup ? [{ path: `${item.targetPath}.bak`, label: "managed backup" }] : []
3053
+ };
3054
+ }
3055
+ function manifestState(item) {
3056
+ if (!existsSync7(item.targetPath))
3057
+ return { state: "missing", observed: "absent" };
3058
+ const observed = readFileSync8(item.targetPath, "utf8");
3059
+ if (observed === item.content)
3060
+ return { state: "installed", observed: "current" };
3061
+ try {
3062
+ const expectedVersion = JSON.parse(item.content).version;
3063
+ const observedVersion = JSON.parse(observed).version;
3064
+ if (typeof expectedVersion === "string" && typeof observedVersion === "string" && expectedVersion !== observedVersion) {
3065
+ return {
3066
+ state: "outdated",
3067
+ observed: `version ${observedVersion}; expected ${expectedVersion}`
3068
+ };
3069
+ }
3070
+ } catch {
3071
+ return { state: "unknown", observed: "unparseable plugin manifest" };
3072
+ }
3073
+ return { state: "drift", observed: "content differs" };
3074
+ }
3075
+ function contentState(item) {
3076
+ if (!existsSync7(item.targetPath))
3077
+ return { state: "missing", observed: "absent" };
3078
+ return readFileSync8(item.targetPath, "utf8") === item.content ? { state: "installed", observed: "current" } : { state: "drift", observed: "content differs" };
3079
+ }
3080
+ function classifyItem(item) {
3081
+ if (item.kind === "plugin-manifest") return manifestState(item);
3082
+ return contentState(item);
3083
+ }
3084
+ function aggregateState(states) {
3085
+ if (states.includes("unknown")) return "unknown";
3086
+ if (states.includes("drift")) return "drift";
3087
+ if (states.includes("outdated")) return "outdated";
3088
+ if (states.includes("missing")) return "missing";
3089
+ return "installed";
3090
+ }
3091
+ function statusSummary(state) {
3092
+ switch (state) {
3093
+ case "installed":
3094
+ return "Claude Code managed plugin is installed and current.";
3095
+ case "missing":
3096
+ return "Claude Code managed plugin is missing.";
3097
+ case "drift":
3098
+ return "Claude Code managed plugin exists but differs from expected output.";
3099
+ case "outdated":
3100
+ return "Claude Code managed plugin includes an older generated manifest.";
3101
+ case "unknown":
3102
+ return "Claude Code managed plugin could not be classified safely.";
3103
+ }
3104
+ }
3105
+ function statusFromSetupPlan(plan) {
3106
+ const classified = plan.items.map((item) => ({
3107
+ item,
3108
+ ...classifyItem(item)
3109
+ }));
3110
+ const state = aggregateState(classified.map((entry) => entry.state));
3111
+ return {
3112
+ harness: "claude",
3113
+ displayName: CLAUDE_CODE_DISPLAY_NAME,
3114
+ state,
3115
+ summary: statusSummary(state),
3116
+ targets: classified.map(
3117
+ ({ item, state: state2, observed }) => targetForItem(item, state2, observed)
3118
+ ),
3119
+ diagnostics: plan.diagnostics.map((message) => ({
3120
+ severity: "minor",
3121
+ message,
3122
+ code: "claude-code-diagnostic"
3123
+ })),
3124
+ actions: claudeCodeActions,
3125
+ disclaimers: [
3126
+ ...claudeCodeDisclaimers(),
3127
+ ...plan.disclaimers.map((message) => ({ message }))
3128
+ ]
3129
+ };
3130
+ }
3131
+ function getClaudeCodeStatus(context = { cwd: process.cwd() }) {
3132
+ let plan;
3133
+ try {
3134
+ plan = buildClaudeCodeSetupPlan(claudeCodeConfig(context, true));
3135
+ } catch (error) {
3136
+ const message = error instanceof Error ? error.message : String(error);
3137
+ return {
3138
+ harness: "claude",
3139
+ displayName: CLAUDE_CODE_DISPLAY_NAME,
3140
+ state: "unknown",
3141
+ summary: `Claude Code setup plan could not be built: ${message}`,
3142
+ targets: [],
3143
+ diagnostics: [
3144
+ {
3145
+ severity: "critical",
3146
+ message,
3147
+ code: "claude-code-plan-build-failed"
3148
+ }
3149
+ ],
3150
+ actions: claudeCodeActions,
3151
+ disclaimers: claudeCodeDisclaimers()
3152
+ };
3153
+ }
3154
+ return statusFromSetupPlan(plan);
3155
+ }
3156
+ function planItemFromSetup(item) {
3157
+ return {
3158
+ title: item.description,
3159
+ target: targetForItem(item),
3160
+ preview: item.content,
3161
+ backup: backupForItem(item)
3162
+ };
3163
+ }
3164
+ function planFromSetup(id, action, title, summary, setupPlan) {
3165
+ const status = statusFromSetupPlan(setupPlan);
3166
+ const canApply = status.state === "installed" || status.state === "missing" || status.state === "outdated" || status.state === "drift";
3167
+ const plan = {
3168
+ id,
3169
+ harness: "claude",
3170
+ action,
3171
+ title,
3172
+ summary,
3173
+ dryRun: true,
3174
+ canApply,
3175
+ targets: status.targets,
3176
+ surfaces: setupPlan.items.map(surfaceForItem),
3177
+ backup: {
3178
+ required: setupPlan.items.some((item) => item.requiresBackup),
3179
+ strategy: "managed-backup-file",
3180
+ description: "Existing Claude Code plugin files are backed up before being overwritten."
3181
+ },
3182
+ items: setupPlan.items.map(planItemFromSetup),
3183
+ warnings: status.diagnostics,
3184
+ disclaimers: [
3185
+ ...claudeCodeDisclaimers(),
3186
+ ...setupPlan.disclaimers.map((message) => ({ message }))
3187
+ ]
3188
+ };
3189
+ claudeCodePlanSources.set(plan, setupPlan);
3190
+ return plan;
3191
+ }
3192
+ function buildClaudeCodeInstallPlan(context = { cwd: process.cwd() }) {
3193
+ return planFromSetup(
3194
+ "claude-code-install-preview",
3195
+ "install",
3196
+ "Install Claude Code plugin package",
3197
+ "Preview Claude Code plugin package install using buildClaudeCodeSetupPlan().",
3198
+ buildClaudeCodeSetupPlan(claudeCodeConfig(context, true))
3199
+ );
3200
+ }
3201
+ function buildClaudeCodeUpdatePlan(context = { cwd: process.cwd() }) {
3202
+ return planFromSetup(
3203
+ "claude-code-update-preview",
3204
+ "update",
3205
+ "Update Claude Code plugin package",
3206
+ "Preview Claude Code managed plugin refresh using buildClaudeCodeSetupPlan().",
3207
+ buildClaudeCodeSetupPlan(claudeCodeConfig(context, true))
3208
+ );
3209
+ }
3210
+ function buildClaudeCodeSyncPlan(context = { cwd: process.cwd() }) {
3211
+ return planFromSetup(
3212
+ "claude-code-sync-preview",
3213
+ "sync",
3214
+ "Sync Claude Code plugin package",
3215
+ "Preview Claude Code managed plugin subagents, MCP, hooks, and skills.",
3216
+ buildClaudeCodeSetupPlan(claudeCodeConfig(context, true))
3217
+ );
3218
+ }
3219
+ function isClaudeCodeRole(role) {
3220
+ return CLAUDE_CODE_ROLE_NAMES.includes(role);
3221
+ }
3222
+ function buildClaudeCodeModelPlan(input, context = { cwd: process.cwd() }) {
3223
+ const status = getClaudeCodeStatus(context);
3224
+ const supportedRoles = input.roles.filter((role) => isClaudeCodeRole(role.role)).filter((role) => isClaudeCodeModelAlias(role.model)).map((role) => ({
3225
+ role: role.role,
3226
+ model: role.model
3227
+ }));
3228
+ const rejectedRoles = input.roles.filter(
3229
+ (role) => !isClaudeCodeRole(role.role) || !isClaudeCodeModelAlias(role.model)
3230
+ );
3231
+ const warnings = [
3232
+ ...status.diagnostics,
3233
+ ...input.warnings ?? [],
3234
+ ...rejectedRoles.map(
3235
+ (role) => warning(
3236
+ `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.`,
3237
+ "claude-code-unsupported-model-role"
3238
+ )
3239
+ )
3240
+ ];
3241
+ if (input.harness !== "claude") {
3242
+ warnings.push(
3243
+ warning(
3244
+ "Model plan target harness must be claude.",
3245
+ "claude-code-model-harness-mismatch"
3246
+ )
3247
+ );
3248
+ }
3249
+ const targets = supportedRoles.map(({ role }) => {
3250
+ const target = status.targets.find(
3251
+ (candidate) => candidate.path?.endsWith(`agents${pathSep()}${role}.md`)
3252
+ );
3253
+ return target ?? {
3254
+ kind: "generated-artifact",
3255
+ label: `Claude Code ${role} subagent`,
3256
+ state: "missing"
3257
+ };
3258
+ });
3259
+ const stateTarget = status.targets.find(
3260
+ (target) => target.path?.endsWith(".thoth-agents-managed-models.json")
3261
+ );
3262
+ const plan = {
3263
+ id: "claude-code-model-config-preview",
3264
+ harness: "claude",
3265
+ action: "model-config",
3266
+ title: "Configure Claude Code subagent model lines",
3267
+ summary: "Preview model changes for generated Claude Code subagent files and managed model state only.",
3268
+ dryRun: true,
3269
+ canApply: input.harness === "claude" && supportedRoles.length > 0 && status.state !== "unknown",
3270
+ targets: [...targets, ...stateTarget ? [stateTarget] : []],
3271
+ surfaces: targets.map((target) => ({
3272
+ id: `claude-code-model:${target.label}`,
3273
+ label: target.label ?? "Claude Code subagent",
3274
+ path: target.path,
3275
+ state: target.state
3276
+ })),
3277
+ backup: {
3278
+ required: true,
3279
+ strategy: "managed-backup-file",
3280
+ description: "Existing subagent files and managed model state are backed up by the managed write helper."
3281
+ },
3282
+ items: supportedRoles.map(({ role, model }) => ({
3283
+ title: `Set ${role} Claude Code subagent model line`,
3284
+ target: targets.find(
3285
+ (target) => target.path?.endsWith(`agents${pathSep()}${role}.md`)
3286
+ ) ?? {
3287
+ kind: "generated-artifact",
3288
+ label: `Claude Code ${role} subagent`
3289
+ },
3290
+ preview: JSON.stringify({ role, model }),
3291
+ backup: { required: true, strategy: "managed-backup-file" }
3292
+ })),
3293
+ warnings,
3294
+ disclaimers: [
3295
+ ...claudeCodeDisclaimers(),
3296
+ ...input.disclaimers ?? [],
3297
+ {
3298
+ message: "Claude Code model configuration writes only generated subagent frontmatter model lines and the managed model state JSON.",
3299
+ code: "claude-code-model-supported-surface"
3300
+ }
3301
+ ]
3302
+ };
3303
+ claudeCodeModelSources.set(plan, {
3304
+ config: claudeCodeConfig(context, false),
3305
+ roles: supportedRoles
3306
+ });
3307
+ return plan;
3308
+ }
3309
+ function pathSep() {
3310
+ return process.platform === "win32" ? "\\" : "/";
3311
+ }
3312
+ function rejectPlan(plan, message, severity = "critical") {
3313
+ return {
3314
+ harness: plan.harness,
3315
+ action: plan.action,
3316
+ applied: false,
3317
+ summary: message,
3318
+ changedTargets: [],
3319
+ backups: [],
3320
+ warnings: [{ severity, message }],
3321
+ disclaimers: claudeCodeDisclaimers()
3322
+ };
3323
+ }
3324
+ function validateClaudeCodePlan(plan) {
3325
+ if (plan.harness !== "claude") {
3326
+ return rejectPlan(plan, "Only Claude Code operation plans can be applied.");
3327
+ }
3328
+ if (!plan.canApply) {
3329
+ return rejectPlan(
3330
+ plan,
3331
+ "Claude Code plan cannot be applied because canApply is false."
3332
+ );
3333
+ }
3334
+ if (!["install", "update", "sync", "model-config"].includes(plan.action)) {
3335
+ return rejectPlan(
3336
+ plan,
3337
+ `Unsupported Claude Code apply action: ${plan.action}.`
3338
+ );
3339
+ }
3340
+ if (plan.items.length === 0) {
3341
+ return rejectPlan(plan, "Claude Code plan has no items to apply.");
3342
+ }
3343
+ return null;
3344
+ }
3345
+ function applyClaudeCodePlan(plan) {
3346
+ const rejection = validateClaudeCodePlan(plan);
3347
+ if (rejection) return rejection;
3348
+ if (plan.action === "model-config") {
3349
+ const source = claudeCodeModelSources.get(plan);
3350
+ if (!source) {
3351
+ return rejectPlan(
3352
+ plan,
3353
+ "Claude Code model plan was not produced by buildClaudeCodeModelPlan in this process."
3354
+ );
3355
+ }
3356
+ const result2 = applyClaudeCodeManagedModelOverrides(
3357
+ source.config,
3358
+ source.roles.map((role) => ({
3359
+ role: role.role,
3360
+ model: role.model
3361
+ }))
3362
+ );
3363
+ return {
3364
+ harness: "claude",
3365
+ action: "model-config",
3366
+ applied: result2.success,
3367
+ summary: result2.success ? "Applied Claude Code subagent model overrides." : result2.error ?? "Failed to apply Claude Code subagent model overrides.",
3368
+ changedTargets: result2.changed.map((path4) => ({
3369
+ kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
3370
+ path: path4,
3371
+ label: basename2(path4),
3372
+ state: "installed"
3373
+ })),
3374
+ backups: result2.changed.filter((path4) => existsSync7(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
3375
+ warnings: result2.success ? [] : [{ severity: "critical", message: result2.error ?? "apply failed." }],
3376
+ disclaimers: claudeCodeDisclaimers()
3377
+ };
3378
+ }
3379
+ const setupPlan = claudeCodePlanSources.get(plan);
3380
+ if (!setupPlan) {
3381
+ return rejectPlan(
3382
+ plan,
3383
+ "Claude Code setup plan was not produced by a Claude Code operation plan builder in this process."
3384
+ );
3385
+ }
3386
+ const result = applyClaudeCodeSetup({ ...setupPlan, dryRun: false });
3387
+ return {
3388
+ harness: "claude",
3389
+ action: plan.action,
3390
+ applied: result.success,
3391
+ summary: result.success ? `Applied Claude Code managed ${plan.action} plan.` : result.error ?? `Failed to apply Claude Code ${plan.action} plan.`,
3392
+ changedTargets: result.changed.map((path4) => ({
3393
+ kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
3394
+ path: path4,
3395
+ label: basename2(path4),
3396
+ state: "installed"
3397
+ })),
3398
+ backups: result.changed.filter((path4) => existsSync7(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
3399
+ warnings: result.success ? [] : [{ severity: "critical", message: result.error ?? "apply failed." }],
3400
+ disclaimers: claudeCodeDisclaimers()
3401
+ };
3402
+ }
3403
+ function defaultClaudeCodeModelRoles() {
3404
+ return CLAUDE_CODE_ROLE_NAMES.map((role) => ({
3405
+ role,
3406
+ model: CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS[role]
3407
+ }));
3408
+ }
3409
+
3410
+ // src/cli/operations/codex.ts
3411
+ import { existsSync as existsSync8, readFileSync as readFileSync9 } from "fs";
3412
+ import { basename as basename3 } from "path";
3413
+ var CODEX_DISPLAY_NAME = "Codex";
3414
+ var codexPlanSources = /* @__PURE__ */ new WeakMap();
3415
+ var codexModelSources = /* @__PURE__ */ new WeakMap();
3416
+ var codexActions = [
3417
+ {
3418
+ id: "codex-status",
3419
+ kind: "status",
3420
+ label: "Status",
3421
+ description: "Inspect managed Codex setup state",
3422
+ dryRun: false,
3423
+ requiresConfirmation: false,
3424
+ supported: true
3425
+ },
3426
+ {
3427
+ id: "codex-list",
3428
+ kind: "list",
3429
+ label: "List",
3430
+ description: "List managed Codex surfaces and actions",
3431
+ dryRun: false,
2306
3432
  requiresConfirmation: false,
2307
3433
  supported: true
2308
3434
  },
@@ -2374,10 +3500,10 @@ function codexDisclaimers() {
2374
3500
  }
2375
3501
  ];
2376
3502
  }
2377
- function warning(message, code) {
3503
+ function warning2(message, code) {
2378
3504
  return { severity: "important", message, code };
2379
3505
  }
2380
- function targetForItem(item, state, observed) {
3506
+ function targetForItem2(item, state, observed) {
2381
3507
  return {
2382
3508
  kind: item.kind === "managed-model-state" ? "memory-state" : "generated-artifact",
2383
3509
  path: item.targetPath,
@@ -2388,15 +3514,15 @@ function targetForItem(item, state, observed) {
2388
3514
  description: item.description
2389
3515
  };
2390
3516
  }
2391
- function surfaceForItem(item) {
3517
+ function surfaceForItem2(item) {
2392
3518
  return {
2393
- id: `${item.kind}:${item.role ?? basename2(item.targetPath)}`,
3519
+ id: `${item.kind}:${item.role ?? basename3(item.targetPath)}`,
2394
3520
  label: item.role ? `Codex ${item.role} subagent TOML` : item.kind.replaceAll("-", " "),
2395
3521
  path: item.targetPath,
2396
3522
  description: item.description
2397
3523
  };
2398
3524
  }
2399
- function backupForItem(item) {
3525
+ function backupForItem2(item) {
2400
3526
  return {
2401
3527
  required: item.requiresBackup,
2402
3528
  strategy: item.requiresBackup ? "managed-backup-file" : "none",
@@ -2404,9 +3530,9 @@ function backupForItem(item) {
2404
3530
  };
2405
3531
  }
2406
3532
  function rootBlockState(item) {
2407
- if (!existsSync5(item.targetPath))
3533
+ if (!existsSync8(item.targetPath))
2408
3534
  return { state: "missing", observed: "absent" };
2409
- const content = readFileSync5(item.targetPath, "utf8");
3535
+ const content = readFileSync9(item.targetPath, "utf8");
2410
3536
  if (item.content && content.includes(item.content)) {
2411
3537
  return { state: "installed", observed: "managed root block present" };
2412
3538
  }
@@ -2416,22 +3542,22 @@ function rootBlockState(item) {
2416
3542
  return { state: "missing", observed: "managed root block absent" };
2417
3543
  }
2418
3544
  function managedModelState(item) {
2419
- if (!existsSync5(item.targetPath))
3545
+ if (!existsSync8(item.targetPath))
2420
3546
  return { state: "missing", observed: "absent" };
2421
3547
  try {
2422
- const parsed = JSON.parse(readFileSync5(item.targetPath, "utf8"));
3548
+ const parsed = JSON.parse(readFileSync9(item.targetPath, "utf8"));
2423
3549
  if (parsed.version !== MANAGED_MODEL_STATE_VERSION || !parsed.models || typeof parsed.models !== "object" || Array.isArray(parsed.models)) {
2424
3550
  return { state: "unknown", observed: "invalid managed model state" };
2425
3551
  }
2426
- return item.content === readFileSync5(item.targetPath, "utf8") ? { state: "installed", observed: "managed model state current" } : { state: "drift", observed: "managed model state differs" };
3552
+ return item.content === readFileSync9(item.targetPath, "utf8") ? { state: "installed", observed: "managed model state current" } : { state: "drift", observed: "managed model state differs" };
2427
3553
  } catch {
2428
3554
  return { state: "unknown", observed: "unparseable managed model state" };
2429
3555
  }
2430
3556
  }
2431
3557
  function userConfigState(item) {
2432
- if (!existsSync5(item.targetPath))
3558
+ if (!existsSync8(item.targetPath))
2433
3559
  return { state: "missing", observed: "absent" };
2434
- const content = readFileSync5(item.targetPath, "utf8");
3560
+ const content = readFileSync9(item.targetPath, "utf8");
2435
3561
  if (/^\s*default_mode_request_user_input\s*=\s*true\b/m.test(content)) {
2436
3562
  return {
2437
3563
  state: "installed",
@@ -2444,10 +3570,10 @@ function userConfigState(item) {
2444
3570
  return { state: "missing", observed: "managed feature flag absent" };
2445
3571
  }
2446
3572
  function marketplaceState(item) {
2447
- if (!existsSync5(item.targetPath))
3573
+ if (!existsSync8(item.targetPath))
2448
3574
  return { state: "missing", observed: "absent" };
2449
3575
  try {
2450
- const parsed = JSON.parse(readFileSync5(item.targetPath, "utf8"));
3576
+ const parsed = JSON.parse(readFileSync9(item.targetPath, "utf8"));
2451
3577
  const plugins = Array.isArray(parsed.plugins) ? parsed.plugins : [];
2452
3578
  const entry = plugins.find(
2453
3579
  (plugin) => plugin && typeof plugin === "object" && "name" in plugin && plugin.name === "thoth-agents"
@@ -2460,11 +3586,11 @@ function marketplaceState(item) {
2460
3586
  return { state: "unknown", observed: "unparseable marketplace JSON" };
2461
3587
  }
2462
3588
  }
2463
- function contentState(item) {
2464
- if (!existsSync5(item.targetPath))
3589
+ function contentState2(item) {
3590
+ if (!existsSync8(item.targetPath))
2465
3591
  return { state: "missing", observed: "absent" };
2466
- const observed = readFileSync5(item.targetPath, "utf8");
2467
- if (basename2(item.targetPath) === "plugin.json" && item.targetPath.replaceAll("\\", "/").includes("/.codex-plugin/")) {
3592
+ const observed = readFileSync9(item.targetPath, "utf8");
3593
+ if (basename3(item.targetPath) === "plugin.json" && item.targetPath.replaceAll("\\", "/").includes("/.codex-plugin/")) {
2468
3594
  try {
2469
3595
  const expectedVersion = JSON.parse(item.content ?? "{}").version;
2470
3596
  const observedVersion = JSON.parse(observed).version;
@@ -2492,7 +3618,7 @@ function contentState(item) {
2492
3618
  }
2493
3619
  return { state: "drift", observed: "content differs" };
2494
3620
  }
2495
- function classifyItem(item) {
3621
+ function classifyItem2(item) {
2496
3622
  if (item.action === "diagnose-only") {
2497
3623
  return { state: "unknown", observed: "diagnostic guidance only" };
2498
3624
  }
@@ -2502,16 +3628,16 @@ function classifyItem(item) {
2502
3628
  }
2503
3629
  if (item.action === "merge-toml") return userConfigState(item);
2504
3630
  if (item.action === "merge-marketplace") return marketplaceState(item);
2505
- return contentState(item);
3631
+ return contentState2(item);
2506
3632
  }
2507
- function aggregateState(states) {
3633
+ function aggregateState2(states) {
2508
3634
  if (states.includes("unknown")) return "unknown";
2509
3635
  if (states.includes("drift")) return "drift";
2510
3636
  if (states.includes("outdated")) return "outdated";
2511
3637
  if (states.includes("missing")) return "missing";
2512
3638
  return "installed";
2513
3639
  }
2514
- function statusSummary(state) {
3640
+ function statusSummary2(state) {
2515
3641
  switch (state) {
2516
3642
  case "installed":
2517
3643
  return "Codex managed setup surfaces are installed and current.";
@@ -2548,8 +3674,8 @@ function getCodexStatus(context = { cwd: process.cwd() }) {
2548
3674
  disclaimers: codexDisclaimers()
2549
3675
  };
2550
3676
  }
2551
- const classified = plan.items.filter((item) => item.action !== "diagnose-only").map((item) => ({ item, ...classifyItem(item) }));
2552
- const state = aggregateState(classified.map((item) => item.state));
3677
+ const classified = plan.items.filter((item) => item.action !== "diagnose-only").map((item) => ({ item, ...classifyItem2(item) }));
3678
+ const state = aggregateState2(classified.map((item) => item.state));
2553
3679
  const diagnostics = plan.diagnostics.map((message) => ({
2554
3680
  severity: "minor",
2555
3681
  message,
@@ -2559,9 +3685,9 @@ function getCodexStatus(context = { cwd: process.cwd() }) {
2559
3685
  harness: "codex",
2560
3686
  displayName: CODEX_DISPLAY_NAME,
2561
3687
  state,
2562
- summary: statusSummary(state),
3688
+ summary: statusSummary2(state),
2563
3689
  targets: classified.map(
2564
- ({ item, state: state2, observed }) => targetForItem(item, state2, observed)
3690
+ ({ item, state: state2, observed }) => targetForItem2(item, state2, observed)
2565
3691
  ),
2566
3692
  diagnostics,
2567
3693
  actions: codexActions,
@@ -2571,15 +3697,15 @@ function getCodexStatus(context = { cwd: process.cwd() }) {
2571
3697
  ]
2572
3698
  };
2573
3699
  }
2574
- function planItemFromSetup(item) {
3700
+ function planItemFromSetup2(item) {
2575
3701
  return {
2576
3702
  title: item.description,
2577
- target: targetForItem(item),
3703
+ target: targetForItem2(item),
2578
3704
  preview: item.content,
2579
- backup: backupForItem(item)
3705
+ backup: backupForItem2(item)
2580
3706
  };
2581
3707
  }
2582
- function planFromSetup(id, action, title, summary, setupPlan, context) {
3708
+ function planFromSetup2(id, action, title, summary, setupPlan, context) {
2583
3709
  const status = getCodexStatus(context);
2584
3710
  const canApply = status.state === "installed" || status.state === "missing" || status.state === "outdated";
2585
3711
  const plan = {
@@ -2591,17 +3717,17 @@ function planFromSetup(id, action, title, summary, setupPlan, context) {
2591
3717
  dryRun: true,
2592
3718
  canApply,
2593
3719
  targets: status.targets,
2594
- surfaces: setupPlan.items.filter((item) => item.action !== "diagnose-only").map(surfaceForItem),
3720
+ surfaces: setupPlan.items.filter((item) => item.action !== "diagnose-only").map(surfaceForItem2),
2595
3721
  backup: {
2596
3722
  required: setupPlan.items.some((item) => item.requiresBackup),
2597
3723
  strategy: "existing-helper",
2598
3724
  description: "Codex setup apply uses the existing installer backup behavior for files that already exist."
2599
3725
  },
2600
- items: setupPlan.items.map(planItemFromSetup),
3726
+ items: setupPlan.items.map(planItemFromSetup2),
2601
3727
  warnings: [
2602
3728
  ...status.diagnostics,
2603
3729
  ...canApply ? [] : [
2604
- warning(
3730
+ warning2(
2605
3731
  `Codex state is ${status.state}; apply is disabled until the state is safely classified or repaired.`,
2606
3732
  "codex-unsafe-state"
2607
3733
  )
@@ -2617,7 +3743,7 @@ function planFromSetup(id, action, title, summary, setupPlan, context) {
2617
3743
  }
2618
3744
  function buildCodexUpdatePlan(context = { cwd: process.cwd() }) {
2619
3745
  const setupPlan = buildCodexSetupPlan(codexConfig(context, true));
2620
- return planFromSetup(
3746
+ return planFromSetup2(
2621
3747
  "codex-update-preview",
2622
3748
  "update",
2623
3749
  "Update Codex managed setup",
@@ -2628,7 +3754,7 @@ function buildCodexUpdatePlan(context = { cwd: process.cwd() }) {
2628
3754
  }
2629
3755
  function buildCodexSyncPlan(context = { cwd: process.cwd() }) {
2630
3756
  const setupPlan = buildCodexSetupPlan(codexConfig(context, true));
2631
- return planFromSetup(
3757
+ return planFromSetup2(
2632
3758
  "codex-sync-preview",
2633
3759
  "sync",
2634
3760
  "Sync Codex managed configuration",
@@ -2639,7 +3765,7 @@ function buildCodexSyncPlan(context = { cwd: process.cwd() }) {
2639
3765
  }
2640
3766
  function buildCodexInstallPlan(context = { cwd: process.cwd() }) {
2641
3767
  const setupPlan = buildCodexSetupPlan(codexConfig(context, true));
2642
- return planFromSetup(
3768
+ return planFromSetup2(
2643
3769
  "codex-install-preview",
2644
3770
  "install",
2645
3771
  "Install Codex managed setup",
@@ -2670,7 +3796,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
2670
3796
  ...status.diagnostics,
2671
3797
  ...input.warnings ?? [],
2672
3798
  ...unsupportedRoles.map(
2673
- (role) => warning(
3799
+ (role) => warning2(
2674
3800
  `Codex does not expose a supported generated model surface for ${role.role}; this plan will not write that role.`,
2675
3801
  "codex-unsupported-model-role"
2676
3802
  )
@@ -2678,7 +3804,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
2678
3804
  ];
2679
3805
  if (input.harness !== "codex") {
2680
3806
  warnings.push(
2681
- warning(
3807
+ warning2(
2682
3808
  "Model plan target harness must be codex.",
2683
3809
  "codex-model-harness-mismatch"
2684
3810
  )
@@ -2745,7 +3871,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
2745
3871
  codexModelSources.set(plan, { config, roles: supportedRoles });
2746
3872
  return plan;
2747
3873
  }
2748
- function rejectPlan(plan, message, severity = "critical") {
3874
+ function rejectPlan2(plan, message, severity = "critical") {
2749
3875
  return {
2750
3876
  harness: plan.harness,
2751
3877
  action: plan.action,
@@ -2759,19 +3885,19 @@ function rejectPlan(plan, message, severity = "critical") {
2759
3885
  }
2760
3886
  function validateCodexPlan(plan) {
2761
3887
  if (plan.harness !== "codex") {
2762
- return rejectPlan(plan, "Only Codex operation plans can be applied.");
3888
+ return rejectPlan2(plan, "Only Codex operation plans can be applied.");
2763
3889
  }
2764
3890
  if (!plan.canApply) {
2765
- return rejectPlan(
3891
+ return rejectPlan2(
2766
3892
  plan,
2767
3893
  "Codex plan cannot be applied because canApply is false."
2768
3894
  );
2769
3895
  }
2770
3896
  if (!["install", "update", "sync", "model-config"].includes(plan.action)) {
2771
- return rejectPlan(plan, `Unsupported Codex apply action: ${plan.action}.`);
3897
+ return rejectPlan2(plan, `Unsupported Codex apply action: ${plan.action}.`);
2772
3898
  }
2773
3899
  if (plan.items.length === 0) {
2774
- return rejectPlan(plan, "Codex plan has no items to apply.");
3900
+ return rejectPlan2(plan, "Codex plan has no items to apply.");
2775
3901
  }
2776
3902
  return null;
2777
3903
  }
@@ -2781,7 +3907,7 @@ function applyCodexPlan(plan) {
2781
3907
  if (plan.action === "model-config") {
2782
3908
  const source = codexModelSources.get(plan);
2783
3909
  if (!source) {
2784
- return rejectPlan(
3910
+ return rejectPlan2(
2785
3911
  plan,
2786
3912
  "Codex model plan was not produced by buildCodexModelPlan in this process."
2787
3913
  );
@@ -2792,13 +3918,13 @@ function applyCodexPlan(plan) {
2792
3918
  action: "model-config",
2793
3919
  applied: result2.success,
2794
3920
  summary: result2.success ? "Applied Codex subagent model overrides." : result2.error ?? "Failed to apply Codex subagent model overrides.",
2795
- changedTargets: result2.changed.map((path2) => ({
2796
- kind: path2.endsWith(".json") ? "memory-state" : "generated-artifact",
2797
- path: path2,
2798
- label: basename2(path2),
3921
+ changedTargets: result2.changed.map((path4) => ({
3922
+ kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
3923
+ path: path4,
3924
+ label: basename3(path4),
2799
3925
  state: "installed"
2800
3926
  })),
2801
- backups: result2.changed.filter((path2) => existsSync5(`${path2}.bak`)).map((path2) => ({ path: `${path2}.bak`, label: "managed backup" })),
3927
+ backups: result2.changed.filter((path4) => existsSync8(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
2802
3928
  warnings: result2.success ? [] : [
2803
3929
  {
2804
3930
  severity: "critical",
@@ -2810,7 +3936,7 @@ function applyCodexPlan(plan) {
2810
3936
  }
2811
3937
  const setupPlan = codexPlanSources.get(plan);
2812
3938
  if (!setupPlan) {
2813
- return rejectPlan(
3939
+ return rejectPlan2(
2814
3940
  plan,
2815
3941
  "Codex setup plan was not produced by a Codex operation plan builder in this process."
2816
3942
  );
@@ -2821,13 +3947,13 @@ function applyCodexPlan(plan) {
2821
3947
  action: plan.action,
2822
3948
  applied: result.success,
2823
3949
  summary: result.success ? `Applied Codex managed ${plan.action} plan.` : result.error ?? `Failed to apply Codex ${plan.action} plan.`,
2824
- changedTargets: result.changed.map((path2) => ({
2825
- kind: path2.endsWith(".json") ? "memory-state" : "generated-artifact",
2826
- path: path2,
2827
- label: basename2(path2),
3950
+ changedTargets: result.changed.map((path4) => ({
3951
+ kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
3952
+ path: path4,
3953
+ label: basename3(path4),
2828
3954
  state: "installed"
2829
3955
  })),
2830
- backups: result.changed.filter((path2) => existsSync5(`${path2}.bak`)).map((path2) => ({ path: `${path2}.bak`, label: "managed backup" })),
3956
+ backups: result.changed.filter((path4) => existsSync8(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
2831
3957
  warnings: result.success ? [] : [
2832
3958
  {
2833
3959
  severity: "critical",
@@ -2839,14 +3965,14 @@ function applyCodexPlan(plan) {
2839
3965
  }
2840
3966
 
2841
3967
  // src/cli/operations/opencode.ts
2842
- import { existsSync as existsSync7 } from "fs";
2843
- import { join as join6 } from "path";
3968
+ import { existsSync as existsSync10 } from "fs";
3969
+ import { join as join10 } from "path";
2844
3970
 
2845
3971
  // src/cli/skills.ts
2846
3972
  import { spawnSync } from "child_process";
2847
- import { existsSync as existsSync6 } from "fs";
2848
- import { homedir as homedir2 } from "os";
2849
- import { join as join5 } from "path";
3973
+ import { existsSync as existsSync9 } from "fs";
3974
+ import { homedir as homedir3 } from "os";
3975
+ import { join as join9 } from "path";
2850
3976
  var RECOMMENDED_SKILLS = [
2851
3977
  {
2852
3978
  name: "simplify",
@@ -2861,11 +3987,11 @@ var RECOMMENDED_SKILLS = [
2861
3987
  description: "Browser automation for visual checks and testing"
2862
3988
  }
2863
3989
  ];
2864
- function getRecommendedSkillPath(skill, homeDir = homedir2()) {
2865
- return join5(homeDir, ".agents", "skills", skill.skillName, "SKILL.md");
3990
+ function getRecommendedSkillPath(skill, homeDir = homedir3()) {
3991
+ return join9(homeDir, ".agents", "skills", skill.skillName, "SKILL.md");
2866
3992
  }
2867
3993
  function isRecommendedSkillInstalled(skill, options = {}) {
2868
- return existsSync6(getRecommendedSkillPath(skill, options.homeDir));
3994
+ return existsSync9(getRecommendedSkillPath(skill, options.homeDir));
2869
3995
  }
2870
3996
  function runSkillInstallCommand(skill) {
2871
3997
  const args = [
@@ -3019,11 +4145,11 @@ function openCodeSkillTargets(context) {
3019
4145
  const homeDir = homeDirFromContext(context);
3020
4146
  const recommendedTargets = RECOMMENDED_SKILLS.map(
3021
4147
  (skill) => {
3022
- const path2 = getRecommendedSkillPath(skill, homeDir);
3023
- const installed = existsSync7(path2);
4148
+ const path4 = getRecommendedSkillPath(skill, homeDir);
4149
+ const installed = existsSync10(path4);
3024
4150
  return {
3025
4151
  kind: "skill",
3026
- path: path2,
4152
+ path: path4,
3027
4153
  label: titleCaseSkillName(skill.skillName),
3028
4154
  state: installed ? "installed" : "missing",
3029
4155
  expected: "recommended global OpenCode skill",
@@ -3032,11 +4158,11 @@ function openCodeSkillTargets(context) {
3032
4158
  }
3033
4159
  );
3034
4160
  const bundledTargets = CUSTOM_SKILLS.map((skill) => {
3035
- const path2 = join6(getCustomSkillsDir(), skill.name, "SKILL.md");
3036
- const installed = existsSync7(path2);
4161
+ const path4 = join10(getCustomSkillsDir(), skill.name, "SKILL.md");
4162
+ const installed = existsSync10(path4);
3037
4163
  return {
3038
4164
  kind: "skill",
3039
- path: path2,
4165
+ path: path4,
3040
4166
  label: titleCaseSkillName(skill.name),
3041
4167
  state: installed ? "installed" : "missing",
3042
4168
  expected: "bundled thoth-agents OpenCode skill",
@@ -3139,8 +4265,8 @@ function getOpenCodeStatus(context = { cwd: process.cwd() }) {
3139
4265
  actions: openCodeActions
3140
4266
  };
3141
4267
  }
3142
- const mainExists = existsSync7(mainPath);
3143
- const liteExists = existsSync7(litePath);
4268
+ const mainExists = existsSync10(mainPath);
4269
+ const liteExists = existsSync10(litePath);
3144
4270
  const mainTarget = {
3145
4271
  ...targetForMainConfig(),
3146
4272
  observed: configPluginMarker(main.config)
@@ -3237,11 +4363,11 @@ function getOpenCodeStatus(context = { cwd: process.cwd() }) {
3237
4363
  actions: openCodeActions
3238
4364
  };
3239
4365
  }
3240
- function defaultBackup(path2) {
4366
+ function defaultBackup(path4) {
3241
4367
  return {
3242
4368
  required: true,
3243
4369
  strategy: "managed-backup-file",
3244
- destinations: [{ path: `${path2}.bak`, label: "managed backup" }]
4370
+ destinations: [{ path: `${path4}.bak`, label: "managed backup" }]
3245
4371
  };
3246
4372
  }
3247
4373
  function defaultDisclaimers() {
@@ -3293,7 +4419,7 @@ function planFromItems(id, action, title, summary, items) {
3293
4419
  };
3294
4420
  }
3295
4421
  function buildOpenCodeUpdatePlan(_context = { cwd: process.cwd() }) {
3296
- const path2 = getExistingConfigPath();
4422
+ const path4 = getExistingConfigPath();
3297
4423
  return planFromItems(
3298
4424
  "opencode-update-preview",
3299
4425
  "update",
@@ -3305,7 +4431,7 @@ function buildOpenCodeUpdatePlan(_context = { cwd: process.cwd() }) {
3305
4431
  target: targetForMainConfig(),
3306
4432
  state: getOpenCodeStatus().state,
3307
4433
  preview: `plugin: ["${EXPECTED_PLUGIN}"]`,
3308
- backup: defaultBackup(path2)
4434
+ backup: defaultBackup(path4)
3309
4435
  }
3310
4436
  ]
3311
4437
  );
@@ -3472,7 +4598,7 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
3472
4598
  }
3473
4599
  return plan;
3474
4600
  }
3475
- function rejectPlan2(plan, message, severity = "critical") {
4601
+ function rejectPlan3(plan, message, severity = "critical") {
3476
4602
  return {
3477
4603
  harness: plan.harness,
3478
4604
  action: plan.action,
@@ -3516,27 +4642,27 @@ function ensureLatestPluginEntry() {
3516
4642
  }
3517
4643
  function validateApplyPlan(plan) {
3518
4644
  if (plan.harness !== "opencode") {
3519
- return rejectPlan2(plan, "Only OpenCode operation plans can be applied.");
4645
+ return rejectPlan3(plan, "Only OpenCode operation plans can be applied.");
3520
4646
  }
3521
4647
  if (!plan.canApply) {
3522
- return rejectPlan2(
4648
+ return rejectPlan3(
3523
4649
  plan,
3524
4650
  "OpenCode plan cannot be applied because canApply is false."
3525
4651
  );
3526
4652
  }
3527
4653
  if (!["install", "update", "sync", "model-config"].includes(plan.action)) {
3528
- return rejectPlan2(
4654
+ return rejectPlan3(
3529
4655
  plan,
3530
4656
  `Unsupported OpenCode apply action: ${plan.action}.`
3531
4657
  );
3532
4658
  }
3533
4659
  if (plan.items.length === 0) {
3534
- return rejectPlan2(plan, "OpenCode plan has no items to apply.");
4660
+ return rejectPlan3(plan, "OpenCode plan has no items to apply.");
3535
4661
  }
3536
4662
  if (plan.items.some(
3537
4663
  (item) => !item.title || !item.target.path && item.target.kind !== "skill" || item.state === "drift" || item.state === "unknown"
3538
4664
  )) {
3539
- return rejectPlan2(
4665
+ return rejectPlan3(
3540
4666
  plan,
3541
4667
  "OpenCode plan contains malformed or unsafe items."
3542
4668
  );
@@ -3548,7 +4674,7 @@ function applyModelPlan(plan) {
3548
4674
  for (const item of plan.items) {
3549
4675
  const match = /^Set (.+) OpenCode model override$/.exec(item.title);
3550
4676
  if (!match) {
3551
- return rejectPlan2(
4677
+ return rejectPlan3(
3552
4678
  plan,
3553
4679
  "OpenCode model plan contains an unrecognized item."
3554
4680
  );
@@ -3557,7 +4683,7 @@ function applyModelPlan(plan) {
3557
4683
  try {
3558
4684
  parsed2 = item.preview ? JSON.parse(item.preview) : {};
3559
4685
  } catch {
3560
- return rejectPlan2(
4686
+ return rejectPlan3(
3561
4687
  plan,
3562
4688
  "OpenCode model plan contains malformed preview JSON."
3563
4689
  );
@@ -3565,7 +4691,7 @@ function applyModelPlan(plan) {
3565
4691
  const role = match[1] ?? "";
3566
4692
  const model = parsed2[role]?.model;
3567
4693
  if (!ROLE_NAMES.includes(role) || typeof model !== "string") {
3568
- return rejectPlan2(
4694
+ return rejectPlan3(
3569
4695
  plan,
3570
4696
  "OpenCode model plan contains an invalid role or model."
3571
4697
  );
@@ -3573,7 +4699,7 @@ function applyModelPlan(plan) {
3573
4699
  roleModels.set(role, model);
3574
4700
  }
3575
4701
  if (roleModels.size === 0) {
3576
- return rejectPlan2(
4702
+ return rejectPlan3(
3577
4703
  plan,
3578
4704
  "OpenCode model plan does not contain any role overrides."
3579
4705
  );
@@ -3609,7 +4735,7 @@ function applyModelPlan(plan) {
3609
4735
  observed: "agents role overrides updated"
3610
4736
  }
3611
4737
  ],
3612
- backups: existsSync7(`${targetPath}.bak`) ? [{ path: `${targetPath}.bak`, label: "managed backup" }] : [],
4738
+ backups: existsSync10(`${targetPath}.bak`) ? [{ path: `${targetPath}.bak`, label: "managed backup" }] : [],
3613
4739
  warnings: [],
3614
4740
  disclaimers: defaultDisclaimers()
3615
4741
  };
@@ -3618,7 +4744,7 @@ function applyInstallSkills(plan) {
3618
4744
  for (const skill of RECOMMENDED_SKILLS) {
3619
4745
  const result = installRecommendedSkill(skill);
3620
4746
  if (result.status === "failed") {
3621
- return rejectPlan2(
4747
+ return rejectPlan3(
3622
4748
  plan,
3623
4749
  `Failed to install recommended OpenCode skill: ${skill.name}.`
3624
4750
  );
@@ -3626,7 +4752,7 @@ function applyInstallSkills(plan) {
3626
4752
  }
3627
4753
  const bundled = installCustomSkills();
3628
4754
  if (!bundled.success) {
3629
- return rejectPlan2(
4755
+ return rejectPlan3(
3630
4756
  plan,
3631
4757
  "Failed to install bundled thoth-agents OpenCode skills."
3632
4758
  );
@@ -3638,7 +4764,7 @@ function applyOpenCodePlan(plan) {
3638
4764
  if (rejection) return rejection;
3639
4765
  const status = getOpenCodeStatus();
3640
4766
  if (!classifyApplySafety(status.state)) {
3641
- return rejectPlan2(
4767
+ return rejectPlan3(
3642
4768
  plan,
3643
4769
  `OpenCode state is ${status.state}; refusing to apply plan without a safe status.`
3644
4770
  );
@@ -3648,7 +4774,7 @@ function applyOpenCodePlan(plan) {
3648
4774
  const backups = [];
3649
4775
  const pluginResult = ensureLatestPluginEntry();
3650
4776
  if (!pluginResult.success) {
3651
- return rejectPlan2(
4777
+ return rejectPlan3(
3652
4778
  plan,
3653
4779
  pluginResult.error ?? "Failed to update OpenCode plugin config."
3654
4780
  );
@@ -3657,7 +4783,7 @@ function applyOpenCodePlan(plan) {
3657
4783
  ...targetForMainConfig("installed"),
3658
4784
  observed: `plugin includes ${EXPECTED_PLUGIN}`
3659
4785
  });
3660
- if (existsSync7(`${pluginResult.configPath}.bak`)) {
4786
+ if (existsSync10(`${pluginResult.configPath}.bak`)) {
3661
4787
  backups.push({
3662
4788
  path: `${pluginResult.configPath}.bak`,
3663
4789
  label: "OpenCode config backup"
@@ -3666,7 +4792,7 @@ function applyOpenCodePlan(plan) {
3666
4792
  if (plan.action === "sync" || plan.action === "install") {
3667
4793
  const defaultAgentResult = disableDefaultAgents();
3668
4794
  if (!defaultAgentResult.success) {
3669
- return rejectPlan2(
4795
+ return rejectPlan3(
3670
4796
  plan,
3671
4797
  defaultAgentResult.error ?? "Failed to disable OpenCode default agents."
3672
4798
  );
@@ -3683,7 +4809,7 @@ function applyOpenCodePlan(plan) {
3683
4809
  getExistingLiteConfigPath()
3684
4810
  );
3685
4811
  if (!liteResult.success) {
3686
- return rejectPlan2(
4812
+ return rejectPlan3(
3687
4813
  plan,
3688
4814
  liteResult.error ?? "Failed to write thoth-agents config."
3689
4815
  );
@@ -3692,7 +4818,7 @@ function applyOpenCodePlan(plan) {
3692
4818
  ...targetForLiteConfig("installed"),
3693
4819
  observed: "seven-agent roster written"
3694
4820
  });
3695
- if (existsSync7(`${liteResult.configPath}.bak`)) {
4821
+ if (existsSync10(`${liteResult.configPath}.bak`)) {
3696
4822
  backups.push({
3697
4823
  path: `${liteResult.configPath}.bak`,
3698
4824
  label: "thoth-agents config backup"
@@ -3724,7 +4850,8 @@ function applyOpenCodePlan(plan) {
3724
4850
  // src/cli/operations/index.ts
3725
4851
  var OPERATION_HARNESSES = {
3726
4852
  opencode: opencodeOperationAdapter,
3727
- codex: codexOperationAdapter
4853
+ codex: codexOperationAdapter,
4854
+ claude: claudeCodeOperationAdapter
3728
4855
  };
3729
4856
  var SUPPORTED_OPERATION_HARNESSES = Object.keys(
3730
4857
  OPERATION_HARNESSES
@@ -3739,14 +4866,27 @@ function getOperationHarness(harness) {
3739
4866
  }
3740
4867
 
3741
4868
  export {
4869
+ claudeCodeAdapter,
3742
4870
  codexAdapter,
3743
4871
  CODEX_ROLE_NAMES,
3744
4872
  parseRoleTomlModel,
3745
4873
  buildCodexSetupPlan,
3746
4874
  formatCodexSetupPlan,
3747
4875
  applyCodexSetup,
4876
+ CLAUDE_CODE_ROLE_NAMES,
4877
+ parseSubagentModel,
4878
+ buildClaudeCodeSetupPlan,
4879
+ applyClaudeCodeSetup,
4880
+ formatClaudeCodeSetupPlan,
3748
4881
  RECOMMENDED_SKILLS,
3749
4882
  installRecommendedSkill,
4883
+ getClaudeCodeStatus,
4884
+ buildClaudeCodeInstallPlan,
4885
+ buildClaudeCodeUpdatePlan,
4886
+ buildClaudeCodeSyncPlan,
4887
+ buildClaudeCodeModelPlan,
4888
+ applyClaudeCodePlan,
4889
+ defaultClaudeCodeModelRoles,
3750
4890
  getCodexStatus,
3751
4891
  buildCodexUpdatePlan,
3752
4892
  buildCodexSyncPlan,