thoth-agents 0.1.19 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -9
- package/dist/agents/prompt-dialects.d.ts +9 -0
- package/dist/{chunk-2SGSRR6L.js → chunk-3NOVCFN7.js} +61 -3
- package/dist/chunk-4WYCZ5Z7.js +698 -0
- package/dist/{chunk-7CTSLCEU.js → chunk-WH3F3GWE.js} +1461 -310
- package/dist/cli/claude-code-install.d.ts +53 -0
- package/dist/cli/claude-code-paths.d.ts +31 -0
- package/dist/cli/codex-install.d.ts +1 -5
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/index.js +85 -27
- package/dist/cli/managed-state-io.d.ts +16 -0
- package/dist/cli/operations/claude-code.d.ts +21 -0
- package/dist/cli/tui/index.js +87 -9
- package/dist/cli/tui/operations.d.ts +2 -0
- package/dist/cli/types.d.ts +3 -3
- package/dist/config/index.d.ts +1 -1
- package/dist/config/schema.d.ts +11 -0
- package/dist/config/utils.d.ts +5 -0
- package/dist/harness/adapters/claude-code.d.ts +24 -0
- package/dist/harness/core/package-version.d.ts +7 -0
- package/dist/harness/types.d.ts +1 -1
- package/dist/harness/writers/claude-code-plugin-package.d.ts +32 -0
- package/dist/harness/writers/claude-code-skill-layout.d.ts +16 -0
- package/dist/harness/writers/claude-code-subagent.d.ts +26 -0
- package/dist/harness/writers/fs-skill-collect.d.ts +7 -0
- package/dist/index.js +4 -476
- package/package.json +1 -1
- package/thoth-agents.schema.json +16 -0
- package/dist/chunk-DYGVRAMS.js +0 -182
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ALL_AGENT_NAMES,
|
|
3
|
+
CLAUDE_CODE_PROMPT_DIALECT,
|
|
4
|
+
CLAUDE_CODE_SUBAGENT_NAMESPACE,
|
|
3
5
|
CODEX_PROMPT_DIALECT,
|
|
4
6
|
CONTEXT7_MCP_URL,
|
|
5
7
|
CUSTOM_SKILLS,
|
|
@@ -7,6 +9,7 @@ import {
|
|
|
7
9
|
DEFAULT_THOTH_COMMAND,
|
|
8
10
|
GREP_APP_MCP_URL,
|
|
9
11
|
appendPromptSections,
|
|
12
|
+
claudeCodeSubagentType,
|
|
10
13
|
composeAgentPrompt,
|
|
11
14
|
createModelFamilySection,
|
|
12
15
|
createOrchestratorPromptSections,
|
|
@@ -23,6 +26,7 @@ import {
|
|
|
23
26
|
getCustomSkillsDir,
|
|
24
27
|
getExistingConfigPath,
|
|
25
28
|
getExistingLiteConfigPath,
|
|
29
|
+
getPrimaryModelId,
|
|
26
30
|
getSkillRegistry,
|
|
27
31
|
installCustomSkills,
|
|
28
32
|
loadAgentPrompt,
|
|
@@ -31,11 +35,58 @@ import {
|
|
|
31
35
|
renderRolePrompt,
|
|
32
36
|
writeConfig,
|
|
33
37
|
writeLiteConfig
|
|
34
|
-
} from "./chunk-
|
|
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
|
|
744
|
-
return `./${
|
|
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
|
-
|
|
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
|
|
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(...
|
|
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
|
|
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 =
|
|
1170
|
-
const files =
|
|
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
|
|
1184
|
-
const content =
|
|
1185
|
-
const sourcePath =
|
|
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}/${
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
1868
|
-
const normalized =
|
|
1869
|
-
if (isAbsolute(
|
|
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
|
|
1893
|
-
return
|
|
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
|
|
1911
|
-
|
|
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(
|
|
1943
|
-
return basename(
|
|
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 =
|
|
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 =
|
|
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 || !
|
|
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
|
-
|
|
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
|
|
2068
|
-
const artifact = artifacts.find((candidate) => candidate.path ===
|
|
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: ${
|
|
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 =
|
|
2090
|
-
const nextManagedModelState =
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
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 =
|
|
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(
|
|
2215
|
-
return
|
|
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
|
-
|
|
2248
|
-
|
|
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
|
-
|
|
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,1150 @@ 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((
|
|
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/
|
|
2285
|
-
import { existsSync as
|
|
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
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2305
|
+
|
|
2306
|
+
// src/harness/adapters/claude-code.ts
|
|
2307
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2308
|
+
|
|
2309
|
+
// src/harness/writers/claude-code-plugin-package.ts
|
|
2310
|
+
var CLAUDE_PLUGIN_MANIFEST_FIELDS = [
|
|
2311
|
+
"name",
|
|
2312
|
+
"version",
|
|
2313
|
+
"description",
|
|
2314
|
+
"author"
|
|
2315
|
+
];
|
|
2316
|
+
var PLUGIN_MANIFEST_PATH = ".claude-plugin/plugin.json";
|
|
2317
|
+
var PLUGIN_ASSETS_PATH = ".claude-plugin/.thoth-agents-plugin-assets.json";
|
|
2318
|
+
function normalizePath2(value) {
|
|
2319
|
+
return value.replace(/\\/g, "/");
|
|
2320
|
+
}
|
|
2321
|
+
function stableJson4(value) {
|
|
2322
|
+
return `${JSON.stringify(value, null, 2)}
|
|
2323
|
+
`;
|
|
2324
|
+
}
|
|
2325
|
+
function orderedManifest(manifest) {
|
|
2326
|
+
const ordered = {};
|
|
2327
|
+
for (const field of CLAUDE_PLUGIN_MANIFEST_FIELDS) {
|
|
2328
|
+
const value = manifest[field];
|
|
2329
|
+
if (value !== void 0) ordered[field] = value;
|
|
2330
|
+
}
|
|
2331
|
+
return ordered;
|
|
2332
|
+
}
|
|
2333
|
+
function provenanceFor(artifact) {
|
|
2334
|
+
const path4 = normalizePath2(artifact.path);
|
|
2335
|
+
if (path4.endsWith("/")) return void 0;
|
|
2336
|
+
const content = typeof artifact.content === "string" ? artifact.content : Buffer.from(artifact.content).toString("utf8");
|
|
2337
|
+
return { path: path4, kind: artifact.kind, sha256: sha256Hash(content) };
|
|
2338
|
+
}
|
|
2339
|
+
function renderClaudeCodePluginPackage(input) {
|
|
2340
|
+
const components = [...input.componentArtifacts].sort(
|
|
2341
|
+
(left, right) => normalizePath2(left.path).localeCompare(normalizePath2(right.path))
|
|
2342
|
+
);
|
|
2343
|
+
const provenance = components.map(provenanceFor).filter((entry) => entry !== void 0).sort((left, right) => left.path.localeCompare(right.path));
|
|
2344
|
+
const manifestArtifact = {
|
|
2345
|
+
harness: "claude",
|
|
2346
|
+
kind: "manifest",
|
|
2347
|
+
path: PLUGIN_MANIFEST_PATH,
|
|
2348
|
+
description: "Deterministic Claude Code plugin package manifest.",
|
|
2349
|
+
content: stableJson4(orderedManifest(input.manifest))
|
|
2350
|
+
};
|
|
2351
|
+
const provenanceArtifact = {
|
|
2352
|
+
harness: "claude",
|
|
2353
|
+
kind: "manifest",
|
|
2354
|
+
path: PLUGIN_ASSETS_PATH,
|
|
2355
|
+
description: "Deterministic Claude Code plugin package asset provenance.",
|
|
2356
|
+
content: stableJson4({ generatedBy: "thoth-agents", assets: provenance })
|
|
2357
|
+
};
|
|
2358
|
+
return {
|
|
2359
|
+
artifacts: [manifestArtifact, provenanceArtifact, ...components],
|
|
2360
|
+
diagnostics: []
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
// src/harness/writers/claude-code-skill-layout.ts
|
|
2365
|
+
import * as fs3 from "fs";
|
|
2366
|
+
import * as path3 from "path";
|
|
2367
|
+
var SKILLS_BASE_PATH = "skills";
|
|
2368
|
+
var SKILLS_MANIFEST_PATH = "skills/.thoth-agents-manifest.json";
|
|
2369
|
+
function renderClaudeCodeSkillLayout(input) {
|
|
2370
|
+
const artifacts = [];
|
|
2371
|
+
const diagnostics = [];
|
|
2372
|
+
const manifest = [];
|
|
2373
|
+
const sourceBaseRoot = input.packageRoot ?? input.projectRoot;
|
|
2374
|
+
for (const skill of [...input.skills].sort(
|
|
2375
|
+
(left, right) => left.name.localeCompare(right.name)
|
|
2376
|
+
)) {
|
|
2377
|
+
const sourceRoot = path3.join(sourceBaseRoot, skill.sourcePath);
|
|
2378
|
+
const files = collectSkillFiles(sourceRoot);
|
|
2379
|
+
if (files.length === 0) {
|
|
2380
|
+
diagnostics.push({
|
|
2381
|
+
severity: "warning",
|
|
2382
|
+
code: "claude-code.skill.source_missing",
|
|
2383
|
+
message: `Skipping Claude Code skill "${skill.name}" because source path "${skill.sourcePath}" was not found.`,
|
|
2384
|
+
harness: "claude",
|
|
2385
|
+
surface: "plugin-skills-directory",
|
|
2386
|
+
fallback: "diagnostic-only"
|
|
2387
|
+
});
|
|
2388
|
+
continue;
|
|
2389
|
+
}
|
|
2390
|
+
for (const file of files) {
|
|
2391
|
+
const relative4 = normalizeSkillPath(path3.relative(sourceRoot, file));
|
|
2392
|
+
const content = fs3.readFileSync(file, "utf8");
|
|
2393
|
+
const sourcePath = normalizeSkillPath(
|
|
2394
|
+
path3.relative(sourceBaseRoot, file)
|
|
2395
|
+
);
|
|
2396
|
+
const outputPath = `${SKILLS_BASE_PATH}/${skill.name}/${relative4}`;
|
|
2397
|
+
artifacts.push({
|
|
2398
|
+
harness: "claude",
|
|
2399
|
+
kind: "skill",
|
|
2400
|
+
path: outputPath,
|
|
2401
|
+
description: `Claude Code plugin-bundled skill artifact for ${skill.name}`,
|
|
2402
|
+
content
|
|
2403
|
+
});
|
|
2404
|
+
manifest.push({
|
|
2405
|
+
name: skill.name,
|
|
2406
|
+
sourcePath,
|
|
2407
|
+
outputPath,
|
|
2408
|
+
sha256: sha256Hash(content)
|
|
2409
|
+
});
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
artifacts.push({
|
|
2413
|
+
harness: "claude",
|
|
2414
|
+
kind: "manifest",
|
|
2415
|
+
path: SKILLS_MANIFEST_PATH,
|
|
2416
|
+
description: "Generated Claude Code plugin-bundled skill source manifest with source hashes.",
|
|
2417
|
+
content: `${JSON.stringify({ generatedBy: "thoth-agents", skills: manifest }, null, 2)}
|
|
2418
|
+
`
|
|
2419
|
+
});
|
|
2420
|
+
return { artifacts, diagnostics };
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
// src/harness/writers/claude-code-subagent.ts
|
|
2424
|
+
var CLAUDE_CODE_MODELS = [
|
|
2425
|
+
"sonnet",
|
|
2426
|
+
"opus",
|
|
2427
|
+
"haiku",
|
|
2428
|
+
"inherit"
|
|
2429
|
+
];
|
|
2430
|
+
function isClaudeCodeModel(value) {
|
|
2431
|
+
return CLAUDE_CODE_MODELS.includes(value);
|
|
2432
|
+
}
|
|
2433
|
+
function yamlScalar(value) {
|
|
2434
|
+
const needsQuoting = /[:#\-?*&!|>'"%@`{}[\],]|^\s|\s$|^$/.test(value);
|
|
2435
|
+
if (!needsQuoting) return value;
|
|
2436
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
2437
|
+
return `"${escaped}"`;
|
|
2438
|
+
}
|
|
2439
|
+
function renderClaudeCodeSubagent(input) {
|
|
2440
|
+
const frontmatter = [
|
|
2441
|
+
"---",
|
|
2442
|
+
`name: ${yamlScalar(input.name)}`,
|
|
2443
|
+
`description: ${yamlScalar(input.description)}`,
|
|
2444
|
+
`model: ${input.model}`,
|
|
2445
|
+
...input.tools !== void 0 ? [`tools: ${yamlScalar(input.tools)}`] : [],
|
|
2446
|
+
"---"
|
|
2447
|
+
].join("\n");
|
|
2448
|
+
const body = input.instructions.trimEnd();
|
|
2449
|
+
return `${frontmatter}
|
|
2450
|
+
|
|
2451
|
+
${body}
|
|
2452
|
+
`;
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
// src/harness/adapters/claude-code.ts
|
|
2456
|
+
var CLAUDE_CODE_CAPABILITIES = CLAUDE_CODE_PROMPT_DIALECT.capabilities.capabilities;
|
|
2457
|
+
var CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS = {
|
|
2458
|
+
explorer: "haiku",
|
|
2459
|
+
librarian: "sonnet",
|
|
2460
|
+
oracle: "opus",
|
|
2461
|
+
designer: "sonnet",
|
|
2462
|
+
quick: "haiku",
|
|
2463
|
+
deep: "sonnet"
|
|
2464
|
+
};
|
|
2465
|
+
var READ_ONLY_ROLE_TOOLS = {
|
|
2466
|
+
explorer: "Read, Grep, Glob",
|
|
2467
|
+
librarian: "Read, Grep, Glob, WebSearch, WebFetch",
|
|
2468
|
+
oracle: "Read, Grep, Glob"
|
|
2469
|
+
};
|
|
2470
|
+
var WRITE_CAPABLE_ROLE_TOOLS = "Read, Edit, Write, Bash, Grep, Glob";
|
|
2471
|
+
function isClaudeCodeSubagentName(name) {
|
|
2472
|
+
return name in CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS;
|
|
2473
|
+
}
|
|
2474
|
+
function getClaudeCodeAgentModel(role, config) {
|
|
2475
|
+
if (!isClaudeCodeSubagentName(role.name)) return "inherit";
|
|
2476
|
+
const override = getPrimaryModelId(config?.agents?.[role.name]?.model);
|
|
2477
|
+
if (override && isClaudeCodeModel(override)) return override;
|
|
2478
|
+
return CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS[role.name];
|
|
2479
|
+
}
|
|
2480
|
+
function toolsForRole(role) {
|
|
2481
|
+
if (role.canMutateWorkspace) return WRITE_CAPABLE_ROLE_TOOLS;
|
|
2482
|
+
return READ_ONLY_ROLE_TOOLS[role.name] ?? "Read, Grep, Glob";
|
|
2483
|
+
}
|
|
2484
|
+
function claudeCodePromptSections(roleName) {
|
|
2485
|
+
switch (roleName) {
|
|
2486
|
+
case "orchestrator":
|
|
2487
|
+
return createOrchestratorPromptSections();
|
|
2488
|
+
case "explorer":
|
|
2489
|
+
case "librarian":
|
|
2490
|
+
case "oracle":
|
|
2491
|
+
return createReadOnlySpecialistPromptSections(roleName);
|
|
2492
|
+
case "designer":
|
|
2493
|
+
case "quick":
|
|
2494
|
+
case "deep":
|
|
2495
|
+
return createWriteCapableSpecialistPromptSections(roleName);
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
function claudeCodeModelFamilyPromptSection(roleName, model) {
|
|
2499
|
+
const section = createModelFamilySection(roleName, model);
|
|
2500
|
+
return section ? renderPromptSection(section, CLAUDE_CODE_PROMPT_DIALECT) : void 0;
|
|
2501
|
+
}
|
|
2502
|
+
function claudeCodeStepBudgetPromptSection(steps) {
|
|
2503
|
+
const section = createStepBudgetSection(steps);
|
|
2504
|
+
return section ? renderPromptSection(section, CLAUDE_CODE_PROMPT_DIALECT) : void 0;
|
|
2505
|
+
}
|
|
2506
|
+
function renderClaudeCodeRolePrompt(roleName, config, model) {
|
|
2507
|
+
const promptOverrides = loadAgentPrompt(roleName, config?.preset);
|
|
2508
|
+
const override = getAgentOverride(config, roleName);
|
|
2509
|
+
const basePrompt = renderRolePrompt(
|
|
2510
|
+
claudeCodePromptSections(roleName),
|
|
2511
|
+
CLAUDE_CODE_PROMPT_DIALECT
|
|
2512
|
+
);
|
|
2513
|
+
const prompt = composeAgentPrompt({
|
|
2514
|
+
basePrompt,
|
|
2515
|
+
customPrompt: promptOverrides.prompt,
|
|
2516
|
+
customAppendPrompt: appendPromptSections(
|
|
2517
|
+
claudeCodeModelFamilyPromptSection(roleName, model),
|
|
2518
|
+
promptOverrides.appendPrompt
|
|
2519
|
+
)
|
|
2520
|
+
});
|
|
2521
|
+
return appendPromptSections(
|
|
2522
|
+
prompt,
|
|
2523
|
+
claudeCodeStepBudgetPromptSection(override?.steps)
|
|
2524
|
+
);
|
|
2525
|
+
}
|
|
2526
|
+
function claudeCodeRoleInstructions(role) {
|
|
2527
|
+
return [
|
|
2528
|
+
"<role-operational-contract>",
|
|
2529
|
+
`- Role: ${role.name}`,
|
|
2530
|
+
`- Mode: ${role.mode}`,
|
|
2531
|
+
`- Scope: ${role.scope}`,
|
|
2532
|
+
`- Responsibility: ${role.responsibility}`,
|
|
2533
|
+
"- Use AskUserQuestion for local blocking decisions.",
|
|
2534
|
+
`- ${role.name} runs as an auto-discovered Claude Code plugin subagent invoked via Task(subagent_type: ${claudeCodeSubagentType(role.name)}); plugin subagents are namespaced with the plugin name. The orchestrator is the main Claude Code session.`,
|
|
2535
|
+
"- Role permissions are enforced by this subagent's frontmatter `tools` allowlist; read-only roles cannot mutate the workspace.",
|
|
2536
|
+
...role.toolGovernance.map((rule) => `- ${rule}`),
|
|
2537
|
+
...role.verification.map((rule) => `- ${rule}`),
|
|
2538
|
+
"</role-operational-contract>"
|
|
2539
|
+
].join("\n");
|
|
2540
|
+
}
|
|
2541
|
+
function roleInstructions2(role, config) {
|
|
2542
|
+
const model = getClaudeCodeAgentModel(role, config);
|
|
2543
|
+
return [
|
|
2544
|
+
renderClaudeCodeRolePrompt(role.name, config, model),
|
|
2545
|
+
claudeCodeRoleInstructions(role),
|
|
2546
|
+
renderMemoryGovernanceInstructions(role, CLAUDE_CODE_PROMPT_DIALECT)
|
|
2547
|
+
].join("\n\n");
|
|
2548
|
+
}
|
|
2549
|
+
function renderClaudeCodeRootInstructions(config) {
|
|
2550
|
+
const rootOverride = getAgentOverride(config, "orchestrator");
|
|
2551
|
+
const rootPrompt = renderClaudeCodeRolePrompt(
|
|
2552
|
+
"orchestrator",
|
|
2553
|
+
config,
|
|
2554
|
+
rootOverride?.model ?? DEFAULT_MODELS.orchestrator
|
|
2555
|
+
);
|
|
2556
|
+
const specialists = getAgentPackContract().roles.filter((role) => role.name !== "orchestrator").map((role) => claudeCodeSubagentType(role.name)).join(", ");
|
|
2557
|
+
return [
|
|
2558
|
+
rootPrompt,
|
|
2559
|
+
"<claude-code-runtime>",
|
|
2560
|
+
"- You ARE the Claude Code main-thread agent: the delegate-first root coordinator. This is your system prompt (activated via the plugin settings.json `agent` key), so orchestrator-only and root-owned rules apply to you directly.",
|
|
2561
|
+
'- As your FIRST action on a new session, when thoth-mem tools are installed and session/project identity is known, call mem_session(action="start") as step 0 before any other thoth-mem call, then save the real user prompt with mem_save(kind="prompt") before later delegation.',
|
|
2562
|
+
"- thoth-mem tools are provided by this plugin's bundled MCP server and are exposed under a plugin namespace; call the available namespaced tool whose name ends in mem_session, mem_recall, mem_context, mem_save, mem_get, or mem_project (do not assume a bare, unnamespaced tool name).",
|
|
2563
|
+
"- If thoth-mem tools or identity values are unavailable, disclose that memory bootstrap could not run and continue without claiming memory was saved.",
|
|
2564
|
+
`- Delegate via the Task tool with \`subagent_type\` set to a plugin-namespaced specialist: ${specialists}. Bare role names (e.g. "explorer") are NOT valid in this harness \u2014 always use the ${CLAUDE_CODE_SUBAGENT_NAMESPACE}: prefix.`,
|
|
2565
|
+
"- Parallel delegation is supported: issue multiple Task calls in one turn for independent work.",
|
|
2566
|
+
'- Before delegating after meaningful context changes, refresh the handoff body with root-owned mem_session(action="summary") or mem_save(kind="session_summary") when available.',
|
|
2567
|
+
"- Use AskUserQuestion for blocking user decisions; do not ask those questions in plain prose.",
|
|
2568
|
+
"- Track progress with TodoWrite; subagents do not own progress checkboxes or root-only memory.",
|
|
2569
|
+
"- Role permissions are enforced at runtime by each subagent's frontmatter `tools` allowlist.",
|
|
2570
|
+
"</claude-code-runtime>"
|
|
2571
|
+
].join("\n");
|
|
2572
|
+
}
|
|
2573
|
+
function claudeCodeMcpServers() {
|
|
2574
|
+
const [exaCommand = "", ...exaArgs] = exa.command;
|
|
2575
|
+
const [thothCommand = "", ...thothArgs] = DEFAULT_THOTH_COMMAND;
|
|
2576
|
+
return {
|
|
2577
|
+
exa: {
|
|
2578
|
+
command: exaCommand,
|
|
2579
|
+
...exaArgs.length > 0 ? { args: exaArgs } : {},
|
|
2580
|
+
...exa.environment && Object.keys(exa.environment).length > 0 ? { env: exa.environment } : {}
|
|
2581
|
+
},
|
|
2582
|
+
context7: { type: "http", url: CONTEXT7_MCP_URL },
|
|
2583
|
+
grep_app: { type: "http", url: GREP_APP_MCP_URL },
|
|
2584
|
+
thoth_mem: {
|
|
2585
|
+
command: thothCommand,
|
|
2586
|
+
...thothArgs.length > 0 ? { args: thothArgs } : {}
|
|
2587
|
+
}
|
|
2588
|
+
};
|
|
2589
|
+
}
|
|
2590
|
+
function stableJson5(value) {
|
|
2591
|
+
return `${JSON.stringify(value, null, 2)}
|
|
2592
|
+
`;
|
|
2593
|
+
}
|
|
2594
|
+
function readRootPackageVersion2(context) {
|
|
2595
|
+
const packageJsonPath = findRootPackageJsonPath([
|
|
2596
|
+
...hasPackageRoot(context) ? [context.packageRoot] : [],
|
|
2597
|
+
context.projectRoot,
|
|
2598
|
+
process.cwd(),
|
|
2599
|
+
fileURLToPath3(new URL(".", import.meta.url))
|
|
2600
|
+
]);
|
|
2601
|
+
return readPackageJsonVersion(packageJsonPath);
|
|
2602
|
+
}
|
|
2603
|
+
function hasConfig(context) {
|
|
2604
|
+
return "config" in context;
|
|
2605
|
+
}
|
|
2606
|
+
function hasPackageRoot(context) {
|
|
2607
|
+
return "packageRoot" in context && typeof context.packageRoot === "string" && context.packageRoot.length > 0;
|
|
2608
|
+
}
|
|
2609
|
+
function createPluginManifest(context) {
|
|
2610
|
+
return {
|
|
2611
|
+
name: "thoth-agents",
|
|
2612
|
+
version: readRootPackageVersion2(context),
|
|
2613
|
+
description: "Delegate-first agent pack with seven roles, thoth-mem persistence, and bundled SDD skills, packaged for Claude Code.",
|
|
2614
|
+
author: { name: "thoth-agents" }
|
|
2615
|
+
};
|
|
2616
|
+
}
|
|
2617
|
+
function renderSubagentArtifacts(config) {
|
|
2618
|
+
const artifacts = [];
|
|
2619
|
+
for (const role of getAgentPackContract().roles.filter(
|
|
2620
|
+
(candidate) => candidate.name !== "orchestrator"
|
|
2621
|
+
)) {
|
|
2622
|
+
const content = renderClaudeCodeSubagent({
|
|
2623
|
+
name: role.name,
|
|
2624
|
+
description: role.responsibility,
|
|
2625
|
+
tools: toolsForRole(role),
|
|
2626
|
+
model: getClaudeCodeAgentModel(role, config),
|
|
2627
|
+
instructions: roleInstructions2(role, config)
|
|
2628
|
+
});
|
|
2629
|
+
artifacts.push({
|
|
2630
|
+
harness: "claude",
|
|
2631
|
+
kind: "agent-config",
|
|
2632
|
+
path: `agents/${role.name}.md`,
|
|
2633
|
+
description: `Claude Code subagent definition for ${role.name}.`,
|
|
2634
|
+
content
|
|
2635
|
+
});
|
|
2636
|
+
}
|
|
2637
|
+
return artifacts;
|
|
2638
|
+
}
|
|
2639
|
+
function renderOrchestratorArtifact(config) {
|
|
2640
|
+
const orchestrator = getAgentPackContract().roles.find(
|
|
2641
|
+
(role) => role.name === "orchestrator"
|
|
2642
|
+
);
|
|
2643
|
+
const content = renderClaudeCodeSubagent({
|
|
2644
|
+
name: "orchestrator",
|
|
2645
|
+
description: orchestrator?.responsibility ?? "Delegate-first root coordinator for SDD workflow and specialist dispatch.",
|
|
2646
|
+
model: "inherit",
|
|
2647
|
+
instructions: renderClaudeCodeRootInstructions(config)
|
|
2648
|
+
});
|
|
2649
|
+
return {
|
|
2650
|
+
harness: "claude",
|
|
2651
|
+
kind: "agent-config",
|
|
2652
|
+
path: "agents/orchestrator.md",
|
|
2653
|
+
description: "Claude Code orchestrator agent, activated as the main thread via settings.json.",
|
|
2654
|
+
content
|
|
2655
|
+
};
|
|
2656
|
+
}
|
|
2657
|
+
var claudeCodeAdapter = {
|
|
2658
|
+
id: "claude",
|
|
2659
|
+
displayName: "Claude Code",
|
|
2660
|
+
capabilities: CLAUDE_CODE_CAPABILITIES,
|
|
2661
|
+
render(context) {
|
|
2662
|
+
const config = hasConfig(context) ? context.config : void 0;
|
|
2663
|
+
const componentArtifacts = [
|
|
2664
|
+
...renderSubagentArtifacts(config),
|
|
2665
|
+
renderOrchestratorArtifact(config),
|
|
2666
|
+
{
|
|
2667
|
+
harness: "claude",
|
|
2668
|
+
kind: "mcp-config",
|
|
2669
|
+
path: ".mcp.json",
|
|
2670
|
+
description: "Claude Code plugin-bundled MCP server definitions.",
|
|
2671
|
+
content: stableJson5({ mcpServers: claudeCodeMcpServers() })
|
|
2672
|
+
},
|
|
2673
|
+
{
|
|
2674
|
+
harness: "claude",
|
|
2675
|
+
kind: "harness-config",
|
|
2676
|
+
path: "settings.json",
|
|
2677
|
+
description: "Activates the orchestrator agent as the Claude Code main thread.",
|
|
2678
|
+
content: stableJson5({ agent: "orchestrator" })
|
|
2679
|
+
}
|
|
2680
|
+
];
|
|
2681
|
+
const skillLayout = renderClaudeCodeSkillLayout({
|
|
2682
|
+
projectRoot: context.projectRoot,
|
|
2683
|
+
...hasPackageRoot(context) ? { packageRoot: context.packageRoot } : {},
|
|
2684
|
+
skills: getSkillRegistry()
|
|
2685
|
+
});
|
|
2686
|
+
componentArtifacts.push(...skillLayout.artifacts);
|
|
2687
|
+
const pluginPackage = renderClaudeCodePluginPackage({
|
|
2688
|
+
manifest: createPluginManifest(context),
|
|
2689
|
+
componentArtifacts
|
|
2690
|
+
});
|
|
2691
|
+
return {
|
|
2692
|
+
harness: "claude",
|
|
2693
|
+
artifacts: pluginPackage.artifacts,
|
|
2694
|
+
diagnostics: [...skillLayout.diagnostics, ...pluginPackage.diagnostics]
|
|
2695
|
+
};
|
|
2696
|
+
}
|
|
2697
|
+
};
|
|
2698
|
+
|
|
2699
|
+
// src/cli/claude-code-install.ts
|
|
2700
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
|
|
2701
|
+
import { join as join8 } from "path";
|
|
2702
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
2703
|
+
|
|
2704
|
+
// src/cli/claude-code-paths.ts
|
|
2705
|
+
import { homedir as homedir2 } from "os";
|
|
2706
|
+
import { join as join7 } from "path";
|
|
2707
|
+
var CLAUDE_CODE_ROLE_NAMES = [
|
|
2708
|
+
"explorer",
|
|
2709
|
+
"librarian",
|
|
2710
|
+
"oracle",
|
|
2711
|
+
"designer",
|
|
2712
|
+
"quick",
|
|
2713
|
+
"deep"
|
|
2714
|
+
];
|
|
2715
|
+
function resolveClaudeCodeTargets(options) {
|
|
2716
|
+
const home = options.homeDir ?? homedir2();
|
|
2717
|
+
const pluginRoot = options.scope === "project" ? join7(options.projectRoot, ".claude", "skills", "thoth-agents") : join7(home, ".claude", "skills", "thoth-agents");
|
|
2718
|
+
return {
|
|
2719
|
+
scope: options.scope,
|
|
2720
|
+
pluginRoot,
|
|
2721
|
+
pluginManifestPath: join7(pluginRoot, ".claude-plugin", "plugin.json"),
|
|
2722
|
+
agentPaths: CLAUDE_CODE_ROLE_NAMES.map((role) => ({
|
|
2723
|
+
role,
|
|
2724
|
+
path: join7(pluginRoot, "agents", `${role}.md`)
|
|
2725
|
+
})),
|
|
2726
|
+
mcpPath: join7(pluginRoot, ".mcp.json"),
|
|
2727
|
+
hooksPath: join7(pluginRoot, "hooks", "hooks.json"),
|
|
2728
|
+
skillsDir: join7(pluginRoot, "skills"),
|
|
2729
|
+
managedModelsPath: join7(pluginRoot, ".thoth-agents-managed-models.json")
|
|
2730
|
+
};
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
// src/cli/claude-code-install.ts
|
|
2734
|
+
var CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION = 1;
|
|
2735
|
+
var isClaudeCodeModelAlias = isClaudeCodeModel;
|
|
2736
|
+
function parseSubagentModel(content) {
|
|
2737
|
+
return /^model:\s*(\S+)\s*$/m.exec(content)?.[1];
|
|
2738
|
+
}
|
|
2739
|
+
function replaceSubagentModel(content, model) {
|
|
2740
|
+
if (/^model:\s*\S+\s*$/m.test(content)) {
|
|
2741
|
+
return content.replace(/^model:\s*\S+\s*$/m, `model: ${model}`);
|
|
2742
|
+
}
|
|
2743
|
+
return content;
|
|
2744
|
+
}
|
|
2745
|
+
function emptyManagedModelState3() {
|
|
2746
|
+
return emptyManagedModelState(CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION);
|
|
2747
|
+
}
|
|
2748
|
+
function parseManagedModelStateJson2(text) {
|
|
2749
|
+
return parseManagedModelStateJson(
|
|
2750
|
+
text,
|
|
2751
|
+
CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION
|
|
2752
|
+
);
|
|
2753
|
+
}
|
|
2754
|
+
function readManagedModelState3(path4) {
|
|
2755
|
+
return readManagedModelState(
|
|
2756
|
+
path4,
|
|
2757
|
+
CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION
|
|
2758
|
+
);
|
|
2759
|
+
}
|
|
2760
|
+
function resolvePackageRoot2(packageRoot) {
|
|
2761
|
+
if (packageRoot) return packageRoot;
|
|
2762
|
+
return findPackageRoot(fileURLToPath4(new URL(".", import.meta.url))) ?? void 0;
|
|
2763
|
+
}
|
|
2764
|
+
function targetKindForArtifact(artifact) {
|
|
2765
|
+
const path4 = artifact.path.replaceAll("\\", "/");
|
|
2766
|
+
if (path4 === ".claude-plugin/plugin.json") return "plugin-manifest";
|
|
2767
|
+
if (path4.startsWith("agents/")) return "subagent";
|
|
2768
|
+
if (path4 === ".mcp.json") return "mcp-config";
|
|
2769
|
+
if (path4.startsWith("hooks/")) return "hook";
|
|
2770
|
+
if (path4.startsWith("skills/")) return "skill";
|
|
2771
|
+
return "plugin-asset";
|
|
2772
|
+
}
|
|
2773
|
+
function roleForArtifact(artifact) {
|
|
2774
|
+
const match = /^agents\/([^/]+)\.md$/.exec(
|
|
2775
|
+
artifact.path.replaceAll("\\", "/")
|
|
2776
|
+
);
|
|
2777
|
+
const name = match?.[1];
|
|
2778
|
+
return name && CLAUDE_CODE_ROLE_NAMES.includes(name) ? name : void 0;
|
|
2779
|
+
}
|
|
2780
|
+
function applyConfiguredModel(content, role, state, nextState, reset) {
|
|
2781
|
+
const renderedModel = parseSubagentModel(content);
|
|
2782
|
+
if (!role || !renderedModel) return content;
|
|
2783
|
+
nextState.models[role] = renderedModel;
|
|
2784
|
+
const configured = reset ? void 0 : state.configuredModels?.[role];
|
|
2785
|
+
if (configured === void 0) return content;
|
|
2786
|
+
nextState.configuredModels ??= {};
|
|
2787
|
+
nextState.configuredModels[role] = configured;
|
|
2788
|
+
return replaceSubagentModel(content, configured);
|
|
2789
|
+
}
|
|
2790
|
+
function buildClaudeCodeSetupPlan(config) {
|
|
2791
|
+
const targets = resolveClaudeCodeTargets({
|
|
2792
|
+
scope: config.scope,
|
|
2793
|
+
projectRoot: config.projectRoot,
|
|
2794
|
+
homeDir: config.homeDir
|
|
2795
|
+
});
|
|
2796
|
+
const packageRoot = resolvePackageRoot2(config.packageRoot);
|
|
2797
|
+
const render = claudeCodeAdapter.render({
|
|
2798
|
+
projectRoot: config.projectRoot,
|
|
2799
|
+
...packageRoot ? { packageRoot } : {}
|
|
2800
|
+
});
|
|
2801
|
+
const state = readManagedModelState3(targets.managedModelsPath);
|
|
2802
|
+
const nextState = emptyManagedModelState3();
|
|
2803
|
+
const items = render.artifacts.map((artifact) => {
|
|
2804
|
+
const role = roleForArtifact(artifact);
|
|
2805
|
+
const rendered = String(artifact.content ?? "");
|
|
2806
|
+
const content = role !== void 0 ? applyConfiguredModel(rendered, role, state, nextState, config.reset) : rendered;
|
|
2807
|
+
const targetPath = join8(targets.pluginRoot, artifact.path);
|
|
2808
|
+
return {
|
|
2809
|
+
kind: targetKindForArtifact(artifact),
|
|
2810
|
+
action: "write-plugin-file",
|
|
2811
|
+
targetPath,
|
|
2812
|
+
description: `Materialize Claude Code plugin asset ${artifact.path}.`,
|
|
2813
|
+
requiresBackup: existsSync6(targetPath),
|
|
2814
|
+
content,
|
|
2815
|
+
...role ? { role } : {}
|
|
2816
|
+
};
|
|
2817
|
+
});
|
|
2818
|
+
items.push({
|
|
2819
|
+
kind: "managed-model-state",
|
|
2820
|
+
action: "write-managed-model-state",
|
|
2821
|
+
targetPath: targets.managedModelsPath,
|
|
2822
|
+
description: "Record thoth-agents-managed Claude Code subagent model ownership state.",
|
|
2823
|
+
requiresBackup: existsSync6(targets.managedModelsPath),
|
|
2824
|
+
content: stableJson3(nextState)
|
|
2825
|
+
});
|
|
2826
|
+
return {
|
|
2827
|
+
dryRun: config.dryRun === true,
|
|
2828
|
+
reset: config.reset,
|
|
2829
|
+
items,
|
|
2830
|
+
pluginRoot: targets.pluginRoot,
|
|
2831
|
+
diagnostics: [
|
|
2832
|
+
`Installed as a skills-directory plugin at ${targets.pluginRoot}; it auto-loads as thoth-agents@skills-dir on the next Claude Code session.`,
|
|
2833
|
+
"Restart Claude Code or run /reload-plugins to activate it; run /plugin (Installed tab) to confirm thoth-agents@skills-dir is loaded.",
|
|
2834
|
+
"The plugin settings.json activates the orchestrator agent as the main thread, so the session starts in delegate-first mode and bootstraps thoth-mem on its first turn."
|
|
2835
|
+
],
|
|
2836
|
+
disclaimers: [
|
|
2837
|
+
"The orchestrator agent is the Claude Code main thread (plugin settings.json `agent` key); while enabled it replaces the default system prompt for every session in scope.",
|
|
2838
|
+
"Role permissions are enforced by each specialist subagent frontmatter `tools` allowlist; the orchestrator inherits all tools.",
|
|
2839
|
+
"Subagent models accept only sonnet, opus, haiku, or inherit.",
|
|
2840
|
+
"User-scope skills-directory plugins load hooks and MCP servers without extra approval; project-scope requires accepting the workspace trust dialog."
|
|
2841
|
+
]
|
|
2842
|
+
};
|
|
2843
|
+
}
|
|
2844
|
+
function applyClaudeCodeSetup(plan) {
|
|
2845
|
+
const changed = [];
|
|
2846
|
+
const diagnostics = uniqueMessages([
|
|
2847
|
+
...plan.diagnostics,
|
|
2848
|
+
...plan.disclaimers
|
|
2849
|
+
]);
|
|
2850
|
+
if (plan.dryRun) return { success: true, changed, diagnostics };
|
|
2851
|
+
try {
|
|
2852
|
+
for (const item of plan.items) {
|
|
2853
|
+
if (writeTextWithBackup(item.targetPath, item.content)) {
|
|
2854
|
+
changed.push(item.targetPath);
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
return { success: true, changed, diagnostics };
|
|
2858
|
+
} catch (error) {
|
|
2859
|
+
return {
|
|
2860
|
+
success: false,
|
|
2861
|
+
changed,
|
|
2862
|
+
diagnostics,
|
|
2863
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2864
|
+
};
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
function applyClaudeCodeManagedModelOverrides(config, overrides) {
|
|
2868
|
+
if (config.dryRun) {
|
|
2869
|
+
return {
|
|
2870
|
+
success: true,
|
|
2871
|
+
changed: [],
|
|
2872
|
+
diagnostics: [
|
|
2873
|
+
"Dry-run Claude Code model override apply requested; no files were written."
|
|
2874
|
+
]
|
|
2875
|
+
};
|
|
2876
|
+
}
|
|
2877
|
+
const plan = buildClaudeCodeSetupPlan({
|
|
2878
|
+
...config,
|
|
2879
|
+
dryRun: true,
|
|
2880
|
+
reset: false
|
|
2881
|
+
});
|
|
2882
|
+
const stateItem = plan.items.find(
|
|
2883
|
+
(item) => item.action === "write-managed-model-state"
|
|
2884
|
+
);
|
|
2885
|
+
const statePath = stateItem?.targetPath;
|
|
2886
|
+
if (!statePath) {
|
|
2887
|
+
return {
|
|
2888
|
+
success: false,
|
|
2889
|
+
changed: [],
|
|
2890
|
+
diagnostics: plan.diagnostics,
|
|
2891
|
+
error: "Claude Code managed model state target was not found."
|
|
2892
|
+
};
|
|
2893
|
+
}
|
|
2894
|
+
const changed = [];
|
|
2895
|
+
const diagnostics = uniqueMessages([
|
|
2896
|
+
...plan.diagnostics,
|
|
2897
|
+
...plan.disclaimers
|
|
2898
|
+
]);
|
|
2899
|
+
const state = parseManagedModelStateJson2(stateItem?.content);
|
|
2900
|
+
const nextState = {
|
|
2901
|
+
version: CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION,
|
|
2902
|
+
models: { ...state.models },
|
|
2903
|
+
...state.configuredModels ? { configuredModels: { ...state.configuredModels } } : {}
|
|
2904
|
+
};
|
|
2905
|
+
try {
|
|
2906
|
+
for (const override of overrides) {
|
|
2907
|
+
if (!isClaudeCodeModelAlias(override.model)) {
|
|
2908
|
+
throw new Error(
|
|
2909
|
+
`Unsupported Claude Code model "${override.model}" for ${override.role}; use sonnet, opus, haiku, or inherit.`
|
|
2910
|
+
);
|
|
2911
|
+
}
|
|
2912
|
+
const roleItem = plan.items.find(
|
|
2913
|
+
(item) => item.kind === "subagent" && item.role === override.role
|
|
2914
|
+
);
|
|
2915
|
+
if (!roleItem) {
|
|
2916
|
+
throw new Error(`Missing Claude Code subagent for ${override.role}.`);
|
|
2917
|
+
}
|
|
2918
|
+
const before = existsSync6(roleItem.targetPath) ? readFileSync7(roleItem.targetPath, "utf8") : roleItem.content;
|
|
2919
|
+
const updated = replaceSubagentModel(before, override.model);
|
|
2920
|
+
if (writeTextWithBackup(roleItem.targetPath, updated)) {
|
|
2921
|
+
changed.push(roleItem.targetPath);
|
|
2922
|
+
}
|
|
2923
|
+
nextState.configuredModels ??= {};
|
|
2924
|
+
nextState.configuredModels[override.role] = override.model;
|
|
2925
|
+
}
|
|
2926
|
+
if (writeTextWithBackup(statePath, stableJson3(nextState))) {
|
|
2927
|
+
changed.push(statePath);
|
|
2928
|
+
}
|
|
2929
|
+
return { success: true, changed, diagnostics };
|
|
2930
|
+
} catch (error) {
|
|
2931
|
+
return {
|
|
2932
|
+
success: false,
|
|
2933
|
+
changed,
|
|
2934
|
+
diagnostics,
|
|
2935
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2936
|
+
};
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
function formatClaudeCodeSetupPlan(plan) {
|
|
2940
|
+
const lines = plan.items.map(
|
|
2941
|
+
(item) => `- ${item.action}: ${item.targetPath} (${item.description})`
|
|
2942
|
+
);
|
|
2943
|
+
return ["Claude Code setup plan:", ...lines].join("\n");
|
|
2944
|
+
}
|
|
2945
|
+
|
|
2946
|
+
// src/cli/operations/claude-code.ts
|
|
2947
|
+
var CLAUDE_CODE_DISPLAY_NAME = "Claude Code";
|
|
2948
|
+
var claudeCodePlanSources = /* @__PURE__ */ new WeakMap();
|
|
2949
|
+
var claudeCodeModelSources = /* @__PURE__ */ new WeakMap();
|
|
2950
|
+
var claudeCodeActions = [
|
|
2951
|
+
{
|
|
2952
|
+
id: "claude-code-status",
|
|
2953
|
+
kind: "status",
|
|
2954
|
+
label: "Status",
|
|
2955
|
+
description: "Inspect managed Claude Code plugin state",
|
|
2956
|
+
dryRun: false,
|
|
2957
|
+
requiresConfirmation: false,
|
|
2958
|
+
supported: true
|
|
2959
|
+
},
|
|
2960
|
+
{
|
|
2961
|
+
id: "claude-code-list",
|
|
2962
|
+
kind: "list",
|
|
2963
|
+
label: "List",
|
|
2964
|
+
description: "List managed Claude Code surfaces and actions",
|
|
2965
|
+
dryRun: false,
|
|
2966
|
+
requiresConfirmation: false,
|
|
2967
|
+
supported: true
|
|
2968
|
+
},
|
|
2969
|
+
{
|
|
2970
|
+
id: "claude-code-install",
|
|
2971
|
+
kind: "install",
|
|
2972
|
+
label: "Install",
|
|
2973
|
+
description: "Preview Claude Code plugin package install",
|
|
2974
|
+
dryRun: true,
|
|
2975
|
+
requiresConfirmation: true,
|
|
2976
|
+
supported: true
|
|
2977
|
+
},
|
|
2978
|
+
{
|
|
2979
|
+
id: "claude-code-update",
|
|
2980
|
+
kind: "update",
|
|
2981
|
+
label: "Update",
|
|
2982
|
+
description: "Preview Claude Code managed plugin refresh",
|
|
2983
|
+
dryRun: true,
|
|
2984
|
+
requiresConfirmation: true,
|
|
2985
|
+
supported: true
|
|
2986
|
+
},
|
|
2987
|
+
{
|
|
2988
|
+
id: "claude-code-sync",
|
|
2989
|
+
kind: "sync",
|
|
2990
|
+
label: "Sync",
|
|
2991
|
+
description: "Preview Claude Code managed plugin sync",
|
|
2992
|
+
dryRun: true,
|
|
2993
|
+
requiresConfirmation: true,
|
|
2994
|
+
supported: true
|
|
2995
|
+
},
|
|
2996
|
+
{
|
|
2997
|
+
id: "claude-code-model-config",
|
|
2998
|
+
kind: "model-config",
|
|
2999
|
+
label: "Model",
|
|
3000
|
+
description: "Preview supported Claude Code subagent model line changes",
|
|
3001
|
+
dryRun: true,
|
|
3002
|
+
requiresConfirmation: true,
|
|
3003
|
+
supported: true
|
|
3004
|
+
}
|
|
3005
|
+
];
|
|
3006
|
+
var claudeCodeOperationAdapter = {
|
|
3007
|
+
id: "claude",
|
|
3008
|
+
displayName: CLAUDE_CODE_DISPLAY_NAME,
|
|
3009
|
+
available: true,
|
|
3010
|
+
description: "Claude Code plugin package and managed subagent surfaces.",
|
|
3011
|
+
actions: claudeCodeActions
|
|
3012
|
+
};
|
|
3013
|
+
function claudeCodeConfig(context = { cwd: process.cwd() }, dryRun) {
|
|
3014
|
+
return {
|
|
3015
|
+
dryRun,
|
|
3016
|
+
reset: false,
|
|
3017
|
+
// Match the installer (install.ts uses 'user') so post-install status,
|
|
3018
|
+
// update, sync, and model commands resolve the same plugin root.
|
|
3019
|
+
scope: context.scope ?? "user",
|
|
3020
|
+
projectRoot: context.cwd,
|
|
3021
|
+
homeDir: context.homeDir,
|
|
3022
|
+
packageRoot: context.packageRoot
|
|
3023
|
+
};
|
|
3024
|
+
}
|
|
3025
|
+
function claudeCodeDisclaimers() {
|
|
3026
|
+
return [
|
|
3027
|
+
{
|
|
3028
|
+
message: "Role permissions are enforced by subagent frontmatter tools; the orchestrator is the main session, injected via the SessionStart hook.",
|
|
3029
|
+
code: "claude-code-first-class"
|
|
3030
|
+
},
|
|
3031
|
+
{
|
|
3032
|
+
message: "Subagent models accept only sonnet, opus, haiku, or inherit.",
|
|
3033
|
+
code: "claude-code-model-aliases"
|
|
3034
|
+
}
|
|
3035
|
+
];
|
|
3036
|
+
}
|
|
3037
|
+
function warning(message, code) {
|
|
3038
|
+
return { severity: "important", message, code };
|
|
3039
|
+
}
|
|
3040
|
+
function targetForItem(item, state, observed) {
|
|
3041
|
+
return {
|
|
3042
|
+
kind: item.kind === "managed-model-state" ? "memory-state" : "generated-artifact",
|
|
3043
|
+
path: item.targetPath,
|
|
3044
|
+
label: item.role ? `Claude Code ${item.role} subagent` : item.kind.replaceAll("-", " "),
|
|
3045
|
+
state,
|
|
3046
|
+
expected: item.action,
|
|
3047
|
+
observed,
|
|
3048
|
+
description: item.description
|
|
3049
|
+
};
|
|
3050
|
+
}
|
|
3051
|
+
function surfaceForItem(item) {
|
|
3052
|
+
return {
|
|
3053
|
+
id: `${item.kind}:${item.role ?? basename2(item.targetPath)}`,
|
|
3054
|
+
label: item.role ? `Claude Code ${item.role} subagent` : item.kind.replaceAll("-", " "),
|
|
3055
|
+
path: item.targetPath,
|
|
3056
|
+
description: item.description
|
|
3057
|
+
};
|
|
3058
|
+
}
|
|
3059
|
+
function backupForItem(item) {
|
|
3060
|
+
return {
|
|
3061
|
+
required: item.requiresBackup,
|
|
3062
|
+
strategy: item.requiresBackup ? "managed-backup-file" : "none",
|
|
3063
|
+
destinations: item.requiresBackup ? [{ path: `${item.targetPath}.bak`, label: "managed backup" }] : []
|
|
3064
|
+
};
|
|
3065
|
+
}
|
|
3066
|
+
function manifestState(item) {
|
|
3067
|
+
if (!existsSync7(item.targetPath))
|
|
3068
|
+
return { state: "missing", observed: "absent" };
|
|
3069
|
+
const observed = readFileSync8(item.targetPath, "utf8");
|
|
3070
|
+
if (observed === item.content)
|
|
3071
|
+
return { state: "installed", observed: "current" };
|
|
3072
|
+
try {
|
|
3073
|
+
const expectedVersion = JSON.parse(item.content).version;
|
|
3074
|
+
const observedVersion = JSON.parse(observed).version;
|
|
3075
|
+
if (typeof expectedVersion === "string" && typeof observedVersion === "string" && expectedVersion !== observedVersion) {
|
|
3076
|
+
return {
|
|
3077
|
+
state: "outdated",
|
|
3078
|
+
observed: `version ${observedVersion}; expected ${expectedVersion}`
|
|
3079
|
+
};
|
|
3080
|
+
}
|
|
3081
|
+
} catch {
|
|
3082
|
+
return { state: "unknown", observed: "unparseable plugin manifest" };
|
|
3083
|
+
}
|
|
3084
|
+
return { state: "drift", observed: "content differs" };
|
|
3085
|
+
}
|
|
3086
|
+
function contentState(item) {
|
|
3087
|
+
if (!existsSync7(item.targetPath))
|
|
3088
|
+
return { state: "missing", observed: "absent" };
|
|
3089
|
+
return readFileSync8(item.targetPath, "utf8") === item.content ? { state: "installed", observed: "current" } : { state: "drift", observed: "content differs" };
|
|
3090
|
+
}
|
|
3091
|
+
function classifyItem(item) {
|
|
3092
|
+
if (item.kind === "plugin-manifest") return manifestState(item);
|
|
3093
|
+
return contentState(item);
|
|
3094
|
+
}
|
|
3095
|
+
function aggregateState(states) {
|
|
3096
|
+
if (states.includes("unknown")) return "unknown";
|
|
3097
|
+
if (states.includes("drift")) return "drift";
|
|
3098
|
+
if (states.includes("outdated")) return "outdated";
|
|
3099
|
+
if (states.includes("missing")) return "missing";
|
|
3100
|
+
return "installed";
|
|
3101
|
+
}
|
|
3102
|
+
function statusSummary(state) {
|
|
3103
|
+
switch (state) {
|
|
3104
|
+
case "installed":
|
|
3105
|
+
return "Claude Code managed plugin is installed and current.";
|
|
3106
|
+
case "missing":
|
|
3107
|
+
return "Claude Code managed plugin is missing.";
|
|
3108
|
+
case "drift":
|
|
3109
|
+
return "Claude Code managed plugin exists but differs from expected output.";
|
|
3110
|
+
case "outdated":
|
|
3111
|
+
return "Claude Code managed plugin includes an older generated manifest.";
|
|
3112
|
+
case "unknown":
|
|
3113
|
+
return "Claude Code managed plugin could not be classified safely.";
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
function statusFromSetupPlan(plan) {
|
|
3117
|
+
const classified = plan.items.map((item) => ({
|
|
3118
|
+
item,
|
|
3119
|
+
...classifyItem(item)
|
|
3120
|
+
}));
|
|
3121
|
+
const state = aggregateState(classified.map((entry) => entry.state));
|
|
3122
|
+
return {
|
|
3123
|
+
harness: "claude",
|
|
3124
|
+
displayName: CLAUDE_CODE_DISPLAY_NAME,
|
|
3125
|
+
state,
|
|
3126
|
+
summary: statusSummary(state),
|
|
3127
|
+
targets: classified.map(
|
|
3128
|
+
({ item, state: state2, observed }) => targetForItem(item, state2, observed)
|
|
3129
|
+
),
|
|
3130
|
+
diagnostics: plan.diagnostics.map((message) => ({
|
|
3131
|
+
severity: "minor",
|
|
3132
|
+
message,
|
|
3133
|
+
code: "claude-code-diagnostic"
|
|
3134
|
+
})),
|
|
3135
|
+
actions: claudeCodeActions,
|
|
3136
|
+
disclaimers: [
|
|
3137
|
+
...claudeCodeDisclaimers(),
|
|
3138
|
+
...plan.disclaimers.map((message) => ({ message }))
|
|
3139
|
+
]
|
|
3140
|
+
};
|
|
3141
|
+
}
|
|
3142
|
+
function getClaudeCodeStatus(context = { cwd: process.cwd() }) {
|
|
3143
|
+
let plan;
|
|
3144
|
+
try {
|
|
3145
|
+
plan = buildClaudeCodeSetupPlan(claudeCodeConfig(context, true));
|
|
3146
|
+
} catch (error) {
|
|
3147
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3148
|
+
return {
|
|
3149
|
+
harness: "claude",
|
|
3150
|
+
displayName: CLAUDE_CODE_DISPLAY_NAME,
|
|
3151
|
+
state: "unknown",
|
|
3152
|
+
summary: `Claude Code setup plan could not be built: ${message}`,
|
|
3153
|
+
targets: [],
|
|
3154
|
+
diagnostics: [
|
|
3155
|
+
{
|
|
3156
|
+
severity: "critical",
|
|
3157
|
+
message,
|
|
3158
|
+
code: "claude-code-plan-build-failed"
|
|
3159
|
+
}
|
|
3160
|
+
],
|
|
3161
|
+
actions: claudeCodeActions,
|
|
3162
|
+
disclaimers: claudeCodeDisclaimers()
|
|
3163
|
+
};
|
|
3164
|
+
}
|
|
3165
|
+
return statusFromSetupPlan(plan);
|
|
3166
|
+
}
|
|
3167
|
+
function planItemFromSetup(item) {
|
|
3168
|
+
return {
|
|
3169
|
+
title: item.description,
|
|
3170
|
+
target: targetForItem(item),
|
|
3171
|
+
preview: item.content,
|
|
3172
|
+
backup: backupForItem(item)
|
|
3173
|
+
};
|
|
3174
|
+
}
|
|
3175
|
+
function planFromSetup(id, action, title, summary, setupPlan) {
|
|
3176
|
+
const status = statusFromSetupPlan(setupPlan);
|
|
3177
|
+
const canApply = status.state === "installed" || status.state === "missing" || status.state === "outdated" || status.state === "drift";
|
|
3178
|
+
const plan = {
|
|
3179
|
+
id,
|
|
3180
|
+
harness: "claude",
|
|
3181
|
+
action,
|
|
3182
|
+
title,
|
|
3183
|
+
summary,
|
|
3184
|
+
dryRun: true,
|
|
3185
|
+
canApply,
|
|
3186
|
+
targets: status.targets,
|
|
3187
|
+
surfaces: setupPlan.items.map(surfaceForItem),
|
|
3188
|
+
backup: {
|
|
3189
|
+
required: setupPlan.items.some((item) => item.requiresBackup),
|
|
3190
|
+
strategy: "managed-backup-file",
|
|
3191
|
+
description: "Existing Claude Code plugin files are backed up before being overwritten."
|
|
3192
|
+
},
|
|
3193
|
+
items: setupPlan.items.map(planItemFromSetup),
|
|
3194
|
+
warnings: status.diagnostics,
|
|
3195
|
+
disclaimers: [
|
|
3196
|
+
...claudeCodeDisclaimers(),
|
|
3197
|
+
...setupPlan.disclaimers.map((message) => ({ message }))
|
|
3198
|
+
]
|
|
3199
|
+
};
|
|
3200
|
+
claudeCodePlanSources.set(plan, setupPlan);
|
|
3201
|
+
return plan;
|
|
3202
|
+
}
|
|
3203
|
+
function buildClaudeCodeInstallPlan(context = { cwd: process.cwd() }) {
|
|
3204
|
+
return planFromSetup(
|
|
3205
|
+
"claude-code-install-preview",
|
|
3206
|
+
"install",
|
|
3207
|
+
"Install Claude Code plugin package",
|
|
3208
|
+
"Preview Claude Code plugin package install using buildClaudeCodeSetupPlan().",
|
|
3209
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context, true))
|
|
3210
|
+
);
|
|
3211
|
+
}
|
|
3212
|
+
function buildClaudeCodeUpdatePlan(context = { cwd: process.cwd() }) {
|
|
3213
|
+
return planFromSetup(
|
|
3214
|
+
"claude-code-update-preview",
|
|
3215
|
+
"update",
|
|
3216
|
+
"Update Claude Code plugin package",
|
|
3217
|
+
"Preview Claude Code managed plugin refresh using buildClaudeCodeSetupPlan().",
|
|
3218
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context, true))
|
|
3219
|
+
);
|
|
3220
|
+
}
|
|
3221
|
+
function buildClaudeCodeSyncPlan(context = { cwd: process.cwd() }) {
|
|
3222
|
+
return planFromSetup(
|
|
3223
|
+
"claude-code-sync-preview",
|
|
3224
|
+
"sync",
|
|
3225
|
+
"Sync Claude Code plugin package",
|
|
3226
|
+
"Preview Claude Code managed plugin subagents, MCP, hooks, and skills.",
|
|
3227
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context, true))
|
|
3228
|
+
);
|
|
3229
|
+
}
|
|
3230
|
+
function isClaudeCodeRole(role) {
|
|
3231
|
+
return CLAUDE_CODE_ROLE_NAMES.includes(role);
|
|
3232
|
+
}
|
|
3233
|
+
function buildClaudeCodeModelPlan(input, context = { cwd: process.cwd() }) {
|
|
3234
|
+
const status = getClaudeCodeStatus(context);
|
|
3235
|
+
const supportedRoles = input.roles.filter((role) => isClaudeCodeRole(role.role)).filter((role) => isClaudeCodeModelAlias(role.model)).map((role) => ({
|
|
3236
|
+
role: role.role,
|
|
3237
|
+
model: role.model
|
|
3238
|
+
}));
|
|
3239
|
+
const rejectedRoles = input.roles.filter(
|
|
3240
|
+
(role) => !isClaudeCodeRole(role.role) || !isClaudeCodeModelAlias(role.model)
|
|
3241
|
+
);
|
|
3242
|
+
const warnings = [
|
|
3243
|
+
...status.diagnostics,
|
|
3244
|
+
...input.warnings ?? [],
|
|
3245
|
+
...rejectedRoles.map(
|
|
3246
|
+
(role) => warning(
|
|
3247
|
+
`Claude Code does not accept role "${role.role}" with model "${role.model}"; roles must be one of ${CLAUDE_CODE_ROLE_NAMES.join(", ")} and models must be sonnet, opus, haiku, or inherit.`,
|
|
3248
|
+
"claude-code-unsupported-model-role"
|
|
3249
|
+
)
|
|
3250
|
+
)
|
|
3251
|
+
];
|
|
3252
|
+
if (input.harness !== "claude") {
|
|
3253
|
+
warnings.push(
|
|
3254
|
+
warning(
|
|
3255
|
+
"Model plan target harness must be claude.",
|
|
3256
|
+
"claude-code-model-harness-mismatch"
|
|
3257
|
+
)
|
|
3258
|
+
);
|
|
3259
|
+
}
|
|
3260
|
+
const targets = supportedRoles.map(({ role }) => {
|
|
3261
|
+
const target = status.targets.find(
|
|
3262
|
+
(candidate) => candidate.path?.endsWith(`agents${pathSep()}${role}.md`)
|
|
3263
|
+
);
|
|
3264
|
+
return target ?? {
|
|
3265
|
+
kind: "generated-artifact",
|
|
3266
|
+
label: `Claude Code ${role} subagent`,
|
|
3267
|
+
state: "missing"
|
|
3268
|
+
};
|
|
3269
|
+
});
|
|
3270
|
+
const stateTarget = status.targets.find(
|
|
3271
|
+
(target) => target.path?.endsWith(".thoth-agents-managed-models.json")
|
|
3272
|
+
);
|
|
3273
|
+
const plan = {
|
|
3274
|
+
id: "claude-code-model-config-preview",
|
|
3275
|
+
harness: "claude",
|
|
3276
|
+
action: "model-config",
|
|
3277
|
+
title: "Configure Claude Code subagent model lines",
|
|
3278
|
+
summary: "Preview model changes for generated Claude Code subagent files and managed model state only.",
|
|
3279
|
+
dryRun: true,
|
|
3280
|
+
canApply: input.harness === "claude" && supportedRoles.length > 0 && status.state !== "unknown",
|
|
3281
|
+
targets: [...targets, ...stateTarget ? [stateTarget] : []],
|
|
3282
|
+
surfaces: targets.map((target) => ({
|
|
3283
|
+
id: `claude-code-model:${target.label}`,
|
|
3284
|
+
label: target.label ?? "Claude Code subagent",
|
|
3285
|
+
path: target.path,
|
|
3286
|
+
state: target.state
|
|
3287
|
+
})),
|
|
3288
|
+
backup: {
|
|
3289
|
+
required: true,
|
|
3290
|
+
strategy: "managed-backup-file",
|
|
3291
|
+
description: "Existing subagent files and managed model state are backed up by the managed write helper."
|
|
3292
|
+
},
|
|
3293
|
+
items: supportedRoles.map(({ role, model }) => ({
|
|
3294
|
+
title: `Set ${role} Claude Code subagent model line`,
|
|
3295
|
+
target: targets.find(
|
|
3296
|
+
(target) => target.path?.endsWith(`agents${pathSep()}${role}.md`)
|
|
3297
|
+
) ?? {
|
|
3298
|
+
kind: "generated-artifact",
|
|
3299
|
+
label: `Claude Code ${role} subagent`
|
|
3300
|
+
},
|
|
3301
|
+
preview: JSON.stringify({ role, model }),
|
|
3302
|
+
backup: { required: true, strategy: "managed-backup-file" }
|
|
3303
|
+
})),
|
|
3304
|
+
warnings,
|
|
3305
|
+
disclaimers: [
|
|
3306
|
+
...claudeCodeDisclaimers(),
|
|
3307
|
+
...input.disclaimers ?? [],
|
|
3308
|
+
{
|
|
3309
|
+
message: "Claude Code model configuration writes only generated subagent frontmatter model lines and the managed model state JSON.",
|
|
3310
|
+
code: "claude-code-model-supported-surface"
|
|
3311
|
+
}
|
|
3312
|
+
]
|
|
3313
|
+
};
|
|
3314
|
+
claudeCodeModelSources.set(plan, {
|
|
3315
|
+
config: claudeCodeConfig(context, false),
|
|
3316
|
+
roles: supportedRoles
|
|
3317
|
+
});
|
|
3318
|
+
return plan;
|
|
3319
|
+
}
|
|
3320
|
+
function pathSep() {
|
|
3321
|
+
return process.platform === "win32" ? "\\" : "/";
|
|
3322
|
+
}
|
|
3323
|
+
function rejectPlan(plan, message, severity = "critical") {
|
|
3324
|
+
return {
|
|
3325
|
+
harness: plan.harness,
|
|
3326
|
+
action: plan.action,
|
|
3327
|
+
applied: false,
|
|
3328
|
+
summary: message,
|
|
3329
|
+
changedTargets: [],
|
|
3330
|
+
backups: [],
|
|
3331
|
+
warnings: [{ severity, message }],
|
|
3332
|
+
disclaimers: claudeCodeDisclaimers()
|
|
3333
|
+
};
|
|
3334
|
+
}
|
|
3335
|
+
function validateClaudeCodePlan(plan) {
|
|
3336
|
+
if (plan.harness !== "claude") {
|
|
3337
|
+
return rejectPlan(plan, "Only Claude Code operation plans can be applied.");
|
|
3338
|
+
}
|
|
3339
|
+
if (!plan.canApply) {
|
|
3340
|
+
return rejectPlan(
|
|
3341
|
+
plan,
|
|
3342
|
+
"Claude Code plan cannot be applied because canApply is false."
|
|
3343
|
+
);
|
|
3344
|
+
}
|
|
3345
|
+
if (!["install", "update", "sync", "model-config"].includes(plan.action)) {
|
|
3346
|
+
return rejectPlan(
|
|
3347
|
+
plan,
|
|
3348
|
+
`Unsupported Claude Code apply action: ${plan.action}.`
|
|
3349
|
+
);
|
|
3350
|
+
}
|
|
3351
|
+
if (plan.items.length === 0) {
|
|
3352
|
+
return rejectPlan(plan, "Claude Code plan has no items to apply.");
|
|
3353
|
+
}
|
|
3354
|
+
return null;
|
|
3355
|
+
}
|
|
3356
|
+
function applyClaudeCodePlan(plan) {
|
|
3357
|
+
const rejection = validateClaudeCodePlan(plan);
|
|
3358
|
+
if (rejection) return rejection;
|
|
3359
|
+
if (plan.action === "model-config") {
|
|
3360
|
+
const source = claudeCodeModelSources.get(plan);
|
|
3361
|
+
if (!source) {
|
|
3362
|
+
return rejectPlan(
|
|
3363
|
+
plan,
|
|
3364
|
+
"Claude Code model plan was not produced by buildClaudeCodeModelPlan in this process."
|
|
3365
|
+
);
|
|
3366
|
+
}
|
|
3367
|
+
const result2 = applyClaudeCodeManagedModelOverrides(
|
|
3368
|
+
source.config,
|
|
3369
|
+
source.roles.map((role) => ({
|
|
3370
|
+
role: role.role,
|
|
3371
|
+
model: role.model
|
|
3372
|
+
}))
|
|
3373
|
+
);
|
|
3374
|
+
return {
|
|
3375
|
+
harness: "claude",
|
|
3376
|
+
action: "model-config",
|
|
3377
|
+
applied: result2.success,
|
|
3378
|
+
summary: result2.success ? "Applied Claude Code subagent model overrides." : result2.error ?? "Failed to apply Claude Code subagent model overrides.",
|
|
3379
|
+
changedTargets: result2.changed.map((path4) => ({
|
|
3380
|
+
kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
|
|
3381
|
+
path: path4,
|
|
3382
|
+
label: basename2(path4),
|
|
3383
|
+
state: "installed"
|
|
3384
|
+
})),
|
|
3385
|
+
backups: result2.changed.filter((path4) => existsSync7(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
|
|
3386
|
+
warnings: result2.success ? [] : [{ severity: "critical", message: result2.error ?? "apply failed." }],
|
|
3387
|
+
disclaimers: claudeCodeDisclaimers()
|
|
3388
|
+
};
|
|
3389
|
+
}
|
|
3390
|
+
const setupPlan = claudeCodePlanSources.get(plan);
|
|
3391
|
+
if (!setupPlan) {
|
|
3392
|
+
return rejectPlan(
|
|
3393
|
+
plan,
|
|
3394
|
+
"Claude Code setup plan was not produced by a Claude Code operation plan builder in this process."
|
|
3395
|
+
);
|
|
3396
|
+
}
|
|
3397
|
+
const result = applyClaudeCodeSetup({ ...setupPlan, dryRun: false });
|
|
3398
|
+
return {
|
|
3399
|
+
harness: "claude",
|
|
3400
|
+
action: plan.action,
|
|
3401
|
+
applied: result.success,
|
|
3402
|
+
summary: result.success ? `Applied Claude Code managed ${plan.action} plan.` : result.error ?? `Failed to apply Claude Code ${plan.action} plan.`,
|
|
3403
|
+
changedTargets: result.changed.map((path4) => ({
|
|
3404
|
+
kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
|
|
3405
|
+
path: path4,
|
|
3406
|
+
label: basename2(path4),
|
|
3407
|
+
state: "installed"
|
|
3408
|
+
})),
|
|
3409
|
+
backups: result.changed.filter((path4) => existsSync7(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
|
|
3410
|
+
warnings: result.success ? [] : [{ severity: "critical", message: result.error ?? "apply failed." }],
|
|
3411
|
+
disclaimers: claudeCodeDisclaimers()
|
|
3412
|
+
};
|
|
3413
|
+
}
|
|
3414
|
+
function defaultClaudeCodeModelRoles() {
|
|
3415
|
+
return CLAUDE_CODE_ROLE_NAMES.map((role) => ({
|
|
3416
|
+
role,
|
|
3417
|
+
model: CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS[role]
|
|
3418
|
+
}));
|
|
3419
|
+
}
|
|
3420
|
+
|
|
3421
|
+
// src/cli/operations/codex.ts
|
|
3422
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9 } from "fs";
|
|
3423
|
+
import { basename as basename3 } from "path";
|
|
3424
|
+
var CODEX_DISPLAY_NAME = "Codex";
|
|
3425
|
+
var codexPlanSources = /* @__PURE__ */ new WeakMap();
|
|
3426
|
+
var codexModelSources = /* @__PURE__ */ new WeakMap();
|
|
3427
|
+
var codexActions = [
|
|
3428
|
+
{
|
|
3429
|
+
id: "codex-status",
|
|
3430
|
+
kind: "status",
|
|
3431
|
+
label: "Status",
|
|
3432
|
+
description: "Inspect managed Codex setup state",
|
|
3433
|
+
dryRun: false,
|
|
3434
|
+
requiresConfirmation: false,
|
|
3435
|
+
supported: true
|
|
3436
|
+
},
|
|
3437
|
+
{
|
|
3438
|
+
id: "codex-list",
|
|
3439
|
+
kind: "list",
|
|
3440
|
+
label: "List",
|
|
3441
|
+
description: "List managed Codex surfaces and actions",
|
|
3442
|
+
dryRun: false,
|
|
2306
3443
|
requiresConfirmation: false,
|
|
2307
3444
|
supported: true
|
|
2308
3445
|
},
|
|
@@ -2374,10 +3511,10 @@ function codexDisclaimers() {
|
|
|
2374
3511
|
}
|
|
2375
3512
|
];
|
|
2376
3513
|
}
|
|
2377
|
-
function
|
|
3514
|
+
function warning2(message, code) {
|
|
2378
3515
|
return { severity: "important", message, code };
|
|
2379
3516
|
}
|
|
2380
|
-
function
|
|
3517
|
+
function targetForItem2(item, state, observed) {
|
|
2381
3518
|
return {
|
|
2382
3519
|
kind: item.kind === "managed-model-state" ? "memory-state" : "generated-artifact",
|
|
2383
3520
|
path: item.targetPath,
|
|
@@ -2388,15 +3525,15 @@ function targetForItem(item, state, observed) {
|
|
|
2388
3525
|
description: item.description
|
|
2389
3526
|
};
|
|
2390
3527
|
}
|
|
2391
|
-
function
|
|
3528
|
+
function surfaceForItem2(item) {
|
|
2392
3529
|
return {
|
|
2393
|
-
id: `${item.kind}:${item.role ??
|
|
3530
|
+
id: `${item.kind}:${item.role ?? basename3(item.targetPath)}`,
|
|
2394
3531
|
label: item.role ? `Codex ${item.role} subagent TOML` : item.kind.replaceAll("-", " "),
|
|
2395
3532
|
path: item.targetPath,
|
|
2396
3533
|
description: item.description
|
|
2397
3534
|
};
|
|
2398
3535
|
}
|
|
2399
|
-
function
|
|
3536
|
+
function backupForItem2(item) {
|
|
2400
3537
|
return {
|
|
2401
3538
|
required: item.requiresBackup,
|
|
2402
3539
|
strategy: item.requiresBackup ? "managed-backup-file" : "none",
|
|
@@ -2404,9 +3541,9 @@ function backupForItem(item) {
|
|
|
2404
3541
|
};
|
|
2405
3542
|
}
|
|
2406
3543
|
function rootBlockState(item) {
|
|
2407
|
-
if (!
|
|
3544
|
+
if (!existsSync8(item.targetPath))
|
|
2408
3545
|
return { state: "missing", observed: "absent" };
|
|
2409
|
-
const content =
|
|
3546
|
+
const content = readFileSync9(item.targetPath, "utf8");
|
|
2410
3547
|
if (item.content && content.includes(item.content)) {
|
|
2411
3548
|
return { state: "installed", observed: "managed root block present" };
|
|
2412
3549
|
}
|
|
@@ -2416,22 +3553,22 @@ function rootBlockState(item) {
|
|
|
2416
3553
|
return { state: "missing", observed: "managed root block absent" };
|
|
2417
3554
|
}
|
|
2418
3555
|
function managedModelState(item) {
|
|
2419
|
-
if (!
|
|
3556
|
+
if (!existsSync8(item.targetPath))
|
|
2420
3557
|
return { state: "missing", observed: "absent" };
|
|
2421
3558
|
try {
|
|
2422
|
-
const parsed = JSON.parse(
|
|
3559
|
+
const parsed = JSON.parse(readFileSync9(item.targetPath, "utf8"));
|
|
2423
3560
|
if (parsed.version !== MANAGED_MODEL_STATE_VERSION || !parsed.models || typeof parsed.models !== "object" || Array.isArray(parsed.models)) {
|
|
2424
3561
|
return { state: "unknown", observed: "invalid managed model state" };
|
|
2425
3562
|
}
|
|
2426
|
-
return item.content ===
|
|
3563
|
+
return item.content === readFileSync9(item.targetPath, "utf8") ? { state: "installed", observed: "managed model state current" } : { state: "drift", observed: "managed model state differs" };
|
|
2427
3564
|
} catch {
|
|
2428
3565
|
return { state: "unknown", observed: "unparseable managed model state" };
|
|
2429
3566
|
}
|
|
2430
3567
|
}
|
|
2431
3568
|
function userConfigState(item) {
|
|
2432
|
-
if (!
|
|
3569
|
+
if (!existsSync8(item.targetPath))
|
|
2433
3570
|
return { state: "missing", observed: "absent" };
|
|
2434
|
-
const content =
|
|
3571
|
+
const content = readFileSync9(item.targetPath, "utf8");
|
|
2435
3572
|
if (/^\s*default_mode_request_user_input\s*=\s*true\b/m.test(content)) {
|
|
2436
3573
|
return {
|
|
2437
3574
|
state: "installed",
|
|
@@ -2444,10 +3581,10 @@ function userConfigState(item) {
|
|
|
2444
3581
|
return { state: "missing", observed: "managed feature flag absent" };
|
|
2445
3582
|
}
|
|
2446
3583
|
function marketplaceState(item) {
|
|
2447
|
-
if (!
|
|
3584
|
+
if (!existsSync8(item.targetPath))
|
|
2448
3585
|
return { state: "missing", observed: "absent" };
|
|
2449
3586
|
try {
|
|
2450
|
-
const parsed = JSON.parse(
|
|
3587
|
+
const parsed = JSON.parse(readFileSync9(item.targetPath, "utf8"));
|
|
2451
3588
|
const plugins = Array.isArray(parsed.plugins) ? parsed.plugins : [];
|
|
2452
3589
|
const entry = plugins.find(
|
|
2453
3590
|
(plugin) => plugin && typeof plugin === "object" && "name" in plugin && plugin.name === "thoth-agents"
|
|
@@ -2460,11 +3597,11 @@ function marketplaceState(item) {
|
|
|
2460
3597
|
return { state: "unknown", observed: "unparseable marketplace JSON" };
|
|
2461
3598
|
}
|
|
2462
3599
|
}
|
|
2463
|
-
function
|
|
2464
|
-
if (!
|
|
3600
|
+
function contentState2(item) {
|
|
3601
|
+
if (!existsSync8(item.targetPath))
|
|
2465
3602
|
return { state: "missing", observed: "absent" };
|
|
2466
|
-
const observed =
|
|
2467
|
-
if (
|
|
3603
|
+
const observed = readFileSync9(item.targetPath, "utf8");
|
|
3604
|
+
if (basename3(item.targetPath) === "plugin.json" && item.targetPath.replaceAll("\\", "/").includes("/.codex-plugin/")) {
|
|
2468
3605
|
try {
|
|
2469
3606
|
const expectedVersion = JSON.parse(item.content ?? "{}").version;
|
|
2470
3607
|
const observedVersion = JSON.parse(observed).version;
|
|
@@ -2492,7 +3629,7 @@ function contentState(item) {
|
|
|
2492
3629
|
}
|
|
2493
3630
|
return { state: "drift", observed: "content differs" };
|
|
2494
3631
|
}
|
|
2495
|
-
function
|
|
3632
|
+
function classifyItem2(item) {
|
|
2496
3633
|
if (item.action === "diagnose-only") {
|
|
2497
3634
|
return { state: "unknown", observed: "diagnostic guidance only" };
|
|
2498
3635
|
}
|
|
@@ -2502,16 +3639,16 @@ function classifyItem(item) {
|
|
|
2502
3639
|
}
|
|
2503
3640
|
if (item.action === "merge-toml") return userConfigState(item);
|
|
2504
3641
|
if (item.action === "merge-marketplace") return marketplaceState(item);
|
|
2505
|
-
return
|
|
3642
|
+
return contentState2(item);
|
|
2506
3643
|
}
|
|
2507
|
-
function
|
|
3644
|
+
function aggregateState2(states) {
|
|
2508
3645
|
if (states.includes("unknown")) return "unknown";
|
|
2509
3646
|
if (states.includes("drift")) return "drift";
|
|
2510
3647
|
if (states.includes("outdated")) return "outdated";
|
|
2511
3648
|
if (states.includes("missing")) return "missing";
|
|
2512
3649
|
return "installed";
|
|
2513
3650
|
}
|
|
2514
|
-
function
|
|
3651
|
+
function statusSummary2(state) {
|
|
2515
3652
|
switch (state) {
|
|
2516
3653
|
case "installed":
|
|
2517
3654
|
return "Codex managed setup surfaces are installed and current.";
|
|
@@ -2548,8 +3685,8 @@ function getCodexStatus(context = { cwd: process.cwd() }) {
|
|
|
2548
3685
|
disclaimers: codexDisclaimers()
|
|
2549
3686
|
};
|
|
2550
3687
|
}
|
|
2551
|
-
const classified = plan.items.filter((item) => item.action !== "diagnose-only").map((item) => ({ item, ...
|
|
2552
|
-
const state =
|
|
3688
|
+
const classified = plan.items.filter((item) => item.action !== "diagnose-only").map((item) => ({ item, ...classifyItem2(item) }));
|
|
3689
|
+
const state = aggregateState2(classified.map((item) => item.state));
|
|
2553
3690
|
const diagnostics = plan.diagnostics.map((message) => ({
|
|
2554
3691
|
severity: "minor",
|
|
2555
3692
|
message,
|
|
@@ -2559,9 +3696,9 @@ function getCodexStatus(context = { cwd: process.cwd() }) {
|
|
|
2559
3696
|
harness: "codex",
|
|
2560
3697
|
displayName: CODEX_DISPLAY_NAME,
|
|
2561
3698
|
state,
|
|
2562
|
-
summary:
|
|
3699
|
+
summary: statusSummary2(state),
|
|
2563
3700
|
targets: classified.map(
|
|
2564
|
-
({ item, state: state2, observed }) =>
|
|
3701
|
+
({ item, state: state2, observed }) => targetForItem2(item, state2, observed)
|
|
2565
3702
|
),
|
|
2566
3703
|
diagnostics,
|
|
2567
3704
|
actions: codexActions,
|
|
@@ -2571,15 +3708,15 @@ function getCodexStatus(context = { cwd: process.cwd() }) {
|
|
|
2571
3708
|
]
|
|
2572
3709
|
};
|
|
2573
3710
|
}
|
|
2574
|
-
function
|
|
3711
|
+
function planItemFromSetup2(item) {
|
|
2575
3712
|
return {
|
|
2576
3713
|
title: item.description,
|
|
2577
|
-
target:
|
|
3714
|
+
target: targetForItem2(item),
|
|
2578
3715
|
preview: item.content,
|
|
2579
|
-
backup:
|
|
3716
|
+
backup: backupForItem2(item)
|
|
2580
3717
|
};
|
|
2581
3718
|
}
|
|
2582
|
-
function
|
|
3719
|
+
function planFromSetup2(id, action, title, summary, setupPlan, context) {
|
|
2583
3720
|
const status = getCodexStatus(context);
|
|
2584
3721
|
const canApply = status.state === "installed" || status.state === "missing" || status.state === "outdated";
|
|
2585
3722
|
const plan = {
|
|
@@ -2591,17 +3728,17 @@ function planFromSetup(id, action, title, summary, setupPlan, context) {
|
|
|
2591
3728
|
dryRun: true,
|
|
2592
3729
|
canApply,
|
|
2593
3730
|
targets: status.targets,
|
|
2594
|
-
surfaces: setupPlan.items.filter((item) => item.action !== "diagnose-only").map(
|
|
3731
|
+
surfaces: setupPlan.items.filter((item) => item.action !== "diagnose-only").map(surfaceForItem2),
|
|
2595
3732
|
backup: {
|
|
2596
3733
|
required: setupPlan.items.some((item) => item.requiresBackup),
|
|
2597
3734
|
strategy: "existing-helper",
|
|
2598
3735
|
description: "Codex setup apply uses the existing installer backup behavior for files that already exist."
|
|
2599
3736
|
},
|
|
2600
|
-
items: setupPlan.items.map(
|
|
3737
|
+
items: setupPlan.items.map(planItemFromSetup2),
|
|
2601
3738
|
warnings: [
|
|
2602
3739
|
...status.diagnostics,
|
|
2603
3740
|
...canApply ? [] : [
|
|
2604
|
-
|
|
3741
|
+
warning2(
|
|
2605
3742
|
`Codex state is ${status.state}; apply is disabled until the state is safely classified or repaired.`,
|
|
2606
3743
|
"codex-unsafe-state"
|
|
2607
3744
|
)
|
|
@@ -2617,7 +3754,7 @@ function planFromSetup(id, action, title, summary, setupPlan, context) {
|
|
|
2617
3754
|
}
|
|
2618
3755
|
function buildCodexUpdatePlan(context = { cwd: process.cwd() }) {
|
|
2619
3756
|
const setupPlan = buildCodexSetupPlan(codexConfig(context, true));
|
|
2620
|
-
return
|
|
3757
|
+
return planFromSetup2(
|
|
2621
3758
|
"codex-update-preview",
|
|
2622
3759
|
"update",
|
|
2623
3760
|
"Update Codex managed setup",
|
|
@@ -2628,7 +3765,7 @@ function buildCodexUpdatePlan(context = { cwd: process.cwd() }) {
|
|
|
2628
3765
|
}
|
|
2629
3766
|
function buildCodexSyncPlan(context = { cwd: process.cwd() }) {
|
|
2630
3767
|
const setupPlan = buildCodexSetupPlan(codexConfig(context, true));
|
|
2631
|
-
return
|
|
3768
|
+
return planFromSetup2(
|
|
2632
3769
|
"codex-sync-preview",
|
|
2633
3770
|
"sync",
|
|
2634
3771
|
"Sync Codex managed configuration",
|
|
@@ -2639,7 +3776,7 @@ function buildCodexSyncPlan(context = { cwd: process.cwd() }) {
|
|
|
2639
3776
|
}
|
|
2640
3777
|
function buildCodexInstallPlan(context = { cwd: process.cwd() }) {
|
|
2641
3778
|
const setupPlan = buildCodexSetupPlan(codexConfig(context, true));
|
|
2642
|
-
return
|
|
3779
|
+
return planFromSetup2(
|
|
2643
3780
|
"codex-install-preview",
|
|
2644
3781
|
"install",
|
|
2645
3782
|
"Install Codex managed setup",
|
|
@@ -2670,7 +3807,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
2670
3807
|
...status.diagnostics,
|
|
2671
3808
|
...input.warnings ?? [],
|
|
2672
3809
|
...unsupportedRoles.map(
|
|
2673
|
-
(role) =>
|
|
3810
|
+
(role) => warning2(
|
|
2674
3811
|
`Codex does not expose a supported generated model surface for ${role.role}; this plan will not write that role.`,
|
|
2675
3812
|
"codex-unsupported-model-role"
|
|
2676
3813
|
)
|
|
@@ -2678,7 +3815,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
2678
3815
|
];
|
|
2679
3816
|
if (input.harness !== "codex") {
|
|
2680
3817
|
warnings.push(
|
|
2681
|
-
|
|
3818
|
+
warning2(
|
|
2682
3819
|
"Model plan target harness must be codex.",
|
|
2683
3820
|
"codex-model-harness-mismatch"
|
|
2684
3821
|
)
|
|
@@ -2745,7 +3882,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
2745
3882
|
codexModelSources.set(plan, { config, roles: supportedRoles });
|
|
2746
3883
|
return plan;
|
|
2747
3884
|
}
|
|
2748
|
-
function
|
|
3885
|
+
function rejectPlan2(plan, message, severity = "critical") {
|
|
2749
3886
|
return {
|
|
2750
3887
|
harness: plan.harness,
|
|
2751
3888
|
action: plan.action,
|
|
@@ -2759,19 +3896,19 @@ function rejectPlan(plan, message, severity = "critical") {
|
|
|
2759
3896
|
}
|
|
2760
3897
|
function validateCodexPlan(plan) {
|
|
2761
3898
|
if (plan.harness !== "codex") {
|
|
2762
|
-
return
|
|
3899
|
+
return rejectPlan2(plan, "Only Codex operation plans can be applied.");
|
|
2763
3900
|
}
|
|
2764
3901
|
if (!plan.canApply) {
|
|
2765
|
-
return
|
|
3902
|
+
return rejectPlan2(
|
|
2766
3903
|
plan,
|
|
2767
3904
|
"Codex plan cannot be applied because canApply is false."
|
|
2768
3905
|
);
|
|
2769
3906
|
}
|
|
2770
3907
|
if (!["install", "update", "sync", "model-config"].includes(plan.action)) {
|
|
2771
|
-
return
|
|
3908
|
+
return rejectPlan2(plan, `Unsupported Codex apply action: ${plan.action}.`);
|
|
2772
3909
|
}
|
|
2773
3910
|
if (plan.items.length === 0) {
|
|
2774
|
-
return
|
|
3911
|
+
return rejectPlan2(plan, "Codex plan has no items to apply.");
|
|
2775
3912
|
}
|
|
2776
3913
|
return null;
|
|
2777
3914
|
}
|
|
@@ -2781,7 +3918,7 @@ function applyCodexPlan(plan) {
|
|
|
2781
3918
|
if (plan.action === "model-config") {
|
|
2782
3919
|
const source = codexModelSources.get(plan);
|
|
2783
3920
|
if (!source) {
|
|
2784
|
-
return
|
|
3921
|
+
return rejectPlan2(
|
|
2785
3922
|
plan,
|
|
2786
3923
|
"Codex model plan was not produced by buildCodexModelPlan in this process."
|
|
2787
3924
|
);
|
|
@@ -2792,13 +3929,13 @@ function applyCodexPlan(plan) {
|
|
|
2792
3929
|
action: "model-config",
|
|
2793
3930
|
applied: result2.success,
|
|
2794
3931
|
summary: result2.success ? "Applied Codex subagent model overrides." : result2.error ?? "Failed to apply Codex subagent model overrides.",
|
|
2795
|
-
changedTargets: result2.changed.map((
|
|
2796
|
-
kind:
|
|
2797
|
-
path:
|
|
2798
|
-
label:
|
|
3932
|
+
changedTargets: result2.changed.map((path4) => ({
|
|
3933
|
+
kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
|
|
3934
|
+
path: path4,
|
|
3935
|
+
label: basename3(path4),
|
|
2799
3936
|
state: "installed"
|
|
2800
3937
|
})),
|
|
2801
|
-
backups: result2.changed.filter((
|
|
3938
|
+
backups: result2.changed.filter((path4) => existsSync8(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
|
|
2802
3939
|
warnings: result2.success ? [] : [
|
|
2803
3940
|
{
|
|
2804
3941
|
severity: "critical",
|
|
@@ -2810,7 +3947,7 @@ function applyCodexPlan(plan) {
|
|
|
2810
3947
|
}
|
|
2811
3948
|
const setupPlan = codexPlanSources.get(plan);
|
|
2812
3949
|
if (!setupPlan) {
|
|
2813
|
-
return
|
|
3950
|
+
return rejectPlan2(
|
|
2814
3951
|
plan,
|
|
2815
3952
|
"Codex setup plan was not produced by a Codex operation plan builder in this process."
|
|
2816
3953
|
);
|
|
@@ -2821,13 +3958,13 @@ function applyCodexPlan(plan) {
|
|
|
2821
3958
|
action: plan.action,
|
|
2822
3959
|
applied: result.success,
|
|
2823
3960
|
summary: result.success ? `Applied Codex managed ${plan.action} plan.` : result.error ?? `Failed to apply Codex ${plan.action} plan.`,
|
|
2824
|
-
changedTargets: result.changed.map((
|
|
2825
|
-
kind:
|
|
2826
|
-
path:
|
|
2827
|
-
label:
|
|
3961
|
+
changedTargets: result.changed.map((path4) => ({
|
|
3962
|
+
kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
|
|
3963
|
+
path: path4,
|
|
3964
|
+
label: basename3(path4),
|
|
2828
3965
|
state: "installed"
|
|
2829
3966
|
})),
|
|
2830
|
-
backups: result.changed.filter((
|
|
3967
|
+
backups: result.changed.filter((path4) => existsSync8(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
|
|
2831
3968
|
warnings: result.success ? [] : [
|
|
2832
3969
|
{
|
|
2833
3970
|
severity: "critical",
|
|
@@ -2839,14 +3976,14 @@ function applyCodexPlan(plan) {
|
|
|
2839
3976
|
}
|
|
2840
3977
|
|
|
2841
3978
|
// src/cli/operations/opencode.ts
|
|
2842
|
-
import { existsSync as
|
|
2843
|
-
import { join as
|
|
3979
|
+
import { existsSync as existsSync10 } from "fs";
|
|
3980
|
+
import { join as join10 } from "path";
|
|
2844
3981
|
|
|
2845
3982
|
// src/cli/skills.ts
|
|
2846
3983
|
import { spawnSync } from "child_process";
|
|
2847
|
-
import { existsSync as
|
|
2848
|
-
import { homedir as
|
|
2849
|
-
import { join as
|
|
3984
|
+
import { existsSync as existsSync9 } from "fs";
|
|
3985
|
+
import { homedir as homedir3 } from "os";
|
|
3986
|
+
import { join as join9 } from "path";
|
|
2850
3987
|
var RECOMMENDED_SKILLS = [
|
|
2851
3988
|
{
|
|
2852
3989
|
name: "simplify",
|
|
@@ -2861,11 +3998,11 @@ var RECOMMENDED_SKILLS = [
|
|
|
2861
3998
|
description: "Browser automation for visual checks and testing"
|
|
2862
3999
|
}
|
|
2863
4000
|
];
|
|
2864
|
-
function getRecommendedSkillPath(skill, homeDir =
|
|
2865
|
-
return
|
|
4001
|
+
function getRecommendedSkillPath(skill, homeDir = homedir3()) {
|
|
4002
|
+
return join9(homeDir, ".agents", "skills", skill.skillName, "SKILL.md");
|
|
2866
4003
|
}
|
|
2867
4004
|
function isRecommendedSkillInstalled(skill, options = {}) {
|
|
2868
|
-
return
|
|
4005
|
+
return existsSync9(getRecommendedSkillPath(skill, options.homeDir));
|
|
2869
4006
|
}
|
|
2870
4007
|
function runSkillInstallCommand(skill) {
|
|
2871
4008
|
const args = [
|
|
@@ -3019,11 +4156,11 @@ function openCodeSkillTargets(context) {
|
|
|
3019
4156
|
const homeDir = homeDirFromContext(context);
|
|
3020
4157
|
const recommendedTargets = RECOMMENDED_SKILLS.map(
|
|
3021
4158
|
(skill) => {
|
|
3022
|
-
const
|
|
3023
|
-
const installed =
|
|
4159
|
+
const path4 = getRecommendedSkillPath(skill, homeDir);
|
|
4160
|
+
const installed = existsSync10(path4);
|
|
3024
4161
|
return {
|
|
3025
4162
|
kind: "skill",
|
|
3026
|
-
path:
|
|
4163
|
+
path: path4,
|
|
3027
4164
|
label: titleCaseSkillName(skill.skillName),
|
|
3028
4165
|
state: installed ? "installed" : "missing",
|
|
3029
4166
|
expected: "recommended global OpenCode skill",
|
|
@@ -3032,11 +4169,11 @@ function openCodeSkillTargets(context) {
|
|
|
3032
4169
|
}
|
|
3033
4170
|
);
|
|
3034
4171
|
const bundledTargets = CUSTOM_SKILLS.map((skill) => {
|
|
3035
|
-
const
|
|
3036
|
-
const installed =
|
|
4172
|
+
const path4 = join10(getCustomSkillsDir(), skill.name, "SKILL.md");
|
|
4173
|
+
const installed = existsSync10(path4);
|
|
3037
4174
|
return {
|
|
3038
4175
|
kind: "skill",
|
|
3039
|
-
path:
|
|
4176
|
+
path: path4,
|
|
3040
4177
|
label: titleCaseSkillName(skill.name),
|
|
3041
4178
|
state: installed ? "installed" : "missing",
|
|
3042
4179
|
expected: "bundled thoth-agents OpenCode skill",
|
|
@@ -3139,8 +4276,8 @@ function getOpenCodeStatus(context = { cwd: process.cwd() }) {
|
|
|
3139
4276
|
actions: openCodeActions
|
|
3140
4277
|
};
|
|
3141
4278
|
}
|
|
3142
|
-
const mainExists =
|
|
3143
|
-
const liteExists =
|
|
4279
|
+
const mainExists = existsSync10(mainPath);
|
|
4280
|
+
const liteExists = existsSync10(litePath);
|
|
3144
4281
|
const mainTarget = {
|
|
3145
4282
|
...targetForMainConfig(),
|
|
3146
4283
|
observed: configPluginMarker(main.config)
|
|
@@ -3237,11 +4374,11 @@ function getOpenCodeStatus(context = { cwd: process.cwd() }) {
|
|
|
3237
4374
|
actions: openCodeActions
|
|
3238
4375
|
};
|
|
3239
4376
|
}
|
|
3240
|
-
function defaultBackup(
|
|
4377
|
+
function defaultBackup(path4) {
|
|
3241
4378
|
return {
|
|
3242
4379
|
required: true,
|
|
3243
4380
|
strategy: "managed-backup-file",
|
|
3244
|
-
destinations: [{ path: `${
|
|
4381
|
+
destinations: [{ path: `${path4}.bak`, label: "managed backup" }]
|
|
3245
4382
|
};
|
|
3246
4383
|
}
|
|
3247
4384
|
function defaultDisclaimers() {
|
|
@@ -3293,7 +4430,7 @@ function planFromItems(id, action, title, summary, items) {
|
|
|
3293
4430
|
};
|
|
3294
4431
|
}
|
|
3295
4432
|
function buildOpenCodeUpdatePlan(_context = { cwd: process.cwd() }) {
|
|
3296
|
-
const
|
|
4433
|
+
const path4 = getExistingConfigPath();
|
|
3297
4434
|
return planFromItems(
|
|
3298
4435
|
"opencode-update-preview",
|
|
3299
4436
|
"update",
|
|
@@ -3305,7 +4442,7 @@ function buildOpenCodeUpdatePlan(_context = { cwd: process.cwd() }) {
|
|
|
3305
4442
|
target: targetForMainConfig(),
|
|
3306
4443
|
state: getOpenCodeStatus().state,
|
|
3307
4444
|
preview: `plugin: ["${EXPECTED_PLUGIN}"]`,
|
|
3308
|
-
backup: defaultBackup(
|
|
4445
|
+
backup: defaultBackup(path4)
|
|
3309
4446
|
}
|
|
3310
4447
|
]
|
|
3311
4448
|
);
|
|
@@ -3472,7 +4609,7 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
3472
4609
|
}
|
|
3473
4610
|
return plan;
|
|
3474
4611
|
}
|
|
3475
|
-
function
|
|
4612
|
+
function rejectPlan3(plan, message, severity = "critical") {
|
|
3476
4613
|
return {
|
|
3477
4614
|
harness: plan.harness,
|
|
3478
4615
|
action: plan.action,
|
|
@@ -3516,27 +4653,27 @@ function ensureLatestPluginEntry() {
|
|
|
3516
4653
|
}
|
|
3517
4654
|
function validateApplyPlan(plan) {
|
|
3518
4655
|
if (plan.harness !== "opencode") {
|
|
3519
|
-
return
|
|
4656
|
+
return rejectPlan3(plan, "Only OpenCode operation plans can be applied.");
|
|
3520
4657
|
}
|
|
3521
4658
|
if (!plan.canApply) {
|
|
3522
|
-
return
|
|
4659
|
+
return rejectPlan3(
|
|
3523
4660
|
plan,
|
|
3524
4661
|
"OpenCode plan cannot be applied because canApply is false."
|
|
3525
4662
|
);
|
|
3526
4663
|
}
|
|
3527
4664
|
if (!["install", "update", "sync", "model-config"].includes(plan.action)) {
|
|
3528
|
-
return
|
|
4665
|
+
return rejectPlan3(
|
|
3529
4666
|
plan,
|
|
3530
4667
|
`Unsupported OpenCode apply action: ${plan.action}.`
|
|
3531
4668
|
);
|
|
3532
4669
|
}
|
|
3533
4670
|
if (plan.items.length === 0) {
|
|
3534
|
-
return
|
|
4671
|
+
return rejectPlan3(plan, "OpenCode plan has no items to apply.");
|
|
3535
4672
|
}
|
|
3536
4673
|
if (plan.items.some(
|
|
3537
4674
|
(item) => !item.title || !item.target.path && item.target.kind !== "skill" || item.state === "drift" || item.state === "unknown"
|
|
3538
4675
|
)) {
|
|
3539
|
-
return
|
|
4676
|
+
return rejectPlan3(
|
|
3540
4677
|
plan,
|
|
3541
4678
|
"OpenCode plan contains malformed or unsafe items."
|
|
3542
4679
|
);
|
|
@@ -3548,7 +4685,7 @@ function applyModelPlan(plan) {
|
|
|
3548
4685
|
for (const item of plan.items) {
|
|
3549
4686
|
const match = /^Set (.+) OpenCode model override$/.exec(item.title);
|
|
3550
4687
|
if (!match) {
|
|
3551
|
-
return
|
|
4688
|
+
return rejectPlan3(
|
|
3552
4689
|
plan,
|
|
3553
4690
|
"OpenCode model plan contains an unrecognized item."
|
|
3554
4691
|
);
|
|
@@ -3557,7 +4694,7 @@ function applyModelPlan(plan) {
|
|
|
3557
4694
|
try {
|
|
3558
4695
|
parsed2 = item.preview ? JSON.parse(item.preview) : {};
|
|
3559
4696
|
} catch {
|
|
3560
|
-
return
|
|
4697
|
+
return rejectPlan3(
|
|
3561
4698
|
plan,
|
|
3562
4699
|
"OpenCode model plan contains malformed preview JSON."
|
|
3563
4700
|
);
|
|
@@ -3565,7 +4702,7 @@ function applyModelPlan(plan) {
|
|
|
3565
4702
|
const role = match[1] ?? "";
|
|
3566
4703
|
const model = parsed2[role]?.model;
|
|
3567
4704
|
if (!ROLE_NAMES.includes(role) || typeof model !== "string") {
|
|
3568
|
-
return
|
|
4705
|
+
return rejectPlan3(
|
|
3569
4706
|
plan,
|
|
3570
4707
|
"OpenCode model plan contains an invalid role or model."
|
|
3571
4708
|
);
|
|
@@ -3573,7 +4710,7 @@ function applyModelPlan(plan) {
|
|
|
3573
4710
|
roleModels.set(role, model);
|
|
3574
4711
|
}
|
|
3575
4712
|
if (roleModels.size === 0) {
|
|
3576
|
-
return
|
|
4713
|
+
return rejectPlan3(
|
|
3577
4714
|
plan,
|
|
3578
4715
|
"OpenCode model plan does not contain any role overrides."
|
|
3579
4716
|
);
|
|
@@ -3609,7 +4746,7 @@ function applyModelPlan(plan) {
|
|
|
3609
4746
|
observed: "agents role overrides updated"
|
|
3610
4747
|
}
|
|
3611
4748
|
],
|
|
3612
|
-
backups:
|
|
4749
|
+
backups: existsSync10(`${targetPath}.bak`) ? [{ path: `${targetPath}.bak`, label: "managed backup" }] : [],
|
|
3613
4750
|
warnings: [],
|
|
3614
4751
|
disclaimers: defaultDisclaimers()
|
|
3615
4752
|
};
|
|
@@ -3618,7 +4755,7 @@ function applyInstallSkills(plan) {
|
|
|
3618
4755
|
for (const skill of RECOMMENDED_SKILLS) {
|
|
3619
4756
|
const result = installRecommendedSkill(skill);
|
|
3620
4757
|
if (result.status === "failed") {
|
|
3621
|
-
return
|
|
4758
|
+
return rejectPlan3(
|
|
3622
4759
|
plan,
|
|
3623
4760
|
`Failed to install recommended OpenCode skill: ${skill.name}.`
|
|
3624
4761
|
);
|
|
@@ -3626,7 +4763,7 @@ function applyInstallSkills(plan) {
|
|
|
3626
4763
|
}
|
|
3627
4764
|
const bundled = installCustomSkills();
|
|
3628
4765
|
if (!bundled.success) {
|
|
3629
|
-
return
|
|
4766
|
+
return rejectPlan3(
|
|
3630
4767
|
plan,
|
|
3631
4768
|
"Failed to install bundled thoth-agents OpenCode skills."
|
|
3632
4769
|
);
|
|
@@ -3638,7 +4775,7 @@ function applyOpenCodePlan(plan) {
|
|
|
3638
4775
|
if (rejection) return rejection;
|
|
3639
4776
|
const status = getOpenCodeStatus();
|
|
3640
4777
|
if (!classifyApplySafety(status.state)) {
|
|
3641
|
-
return
|
|
4778
|
+
return rejectPlan3(
|
|
3642
4779
|
plan,
|
|
3643
4780
|
`OpenCode state is ${status.state}; refusing to apply plan without a safe status.`
|
|
3644
4781
|
);
|
|
@@ -3648,7 +4785,7 @@ function applyOpenCodePlan(plan) {
|
|
|
3648
4785
|
const backups = [];
|
|
3649
4786
|
const pluginResult = ensureLatestPluginEntry();
|
|
3650
4787
|
if (!pluginResult.success) {
|
|
3651
|
-
return
|
|
4788
|
+
return rejectPlan3(
|
|
3652
4789
|
plan,
|
|
3653
4790
|
pluginResult.error ?? "Failed to update OpenCode plugin config."
|
|
3654
4791
|
);
|
|
@@ -3657,7 +4794,7 @@ function applyOpenCodePlan(plan) {
|
|
|
3657
4794
|
...targetForMainConfig("installed"),
|
|
3658
4795
|
observed: `plugin includes ${EXPECTED_PLUGIN}`
|
|
3659
4796
|
});
|
|
3660
|
-
if (
|
|
4797
|
+
if (existsSync10(`${pluginResult.configPath}.bak`)) {
|
|
3661
4798
|
backups.push({
|
|
3662
4799
|
path: `${pluginResult.configPath}.bak`,
|
|
3663
4800
|
label: "OpenCode config backup"
|
|
@@ -3666,7 +4803,7 @@ function applyOpenCodePlan(plan) {
|
|
|
3666
4803
|
if (plan.action === "sync" || plan.action === "install") {
|
|
3667
4804
|
const defaultAgentResult = disableDefaultAgents();
|
|
3668
4805
|
if (!defaultAgentResult.success) {
|
|
3669
|
-
return
|
|
4806
|
+
return rejectPlan3(
|
|
3670
4807
|
plan,
|
|
3671
4808
|
defaultAgentResult.error ?? "Failed to disable OpenCode default agents."
|
|
3672
4809
|
);
|
|
@@ -3683,7 +4820,7 @@ function applyOpenCodePlan(plan) {
|
|
|
3683
4820
|
getExistingLiteConfigPath()
|
|
3684
4821
|
);
|
|
3685
4822
|
if (!liteResult.success) {
|
|
3686
|
-
return
|
|
4823
|
+
return rejectPlan3(
|
|
3687
4824
|
plan,
|
|
3688
4825
|
liteResult.error ?? "Failed to write thoth-agents config."
|
|
3689
4826
|
);
|
|
@@ -3692,7 +4829,7 @@ function applyOpenCodePlan(plan) {
|
|
|
3692
4829
|
...targetForLiteConfig("installed"),
|
|
3693
4830
|
observed: "seven-agent roster written"
|
|
3694
4831
|
});
|
|
3695
|
-
if (
|
|
4832
|
+
if (existsSync10(`${liteResult.configPath}.bak`)) {
|
|
3696
4833
|
backups.push({
|
|
3697
4834
|
path: `${liteResult.configPath}.bak`,
|
|
3698
4835
|
label: "thoth-agents config backup"
|
|
@@ -3724,7 +4861,8 @@ function applyOpenCodePlan(plan) {
|
|
|
3724
4861
|
// src/cli/operations/index.ts
|
|
3725
4862
|
var OPERATION_HARNESSES = {
|
|
3726
4863
|
opencode: opencodeOperationAdapter,
|
|
3727
|
-
codex: codexOperationAdapter
|
|
4864
|
+
codex: codexOperationAdapter,
|
|
4865
|
+
claude: claudeCodeOperationAdapter
|
|
3728
4866
|
};
|
|
3729
4867
|
var SUPPORTED_OPERATION_HARNESSES = Object.keys(
|
|
3730
4868
|
OPERATION_HARNESSES
|
|
@@ -3739,14 +4877,27 @@ function getOperationHarness(harness) {
|
|
|
3739
4877
|
}
|
|
3740
4878
|
|
|
3741
4879
|
export {
|
|
4880
|
+
claudeCodeAdapter,
|
|
3742
4881
|
codexAdapter,
|
|
3743
4882
|
CODEX_ROLE_NAMES,
|
|
3744
4883
|
parseRoleTomlModel,
|
|
3745
4884
|
buildCodexSetupPlan,
|
|
3746
4885
|
formatCodexSetupPlan,
|
|
3747
4886
|
applyCodexSetup,
|
|
4887
|
+
CLAUDE_CODE_ROLE_NAMES,
|
|
4888
|
+
parseSubagentModel,
|
|
4889
|
+
buildClaudeCodeSetupPlan,
|
|
4890
|
+
applyClaudeCodeSetup,
|
|
4891
|
+
formatClaudeCodeSetupPlan,
|
|
3748
4892
|
RECOMMENDED_SKILLS,
|
|
3749
4893
|
installRecommendedSkill,
|
|
4894
|
+
getClaudeCodeStatus,
|
|
4895
|
+
buildClaudeCodeInstallPlan,
|
|
4896
|
+
buildClaudeCodeUpdatePlan,
|
|
4897
|
+
buildClaudeCodeSyncPlan,
|
|
4898
|
+
buildClaudeCodeModelPlan,
|
|
4899
|
+
applyClaudeCodePlan,
|
|
4900
|
+
defaultClaudeCodeModelRoles,
|
|
3750
4901
|
getCodexStatus,
|
|
3751
4902
|
buildCodexUpdatePlan,
|
|
3752
4903
|
buildCodexSyncPlan,
|