thoth-agents 0.1.19 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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-FRZZ25ND.js} +1450 -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,1139 @@ function managedRefreshRoots(plan) {
|
|
|
2278
2296
|
group.push(item);
|
|
2279
2297
|
refreshGroups.set(item.kind, group);
|
|
2280
2298
|
}
|
|
2281
|
-
return [...refreshGroups].filter(([kind]) => kind === "personal-plugin-source").map(([, items]) => commonTargetDirectory(items)).filter((
|
|
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
|
+
function isClaudeCodeSubagentName(name) {
|
|
2466
|
+
return name in CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS;
|
|
2467
|
+
}
|
|
2468
|
+
function getClaudeCodeAgentModel(role, config) {
|
|
2469
|
+
if (!isClaudeCodeSubagentName(role.name)) return "inherit";
|
|
2470
|
+
const override = getPrimaryModelId(config?.agents?.[role.name]?.model);
|
|
2471
|
+
if (override && isClaudeCodeModel(override)) return override;
|
|
2472
|
+
return CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS[role.name];
|
|
2473
|
+
}
|
|
2474
|
+
function claudeCodePromptSections(roleName) {
|
|
2475
|
+
switch (roleName) {
|
|
2476
|
+
case "orchestrator":
|
|
2477
|
+
return createOrchestratorPromptSections();
|
|
2478
|
+
case "explorer":
|
|
2479
|
+
case "librarian":
|
|
2480
|
+
case "oracle":
|
|
2481
|
+
return createReadOnlySpecialistPromptSections(roleName);
|
|
2482
|
+
case "designer":
|
|
2483
|
+
case "quick":
|
|
2484
|
+
case "deep":
|
|
2485
|
+
return createWriteCapableSpecialistPromptSections(roleName);
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
function claudeCodeModelFamilyPromptSection(roleName, model) {
|
|
2489
|
+
const section = createModelFamilySection(roleName, model);
|
|
2490
|
+
return section ? renderPromptSection(section, CLAUDE_CODE_PROMPT_DIALECT) : void 0;
|
|
2491
|
+
}
|
|
2492
|
+
function claudeCodeStepBudgetPromptSection(steps) {
|
|
2493
|
+
const section = createStepBudgetSection(steps);
|
|
2494
|
+
return section ? renderPromptSection(section, CLAUDE_CODE_PROMPT_DIALECT) : void 0;
|
|
2495
|
+
}
|
|
2496
|
+
function renderClaudeCodeRolePrompt(roleName, config, model) {
|
|
2497
|
+
const promptOverrides = loadAgentPrompt(roleName, config?.preset);
|
|
2498
|
+
const override = getAgentOverride(config, roleName);
|
|
2499
|
+
const basePrompt = renderRolePrompt(
|
|
2500
|
+
claudeCodePromptSections(roleName),
|
|
2501
|
+
CLAUDE_CODE_PROMPT_DIALECT
|
|
2502
|
+
);
|
|
2503
|
+
const prompt = composeAgentPrompt({
|
|
2504
|
+
basePrompt,
|
|
2505
|
+
customPrompt: promptOverrides.prompt,
|
|
2506
|
+
customAppendPrompt: appendPromptSections(
|
|
2507
|
+
claudeCodeModelFamilyPromptSection(roleName, model),
|
|
2508
|
+
promptOverrides.appendPrompt
|
|
2509
|
+
)
|
|
2510
|
+
});
|
|
2511
|
+
return appendPromptSections(
|
|
2512
|
+
prompt,
|
|
2513
|
+
claudeCodeStepBudgetPromptSection(override?.steps)
|
|
2514
|
+
);
|
|
2515
|
+
}
|
|
2516
|
+
function claudeCodeRoleInstructions(role) {
|
|
2517
|
+
return [
|
|
2518
|
+
"<role-operational-contract>",
|
|
2519
|
+
`- Role: ${role.name}`,
|
|
2520
|
+
`- Mode: ${role.mode}`,
|
|
2521
|
+
`- Scope: ${role.scope}`,
|
|
2522
|
+
`- Responsibility: ${role.responsibility}`,
|
|
2523
|
+
"- Use AskUserQuestion for local blocking decisions.",
|
|
2524
|
+
`- ${role.name} runs as an auto-discovered Claude Code plugin subagent invoked via Task(subagent_type: ${claudeCodeSubagentType(role.name)}); plugin subagents are namespaced with the plugin name. The orchestrator is the main Claude Code session.`,
|
|
2525
|
+
"- This subagent inherits ALL of the main thread's tools, including MCP servers (thoth-mem, context7, exa, grep_app); read-only roles (explorer, librarian, oracle) MUST NOT mutate the workspace per this operational contract (instruction-level, not tooling-enforced).",
|
|
2526
|
+
...role.toolGovernance.map((rule) => `- ${rule}`),
|
|
2527
|
+
...role.verification.map((rule) => `- ${rule}`),
|
|
2528
|
+
"</role-operational-contract>"
|
|
2529
|
+
].join("\n");
|
|
2530
|
+
}
|
|
2531
|
+
function roleInstructions2(role, config) {
|
|
2532
|
+
const model = getClaudeCodeAgentModel(role, config);
|
|
2533
|
+
return [
|
|
2534
|
+
renderClaudeCodeRolePrompt(role.name, config, model),
|
|
2535
|
+
claudeCodeRoleInstructions(role),
|
|
2536
|
+
renderMemoryGovernanceInstructions(role, CLAUDE_CODE_PROMPT_DIALECT)
|
|
2537
|
+
].join("\n\n");
|
|
2538
|
+
}
|
|
2539
|
+
function renderClaudeCodeRootInstructions(config) {
|
|
2540
|
+
const rootOverride = getAgentOverride(config, "orchestrator");
|
|
2541
|
+
const rootPrompt = renderClaudeCodeRolePrompt(
|
|
2542
|
+
"orchestrator",
|
|
2543
|
+
config,
|
|
2544
|
+
rootOverride?.model ?? DEFAULT_MODELS.orchestrator
|
|
2545
|
+
);
|
|
2546
|
+
const specialists = getAgentPackContract().roles.filter((role) => role.name !== "orchestrator").map((role) => claudeCodeSubagentType(role.name)).join(", ");
|
|
2547
|
+
return [
|
|
2548
|
+
rootPrompt,
|
|
2549
|
+
"<claude-code-runtime>",
|
|
2550
|
+
"- You ARE the Claude Code main-thread agent: the delegate-first root coordinator. This is your system prompt (activated via the plugin settings.json `agent` key), so orchestrator-only and root-owned rules apply to you directly.",
|
|
2551
|
+
'- As your FIRST action on a new session, when thoth-mem tools are installed and session/project identity is known, call mem_session(action="start") as step 0 before any other thoth-mem call, then save the real user prompt with mem_save(kind="prompt") before later delegation.',
|
|
2552
|
+
"- thoth-mem tools are provided by this plugin's bundled MCP server and are exposed under a plugin namespace; call the available namespaced tool whose name ends in mem_session, mem_recall, mem_context, mem_save, mem_get, or mem_project (do not assume a bare, unnamespaced tool name).",
|
|
2553
|
+
"- If thoth-mem tools or identity values are unavailable, disclose that memory bootstrap could not run and continue without claiming memory was saved.",
|
|
2554
|
+
`- Delegate via the Task tool with \`subagent_type\` set to a plugin-namespaced specialist: ${specialists}. Bare role names (e.g. "explorer") are NOT valid in this harness \u2014 always use the ${CLAUDE_CODE_SUBAGENT_NAMESPACE}: prefix.`,
|
|
2555
|
+
"- Parallel delegation is supported: issue multiple Task calls in one turn for independent work.",
|
|
2556
|
+
'- Before delegating after meaningful context changes, refresh the handoff body with root-owned mem_session(action="summary") or mem_save(kind="session_summary") when available.',
|
|
2557
|
+
"- Use AskUserQuestion for blocking user decisions; do not ask those questions in plain prose.",
|
|
2558
|
+
"- Track progress with TodoWrite; subagents do not own progress checkboxes or root-only memory.",
|
|
2559
|
+
"- Subagents inherit all of your tools (including MCP servers); role permissions are instruction-level, so read-only roles (explorer, librarian, oracle) must not mutate the workspace per their operational contract.",
|
|
2560
|
+
"</claude-code-runtime>"
|
|
2561
|
+
].join("\n");
|
|
2562
|
+
}
|
|
2563
|
+
function claudeCodeMcpServers() {
|
|
2564
|
+
const [exaCommand = "", ...exaArgs] = exa.command;
|
|
2565
|
+
const [thothCommand = "", ...thothArgs] = DEFAULT_THOTH_COMMAND;
|
|
2566
|
+
return {
|
|
2567
|
+
exa: {
|
|
2568
|
+
command: exaCommand,
|
|
2569
|
+
...exaArgs.length > 0 ? { args: exaArgs } : {},
|
|
2570
|
+
...exa.environment && Object.keys(exa.environment).length > 0 ? { env: exa.environment } : {}
|
|
2571
|
+
},
|
|
2572
|
+
context7: { type: "http", url: CONTEXT7_MCP_URL },
|
|
2573
|
+
grep_app: { type: "http", url: GREP_APP_MCP_URL },
|
|
2574
|
+
thoth_mem: {
|
|
2575
|
+
command: thothCommand,
|
|
2576
|
+
...thothArgs.length > 0 ? { args: thothArgs } : {}
|
|
2577
|
+
}
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2580
|
+
function stableJson5(value) {
|
|
2581
|
+
return `${JSON.stringify(value, null, 2)}
|
|
2582
|
+
`;
|
|
2583
|
+
}
|
|
2584
|
+
function readRootPackageVersion2(context) {
|
|
2585
|
+
const packageJsonPath = findRootPackageJsonPath([
|
|
2586
|
+
...hasPackageRoot(context) ? [context.packageRoot] : [],
|
|
2587
|
+
context.projectRoot,
|
|
2588
|
+
process.cwd(),
|
|
2589
|
+
fileURLToPath3(new URL(".", import.meta.url))
|
|
2590
|
+
]);
|
|
2591
|
+
return readPackageJsonVersion(packageJsonPath);
|
|
2592
|
+
}
|
|
2593
|
+
function hasConfig(context) {
|
|
2594
|
+
return "config" in context;
|
|
2595
|
+
}
|
|
2596
|
+
function hasPackageRoot(context) {
|
|
2597
|
+
return "packageRoot" in context && typeof context.packageRoot === "string" && context.packageRoot.length > 0;
|
|
2598
|
+
}
|
|
2599
|
+
function createPluginManifest(context) {
|
|
2600
|
+
return {
|
|
2601
|
+
name: "thoth-agents",
|
|
2602
|
+
version: readRootPackageVersion2(context),
|
|
2603
|
+
description: "Delegate-first agent pack with seven roles, thoth-mem persistence, and bundled SDD skills, packaged for Claude Code.",
|
|
2604
|
+
author: { name: "thoth-agents" }
|
|
2605
|
+
};
|
|
2606
|
+
}
|
|
2607
|
+
function renderSubagentArtifacts(config) {
|
|
2608
|
+
const artifacts = [];
|
|
2609
|
+
for (const role of getAgentPackContract().roles.filter(
|
|
2610
|
+
(candidate) => candidate.name !== "orchestrator"
|
|
2611
|
+
)) {
|
|
2612
|
+
const content = renderClaudeCodeSubagent({
|
|
2613
|
+
name: role.name,
|
|
2614
|
+
description: role.responsibility,
|
|
2615
|
+
model: getClaudeCodeAgentModel(role, config),
|
|
2616
|
+
instructions: roleInstructions2(role, config)
|
|
2617
|
+
});
|
|
2618
|
+
artifacts.push({
|
|
2619
|
+
harness: "claude",
|
|
2620
|
+
kind: "agent-config",
|
|
2621
|
+
path: `agents/${role.name}.md`,
|
|
2622
|
+
description: `Claude Code subagent definition for ${role.name}.`,
|
|
2623
|
+
content
|
|
2624
|
+
});
|
|
2625
|
+
}
|
|
2626
|
+
return artifacts;
|
|
2627
|
+
}
|
|
2628
|
+
function renderOrchestratorArtifact(config) {
|
|
2629
|
+
const orchestrator = getAgentPackContract().roles.find(
|
|
2630
|
+
(role) => role.name === "orchestrator"
|
|
2631
|
+
);
|
|
2632
|
+
const content = renderClaudeCodeSubagent({
|
|
2633
|
+
name: "orchestrator",
|
|
2634
|
+
description: orchestrator?.responsibility ?? "Delegate-first root coordinator for SDD workflow and specialist dispatch.",
|
|
2635
|
+
model: "inherit",
|
|
2636
|
+
instructions: renderClaudeCodeRootInstructions(config)
|
|
2637
|
+
});
|
|
2638
|
+
return {
|
|
2639
|
+
harness: "claude",
|
|
2640
|
+
kind: "agent-config",
|
|
2641
|
+
path: "agents/orchestrator.md",
|
|
2642
|
+
description: "Claude Code orchestrator agent, activated as the main thread via settings.json.",
|
|
2643
|
+
content
|
|
2644
|
+
};
|
|
2645
|
+
}
|
|
2646
|
+
var claudeCodeAdapter = {
|
|
2647
|
+
id: "claude",
|
|
2648
|
+
displayName: "Claude Code",
|
|
2649
|
+
capabilities: CLAUDE_CODE_CAPABILITIES,
|
|
2650
|
+
render(context) {
|
|
2651
|
+
const config = hasConfig(context) ? context.config : void 0;
|
|
2652
|
+
const componentArtifacts = [
|
|
2653
|
+
...renderSubagentArtifacts(config),
|
|
2654
|
+
renderOrchestratorArtifact(config),
|
|
2655
|
+
{
|
|
2656
|
+
harness: "claude",
|
|
2657
|
+
kind: "mcp-config",
|
|
2658
|
+
path: ".mcp.json",
|
|
2659
|
+
description: "Claude Code plugin-bundled MCP server definitions.",
|
|
2660
|
+
content: stableJson5({ mcpServers: claudeCodeMcpServers() })
|
|
2661
|
+
},
|
|
2662
|
+
{
|
|
2663
|
+
harness: "claude",
|
|
2664
|
+
kind: "harness-config",
|
|
2665
|
+
path: "settings.json",
|
|
2666
|
+
description: "Activates the orchestrator agent as the Claude Code main thread.",
|
|
2667
|
+
content: stableJson5({ agent: "orchestrator" })
|
|
2668
|
+
}
|
|
2669
|
+
];
|
|
2670
|
+
const skillLayout = renderClaudeCodeSkillLayout({
|
|
2671
|
+
projectRoot: context.projectRoot,
|
|
2672
|
+
...hasPackageRoot(context) ? { packageRoot: context.packageRoot } : {},
|
|
2673
|
+
skills: getSkillRegistry()
|
|
2674
|
+
});
|
|
2675
|
+
componentArtifacts.push(...skillLayout.artifacts);
|
|
2676
|
+
const pluginPackage = renderClaudeCodePluginPackage({
|
|
2677
|
+
manifest: createPluginManifest(context),
|
|
2678
|
+
componentArtifacts
|
|
2679
|
+
});
|
|
2680
|
+
return {
|
|
2681
|
+
harness: "claude",
|
|
2682
|
+
artifacts: pluginPackage.artifacts,
|
|
2683
|
+
diagnostics: [...skillLayout.diagnostics, ...pluginPackage.diagnostics]
|
|
2684
|
+
};
|
|
2685
|
+
}
|
|
2686
|
+
};
|
|
2687
|
+
|
|
2688
|
+
// src/cli/claude-code-install.ts
|
|
2689
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
|
|
2690
|
+
import { join as join8 } from "path";
|
|
2691
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
2692
|
+
|
|
2693
|
+
// src/cli/claude-code-paths.ts
|
|
2694
|
+
import { homedir as homedir2 } from "os";
|
|
2695
|
+
import { join as join7 } from "path";
|
|
2696
|
+
var CLAUDE_CODE_ROLE_NAMES = [
|
|
2697
|
+
"explorer",
|
|
2698
|
+
"librarian",
|
|
2699
|
+
"oracle",
|
|
2700
|
+
"designer",
|
|
2701
|
+
"quick",
|
|
2702
|
+
"deep"
|
|
2703
|
+
];
|
|
2704
|
+
function resolveClaudeCodeTargets(options) {
|
|
2705
|
+
const home = options.homeDir ?? homedir2();
|
|
2706
|
+
const pluginRoot = options.scope === "project" ? join7(options.projectRoot, ".claude", "skills", "thoth-agents") : join7(home, ".claude", "skills", "thoth-agents");
|
|
2707
|
+
return {
|
|
2708
|
+
scope: options.scope,
|
|
2709
|
+
pluginRoot,
|
|
2710
|
+
pluginManifestPath: join7(pluginRoot, ".claude-plugin", "plugin.json"),
|
|
2711
|
+
agentPaths: CLAUDE_CODE_ROLE_NAMES.map((role) => ({
|
|
2712
|
+
role,
|
|
2713
|
+
path: join7(pluginRoot, "agents", `${role}.md`)
|
|
2714
|
+
})),
|
|
2715
|
+
mcpPath: join7(pluginRoot, ".mcp.json"),
|
|
2716
|
+
hooksPath: join7(pluginRoot, "hooks", "hooks.json"),
|
|
2717
|
+
skillsDir: join7(pluginRoot, "skills"),
|
|
2718
|
+
managedModelsPath: join7(pluginRoot, ".thoth-agents-managed-models.json")
|
|
2719
|
+
};
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
// src/cli/claude-code-install.ts
|
|
2723
|
+
var CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION = 1;
|
|
2724
|
+
var isClaudeCodeModelAlias = isClaudeCodeModel;
|
|
2725
|
+
function parseSubagentModel(content) {
|
|
2726
|
+
return /^model:\s*(\S+)\s*$/m.exec(content)?.[1];
|
|
2727
|
+
}
|
|
2728
|
+
function replaceSubagentModel(content, model) {
|
|
2729
|
+
if (/^model:\s*\S+\s*$/m.test(content)) {
|
|
2730
|
+
return content.replace(/^model:\s*\S+\s*$/m, `model: ${model}`);
|
|
2731
|
+
}
|
|
2732
|
+
return content;
|
|
2733
|
+
}
|
|
2734
|
+
function emptyManagedModelState3() {
|
|
2735
|
+
return emptyManagedModelState(CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION);
|
|
2736
|
+
}
|
|
2737
|
+
function parseManagedModelStateJson2(text) {
|
|
2738
|
+
return parseManagedModelStateJson(
|
|
2739
|
+
text,
|
|
2740
|
+
CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION
|
|
2741
|
+
);
|
|
2742
|
+
}
|
|
2743
|
+
function readManagedModelState3(path4) {
|
|
2744
|
+
return readManagedModelState(
|
|
2745
|
+
path4,
|
|
2746
|
+
CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION
|
|
2747
|
+
);
|
|
2748
|
+
}
|
|
2749
|
+
function resolvePackageRoot2(packageRoot) {
|
|
2750
|
+
if (packageRoot) return packageRoot;
|
|
2751
|
+
return findPackageRoot(fileURLToPath4(new URL(".", import.meta.url))) ?? void 0;
|
|
2752
|
+
}
|
|
2753
|
+
function targetKindForArtifact(artifact) {
|
|
2754
|
+
const path4 = artifact.path.replaceAll("\\", "/");
|
|
2755
|
+
if (path4 === ".claude-plugin/plugin.json") return "plugin-manifest";
|
|
2756
|
+
if (path4.startsWith("agents/")) return "subagent";
|
|
2757
|
+
if (path4 === ".mcp.json") return "mcp-config";
|
|
2758
|
+
if (path4.startsWith("hooks/")) return "hook";
|
|
2759
|
+
if (path4.startsWith("skills/")) return "skill";
|
|
2760
|
+
return "plugin-asset";
|
|
2761
|
+
}
|
|
2762
|
+
function roleForArtifact(artifact) {
|
|
2763
|
+
const match = /^agents\/([^/]+)\.md$/.exec(
|
|
2764
|
+
artifact.path.replaceAll("\\", "/")
|
|
2765
|
+
);
|
|
2766
|
+
const name = match?.[1];
|
|
2767
|
+
return name && CLAUDE_CODE_ROLE_NAMES.includes(name) ? name : void 0;
|
|
2768
|
+
}
|
|
2769
|
+
function applyConfiguredModel(content, role, state, nextState, reset) {
|
|
2770
|
+
const renderedModel = parseSubagentModel(content);
|
|
2771
|
+
if (!role || !renderedModel) return content;
|
|
2772
|
+
nextState.models[role] = renderedModel;
|
|
2773
|
+
const configured = reset ? void 0 : state.configuredModels?.[role];
|
|
2774
|
+
if (configured === void 0) return content;
|
|
2775
|
+
nextState.configuredModels ??= {};
|
|
2776
|
+
nextState.configuredModels[role] = configured;
|
|
2777
|
+
return replaceSubagentModel(content, configured);
|
|
2778
|
+
}
|
|
2779
|
+
function buildClaudeCodeSetupPlan(config) {
|
|
2780
|
+
const targets = resolveClaudeCodeTargets({
|
|
2781
|
+
scope: config.scope,
|
|
2782
|
+
projectRoot: config.projectRoot,
|
|
2783
|
+
homeDir: config.homeDir
|
|
2784
|
+
});
|
|
2785
|
+
const packageRoot = resolvePackageRoot2(config.packageRoot);
|
|
2786
|
+
const render = claudeCodeAdapter.render({
|
|
2787
|
+
projectRoot: config.projectRoot,
|
|
2788
|
+
...packageRoot ? { packageRoot } : {}
|
|
2789
|
+
});
|
|
2790
|
+
const state = readManagedModelState3(targets.managedModelsPath);
|
|
2791
|
+
const nextState = emptyManagedModelState3();
|
|
2792
|
+
const items = render.artifacts.map((artifact) => {
|
|
2793
|
+
const role = roleForArtifact(artifact);
|
|
2794
|
+
const rendered = String(artifact.content ?? "");
|
|
2795
|
+
const content = role !== void 0 ? applyConfiguredModel(rendered, role, state, nextState, config.reset) : rendered;
|
|
2796
|
+
const targetPath = join8(targets.pluginRoot, artifact.path);
|
|
2797
|
+
return {
|
|
2798
|
+
kind: targetKindForArtifact(artifact),
|
|
2799
|
+
action: "write-plugin-file",
|
|
2800
|
+
targetPath,
|
|
2801
|
+
description: `Materialize Claude Code plugin asset ${artifact.path}.`,
|
|
2802
|
+
requiresBackup: existsSync6(targetPath),
|
|
2803
|
+
content,
|
|
2804
|
+
...role ? { role } : {}
|
|
2805
|
+
};
|
|
2806
|
+
});
|
|
2807
|
+
items.push({
|
|
2808
|
+
kind: "managed-model-state",
|
|
2809
|
+
action: "write-managed-model-state",
|
|
2810
|
+
targetPath: targets.managedModelsPath,
|
|
2811
|
+
description: "Record thoth-agents-managed Claude Code subagent model ownership state.",
|
|
2812
|
+
requiresBackup: existsSync6(targets.managedModelsPath),
|
|
2813
|
+
content: stableJson3(nextState)
|
|
2814
|
+
});
|
|
2815
|
+
return {
|
|
2816
|
+
dryRun: config.dryRun === true,
|
|
2817
|
+
reset: config.reset,
|
|
2818
|
+
items,
|
|
2819
|
+
pluginRoot: targets.pluginRoot,
|
|
2820
|
+
diagnostics: [
|
|
2821
|
+
`Installed as a skills-directory plugin at ${targets.pluginRoot}; it auto-loads as thoth-agents@skills-dir on the next Claude Code session.`,
|
|
2822
|
+
"Restart Claude Code or run /reload-plugins to activate it; run /plugin (Installed tab) to confirm thoth-agents@skills-dir is loaded.",
|
|
2823
|
+
"The plugin settings.json activates the orchestrator agent as the main thread, so the session starts in delegate-first mode and bootstraps thoth-mem on its first turn."
|
|
2824
|
+
],
|
|
2825
|
+
disclaimers: [
|
|
2826
|
+
"The orchestrator agent is the Claude Code main thread (plugin settings.json `agent` key); while enabled it replaces the default system prompt for every session in scope.",
|
|
2827
|
+
"Every subagent inherits all main-thread tools, including MCP servers; read-only roles must not mutate the workspace per their operational contract (instruction-level, not tooling-enforced).",
|
|
2828
|
+
"Subagent models accept only sonnet, opus, haiku, or inherit.",
|
|
2829
|
+
"User-scope skills-directory plugins load hooks and MCP servers without extra approval; project-scope requires accepting the workspace trust dialog."
|
|
2830
|
+
]
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2833
|
+
function applyClaudeCodeSetup(plan) {
|
|
2834
|
+
const changed = [];
|
|
2835
|
+
const diagnostics = uniqueMessages([
|
|
2836
|
+
...plan.diagnostics,
|
|
2837
|
+
...plan.disclaimers
|
|
2838
|
+
]);
|
|
2839
|
+
if (plan.dryRun) return { success: true, changed, diagnostics };
|
|
2840
|
+
try {
|
|
2841
|
+
for (const item of plan.items) {
|
|
2842
|
+
if (writeTextWithBackup(item.targetPath, item.content)) {
|
|
2843
|
+
changed.push(item.targetPath);
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
return { success: true, changed, diagnostics };
|
|
2847
|
+
} catch (error) {
|
|
2848
|
+
return {
|
|
2849
|
+
success: false,
|
|
2850
|
+
changed,
|
|
2851
|
+
diagnostics,
|
|
2852
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2853
|
+
};
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
function applyClaudeCodeManagedModelOverrides(config, overrides) {
|
|
2857
|
+
if (config.dryRun) {
|
|
2858
|
+
return {
|
|
2859
|
+
success: true,
|
|
2860
|
+
changed: [],
|
|
2861
|
+
diagnostics: [
|
|
2862
|
+
"Dry-run Claude Code model override apply requested; no files were written."
|
|
2863
|
+
]
|
|
2864
|
+
};
|
|
2865
|
+
}
|
|
2866
|
+
const plan = buildClaudeCodeSetupPlan({
|
|
2867
|
+
...config,
|
|
2868
|
+
dryRun: true,
|
|
2869
|
+
reset: false
|
|
2870
|
+
});
|
|
2871
|
+
const stateItem = plan.items.find(
|
|
2872
|
+
(item) => item.action === "write-managed-model-state"
|
|
2873
|
+
);
|
|
2874
|
+
const statePath = stateItem?.targetPath;
|
|
2875
|
+
if (!statePath) {
|
|
2876
|
+
return {
|
|
2877
|
+
success: false,
|
|
2878
|
+
changed: [],
|
|
2879
|
+
diagnostics: plan.diagnostics,
|
|
2880
|
+
error: "Claude Code managed model state target was not found."
|
|
2881
|
+
};
|
|
2882
|
+
}
|
|
2883
|
+
const changed = [];
|
|
2884
|
+
const diagnostics = uniqueMessages([
|
|
2885
|
+
...plan.diagnostics,
|
|
2886
|
+
...plan.disclaimers
|
|
2887
|
+
]);
|
|
2888
|
+
const state = parseManagedModelStateJson2(stateItem?.content);
|
|
2889
|
+
const nextState = {
|
|
2890
|
+
version: CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION,
|
|
2891
|
+
models: { ...state.models },
|
|
2892
|
+
...state.configuredModels ? { configuredModels: { ...state.configuredModels } } : {}
|
|
2893
|
+
};
|
|
2894
|
+
try {
|
|
2895
|
+
for (const override of overrides) {
|
|
2896
|
+
if (!isClaudeCodeModelAlias(override.model)) {
|
|
2897
|
+
throw new Error(
|
|
2898
|
+
`Unsupported Claude Code model "${override.model}" for ${override.role}; use sonnet, opus, haiku, or inherit.`
|
|
2899
|
+
);
|
|
2900
|
+
}
|
|
2901
|
+
const roleItem = plan.items.find(
|
|
2902
|
+
(item) => item.kind === "subagent" && item.role === override.role
|
|
2903
|
+
);
|
|
2904
|
+
if (!roleItem) {
|
|
2905
|
+
throw new Error(`Missing Claude Code subagent for ${override.role}.`);
|
|
2906
|
+
}
|
|
2907
|
+
const before = existsSync6(roleItem.targetPath) ? readFileSync7(roleItem.targetPath, "utf8") : roleItem.content;
|
|
2908
|
+
const updated = replaceSubagentModel(before, override.model);
|
|
2909
|
+
if (writeTextWithBackup(roleItem.targetPath, updated)) {
|
|
2910
|
+
changed.push(roleItem.targetPath);
|
|
2911
|
+
}
|
|
2912
|
+
nextState.configuredModels ??= {};
|
|
2913
|
+
nextState.configuredModels[override.role] = override.model;
|
|
2914
|
+
}
|
|
2915
|
+
if (writeTextWithBackup(statePath, stableJson3(nextState))) {
|
|
2916
|
+
changed.push(statePath);
|
|
2917
|
+
}
|
|
2918
|
+
return { success: true, changed, diagnostics };
|
|
2919
|
+
} catch (error) {
|
|
2920
|
+
return {
|
|
2921
|
+
success: false,
|
|
2922
|
+
changed,
|
|
2923
|
+
diagnostics,
|
|
2924
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2925
|
+
};
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
function formatClaudeCodeSetupPlan(plan) {
|
|
2929
|
+
const lines = plan.items.map(
|
|
2930
|
+
(item) => `- ${item.action}: ${item.targetPath} (${item.description})`
|
|
2931
|
+
);
|
|
2932
|
+
return ["Claude Code setup plan:", ...lines].join("\n");
|
|
2933
|
+
}
|
|
2934
|
+
|
|
2935
|
+
// src/cli/operations/claude-code.ts
|
|
2936
|
+
var CLAUDE_CODE_DISPLAY_NAME = "Claude Code";
|
|
2937
|
+
var claudeCodePlanSources = /* @__PURE__ */ new WeakMap();
|
|
2938
|
+
var claudeCodeModelSources = /* @__PURE__ */ new WeakMap();
|
|
2939
|
+
var claudeCodeActions = [
|
|
2940
|
+
{
|
|
2941
|
+
id: "claude-code-status",
|
|
2942
|
+
kind: "status",
|
|
2943
|
+
label: "Status",
|
|
2944
|
+
description: "Inspect managed Claude Code plugin state",
|
|
2945
|
+
dryRun: false,
|
|
2946
|
+
requiresConfirmation: false,
|
|
2947
|
+
supported: true
|
|
2948
|
+
},
|
|
2949
|
+
{
|
|
2950
|
+
id: "claude-code-list",
|
|
2951
|
+
kind: "list",
|
|
2952
|
+
label: "List",
|
|
2953
|
+
description: "List managed Claude Code surfaces and actions",
|
|
2954
|
+
dryRun: false,
|
|
2955
|
+
requiresConfirmation: false,
|
|
2956
|
+
supported: true
|
|
2957
|
+
},
|
|
2958
|
+
{
|
|
2959
|
+
id: "claude-code-install",
|
|
2960
|
+
kind: "install",
|
|
2961
|
+
label: "Install",
|
|
2962
|
+
description: "Preview Claude Code plugin package install",
|
|
2963
|
+
dryRun: true,
|
|
2964
|
+
requiresConfirmation: true,
|
|
2965
|
+
supported: true
|
|
2966
|
+
},
|
|
2967
|
+
{
|
|
2968
|
+
id: "claude-code-update",
|
|
2969
|
+
kind: "update",
|
|
2970
|
+
label: "Update",
|
|
2971
|
+
description: "Preview Claude Code managed plugin refresh",
|
|
2972
|
+
dryRun: true,
|
|
2973
|
+
requiresConfirmation: true,
|
|
2974
|
+
supported: true
|
|
2975
|
+
},
|
|
2976
|
+
{
|
|
2977
|
+
id: "claude-code-sync",
|
|
2978
|
+
kind: "sync",
|
|
2979
|
+
label: "Sync",
|
|
2980
|
+
description: "Preview Claude Code managed plugin sync",
|
|
2981
|
+
dryRun: true,
|
|
2982
|
+
requiresConfirmation: true,
|
|
2983
|
+
supported: true
|
|
2984
|
+
},
|
|
2985
|
+
{
|
|
2986
|
+
id: "claude-code-model-config",
|
|
2987
|
+
kind: "model-config",
|
|
2988
|
+
label: "Model",
|
|
2989
|
+
description: "Preview supported Claude Code subagent model line changes",
|
|
2990
|
+
dryRun: true,
|
|
2991
|
+
requiresConfirmation: true,
|
|
2992
|
+
supported: true
|
|
2993
|
+
}
|
|
2994
|
+
];
|
|
2995
|
+
var claudeCodeOperationAdapter = {
|
|
2996
|
+
id: "claude",
|
|
2997
|
+
displayName: CLAUDE_CODE_DISPLAY_NAME,
|
|
2998
|
+
available: true,
|
|
2999
|
+
description: "Claude Code plugin package and managed subagent surfaces.",
|
|
3000
|
+
actions: claudeCodeActions
|
|
3001
|
+
};
|
|
3002
|
+
function claudeCodeConfig(context = { cwd: process.cwd() }, dryRun) {
|
|
3003
|
+
return {
|
|
3004
|
+
dryRun,
|
|
3005
|
+
reset: false,
|
|
3006
|
+
// Match the installer (install.ts uses 'user') so post-install status,
|
|
3007
|
+
// update, sync, and model commands resolve the same plugin root.
|
|
3008
|
+
scope: context.scope ?? "user",
|
|
3009
|
+
projectRoot: context.cwd,
|
|
3010
|
+
homeDir: context.homeDir,
|
|
3011
|
+
packageRoot: context.packageRoot
|
|
3012
|
+
};
|
|
3013
|
+
}
|
|
3014
|
+
function claudeCodeDisclaimers() {
|
|
3015
|
+
return [
|
|
3016
|
+
{
|
|
3017
|
+
message: "Subagents inherit all main-thread tools (including MCP servers); read-only roles must not mutate the workspace per their operational contract (instruction-level, not tooling-enforced). The orchestrator is the main session.",
|
|
3018
|
+
code: "claude-code-first-class"
|
|
3019
|
+
},
|
|
3020
|
+
{
|
|
3021
|
+
message: "Subagent models accept only sonnet, opus, haiku, or inherit.",
|
|
3022
|
+
code: "claude-code-model-aliases"
|
|
3023
|
+
}
|
|
3024
|
+
];
|
|
3025
|
+
}
|
|
3026
|
+
function warning(message, code) {
|
|
3027
|
+
return { severity: "important", message, code };
|
|
3028
|
+
}
|
|
3029
|
+
function targetForItem(item, state, observed) {
|
|
3030
|
+
return {
|
|
3031
|
+
kind: item.kind === "managed-model-state" ? "memory-state" : "generated-artifact",
|
|
3032
|
+
path: item.targetPath,
|
|
3033
|
+
label: item.role ? `Claude Code ${item.role} subagent` : item.kind.replaceAll("-", " "),
|
|
3034
|
+
state,
|
|
3035
|
+
expected: item.action,
|
|
3036
|
+
observed,
|
|
3037
|
+
description: item.description
|
|
3038
|
+
};
|
|
3039
|
+
}
|
|
3040
|
+
function surfaceForItem(item) {
|
|
3041
|
+
return {
|
|
3042
|
+
id: `${item.kind}:${item.role ?? basename2(item.targetPath)}`,
|
|
3043
|
+
label: item.role ? `Claude Code ${item.role} subagent` : item.kind.replaceAll("-", " "),
|
|
3044
|
+
path: item.targetPath,
|
|
3045
|
+
description: item.description
|
|
3046
|
+
};
|
|
3047
|
+
}
|
|
3048
|
+
function backupForItem(item) {
|
|
3049
|
+
return {
|
|
3050
|
+
required: item.requiresBackup,
|
|
3051
|
+
strategy: item.requiresBackup ? "managed-backup-file" : "none",
|
|
3052
|
+
destinations: item.requiresBackup ? [{ path: `${item.targetPath}.bak`, label: "managed backup" }] : []
|
|
3053
|
+
};
|
|
3054
|
+
}
|
|
3055
|
+
function manifestState(item) {
|
|
3056
|
+
if (!existsSync7(item.targetPath))
|
|
3057
|
+
return { state: "missing", observed: "absent" };
|
|
3058
|
+
const observed = readFileSync8(item.targetPath, "utf8");
|
|
3059
|
+
if (observed === item.content)
|
|
3060
|
+
return { state: "installed", observed: "current" };
|
|
3061
|
+
try {
|
|
3062
|
+
const expectedVersion = JSON.parse(item.content).version;
|
|
3063
|
+
const observedVersion = JSON.parse(observed).version;
|
|
3064
|
+
if (typeof expectedVersion === "string" && typeof observedVersion === "string" && expectedVersion !== observedVersion) {
|
|
3065
|
+
return {
|
|
3066
|
+
state: "outdated",
|
|
3067
|
+
observed: `version ${observedVersion}; expected ${expectedVersion}`
|
|
3068
|
+
};
|
|
3069
|
+
}
|
|
3070
|
+
} catch {
|
|
3071
|
+
return { state: "unknown", observed: "unparseable plugin manifest" };
|
|
3072
|
+
}
|
|
3073
|
+
return { state: "drift", observed: "content differs" };
|
|
3074
|
+
}
|
|
3075
|
+
function contentState(item) {
|
|
3076
|
+
if (!existsSync7(item.targetPath))
|
|
3077
|
+
return { state: "missing", observed: "absent" };
|
|
3078
|
+
return readFileSync8(item.targetPath, "utf8") === item.content ? { state: "installed", observed: "current" } : { state: "drift", observed: "content differs" };
|
|
3079
|
+
}
|
|
3080
|
+
function classifyItem(item) {
|
|
3081
|
+
if (item.kind === "plugin-manifest") return manifestState(item);
|
|
3082
|
+
return contentState(item);
|
|
3083
|
+
}
|
|
3084
|
+
function aggregateState(states) {
|
|
3085
|
+
if (states.includes("unknown")) return "unknown";
|
|
3086
|
+
if (states.includes("drift")) return "drift";
|
|
3087
|
+
if (states.includes("outdated")) return "outdated";
|
|
3088
|
+
if (states.includes("missing")) return "missing";
|
|
3089
|
+
return "installed";
|
|
3090
|
+
}
|
|
3091
|
+
function statusSummary(state) {
|
|
3092
|
+
switch (state) {
|
|
3093
|
+
case "installed":
|
|
3094
|
+
return "Claude Code managed plugin is installed and current.";
|
|
3095
|
+
case "missing":
|
|
3096
|
+
return "Claude Code managed plugin is missing.";
|
|
3097
|
+
case "drift":
|
|
3098
|
+
return "Claude Code managed plugin exists but differs from expected output.";
|
|
3099
|
+
case "outdated":
|
|
3100
|
+
return "Claude Code managed plugin includes an older generated manifest.";
|
|
3101
|
+
case "unknown":
|
|
3102
|
+
return "Claude Code managed plugin could not be classified safely.";
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
function statusFromSetupPlan(plan) {
|
|
3106
|
+
const classified = plan.items.map((item) => ({
|
|
3107
|
+
item,
|
|
3108
|
+
...classifyItem(item)
|
|
3109
|
+
}));
|
|
3110
|
+
const state = aggregateState(classified.map((entry) => entry.state));
|
|
3111
|
+
return {
|
|
3112
|
+
harness: "claude",
|
|
3113
|
+
displayName: CLAUDE_CODE_DISPLAY_NAME,
|
|
3114
|
+
state,
|
|
3115
|
+
summary: statusSummary(state),
|
|
3116
|
+
targets: classified.map(
|
|
3117
|
+
({ item, state: state2, observed }) => targetForItem(item, state2, observed)
|
|
3118
|
+
),
|
|
3119
|
+
diagnostics: plan.diagnostics.map((message) => ({
|
|
3120
|
+
severity: "minor",
|
|
3121
|
+
message,
|
|
3122
|
+
code: "claude-code-diagnostic"
|
|
3123
|
+
})),
|
|
3124
|
+
actions: claudeCodeActions,
|
|
3125
|
+
disclaimers: [
|
|
3126
|
+
...claudeCodeDisclaimers(),
|
|
3127
|
+
...plan.disclaimers.map((message) => ({ message }))
|
|
3128
|
+
]
|
|
3129
|
+
};
|
|
3130
|
+
}
|
|
3131
|
+
function getClaudeCodeStatus(context = { cwd: process.cwd() }) {
|
|
3132
|
+
let plan;
|
|
3133
|
+
try {
|
|
3134
|
+
plan = buildClaudeCodeSetupPlan(claudeCodeConfig(context, true));
|
|
3135
|
+
} catch (error) {
|
|
3136
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3137
|
+
return {
|
|
3138
|
+
harness: "claude",
|
|
3139
|
+
displayName: CLAUDE_CODE_DISPLAY_NAME,
|
|
3140
|
+
state: "unknown",
|
|
3141
|
+
summary: `Claude Code setup plan could not be built: ${message}`,
|
|
3142
|
+
targets: [],
|
|
3143
|
+
diagnostics: [
|
|
3144
|
+
{
|
|
3145
|
+
severity: "critical",
|
|
3146
|
+
message,
|
|
3147
|
+
code: "claude-code-plan-build-failed"
|
|
3148
|
+
}
|
|
3149
|
+
],
|
|
3150
|
+
actions: claudeCodeActions,
|
|
3151
|
+
disclaimers: claudeCodeDisclaimers()
|
|
3152
|
+
};
|
|
3153
|
+
}
|
|
3154
|
+
return statusFromSetupPlan(plan);
|
|
3155
|
+
}
|
|
3156
|
+
function planItemFromSetup(item) {
|
|
3157
|
+
return {
|
|
3158
|
+
title: item.description,
|
|
3159
|
+
target: targetForItem(item),
|
|
3160
|
+
preview: item.content,
|
|
3161
|
+
backup: backupForItem(item)
|
|
3162
|
+
};
|
|
3163
|
+
}
|
|
3164
|
+
function planFromSetup(id, action, title, summary, setupPlan) {
|
|
3165
|
+
const status = statusFromSetupPlan(setupPlan);
|
|
3166
|
+
const canApply = status.state === "installed" || status.state === "missing" || status.state === "outdated" || status.state === "drift";
|
|
3167
|
+
const plan = {
|
|
3168
|
+
id,
|
|
3169
|
+
harness: "claude",
|
|
3170
|
+
action,
|
|
3171
|
+
title,
|
|
3172
|
+
summary,
|
|
3173
|
+
dryRun: true,
|
|
3174
|
+
canApply,
|
|
3175
|
+
targets: status.targets,
|
|
3176
|
+
surfaces: setupPlan.items.map(surfaceForItem),
|
|
3177
|
+
backup: {
|
|
3178
|
+
required: setupPlan.items.some((item) => item.requiresBackup),
|
|
3179
|
+
strategy: "managed-backup-file",
|
|
3180
|
+
description: "Existing Claude Code plugin files are backed up before being overwritten."
|
|
3181
|
+
},
|
|
3182
|
+
items: setupPlan.items.map(planItemFromSetup),
|
|
3183
|
+
warnings: status.diagnostics,
|
|
3184
|
+
disclaimers: [
|
|
3185
|
+
...claudeCodeDisclaimers(),
|
|
3186
|
+
...setupPlan.disclaimers.map((message) => ({ message }))
|
|
3187
|
+
]
|
|
3188
|
+
};
|
|
3189
|
+
claudeCodePlanSources.set(plan, setupPlan);
|
|
3190
|
+
return plan;
|
|
3191
|
+
}
|
|
3192
|
+
function buildClaudeCodeInstallPlan(context = { cwd: process.cwd() }) {
|
|
3193
|
+
return planFromSetup(
|
|
3194
|
+
"claude-code-install-preview",
|
|
3195
|
+
"install",
|
|
3196
|
+
"Install Claude Code plugin package",
|
|
3197
|
+
"Preview Claude Code plugin package install using buildClaudeCodeSetupPlan().",
|
|
3198
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context, true))
|
|
3199
|
+
);
|
|
3200
|
+
}
|
|
3201
|
+
function buildClaudeCodeUpdatePlan(context = { cwd: process.cwd() }) {
|
|
3202
|
+
return planFromSetup(
|
|
3203
|
+
"claude-code-update-preview",
|
|
3204
|
+
"update",
|
|
3205
|
+
"Update Claude Code plugin package",
|
|
3206
|
+
"Preview Claude Code managed plugin refresh using buildClaudeCodeSetupPlan().",
|
|
3207
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context, true))
|
|
3208
|
+
);
|
|
3209
|
+
}
|
|
3210
|
+
function buildClaudeCodeSyncPlan(context = { cwd: process.cwd() }) {
|
|
3211
|
+
return planFromSetup(
|
|
3212
|
+
"claude-code-sync-preview",
|
|
3213
|
+
"sync",
|
|
3214
|
+
"Sync Claude Code plugin package",
|
|
3215
|
+
"Preview Claude Code managed plugin subagents, MCP, hooks, and skills.",
|
|
3216
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context, true))
|
|
3217
|
+
);
|
|
3218
|
+
}
|
|
3219
|
+
function isClaudeCodeRole(role) {
|
|
3220
|
+
return CLAUDE_CODE_ROLE_NAMES.includes(role);
|
|
3221
|
+
}
|
|
3222
|
+
function buildClaudeCodeModelPlan(input, context = { cwd: process.cwd() }) {
|
|
3223
|
+
const status = getClaudeCodeStatus(context);
|
|
3224
|
+
const supportedRoles = input.roles.filter((role) => isClaudeCodeRole(role.role)).filter((role) => isClaudeCodeModelAlias(role.model)).map((role) => ({
|
|
3225
|
+
role: role.role,
|
|
3226
|
+
model: role.model
|
|
3227
|
+
}));
|
|
3228
|
+
const rejectedRoles = input.roles.filter(
|
|
3229
|
+
(role) => !isClaudeCodeRole(role.role) || !isClaudeCodeModelAlias(role.model)
|
|
3230
|
+
);
|
|
3231
|
+
const warnings = [
|
|
3232
|
+
...status.diagnostics,
|
|
3233
|
+
...input.warnings ?? [],
|
|
3234
|
+
...rejectedRoles.map(
|
|
3235
|
+
(role) => warning(
|
|
3236
|
+
`Claude Code does not accept role "${role.role}" with model "${role.model}"; roles must be one of ${CLAUDE_CODE_ROLE_NAMES.join(", ")} and models must be sonnet, opus, haiku, or inherit.`,
|
|
3237
|
+
"claude-code-unsupported-model-role"
|
|
3238
|
+
)
|
|
3239
|
+
)
|
|
3240
|
+
];
|
|
3241
|
+
if (input.harness !== "claude") {
|
|
3242
|
+
warnings.push(
|
|
3243
|
+
warning(
|
|
3244
|
+
"Model plan target harness must be claude.",
|
|
3245
|
+
"claude-code-model-harness-mismatch"
|
|
3246
|
+
)
|
|
3247
|
+
);
|
|
3248
|
+
}
|
|
3249
|
+
const targets = supportedRoles.map(({ role }) => {
|
|
3250
|
+
const target = status.targets.find(
|
|
3251
|
+
(candidate) => candidate.path?.endsWith(`agents${pathSep()}${role}.md`)
|
|
3252
|
+
);
|
|
3253
|
+
return target ?? {
|
|
3254
|
+
kind: "generated-artifact",
|
|
3255
|
+
label: `Claude Code ${role} subagent`,
|
|
3256
|
+
state: "missing"
|
|
3257
|
+
};
|
|
3258
|
+
});
|
|
3259
|
+
const stateTarget = status.targets.find(
|
|
3260
|
+
(target) => target.path?.endsWith(".thoth-agents-managed-models.json")
|
|
3261
|
+
);
|
|
3262
|
+
const plan = {
|
|
3263
|
+
id: "claude-code-model-config-preview",
|
|
3264
|
+
harness: "claude",
|
|
3265
|
+
action: "model-config",
|
|
3266
|
+
title: "Configure Claude Code subagent model lines",
|
|
3267
|
+
summary: "Preview model changes for generated Claude Code subagent files and managed model state only.",
|
|
3268
|
+
dryRun: true,
|
|
3269
|
+
canApply: input.harness === "claude" && supportedRoles.length > 0 && status.state !== "unknown",
|
|
3270
|
+
targets: [...targets, ...stateTarget ? [stateTarget] : []],
|
|
3271
|
+
surfaces: targets.map((target) => ({
|
|
3272
|
+
id: `claude-code-model:${target.label}`,
|
|
3273
|
+
label: target.label ?? "Claude Code subagent",
|
|
3274
|
+
path: target.path,
|
|
3275
|
+
state: target.state
|
|
3276
|
+
})),
|
|
3277
|
+
backup: {
|
|
3278
|
+
required: true,
|
|
3279
|
+
strategy: "managed-backup-file",
|
|
3280
|
+
description: "Existing subagent files and managed model state are backed up by the managed write helper."
|
|
3281
|
+
},
|
|
3282
|
+
items: supportedRoles.map(({ role, model }) => ({
|
|
3283
|
+
title: `Set ${role} Claude Code subagent model line`,
|
|
3284
|
+
target: targets.find(
|
|
3285
|
+
(target) => target.path?.endsWith(`agents${pathSep()}${role}.md`)
|
|
3286
|
+
) ?? {
|
|
3287
|
+
kind: "generated-artifact",
|
|
3288
|
+
label: `Claude Code ${role} subagent`
|
|
3289
|
+
},
|
|
3290
|
+
preview: JSON.stringify({ role, model }),
|
|
3291
|
+
backup: { required: true, strategy: "managed-backup-file" }
|
|
3292
|
+
})),
|
|
3293
|
+
warnings,
|
|
3294
|
+
disclaimers: [
|
|
3295
|
+
...claudeCodeDisclaimers(),
|
|
3296
|
+
...input.disclaimers ?? [],
|
|
3297
|
+
{
|
|
3298
|
+
message: "Claude Code model configuration writes only generated subagent frontmatter model lines and the managed model state JSON.",
|
|
3299
|
+
code: "claude-code-model-supported-surface"
|
|
3300
|
+
}
|
|
3301
|
+
]
|
|
3302
|
+
};
|
|
3303
|
+
claudeCodeModelSources.set(plan, {
|
|
3304
|
+
config: claudeCodeConfig(context, false),
|
|
3305
|
+
roles: supportedRoles
|
|
3306
|
+
});
|
|
3307
|
+
return plan;
|
|
3308
|
+
}
|
|
3309
|
+
function pathSep() {
|
|
3310
|
+
return process.platform === "win32" ? "\\" : "/";
|
|
3311
|
+
}
|
|
3312
|
+
function rejectPlan(plan, message, severity = "critical") {
|
|
3313
|
+
return {
|
|
3314
|
+
harness: plan.harness,
|
|
3315
|
+
action: plan.action,
|
|
3316
|
+
applied: false,
|
|
3317
|
+
summary: message,
|
|
3318
|
+
changedTargets: [],
|
|
3319
|
+
backups: [],
|
|
3320
|
+
warnings: [{ severity, message }],
|
|
3321
|
+
disclaimers: claudeCodeDisclaimers()
|
|
3322
|
+
};
|
|
3323
|
+
}
|
|
3324
|
+
function validateClaudeCodePlan(plan) {
|
|
3325
|
+
if (plan.harness !== "claude") {
|
|
3326
|
+
return rejectPlan(plan, "Only Claude Code operation plans can be applied.");
|
|
3327
|
+
}
|
|
3328
|
+
if (!plan.canApply) {
|
|
3329
|
+
return rejectPlan(
|
|
3330
|
+
plan,
|
|
3331
|
+
"Claude Code plan cannot be applied because canApply is false."
|
|
3332
|
+
);
|
|
3333
|
+
}
|
|
3334
|
+
if (!["install", "update", "sync", "model-config"].includes(plan.action)) {
|
|
3335
|
+
return rejectPlan(
|
|
3336
|
+
plan,
|
|
3337
|
+
`Unsupported Claude Code apply action: ${plan.action}.`
|
|
3338
|
+
);
|
|
3339
|
+
}
|
|
3340
|
+
if (plan.items.length === 0) {
|
|
3341
|
+
return rejectPlan(plan, "Claude Code plan has no items to apply.");
|
|
3342
|
+
}
|
|
3343
|
+
return null;
|
|
3344
|
+
}
|
|
3345
|
+
function applyClaudeCodePlan(plan) {
|
|
3346
|
+
const rejection = validateClaudeCodePlan(plan);
|
|
3347
|
+
if (rejection) return rejection;
|
|
3348
|
+
if (plan.action === "model-config") {
|
|
3349
|
+
const source = claudeCodeModelSources.get(plan);
|
|
3350
|
+
if (!source) {
|
|
3351
|
+
return rejectPlan(
|
|
3352
|
+
plan,
|
|
3353
|
+
"Claude Code model plan was not produced by buildClaudeCodeModelPlan in this process."
|
|
3354
|
+
);
|
|
3355
|
+
}
|
|
3356
|
+
const result2 = applyClaudeCodeManagedModelOverrides(
|
|
3357
|
+
source.config,
|
|
3358
|
+
source.roles.map((role) => ({
|
|
3359
|
+
role: role.role,
|
|
3360
|
+
model: role.model
|
|
3361
|
+
}))
|
|
3362
|
+
);
|
|
3363
|
+
return {
|
|
3364
|
+
harness: "claude",
|
|
3365
|
+
action: "model-config",
|
|
3366
|
+
applied: result2.success,
|
|
3367
|
+
summary: result2.success ? "Applied Claude Code subagent model overrides." : result2.error ?? "Failed to apply Claude Code subagent model overrides.",
|
|
3368
|
+
changedTargets: result2.changed.map((path4) => ({
|
|
3369
|
+
kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
|
|
3370
|
+
path: path4,
|
|
3371
|
+
label: basename2(path4),
|
|
3372
|
+
state: "installed"
|
|
3373
|
+
})),
|
|
3374
|
+
backups: result2.changed.filter((path4) => existsSync7(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
|
|
3375
|
+
warnings: result2.success ? [] : [{ severity: "critical", message: result2.error ?? "apply failed." }],
|
|
3376
|
+
disclaimers: claudeCodeDisclaimers()
|
|
3377
|
+
};
|
|
3378
|
+
}
|
|
3379
|
+
const setupPlan = claudeCodePlanSources.get(plan);
|
|
3380
|
+
if (!setupPlan) {
|
|
3381
|
+
return rejectPlan(
|
|
3382
|
+
plan,
|
|
3383
|
+
"Claude Code setup plan was not produced by a Claude Code operation plan builder in this process."
|
|
3384
|
+
);
|
|
3385
|
+
}
|
|
3386
|
+
const result = applyClaudeCodeSetup({ ...setupPlan, dryRun: false });
|
|
3387
|
+
return {
|
|
3388
|
+
harness: "claude",
|
|
3389
|
+
action: plan.action,
|
|
3390
|
+
applied: result.success,
|
|
3391
|
+
summary: result.success ? `Applied Claude Code managed ${plan.action} plan.` : result.error ?? `Failed to apply Claude Code ${plan.action} plan.`,
|
|
3392
|
+
changedTargets: result.changed.map((path4) => ({
|
|
3393
|
+
kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
|
|
3394
|
+
path: path4,
|
|
3395
|
+
label: basename2(path4),
|
|
3396
|
+
state: "installed"
|
|
3397
|
+
})),
|
|
3398
|
+
backups: result.changed.filter((path4) => existsSync7(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
|
|
3399
|
+
warnings: result.success ? [] : [{ severity: "critical", message: result.error ?? "apply failed." }],
|
|
3400
|
+
disclaimers: claudeCodeDisclaimers()
|
|
3401
|
+
};
|
|
3402
|
+
}
|
|
3403
|
+
function defaultClaudeCodeModelRoles() {
|
|
3404
|
+
return CLAUDE_CODE_ROLE_NAMES.map((role) => ({
|
|
3405
|
+
role,
|
|
3406
|
+
model: CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS[role]
|
|
3407
|
+
}));
|
|
3408
|
+
}
|
|
3409
|
+
|
|
3410
|
+
// src/cli/operations/codex.ts
|
|
3411
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9 } from "fs";
|
|
3412
|
+
import { basename as basename3 } from "path";
|
|
3413
|
+
var CODEX_DISPLAY_NAME = "Codex";
|
|
3414
|
+
var codexPlanSources = /* @__PURE__ */ new WeakMap();
|
|
3415
|
+
var codexModelSources = /* @__PURE__ */ new WeakMap();
|
|
3416
|
+
var codexActions = [
|
|
3417
|
+
{
|
|
3418
|
+
id: "codex-status",
|
|
3419
|
+
kind: "status",
|
|
3420
|
+
label: "Status",
|
|
3421
|
+
description: "Inspect managed Codex setup state",
|
|
3422
|
+
dryRun: false,
|
|
3423
|
+
requiresConfirmation: false,
|
|
3424
|
+
supported: true
|
|
3425
|
+
},
|
|
3426
|
+
{
|
|
3427
|
+
id: "codex-list",
|
|
3428
|
+
kind: "list",
|
|
3429
|
+
label: "List",
|
|
3430
|
+
description: "List managed Codex surfaces and actions",
|
|
3431
|
+
dryRun: false,
|
|
2306
3432
|
requiresConfirmation: false,
|
|
2307
3433
|
supported: true
|
|
2308
3434
|
},
|
|
@@ -2374,10 +3500,10 @@ function codexDisclaimers() {
|
|
|
2374
3500
|
}
|
|
2375
3501
|
];
|
|
2376
3502
|
}
|
|
2377
|
-
function
|
|
3503
|
+
function warning2(message, code) {
|
|
2378
3504
|
return { severity: "important", message, code };
|
|
2379
3505
|
}
|
|
2380
|
-
function
|
|
3506
|
+
function targetForItem2(item, state, observed) {
|
|
2381
3507
|
return {
|
|
2382
3508
|
kind: item.kind === "managed-model-state" ? "memory-state" : "generated-artifact",
|
|
2383
3509
|
path: item.targetPath,
|
|
@@ -2388,15 +3514,15 @@ function targetForItem(item, state, observed) {
|
|
|
2388
3514
|
description: item.description
|
|
2389
3515
|
};
|
|
2390
3516
|
}
|
|
2391
|
-
function
|
|
3517
|
+
function surfaceForItem2(item) {
|
|
2392
3518
|
return {
|
|
2393
|
-
id: `${item.kind}:${item.role ??
|
|
3519
|
+
id: `${item.kind}:${item.role ?? basename3(item.targetPath)}`,
|
|
2394
3520
|
label: item.role ? `Codex ${item.role} subagent TOML` : item.kind.replaceAll("-", " "),
|
|
2395
3521
|
path: item.targetPath,
|
|
2396
3522
|
description: item.description
|
|
2397
3523
|
};
|
|
2398
3524
|
}
|
|
2399
|
-
function
|
|
3525
|
+
function backupForItem2(item) {
|
|
2400
3526
|
return {
|
|
2401
3527
|
required: item.requiresBackup,
|
|
2402
3528
|
strategy: item.requiresBackup ? "managed-backup-file" : "none",
|
|
@@ -2404,9 +3530,9 @@ function backupForItem(item) {
|
|
|
2404
3530
|
};
|
|
2405
3531
|
}
|
|
2406
3532
|
function rootBlockState(item) {
|
|
2407
|
-
if (!
|
|
3533
|
+
if (!existsSync8(item.targetPath))
|
|
2408
3534
|
return { state: "missing", observed: "absent" };
|
|
2409
|
-
const content =
|
|
3535
|
+
const content = readFileSync9(item.targetPath, "utf8");
|
|
2410
3536
|
if (item.content && content.includes(item.content)) {
|
|
2411
3537
|
return { state: "installed", observed: "managed root block present" };
|
|
2412
3538
|
}
|
|
@@ -2416,22 +3542,22 @@ function rootBlockState(item) {
|
|
|
2416
3542
|
return { state: "missing", observed: "managed root block absent" };
|
|
2417
3543
|
}
|
|
2418
3544
|
function managedModelState(item) {
|
|
2419
|
-
if (!
|
|
3545
|
+
if (!existsSync8(item.targetPath))
|
|
2420
3546
|
return { state: "missing", observed: "absent" };
|
|
2421
3547
|
try {
|
|
2422
|
-
const parsed = JSON.parse(
|
|
3548
|
+
const parsed = JSON.parse(readFileSync9(item.targetPath, "utf8"));
|
|
2423
3549
|
if (parsed.version !== MANAGED_MODEL_STATE_VERSION || !parsed.models || typeof parsed.models !== "object" || Array.isArray(parsed.models)) {
|
|
2424
3550
|
return { state: "unknown", observed: "invalid managed model state" };
|
|
2425
3551
|
}
|
|
2426
|
-
return item.content ===
|
|
3552
|
+
return item.content === readFileSync9(item.targetPath, "utf8") ? { state: "installed", observed: "managed model state current" } : { state: "drift", observed: "managed model state differs" };
|
|
2427
3553
|
} catch {
|
|
2428
3554
|
return { state: "unknown", observed: "unparseable managed model state" };
|
|
2429
3555
|
}
|
|
2430
3556
|
}
|
|
2431
3557
|
function userConfigState(item) {
|
|
2432
|
-
if (!
|
|
3558
|
+
if (!existsSync8(item.targetPath))
|
|
2433
3559
|
return { state: "missing", observed: "absent" };
|
|
2434
|
-
const content =
|
|
3560
|
+
const content = readFileSync9(item.targetPath, "utf8");
|
|
2435
3561
|
if (/^\s*default_mode_request_user_input\s*=\s*true\b/m.test(content)) {
|
|
2436
3562
|
return {
|
|
2437
3563
|
state: "installed",
|
|
@@ -2444,10 +3570,10 @@ function userConfigState(item) {
|
|
|
2444
3570
|
return { state: "missing", observed: "managed feature flag absent" };
|
|
2445
3571
|
}
|
|
2446
3572
|
function marketplaceState(item) {
|
|
2447
|
-
if (!
|
|
3573
|
+
if (!existsSync8(item.targetPath))
|
|
2448
3574
|
return { state: "missing", observed: "absent" };
|
|
2449
3575
|
try {
|
|
2450
|
-
const parsed = JSON.parse(
|
|
3576
|
+
const parsed = JSON.parse(readFileSync9(item.targetPath, "utf8"));
|
|
2451
3577
|
const plugins = Array.isArray(parsed.plugins) ? parsed.plugins : [];
|
|
2452
3578
|
const entry = plugins.find(
|
|
2453
3579
|
(plugin) => plugin && typeof plugin === "object" && "name" in plugin && plugin.name === "thoth-agents"
|
|
@@ -2460,11 +3586,11 @@ function marketplaceState(item) {
|
|
|
2460
3586
|
return { state: "unknown", observed: "unparseable marketplace JSON" };
|
|
2461
3587
|
}
|
|
2462
3588
|
}
|
|
2463
|
-
function
|
|
2464
|
-
if (!
|
|
3589
|
+
function contentState2(item) {
|
|
3590
|
+
if (!existsSync8(item.targetPath))
|
|
2465
3591
|
return { state: "missing", observed: "absent" };
|
|
2466
|
-
const observed =
|
|
2467
|
-
if (
|
|
3592
|
+
const observed = readFileSync9(item.targetPath, "utf8");
|
|
3593
|
+
if (basename3(item.targetPath) === "plugin.json" && item.targetPath.replaceAll("\\", "/").includes("/.codex-plugin/")) {
|
|
2468
3594
|
try {
|
|
2469
3595
|
const expectedVersion = JSON.parse(item.content ?? "{}").version;
|
|
2470
3596
|
const observedVersion = JSON.parse(observed).version;
|
|
@@ -2492,7 +3618,7 @@ function contentState(item) {
|
|
|
2492
3618
|
}
|
|
2493
3619
|
return { state: "drift", observed: "content differs" };
|
|
2494
3620
|
}
|
|
2495
|
-
function
|
|
3621
|
+
function classifyItem2(item) {
|
|
2496
3622
|
if (item.action === "diagnose-only") {
|
|
2497
3623
|
return { state: "unknown", observed: "diagnostic guidance only" };
|
|
2498
3624
|
}
|
|
@@ -2502,16 +3628,16 @@ function classifyItem(item) {
|
|
|
2502
3628
|
}
|
|
2503
3629
|
if (item.action === "merge-toml") return userConfigState(item);
|
|
2504
3630
|
if (item.action === "merge-marketplace") return marketplaceState(item);
|
|
2505
|
-
return
|
|
3631
|
+
return contentState2(item);
|
|
2506
3632
|
}
|
|
2507
|
-
function
|
|
3633
|
+
function aggregateState2(states) {
|
|
2508
3634
|
if (states.includes("unknown")) return "unknown";
|
|
2509
3635
|
if (states.includes("drift")) return "drift";
|
|
2510
3636
|
if (states.includes("outdated")) return "outdated";
|
|
2511
3637
|
if (states.includes("missing")) return "missing";
|
|
2512
3638
|
return "installed";
|
|
2513
3639
|
}
|
|
2514
|
-
function
|
|
3640
|
+
function statusSummary2(state) {
|
|
2515
3641
|
switch (state) {
|
|
2516
3642
|
case "installed":
|
|
2517
3643
|
return "Codex managed setup surfaces are installed and current.";
|
|
@@ -2548,8 +3674,8 @@ function getCodexStatus(context = { cwd: process.cwd() }) {
|
|
|
2548
3674
|
disclaimers: codexDisclaimers()
|
|
2549
3675
|
};
|
|
2550
3676
|
}
|
|
2551
|
-
const classified = plan.items.filter((item) => item.action !== "diagnose-only").map((item) => ({ item, ...
|
|
2552
|
-
const state =
|
|
3677
|
+
const classified = plan.items.filter((item) => item.action !== "diagnose-only").map((item) => ({ item, ...classifyItem2(item) }));
|
|
3678
|
+
const state = aggregateState2(classified.map((item) => item.state));
|
|
2553
3679
|
const diagnostics = plan.diagnostics.map((message) => ({
|
|
2554
3680
|
severity: "minor",
|
|
2555
3681
|
message,
|
|
@@ -2559,9 +3685,9 @@ function getCodexStatus(context = { cwd: process.cwd() }) {
|
|
|
2559
3685
|
harness: "codex",
|
|
2560
3686
|
displayName: CODEX_DISPLAY_NAME,
|
|
2561
3687
|
state,
|
|
2562
|
-
summary:
|
|
3688
|
+
summary: statusSummary2(state),
|
|
2563
3689
|
targets: classified.map(
|
|
2564
|
-
({ item, state: state2, observed }) =>
|
|
3690
|
+
({ item, state: state2, observed }) => targetForItem2(item, state2, observed)
|
|
2565
3691
|
),
|
|
2566
3692
|
diagnostics,
|
|
2567
3693
|
actions: codexActions,
|
|
@@ -2571,15 +3697,15 @@ function getCodexStatus(context = { cwd: process.cwd() }) {
|
|
|
2571
3697
|
]
|
|
2572
3698
|
};
|
|
2573
3699
|
}
|
|
2574
|
-
function
|
|
3700
|
+
function planItemFromSetup2(item) {
|
|
2575
3701
|
return {
|
|
2576
3702
|
title: item.description,
|
|
2577
|
-
target:
|
|
3703
|
+
target: targetForItem2(item),
|
|
2578
3704
|
preview: item.content,
|
|
2579
|
-
backup:
|
|
3705
|
+
backup: backupForItem2(item)
|
|
2580
3706
|
};
|
|
2581
3707
|
}
|
|
2582
|
-
function
|
|
3708
|
+
function planFromSetup2(id, action, title, summary, setupPlan, context) {
|
|
2583
3709
|
const status = getCodexStatus(context);
|
|
2584
3710
|
const canApply = status.state === "installed" || status.state === "missing" || status.state === "outdated";
|
|
2585
3711
|
const plan = {
|
|
@@ -2591,17 +3717,17 @@ function planFromSetup(id, action, title, summary, setupPlan, context) {
|
|
|
2591
3717
|
dryRun: true,
|
|
2592
3718
|
canApply,
|
|
2593
3719
|
targets: status.targets,
|
|
2594
|
-
surfaces: setupPlan.items.filter((item) => item.action !== "diagnose-only").map(
|
|
3720
|
+
surfaces: setupPlan.items.filter((item) => item.action !== "diagnose-only").map(surfaceForItem2),
|
|
2595
3721
|
backup: {
|
|
2596
3722
|
required: setupPlan.items.some((item) => item.requiresBackup),
|
|
2597
3723
|
strategy: "existing-helper",
|
|
2598
3724
|
description: "Codex setup apply uses the existing installer backup behavior for files that already exist."
|
|
2599
3725
|
},
|
|
2600
|
-
items: setupPlan.items.map(
|
|
3726
|
+
items: setupPlan.items.map(planItemFromSetup2),
|
|
2601
3727
|
warnings: [
|
|
2602
3728
|
...status.diagnostics,
|
|
2603
3729
|
...canApply ? [] : [
|
|
2604
|
-
|
|
3730
|
+
warning2(
|
|
2605
3731
|
`Codex state is ${status.state}; apply is disabled until the state is safely classified or repaired.`,
|
|
2606
3732
|
"codex-unsafe-state"
|
|
2607
3733
|
)
|
|
@@ -2617,7 +3743,7 @@ function planFromSetup(id, action, title, summary, setupPlan, context) {
|
|
|
2617
3743
|
}
|
|
2618
3744
|
function buildCodexUpdatePlan(context = { cwd: process.cwd() }) {
|
|
2619
3745
|
const setupPlan = buildCodexSetupPlan(codexConfig(context, true));
|
|
2620
|
-
return
|
|
3746
|
+
return planFromSetup2(
|
|
2621
3747
|
"codex-update-preview",
|
|
2622
3748
|
"update",
|
|
2623
3749
|
"Update Codex managed setup",
|
|
@@ -2628,7 +3754,7 @@ function buildCodexUpdatePlan(context = { cwd: process.cwd() }) {
|
|
|
2628
3754
|
}
|
|
2629
3755
|
function buildCodexSyncPlan(context = { cwd: process.cwd() }) {
|
|
2630
3756
|
const setupPlan = buildCodexSetupPlan(codexConfig(context, true));
|
|
2631
|
-
return
|
|
3757
|
+
return planFromSetup2(
|
|
2632
3758
|
"codex-sync-preview",
|
|
2633
3759
|
"sync",
|
|
2634
3760
|
"Sync Codex managed configuration",
|
|
@@ -2639,7 +3765,7 @@ function buildCodexSyncPlan(context = { cwd: process.cwd() }) {
|
|
|
2639
3765
|
}
|
|
2640
3766
|
function buildCodexInstallPlan(context = { cwd: process.cwd() }) {
|
|
2641
3767
|
const setupPlan = buildCodexSetupPlan(codexConfig(context, true));
|
|
2642
|
-
return
|
|
3768
|
+
return planFromSetup2(
|
|
2643
3769
|
"codex-install-preview",
|
|
2644
3770
|
"install",
|
|
2645
3771
|
"Install Codex managed setup",
|
|
@@ -2670,7 +3796,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
2670
3796
|
...status.diagnostics,
|
|
2671
3797
|
...input.warnings ?? [],
|
|
2672
3798
|
...unsupportedRoles.map(
|
|
2673
|
-
(role) =>
|
|
3799
|
+
(role) => warning2(
|
|
2674
3800
|
`Codex does not expose a supported generated model surface for ${role.role}; this plan will not write that role.`,
|
|
2675
3801
|
"codex-unsupported-model-role"
|
|
2676
3802
|
)
|
|
@@ -2678,7 +3804,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
2678
3804
|
];
|
|
2679
3805
|
if (input.harness !== "codex") {
|
|
2680
3806
|
warnings.push(
|
|
2681
|
-
|
|
3807
|
+
warning2(
|
|
2682
3808
|
"Model plan target harness must be codex.",
|
|
2683
3809
|
"codex-model-harness-mismatch"
|
|
2684
3810
|
)
|
|
@@ -2745,7 +3871,7 @@ function buildCodexModelPlan(input, context = { cwd: process.cwd() }) {
|
|
|
2745
3871
|
codexModelSources.set(plan, { config, roles: supportedRoles });
|
|
2746
3872
|
return plan;
|
|
2747
3873
|
}
|
|
2748
|
-
function
|
|
3874
|
+
function rejectPlan2(plan, message, severity = "critical") {
|
|
2749
3875
|
return {
|
|
2750
3876
|
harness: plan.harness,
|
|
2751
3877
|
action: plan.action,
|
|
@@ -2759,19 +3885,19 @@ function rejectPlan(plan, message, severity = "critical") {
|
|
|
2759
3885
|
}
|
|
2760
3886
|
function validateCodexPlan(plan) {
|
|
2761
3887
|
if (plan.harness !== "codex") {
|
|
2762
|
-
return
|
|
3888
|
+
return rejectPlan2(plan, "Only Codex operation plans can be applied.");
|
|
2763
3889
|
}
|
|
2764
3890
|
if (!plan.canApply) {
|
|
2765
|
-
return
|
|
3891
|
+
return rejectPlan2(
|
|
2766
3892
|
plan,
|
|
2767
3893
|
"Codex plan cannot be applied because canApply is false."
|
|
2768
3894
|
);
|
|
2769
3895
|
}
|
|
2770
3896
|
if (!["install", "update", "sync", "model-config"].includes(plan.action)) {
|
|
2771
|
-
return
|
|
3897
|
+
return rejectPlan2(plan, `Unsupported Codex apply action: ${plan.action}.`);
|
|
2772
3898
|
}
|
|
2773
3899
|
if (plan.items.length === 0) {
|
|
2774
|
-
return
|
|
3900
|
+
return rejectPlan2(plan, "Codex plan has no items to apply.");
|
|
2775
3901
|
}
|
|
2776
3902
|
return null;
|
|
2777
3903
|
}
|
|
@@ -2781,7 +3907,7 @@ function applyCodexPlan(plan) {
|
|
|
2781
3907
|
if (plan.action === "model-config") {
|
|
2782
3908
|
const source = codexModelSources.get(plan);
|
|
2783
3909
|
if (!source) {
|
|
2784
|
-
return
|
|
3910
|
+
return rejectPlan2(
|
|
2785
3911
|
plan,
|
|
2786
3912
|
"Codex model plan was not produced by buildCodexModelPlan in this process."
|
|
2787
3913
|
);
|
|
@@ -2792,13 +3918,13 @@ function applyCodexPlan(plan) {
|
|
|
2792
3918
|
action: "model-config",
|
|
2793
3919
|
applied: result2.success,
|
|
2794
3920
|
summary: result2.success ? "Applied Codex subagent model overrides." : result2.error ?? "Failed to apply Codex subagent model overrides.",
|
|
2795
|
-
changedTargets: result2.changed.map((
|
|
2796
|
-
kind:
|
|
2797
|
-
path:
|
|
2798
|
-
label:
|
|
3921
|
+
changedTargets: result2.changed.map((path4) => ({
|
|
3922
|
+
kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
|
|
3923
|
+
path: path4,
|
|
3924
|
+
label: basename3(path4),
|
|
2799
3925
|
state: "installed"
|
|
2800
3926
|
})),
|
|
2801
|
-
backups: result2.changed.filter((
|
|
3927
|
+
backups: result2.changed.filter((path4) => existsSync8(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
|
|
2802
3928
|
warnings: result2.success ? [] : [
|
|
2803
3929
|
{
|
|
2804
3930
|
severity: "critical",
|
|
@@ -2810,7 +3936,7 @@ function applyCodexPlan(plan) {
|
|
|
2810
3936
|
}
|
|
2811
3937
|
const setupPlan = codexPlanSources.get(plan);
|
|
2812
3938
|
if (!setupPlan) {
|
|
2813
|
-
return
|
|
3939
|
+
return rejectPlan2(
|
|
2814
3940
|
plan,
|
|
2815
3941
|
"Codex setup plan was not produced by a Codex operation plan builder in this process."
|
|
2816
3942
|
);
|
|
@@ -2821,13 +3947,13 @@ function applyCodexPlan(plan) {
|
|
|
2821
3947
|
action: plan.action,
|
|
2822
3948
|
applied: result.success,
|
|
2823
3949
|
summary: result.success ? `Applied Codex managed ${plan.action} plan.` : result.error ?? `Failed to apply Codex ${plan.action} plan.`,
|
|
2824
|
-
changedTargets: result.changed.map((
|
|
2825
|
-
kind:
|
|
2826
|
-
path:
|
|
2827
|
-
label:
|
|
3950
|
+
changedTargets: result.changed.map((path4) => ({
|
|
3951
|
+
kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
|
|
3952
|
+
path: path4,
|
|
3953
|
+
label: basename3(path4),
|
|
2828
3954
|
state: "installed"
|
|
2829
3955
|
})),
|
|
2830
|
-
backups: result.changed.filter((
|
|
3956
|
+
backups: result.changed.filter((path4) => existsSync8(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
|
|
2831
3957
|
warnings: result.success ? [] : [
|
|
2832
3958
|
{
|
|
2833
3959
|
severity: "critical",
|
|
@@ -2839,14 +3965,14 @@ function applyCodexPlan(plan) {
|
|
|
2839
3965
|
}
|
|
2840
3966
|
|
|
2841
3967
|
// src/cli/operations/opencode.ts
|
|
2842
|
-
import { existsSync as
|
|
2843
|
-
import { join as
|
|
3968
|
+
import { existsSync as existsSync10 } from "fs";
|
|
3969
|
+
import { join as join10 } from "path";
|
|
2844
3970
|
|
|
2845
3971
|
// src/cli/skills.ts
|
|
2846
3972
|
import { spawnSync } from "child_process";
|
|
2847
|
-
import { existsSync as
|
|
2848
|
-
import { homedir as
|
|
2849
|
-
import { join as
|
|
3973
|
+
import { existsSync as existsSync9 } from "fs";
|
|
3974
|
+
import { homedir as homedir3 } from "os";
|
|
3975
|
+
import { join as join9 } from "path";
|
|
2850
3976
|
var RECOMMENDED_SKILLS = [
|
|
2851
3977
|
{
|
|
2852
3978
|
name: "simplify",
|
|
@@ -2861,11 +3987,11 @@ var RECOMMENDED_SKILLS = [
|
|
|
2861
3987
|
description: "Browser automation for visual checks and testing"
|
|
2862
3988
|
}
|
|
2863
3989
|
];
|
|
2864
|
-
function getRecommendedSkillPath(skill, homeDir =
|
|
2865
|
-
return
|
|
3990
|
+
function getRecommendedSkillPath(skill, homeDir = homedir3()) {
|
|
3991
|
+
return join9(homeDir, ".agents", "skills", skill.skillName, "SKILL.md");
|
|
2866
3992
|
}
|
|
2867
3993
|
function isRecommendedSkillInstalled(skill, options = {}) {
|
|
2868
|
-
return
|
|
3994
|
+
return existsSync9(getRecommendedSkillPath(skill, options.homeDir));
|
|
2869
3995
|
}
|
|
2870
3996
|
function runSkillInstallCommand(skill) {
|
|
2871
3997
|
const args = [
|
|
@@ -3019,11 +4145,11 @@ function openCodeSkillTargets(context) {
|
|
|
3019
4145
|
const homeDir = homeDirFromContext(context);
|
|
3020
4146
|
const recommendedTargets = RECOMMENDED_SKILLS.map(
|
|
3021
4147
|
(skill) => {
|
|
3022
|
-
const
|
|
3023
|
-
const installed =
|
|
4148
|
+
const path4 = getRecommendedSkillPath(skill, homeDir);
|
|
4149
|
+
const installed = existsSync10(path4);
|
|
3024
4150
|
return {
|
|
3025
4151
|
kind: "skill",
|
|
3026
|
-
path:
|
|
4152
|
+
path: path4,
|
|
3027
4153
|
label: titleCaseSkillName(skill.skillName),
|
|
3028
4154
|
state: installed ? "installed" : "missing",
|
|
3029
4155
|
expected: "recommended global OpenCode skill",
|
|
@@ -3032,11 +4158,11 @@ function openCodeSkillTargets(context) {
|
|
|
3032
4158
|
}
|
|
3033
4159
|
);
|
|
3034
4160
|
const bundledTargets = CUSTOM_SKILLS.map((skill) => {
|
|
3035
|
-
const
|
|
3036
|
-
const installed =
|
|
4161
|
+
const path4 = join10(getCustomSkillsDir(), skill.name, "SKILL.md");
|
|
4162
|
+
const installed = existsSync10(path4);
|
|
3037
4163
|
return {
|
|
3038
4164
|
kind: "skill",
|
|
3039
|
-
path:
|
|
4165
|
+
path: path4,
|
|
3040
4166
|
label: titleCaseSkillName(skill.name),
|
|
3041
4167
|
state: installed ? "installed" : "missing",
|
|
3042
4168
|
expected: "bundled thoth-agents OpenCode skill",
|
|
@@ -3139,8 +4265,8 @@ function getOpenCodeStatus(context = { cwd: process.cwd() }) {
|
|
|
3139
4265
|
actions: openCodeActions
|
|
3140
4266
|
};
|
|
3141
4267
|
}
|
|
3142
|
-
const mainExists =
|
|
3143
|
-
const liteExists =
|
|
4268
|
+
const mainExists = existsSync10(mainPath);
|
|
4269
|
+
const liteExists = existsSync10(litePath);
|
|
3144
4270
|
const mainTarget = {
|
|
3145
4271
|
...targetForMainConfig(),
|
|
3146
4272
|
observed: configPluginMarker(main.config)
|
|
@@ -3237,11 +4363,11 @@ function getOpenCodeStatus(context = { cwd: process.cwd() }) {
|
|
|
3237
4363
|
actions: openCodeActions
|
|
3238
4364
|
};
|
|
3239
4365
|
}
|
|
3240
|
-
function defaultBackup(
|
|
4366
|
+
function defaultBackup(path4) {
|
|
3241
4367
|
return {
|
|
3242
4368
|
required: true,
|
|
3243
4369
|
strategy: "managed-backup-file",
|
|
3244
|
-
destinations: [{ path: `${
|
|
4370
|
+
destinations: [{ path: `${path4}.bak`, label: "managed backup" }]
|
|
3245
4371
|
};
|
|
3246
4372
|
}
|
|
3247
4373
|
function defaultDisclaimers() {
|
|
@@ -3293,7 +4419,7 @@ function planFromItems(id, action, title, summary, items) {
|
|
|
3293
4419
|
};
|
|
3294
4420
|
}
|
|
3295
4421
|
function buildOpenCodeUpdatePlan(_context = { cwd: process.cwd() }) {
|
|
3296
|
-
const
|
|
4422
|
+
const path4 = getExistingConfigPath();
|
|
3297
4423
|
return planFromItems(
|
|
3298
4424
|
"opencode-update-preview",
|
|
3299
4425
|
"update",
|
|
@@ -3305,7 +4431,7 @@ function buildOpenCodeUpdatePlan(_context = { cwd: process.cwd() }) {
|
|
|
3305
4431
|
target: targetForMainConfig(),
|
|
3306
4432
|
state: getOpenCodeStatus().state,
|
|
3307
4433
|
preview: `plugin: ["${EXPECTED_PLUGIN}"]`,
|
|
3308
|
-
backup: defaultBackup(
|
|
4434
|
+
backup: defaultBackup(path4)
|
|
3309
4435
|
}
|
|
3310
4436
|
]
|
|
3311
4437
|
);
|
|
@@ -3472,7 +4598,7 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
3472
4598
|
}
|
|
3473
4599
|
return plan;
|
|
3474
4600
|
}
|
|
3475
|
-
function
|
|
4601
|
+
function rejectPlan3(plan, message, severity = "critical") {
|
|
3476
4602
|
return {
|
|
3477
4603
|
harness: plan.harness,
|
|
3478
4604
|
action: plan.action,
|
|
@@ -3516,27 +4642,27 @@ function ensureLatestPluginEntry() {
|
|
|
3516
4642
|
}
|
|
3517
4643
|
function validateApplyPlan(plan) {
|
|
3518
4644
|
if (plan.harness !== "opencode") {
|
|
3519
|
-
return
|
|
4645
|
+
return rejectPlan3(plan, "Only OpenCode operation plans can be applied.");
|
|
3520
4646
|
}
|
|
3521
4647
|
if (!plan.canApply) {
|
|
3522
|
-
return
|
|
4648
|
+
return rejectPlan3(
|
|
3523
4649
|
plan,
|
|
3524
4650
|
"OpenCode plan cannot be applied because canApply is false."
|
|
3525
4651
|
);
|
|
3526
4652
|
}
|
|
3527
4653
|
if (!["install", "update", "sync", "model-config"].includes(plan.action)) {
|
|
3528
|
-
return
|
|
4654
|
+
return rejectPlan3(
|
|
3529
4655
|
plan,
|
|
3530
4656
|
`Unsupported OpenCode apply action: ${plan.action}.`
|
|
3531
4657
|
);
|
|
3532
4658
|
}
|
|
3533
4659
|
if (plan.items.length === 0) {
|
|
3534
|
-
return
|
|
4660
|
+
return rejectPlan3(plan, "OpenCode plan has no items to apply.");
|
|
3535
4661
|
}
|
|
3536
4662
|
if (plan.items.some(
|
|
3537
4663
|
(item) => !item.title || !item.target.path && item.target.kind !== "skill" || item.state === "drift" || item.state === "unknown"
|
|
3538
4664
|
)) {
|
|
3539
|
-
return
|
|
4665
|
+
return rejectPlan3(
|
|
3540
4666
|
plan,
|
|
3541
4667
|
"OpenCode plan contains malformed or unsafe items."
|
|
3542
4668
|
);
|
|
@@ -3548,7 +4674,7 @@ function applyModelPlan(plan) {
|
|
|
3548
4674
|
for (const item of plan.items) {
|
|
3549
4675
|
const match = /^Set (.+) OpenCode model override$/.exec(item.title);
|
|
3550
4676
|
if (!match) {
|
|
3551
|
-
return
|
|
4677
|
+
return rejectPlan3(
|
|
3552
4678
|
plan,
|
|
3553
4679
|
"OpenCode model plan contains an unrecognized item."
|
|
3554
4680
|
);
|
|
@@ -3557,7 +4683,7 @@ function applyModelPlan(plan) {
|
|
|
3557
4683
|
try {
|
|
3558
4684
|
parsed2 = item.preview ? JSON.parse(item.preview) : {};
|
|
3559
4685
|
} catch {
|
|
3560
|
-
return
|
|
4686
|
+
return rejectPlan3(
|
|
3561
4687
|
plan,
|
|
3562
4688
|
"OpenCode model plan contains malformed preview JSON."
|
|
3563
4689
|
);
|
|
@@ -3565,7 +4691,7 @@ function applyModelPlan(plan) {
|
|
|
3565
4691
|
const role = match[1] ?? "";
|
|
3566
4692
|
const model = parsed2[role]?.model;
|
|
3567
4693
|
if (!ROLE_NAMES.includes(role) || typeof model !== "string") {
|
|
3568
|
-
return
|
|
4694
|
+
return rejectPlan3(
|
|
3569
4695
|
plan,
|
|
3570
4696
|
"OpenCode model plan contains an invalid role or model."
|
|
3571
4697
|
);
|
|
@@ -3573,7 +4699,7 @@ function applyModelPlan(plan) {
|
|
|
3573
4699
|
roleModels.set(role, model);
|
|
3574
4700
|
}
|
|
3575
4701
|
if (roleModels.size === 0) {
|
|
3576
|
-
return
|
|
4702
|
+
return rejectPlan3(
|
|
3577
4703
|
plan,
|
|
3578
4704
|
"OpenCode model plan does not contain any role overrides."
|
|
3579
4705
|
);
|
|
@@ -3609,7 +4735,7 @@ function applyModelPlan(plan) {
|
|
|
3609
4735
|
observed: "agents role overrides updated"
|
|
3610
4736
|
}
|
|
3611
4737
|
],
|
|
3612
|
-
backups:
|
|
4738
|
+
backups: existsSync10(`${targetPath}.bak`) ? [{ path: `${targetPath}.bak`, label: "managed backup" }] : [],
|
|
3613
4739
|
warnings: [],
|
|
3614
4740
|
disclaimers: defaultDisclaimers()
|
|
3615
4741
|
};
|
|
@@ -3618,7 +4744,7 @@ function applyInstallSkills(plan) {
|
|
|
3618
4744
|
for (const skill of RECOMMENDED_SKILLS) {
|
|
3619
4745
|
const result = installRecommendedSkill(skill);
|
|
3620
4746
|
if (result.status === "failed") {
|
|
3621
|
-
return
|
|
4747
|
+
return rejectPlan3(
|
|
3622
4748
|
plan,
|
|
3623
4749
|
`Failed to install recommended OpenCode skill: ${skill.name}.`
|
|
3624
4750
|
);
|
|
@@ -3626,7 +4752,7 @@ function applyInstallSkills(plan) {
|
|
|
3626
4752
|
}
|
|
3627
4753
|
const bundled = installCustomSkills();
|
|
3628
4754
|
if (!bundled.success) {
|
|
3629
|
-
return
|
|
4755
|
+
return rejectPlan3(
|
|
3630
4756
|
plan,
|
|
3631
4757
|
"Failed to install bundled thoth-agents OpenCode skills."
|
|
3632
4758
|
);
|
|
@@ -3638,7 +4764,7 @@ function applyOpenCodePlan(plan) {
|
|
|
3638
4764
|
if (rejection) return rejection;
|
|
3639
4765
|
const status = getOpenCodeStatus();
|
|
3640
4766
|
if (!classifyApplySafety(status.state)) {
|
|
3641
|
-
return
|
|
4767
|
+
return rejectPlan3(
|
|
3642
4768
|
plan,
|
|
3643
4769
|
`OpenCode state is ${status.state}; refusing to apply plan without a safe status.`
|
|
3644
4770
|
);
|
|
@@ -3648,7 +4774,7 @@ function applyOpenCodePlan(plan) {
|
|
|
3648
4774
|
const backups = [];
|
|
3649
4775
|
const pluginResult = ensureLatestPluginEntry();
|
|
3650
4776
|
if (!pluginResult.success) {
|
|
3651
|
-
return
|
|
4777
|
+
return rejectPlan3(
|
|
3652
4778
|
plan,
|
|
3653
4779
|
pluginResult.error ?? "Failed to update OpenCode plugin config."
|
|
3654
4780
|
);
|
|
@@ -3657,7 +4783,7 @@ function applyOpenCodePlan(plan) {
|
|
|
3657
4783
|
...targetForMainConfig("installed"),
|
|
3658
4784
|
observed: `plugin includes ${EXPECTED_PLUGIN}`
|
|
3659
4785
|
});
|
|
3660
|
-
if (
|
|
4786
|
+
if (existsSync10(`${pluginResult.configPath}.bak`)) {
|
|
3661
4787
|
backups.push({
|
|
3662
4788
|
path: `${pluginResult.configPath}.bak`,
|
|
3663
4789
|
label: "OpenCode config backup"
|
|
@@ -3666,7 +4792,7 @@ function applyOpenCodePlan(plan) {
|
|
|
3666
4792
|
if (plan.action === "sync" || plan.action === "install") {
|
|
3667
4793
|
const defaultAgentResult = disableDefaultAgents();
|
|
3668
4794
|
if (!defaultAgentResult.success) {
|
|
3669
|
-
return
|
|
4795
|
+
return rejectPlan3(
|
|
3670
4796
|
plan,
|
|
3671
4797
|
defaultAgentResult.error ?? "Failed to disable OpenCode default agents."
|
|
3672
4798
|
);
|
|
@@ -3683,7 +4809,7 @@ function applyOpenCodePlan(plan) {
|
|
|
3683
4809
|
getExistingLiteConfigPath()
|
|
3684
4810
|
);
|
|
3685
4811
|
if (!liteResult.success) {
|
|
3686
|
-
return
|
|
4812
|
+
return rejectPlan3(
|
|
3687
4813
|
plan,
|
|
3688
4814
|
liteResult.error ?? "Failed to write thoth-agents config."
|
|
3689
4815
|
);
|
|
@@ -3692,7 +4818,7 @@ function applyOpenCodePlan(plan) {
|
|
|
3692
4818
|
...targetForLiteConfig("installed"),
|
|
3693
4819
|
observed: "seven-agent roster written"
|
|
3694
4820
|
});
|
|
3695
|
-
if (
|
|
4821
|
+
if (existsSync10(`${liteResult.configPath}.bak`)) {
|
|
3696
4822
|
backups.push({
|
|
3697
4823
|
path: `${liteResult.configPath}.bak`,
|
|
3698
4824
|
label: "thoth-agents config backup"
|
|
@@ -3724,7 +4850,8 @@ function applyOpenCodePlan(plan) {
|
|
|
3724
4850
|
// src/cli/operations/index.ts
|
|
3725
4851
|
var OPERATION_HARNESSES = {
|
|
3726
4852
|
opencode: opencodeOperationAdapter,
|
|
3727
|
-
codex: codexOperationAdapter
|
|
4853
|
+
codex: codexOperationAdapter,
|
|
4854
|
+
claude: claudeCodeOperationAdapter
|
|
3728
4855
|
};
|
|
3729
4856
|
var SUPPORTED_OPERATION_HARNESSES = Object.keys(
|
|
3730
4857
|
OPERATION_HARNESSES
|
|
@@ -3739,14 +4866,27 @@ function getOperationHarness(harness) {
|
|
|
3739
4866
|
}
|
|
3740
4867
|
|
|
3741
4868
|
export {
|
|
4869
|
+
claudeCodeAdapter,
|
|
3742
4870
|
codexAdapter,
|
|
3743
4871
|
CODEX_ROLE_NAMES,
|
|
3744
4872
|
parseRoleTomlModel,
|
|
3745
4873
|
buildCodexSetupPlan,
|
|
3746
4874
|
formatCodexSetupPlan,
|
|
3747
4875
|
applyCodexSetup,
|
|
4876
|
+
CLAUDE_CODE_ROLE_NAMES,
|
|
4877
|
+
parseSubagentModel,
|
|
4878
|
+
buildClaudeCodeSetupPlan,
|
|
4879
|
+
applyClaudeCodeSetup,
|
|
4880
|
+
formatClaudeCodeSetupPlan,
|
|
3748
4881
|
RECOMMENDED_SKILLS,
|
|
3749
4882
|
installRecommendedSkill,
|
|
4883
|
+
getClaudeCodeStatus,
|
|
4884
|
+
buildClaudeCodeInstallPlan,
|
|
4885
|
+
buildClaudeCodeUpdatePlan,
|
|
4886
|
+
buildClaudeCodeSyncPlan,
|
|
4887
|
+
buildClaudeCodeModelPlan,
|
|
4888
|
+
applyClaudeCodePlan,
|
|
4889
|
+
defaultClaudeCodeModelRoles,
|
|
3750
4890
|
getCodexStatus,
|
|
3751
4891
|
buildCodexUpdatePlan,
|
|
3752
4892
|
buildCodexSyncPlan,
|