thoth-agents 0.2.11 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +20 -0
- package/.claude-plugin/marketplace.json +21 -0
- package/README.md +301 -311
- package/dist/agents/configured-role-prompt.d.ts +9 -0
- package/dist/agents/prompt-sections.d.ts +8 -4
- package/dist/agents/prompt-utils.d.ts +0 -4
- package/dist/{chunk-ASKXWU6E.js → chunk-2MZ5C2RN.js} +1078 -1725
- package/dist/chunk-XO74DXBZ.js +1781 -0
- package/dist/{chunk-6BNAV534.js → chunk-YUCGIJPP.js} +12 -30
- package/dist/cli/claude-code-install.d.ts +24 -26
- package/dist/cli/claude-code-paths.d.ts +0 -28
- package/dist/cli/codex-install.d.ts +2 -2
- package/dist/cli/codex-paths.d.ts +0 -2
- package/dist/cli/index.js +279 -119
- package/dist/cli/install.d.ts +5 -1
- package/dist/cli/model-catalog/cache.d.ts +1 -1
- package/dist/cli/operations/claude-code.d.ts +6 -3
- package/dist/cli/operations/codex.d.ts +2 -1
- package/dist/cli/operations/opencode.d.ts +2 -1
- package/dist/cli/operations/types.d.ts +9 -1
- package/dist/cli/package-root.d.ts +1 -0
- package/dist/cli/providers.d.ts +6 -116
- package/dist/cli/skills.d.ts +19 -32
- package/dist/cli/thoth-mem-install.d.ts +45 -0
- package/dist/cli/tui/components/StatusView.d.ts +12 -1
- package/dist/cli/tui/index.js +256 -142
- package/dist/cli/tui/operations.d.ts +4 -3
- package/dist/cli/types.d.ts +0 -10
- package/dist/config/constants.d.ts +20 -16
- package/dist/config/index.d.ts +0 -1
- package/dist/config/loader.d.ts +1 -1
- package/dist/config/schema.d.ts +2 -38
- package/dist/config/utils.d.ts +1 -2
- package/dist/harness/adapters/codex-surfaces.d.ts +3 -3
- package/dist/harness/core/agent-pack.d.ts +37 -29
- package/dist/harness/core/memory-governance.d.ts +24 -48
- package/dist/harness/core/sdd.d.ts +364 -131
- package/dist/harness/generate-integration-packages.d.ts +20 -0
- package/dist/harness/opencode-init-command.d.ts +11 -0
- package/dist/harness/types.d.ts +14 -1
- package/dist/harness/writers/claude-code-subagent.d.ts +3 -1
- package/dist/hooks/index.d.ts +0 -5
- package/dist/index.js +562 -1314
- package/dist/mcp/index.d.ts +1 -2
- package/package.json +17 -12
- package/plugin/.claude-plugin/.thoth-agents-plugin-assets.json +50 -0
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/.codex-plugin/.thoth-agents-plugin-assets.json +16 -0
- package/plugin/.codex-plugin/plugin.json +7 -0
- package/plugin/.mcp.json +19 -0
- package/plugin/agents/deep.md +89 -0
- package/plugin/agents/designer.md +89 -0
- package/plugin/agents/explorer.md +90 -0
- package/plugin/agents/librarian.md +90 -0
- package/plugin/agents/oracle.md +92 -0
- package/plugin/agents/orchestrator.md +128 -0
- package/plugin/agents/quick.md +89 -0
- package/plugin/codex.mcp.json +17 -0
- package/plugin/settings.json +3 -0
- package/plugin/skills/thoth-archive/SKILL.md +40 -0
- package/plugin/skills/thoth-archive/scripts/archive.mjs +748 -0
- package/plugin/skills/thoth-constitution/SKILL.md +45 -0
- package/plugin/skills/thoth-constitution/scripts/validate.mjs +167 -0
- package/plugin/skills/thoth-constitution/templates/constitution.md +54 -0
- package/plugin/skills/thoth-init/SKILL.md +30 -0
- package/plugin/skills/thoth-init/scripts/init.mjs +141 -0
- package/plugin/skills/thoth-sdd/SKILL.md +83 -0
- package/plugin/skills/thoth-sdd/references/phases/analyze.md +21 -0
- package/plugin/skills/thoth-sdd/references/phases/checklist.md +20 -0
- package/plugin/skills/thoth-sdd/references/phases/clarify.md +15 -0
- package/plugin/skills/thoth-sdd/references/phases/converge.md +14 -0
- package/plugin/skills/thoth-sdd/references/phases/explore.md +10 -0
- package/plugin/skills/thoth-sdd/references/phases/implement.md +19 -0
- package/plugin/skills/thoth-sdd/references/phases/plan.md +15 -0
- package/plugin/skills/thoth-sdd/references/phases/specify.md +21 -0
- package/plugin/skills/thoth-sdd/references/phases/tasks.md +20 -0
- package/plugin/skills/thoth-sdd/references/phases/verify.md +22 -0
- package/plugin/skills/thoth-sdd/scripts/validate.mjs +1117 -0
- package/plugin/skills/thoth-sdd/templates/archive-report.md +25 -0
- package/plugin/skills/thoth-sdd/templates/checklist.md +27 -0
- package/plugin/skills/thoth-sdd/templates/plan.md +38 -0
- package/plugin/skills/thoth-sdd/templates/spec.md +54 -0
- package/plugin/skills/thoth-sdd/templates/tasks.md +29 -0
- package/plugin/skills/thoth-sdd/templates/verify-report.md +28 -0
- package/skills/README.md +12 -0
- package/skills/thoth-archive/SKILL.md +40 -0
- package/skills/thoth-archive/scripts/archive.mjs +748 -0
- package/skills/thoth-constitution/SKILL.md +45 -0
- package/skills/thoth-constitution/scripts/validate.mjs +167 -0
- package/skills/thoth-constitution/templates/constitution.md +54 -0
- package/skills/thoth-init/SKILL.md +30 -0
- package/skills/thoth-init/scripts/init.mjs +141 -0
- package/skills/thoth-sdd/SKILL.md +83 -0
- package/skills/thoth-sdd/references/phases/analyze.md +21 -0
- package/skills/thoth-sdd/references/phases/checklist.md +20 -0
- package/skills/thoth-sdd/references/phases/clarify.md +15 -0
- package/skills/thoth-sdd/references/phases/converge.md +14 -0
- package/skills/thoth-sdd/references/phases/explore.md +10 -0
- package/skills/thoth-sdd/references/phases/implement.md +19 -0
- package/skills/thoth-sdd/references/phases/plan.md +15 -0
- package/skills/thoth-sdd/references/phases/specify.md +21 -0
- package/skills/thoth-sdd/references/phases/tasks.md +20 -0
- package/skills/thoth-sdd/references/phases/verify.md +22 -0
- package/skills/thoth-sdd/scripts/validate.mjs +1117 -0
- package/skills/thoth-sdd/templates/archive-report.md +25 -0
- package/skills/thoth-sdd/templates/checklist.md +27 -0
- package/skills/thoth-sdd/templates/plan.md +38 -0
- package/skills/thoth-sdd/templates/spec.md +54 -0
- package/skills/thoth-sdd/templates/tasks.md +29 -0
- package/skills/thoth-sdd/templates/verify-report.md +28 -0
- package/thoth-agents.schema.json +0 -39
- package/dist/chunk-OZE7JVGS.js +0 -2344
- package/dist/cli/custom-skills.d.ts +0 -49
- package/dist/cli/skill-manifest.d.ts +0 -32
- package/dist/harness/core/skills.d.ts +0 -211
- package/dist/harness/generate-codex-plugin.d.ts +0 -9
- package/dist/harness/writers/claude-code-skill-layout.d.ts +0 -16
- package/dist/harness/writers/fs-skill-collect.d.ts +0 -7
- package/dist/harness/writers/skill-layout.d.ts +0 -16
- package/dist/hooks/chat-headers.d.ts +0 -16
- package/dist/hooks/phase-reminder/index.d.ts +0 -28
- package/dist/hooks/post-read-nudge/index.d.ts +0 -18
- package/dist/hooks/skill-sync.d.ts +0 -10
- package/dist/hooks/thoth-mem/index.d.ts +0 -46
- package/dist/hooks/thoth-mem/protocol.d.ts +0 -7
- package/dist/mcp/thoth.d.ts +0 -3
- package/dist/sdd/artifact-governance/artifact-loader.d.ts +0 -130
- package/dist/sdd/artifact-governance/index.d.ts +0 -6
- package/dist/sdd/artifact-governance/tasks-validator.d.ts +0 -17
- package/dist/sdd/artifact-governance/types.d.ts +0 -52
- package/dist/thoth/client.d.ts +0 -14
- package/dist/thoth/index.d.ts +0 -2
- package/src/skills/_shared/openspec-convention.md +0 -350
- package/src/skills/_shared/persistence-contract.md +0 -163
- package/src/skills/_shared/thoth-mem-convention.md +0 -123
- package/src/skills/executing-plans/SKILL.md +0 -288
- package/src/skills/plan-reviewer/SKILL.md +0 -219
- package/src/skills/requirements-interview/SKILL.md +0 -233
- package/src/skills/sdd-apply/SKILL.md +0 -89
- package/src/skills/sdd-archive/SKILL.md +0 -92
- package/src/skills/sdd-clarify/SKILL.md +0 -105
- package/src/skills/sdd-constitution/SKILL.md +0 -107
- package/src/skills/sdd-design/SKILL.md +0 -155
- package/src/skills/sdd-init/SKILL.md +0 -233
- package/src/skills/sdd-propose/SKILL.md +0 -101
- package/src/skills/sdd-spec/SKILL.md +0 -136
- package/src/skills/sdd-tasks/SKILL.md +0 -232
- package/src/skills/sdd-verify/SKILL.md +0 -131
- package/src/skills/thoth-mem-agents/SKILL.md +0 -210
|
@@ -5,31 +5,25 @@ import {
|
|
|
5
5
|
CODEX_PROMPT_DIALECT,
|
|
6
6
|
CONFIRMED_OPENAI_SUBAGENT_PRESET,
|
|
7
7
|
CONTEXT7_MCP_URL,
|
|
8
|
-
CUSTOM_SKILLS,
|
|
9
8
|
DEFAULT_MODELS,
|
|
10
|
-
DEFAULT_THOTH_COMMAND,
|
|
11
9
|
GREP_APP_MCP_URL,
|
|
12
10
|
appendPromptSections,
|
|
13
|
-
checkCustomSkillsNeedUpdate,
|
|
14
11
|
claudeCodeSubagentType,
|
|
15
12
|
composeAgentPrompt,
|
|
16
13
|
createModelFamilySection,
|
|
17
|
-
|
|
18
|
-
createReadOnlySpecialistPromptSections,
|
|
14
|
+
createRolePromptSections,
|
|
19
15
|
createStepBudgetSection,
|
|
20
|
-
createWriteCapableSpecialistPromptSections,
|
|
21
16
|
ensureConfigDir,
|
|
22
17
|
exa,
|
|
23
|
-
findPackageRoot,
|
|
24
18
|
generateLiteConfig,
|
|
25
19
|
getAgentOverride,
|
|
26
|
-
|
|
20
|
+
getAgentPackContract,
|
|
21
|
+
getDefaultOpenCodeModel,
|
|
22
|
+
getDefaultOpenCodeVariant,
|
|
27
23
|
getExistingConfigPath,
|
|
28
24
|
getExistingLiteConfigPath,
|
|
29
25
|
getOpenCodeManagedModelStatePath,
|
|
30
26
|
getPrimaryModelId,
|
|
31
|
-
getSkillRegistry,
|
|
32
|
-
installCustomSkills,
|
|
33
27
|
loadAgentPrompt,
|
|
34
28
|
parseConfig,
|
|
35
29
|
renderPromptSection,
|
|
@@ -37,7 +31,7 @@ import {
|
|
|
37
31
|
updateOpenCodeMainConfig,
|
|
38
32
|
writeConfig,
|
|
39
33
|
writeLiteConfig
|
|
40
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-XO74DXBZ.js";
|
|
41
35
|
|
|
42
36
|
// src/cli/codex-paths.ts
|
|
43
37
|
import { homedir } from "os";
|
|
@@ -72,14 +66,7 @@ function resolveCodexTargets(options) {
|
|
|
72
66
|
".thoth-agents-managed-models.json"
|
|
73
67
|
),
|
|
74
68
|
skillsDir: options.scope === "project" ? join(options.projectRoot, ".agents", "skills") : join(options.homeDir ?? homedir(), ".agents", "skills"),
|
|
75
|
-
packageRoot: join(options.projectRoot, ".codex-plugin")
|
|
76
|
-
personalPluginRoot: join(codexHome, "plugins", "thoth-agents"),
|
|
77
|
-
personalMarketplacePath: join(
|
|
78
|
-
options.homeDir ?? homedir(),
|
|
79
|
-
".agents",
|
|
80
|
-
"plugins",
|
|
81
|
-
"marketplace.json"
|
|
82
|
-
)
|
|
69
|
+
packageRoot: join(options.projectRoot, ".codex-plugin")
|
|
83
70
|
};
|
|
84
71
|
}
|
|
85
72
|
|
|
@@ -96,234 +83,80 @@ function isExplicitEffort(selection) {
|
|
|
96
83
|
}
|
|
97
84
|
|
|
98
85
|
// src/cli/codex-install.ts
|
|
99
|
-
import { existsSync as existsSync5, readFileSync as
|
|
100
|
-
import { basename
|
|
86
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
87
|
+
import { basename } from "path";
|
|
101
88
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
102
89
|
|
|
103
90
|
// src/harness/adapters/codex.ts
|
|
104
91
|
import { fileURLToPath } from "url";
|
|
105
92
|
|
|
106
|
-
// src/
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
scope: "local repository discovery",
|
|
131
|
-
responsibility: "Find workspace facts fast and return paths, lines, symbols, constraints, edit targets, and conclusions.",
|
|
132
|
-
toolGovernance: [
|
|
133
|
-
"read/search/code-navigation tools only",
|
|
134
|
-
"no durable memory writes",
|
|
135
|
-
"no task delegation or progress ownership"
|
|
136
|
-
],
|
|
137
|
-
verification: ["reports confidence, anchors, unchecked areas, and gaps"]
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
name: "librarian",
|
|
141
|
-
mode: "read-only",
|
|
142
|
-
dispatch: "task",
|
|
143
|
-
canMutateWorkspace: false,
|
|
144
|
-
scope: "external research plus local confirmation when needed",
|
|
145
|
-
responsibility: "Gather authoritative external evidence and distinguish official docs from examples.",
|
|
146
|
-
toolGovernance: [
|
|
147
|
-
"research and read-only local confirmation tools",
|
|
148
|
-
"no workspace mutation",
|
|
149
|
-
"no durable memory writes"
|
|
150
|
-
],
|
|
151
|
-
verification: ["sources every substantive external claim with a URL"]
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
name: "oracle",
|
|
155
|
-
mode: "read-only",
|
|
156
|
-
dispatch: "synchronous-task-only",
|
|
157
|
-
canMutateWorkspace: false,
|
|
158
|
-
scope: "advice, diagnosis, architecture, code review, and plan review",
|
|
159
|
-
responsibility: "Provide strategic technical guidance anchored to evidence and review SDD plans.",
|
|
160
|
-
toolGovernance: [
|
|
161
|
-
"read-only analysis and review",
|
|
162
|
-
"no implementation; may perform read-only SDD plan review and verification review, but does not persist artifacts",
|
|
163
|
-
"no task delegation"
|
|
164
|
-
],
|
|
165
|
-
verification: ["separates observations, risks, and recommendations"]
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
name: "designer",
|
|
169
|
-
mode: "write-capable",
|
|
170
|
-
dispatch: "synchronous-task-only",
|
|
171
|
-
canMutateWorkspace: true,
|
|
172
|
-
scope: "UI/UX decisions, implementation, and visual verification",
|
|
173
|
-
responsibility: "Own user-facing implementation choices and visual QA for UI work.",
|
|
174
|
-
toolGovernance: [
|
|
175
|
-
"may edit focused UI/UX files",
|
|
176
|
-
"owns screenshots and visual QA",
|
|
177
|
-
"does not delegate or own SDD progress"
|
|
178
|
-
],
|
|
179
|
-
verification: ["includes visual verification status when applicable"]
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
name: "quick",
|
|
183
|
-
mode: "write-capable",
|
|
184
|
-
dispatch: "synchronous-task-only",
|
|
185
|
-
canMutateWorkspace: true,
|
|
186
|
-
scope: "fast bounded implementation",
|
|
187
|
-
responsibility: "Implement well-defined narrow or mechanical changes with focused verification.",
|
|
188
|
-
toolGovernance: [
|
|
189
|
-
"may edit bounded targets",
|
|
190
|
-
"does not perform broad rediscovery",
|
|
191
|
-
"does not delegate or own SDD progress"
|
|
192
|
-
],
|
|
193
|
-
verification: ["runs the smallest sufficient focused check"]
|
|
194
|
-
},
|
|
195
|
-
{
|
|
196
|
-
name: "deep",
|
|
197
|
-
mode: "write-capable",
|
|
198
|
-
dispatch: "synchronous-task-only",
|
|
199
|
-
canMutateWorkspace: true,
|
|
200
|
-
scope: "thorough implementation and verification",
|
|
201
|
-
responsibility: "Handle correctness-critical, multi-file, or edge-case-heavy changes with full local context analysis.",
|
|
202
|
-
toolGovernance: [
|
|
203
|
-
"may edit implementation and tests",
|
|
204
|
-
"validates shared behavior against related code and call sites",
|
|
205
|
-
"does not delegate or own SDD progress"
|
|
206
|
-
],
|
|
207
|
-
verification: ["does not skip verification and reports edge-case evidence"]
|
|
208
|
-
}
|
|
209
|
-
];
|
|
210
|
-
var DELEGATE_FIRST_RULES = [
|
|
211
|
-
"The orchestrator coordinates, decides, asks blocking questions, and delegates evidence or action.",
|
|
212
|
-
"Explorer, librarian, and oracle remain read-only specialists.",
|
|
213
|
-
"Designer, quick, and deep are write-capable leaf agents with synchronous task dispatch.",
|
|
214
|
-
"Subagents return findings, diffs, verification, and blockers rather than raw file dumps.",
|
|
215
|
-
"No leaf agent owns SDD progress checkboxes or orchestrator-only memory."
|
|
216
|
-
];
|
|
217
|
-
var VERIFICATION_PROTOCOL = [
|
|
218
|
-
"Completion reports include changed files and verification evidence.",
|
|
219
|
-
"Behavior changes require the smallest sufficient automated check or an explicitly documented check.",
|
|
220
|
-
"Visual changes require designer-owned visual QA when feasible."
|
|
221
|
-
];
|
|
222
|
-
var AGENT_PACK_CONTRACT = {
|
|
223
|
-
roles: [...AGENT_ROLES],
|
|
224
|
-
delegateFirstRules: [...DELEGATE_FIRST_RULES],
|
|
225
|
-
verificationProtocol: [...VERIFICATION_PROTOCOL]
|
|
226
|
-
};
|
|
227
|
-
function getAgentPackContract() {
|
|
228
|
-
return {
|
|
229
|
-
roles: AGENT_PACK_CONTRACT.roles.map((role) => ({
|
|
230
|
-
...role,
|
|
231
|
-
toolGovernance: [...role.toolGovernance],
|
|
232
|
-
verification: [...role.verification]
|
|
233
|
-
})),
|
|
234
|
-
delegateFirstRules: [...AGENT_PACK_CONTRACT.delegateFirstRules],
|
|
235
|
-
verificationProtocol: [...AGENT_PACK_CONTRACT.verificationProtocol]
|
|
236
|
-
};
|
|
93
|
+
// src/agents/configured-role-prompt.ts
|
|
94
|
+
function renderConfiguredRolePrompt({
|
|
95
|
+
role,
|
|
96
|
+
dialect,
|
|
97
|
+
config,
|
|
98
|
+
model
|
|
99
|
+
}) {
|
|
100
|
+
const promptOverrides = loadAgentPrompt(role, config?.preset);
|
|
101
|
+
const override = getAgentOverride(config, role);
|
|
102
|
+
const modelSection = createModelFamilySection(role, model);
|
|
103
|
+
const stepSection = createStepBudgetSection(override?.steps);
|
|
104
|
+
const basePrompt = renderRolePrompt(createRolePromptSections(role), dialect);
|
|
105
|
+
const prompt = composeAgentPrompt({
|
|
106
|
+
basePrompt,
|
|
107
|
+
customPrompt: promptOverrides.prompt,
|
|
108
|
+
customAppendPrompt: appendPromptSections(
|
|
109
|
+
modelSection ? renderPromptSection(modelSection, dialect) : void 0,
|
|
110
|
+
promptOverrides.appendPrompt
|
|
111
|
+
)
|
|
112
|
+
});
|
|
113
|
+
return appendPromptSections(
|
|
114
|
+
prompt,
|
|
115
|
+
stepSection ? renderPromptSection(stepSection, dialect) : void 0
|
|
116
|
+
);
|
|
237
117
|
}
|
|
238
118
|
|
|
239
119
|
// src/harness/core/memory-governance.ts
|
|
240
|
-
var ROOT_OWNED_OPERATIONS = [
|
|
241
|
-
{ tool: "mem_session", action: "start" },
|
|
242
|
-
{ tool: "mem_session", action: "checkpoint" },
|
|
243
|
-
{ tool: "mem_session", action: "summary" },
|
|
244
|
-
{ tool: "mem_save", kind: "prompt" },
|
|
245
|
-
{ tool: "mem_save", kind: "session_summary" }
|
|
246
|
-
];
|
|
247
|
-
var PARENT_SCOPED_READ_TOOLS = [
|
|
248
|
-
"mem_recall",
|
|
249
|
-
"mem_context",
|
|
250
|
-
"mem_get",
|
|
251
|
-
"mem_project"
|
|
252
|
-
];
|
|
253
|
-
var WRITE_CAPABLE_DELEGATED_TOOLS = [
|
|
254
|
-
...PARENT_SCOPED_READ_TOOLS,
|
|
255
|
-
"mem_save"
|
|
256
|
-
];
|
|
257
|
-
var ALL_MEMORY_TOOLS = [
|
|
258
|
-
"mem_save",
|
|
259
|
-
"mem_recall",
|
|
260
|
-
"mem_context",
|
|
261
|
-
"mem_get",
|
|
262
|
-
"mem_project",
|
|
263
|
-
"mem_session"
|
|
264
|
-
];
|
|
265
|
-
function uniqueTools(tools) {
|
|
266
|
-
return [...new Set(tools)];
|
|
267
|
-
}
|
|
268
|
-
function roleAllowedTools(role) {
|
|
269
|
-
if (role.name === "orchestrator") {
|
|
270
|
-
return [...ALL_MEMORY_TOOLS];
|
|
271
|
-
}
|
|
272
|
-
if (role.mode === "read-only") {
|
|
273
|
-
return [...PARENT_SCOPED_READ_TOOLS];
|
|
274
|
-
}
|
|
275
|
-
return uniqueTools([...WRITE_CAPABLE_DELEGATED_TOOLS]);
|
|
276
|
-
}
|
|
277
120
|
function roleRules(role) {
|
|
278
|
-
const
|
|
279
|
-
"
|
|
280
|
-
|
|
281
|
-
"
|
|
282
|
-
|
|
283
|
-
"
|
|
284
|
-
|
|
285
|
-
"
|
|
286
|
-
"
|
|
121
|
+
const sharedRules = [
|
|
122
|
+
"thoth-mem remains an external provider; its installed provider guidance is authoritative for memory operations and thoth-agents does not prescribe the mechanism.",
|
|
123
|
+
"Provider-dependent use requires parent-scoped authorization with the stable root session identity or explicit unavailable state and project supplied by dispatch.",
|
|
124
|
+
"The MEMORY authorization is none, recall, or observe: none forbids provider work, recall permits bounded reads, and observe additionally permits one bounded durable observation under the delegated scope.",
|
|
125
|
+
"Only authorized context may be used, and the delegate must report missing, stale, contradictory, or insufficient context instead of guessing.",
|
|
126
|
+
"Memory authorization never changes workspace permissions or grants control of root lifecycle and real-user-intent ownership.",
|
|
127
|
+
"A handoff must keep accepted scope, decisions, permissions, and artifacts plus bounded memory context available to the authorized delegate.",
|
|
128
|
+
"Completion continuity is a provider-confirmed semantic summary outcome owned by the root.",
|
|
129
|
+
"Missing capability evidence is reported as degraded or unsupported and never as successful persistence or recovery.",
|
|
130
|
+
"openspec/ is the canonical SDD store; do not mirror spec.md, plan.md, tasks.md, verification reports, or archive reports into provider memory.",
|
|
131
|
+
"Do not invent a consumer fallback or silently change the selected persistence mode."
|
|
287
132
|
];
|
|
288
133
|
if (role.name === "orchestrator") {
|
|
289
134
|
return [
|
|
290
|
-
|
|
291
|
-
"
|
|
292
|
-
|
|
293
|
-
"Dispatch task instructions plus recovery instructions, not the handoff body, raw transcripts, or generated subagent prompts.",
|
|
294
|
-
"Dispatch parent session_id and project when authorizing subagent memory use.",
|
|
295
|
-
'Root recall uses mem_recall(mode="compact") -> mem_recall(mode="context") -> mem_get(...); use mem_context(recall_query=...) and bounded mem_project(action="graph"|"topics"|"topic") for supplemental context.',
|
|
296
|
-
"Protect the sdd/* topic namespace and write SDD memory artifacts only in thoth-mem or hybrid persistence modes."
|
|
135
|
+
...sharedRules,
|
|
136
|
+
"The root coordinator owns authorization, root lifecycle, real-user-intent handling, and semantic completion outcomes.",
|
|
137
|
+
"Dispatch task and authorization context without embedding secrets, raw transcripts, or irrelevant context."
|
|
297
138
|
];
|
|
298
139
|
}
|
|
299
140
|
if (role.mode === "read-only") {
|
|
300
141
|
return [
|
|
301
|
-
...
|
|
302
|
-
"
|
|
303
|
-
"
|
|
304
|
-
"Read-only agents must never write durable memory or save prompts."
|
|
142
|
+
...sharedRules,
|
|
143
|
+
"For a read-only workspace role, observe may authorize a durable provider observation but does not authorize workspace mutation.",
|
|
144
|
+
"Root lifecycle ownership never transfers to this delegate."
|
|
305
145
|
];
|
|
306
146
|
}
|
|
307
147
|
return [
|
|
308
|
-
...
|
|
309
|
-
"
|
|
310
|
-
|
|
311
|
-
"Project-scoped read tools require explicit delegated permission and must stay bounded to the parent session/project scope."
|
|
148
|
+
...sharedRules,
|
|
149
|
+
"For a write-capable workspace role, provider observations still require observe authorization and the delegated parent scope.",
|
|
150
|
+
"Root lifecycle ownership never transfers to this delegate."
|
|
312
151
|
];
|
|
313
152
|
}
|
|
314
153
|
function getRoleMemoryGovernance(role) {
|
|
315
|
-
const allowedTools = roleAllowedTools(role);
|
|
316
154
|
return {
|
|
317
155
|
role: role.name,
|
|
318
|
-
|
|
319
|
-
allowedTools,
|
|
320
|
-
forbiddenTools: ALL_MEMORY_TOOLS.filter(
|
|
321
|
-
(tool) => !allowedTools.includes(tool)
|
|
322
|
-
),
|
|
156
|
+
workspaceMode: role.mode,
|
|
323
157
|
requiresParentContext: role.name !== "orchestrator",
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
protectsSddNamespace: true,
|
|
158
|
+
availableAuthorizations: ["none", "recall", "observe"],
|
|
159
|
+
ownsRootLifecycle: role.name === "orchestrator",
|
|
327
160
|
rules: roleRules(role)
|
|
328
161
|
};
|
|
329
162
|
}
|
|
@@ -334,10 +167,10 @@ function renderMemoryGovernanceInstructions(role, dialect) {
|
|
|
334
167
|
`- Progress ownership remains with the coordinator; report memory-governance verification for tracking in ${dialect.tools.progressTool}.`
|
|
335
168
|
] : [];
|
|
336
169
|
return [
|
|
337
|
-
"thoth-mem governance:",
|
|
170
|
+
"External thoth-mem provider memory governance:",
|
|
338
171
|
...governance.rules.map((rule) => `- ${rule}`),
|
|
339
172
|
...harnessRules,
|
|
340
|
-
`- Runtime enforcement: ${role.name === "orchestrator" ? "root-owned
|
|
173
|
+
`- Runtime enforcement: ${role.name === "orchestrator" ? "root-owned outcomes" : "instruction-level unless the target harness validates per-agent memory controls"}.`
|
|
341
174
|
].join("\n");
|
|
342
175
|
}
|
|
343
176
|
function memoryGovernanceDiagnostics(input) {
|
|
@@ -346,7 +179,7 @@ function memoryGovernanceDiagnostics(input) {
|
|
|
346
179
|
diagnostics.push({
|
|
347
180
|
severity: "warning",
|
|
348
181
|
code: `${input.harness}.permission.memory.enforcement_gap`,
|
|
349
|
-
message: "Runtime controls for
|
|
182
|
+
message: "Runtime controls for provider authorization are unavailable; instruction-level guidance must report capability as degraded or unsupported rather than treating instruction text as runtime support.",
|
|
350
183
|
harness: input.harness,
|
|
351
184
|
capability: "rolePermissions",
|
|
352
185
|
fallback: "instruction-only"
|
|
@@ -356,7 +189,7 @@ function memoryGovernanceDiagnostics(input) {
|
|
|
356
189
|
diagnostics.push({
|
|
357
190
|
severity: input.parentContextInjection === "unknown" ? "error" : "warning",
|
|
358
191
|
code: `${input.harness}.context.parent_injection.unvalidated`,
|
|
359
|
-
message: "Parent
|
|
192
|
+
message: "Parent-scoped authorization is not runtime-enforced; provider-backed continuity is degraded or unsupported without evidenced authorized context.",
|
|
360
193
|
harness: input.harness,
|
|
361
194
|
capability: "parentContextInjection",
|
|
362
195
|
fallback: "instruction-only"
|
|
@@ -366,7 +199,7 @@ function memoryGovernanceDiagnostics(input) {
|
|
|
366
199
|
diagnostics.push({
|
|
367
200
|
severity: "warning",
|
|
368
201
|
code: `${input.harness}.permission.memory_write.enforcement_gap`,
|
|
369
|
-
message:
|
|
202
|
+
message: "Runtime controls for delegated provider writes are unavailable; report the outcome as degraded or unsupported unless caller-supplied evidence proves success.",
|
|
370
203
|
harness: input.harness,
|
|
371
204
|
capability: "memoryGovernanceEnforcement",
|
|
372
205
|
fallback: "instruction-only"
|
|
@@ -590,10 +423,10 @@ var CODEX_SURFACES = [
|
|
|
590
423
|
target: "mcp-config",
|
|
591
424
|
status: "validated",
|
|
592
425
|
artifactKind: "mcp-config",
|
|
593
|
-
path: ".codex-plugin
|
|
594
|
-
fields: ["mcpServers", "
|
|
426
|
+
path: ".codex-plugin/codex.mcp.json",
|
|
427
|
+
fields: ["mcpServers", "./codex.mcp.json"],
|
|
595
428
|
diagnosticCode: "codex.surface.plugin_mcp_json.validated",
|
|
596
|
-
summary: "Codex plugin packages can
|
|
429
|
+
summary: "Codex plugin packages can reference bundled MCP server definitions from the plugin root.",
|
|
597
430
|
evidence: "OpenAI Codex plugin docs describe bundled .mcp.json server definitions referenced from plugin.json using plugin-root relative paths."
|
|
598
431
|
},
|
|
599
432
|
{
|
|
@@ -843,10 +676,21 @@ var PLUGIN_MANIFEST_SURFACE_ID = "plugin-manifest-json";
|
|
|
843
676
|
function normalizePath(value) {
|
|
844
677
|
return value.replace(/\\/g, "/");
|
|
845
678
|
}
|
|
679
|
+
function stripProviderMcp(content) {
|
|
680
|
+
if (content === void 0) return void 0;
|
|
681
|
+
try {
|
|
682
|
+
const parsed = JSON.parse(content);
|
|
683
|
+
if (!parsed.mcpServers) return content;
|
|
684
|
+
const { thoth_mem: _provider, ...mcpServers } = parsed.mcpServers;
|
|
685
|
+
return stableJson({ ...parsed, mcpServers });
|
|
686
|
+
} catch {
|
|
687
|
+
return content;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
846
690
|
function pluginRootReference(pathValue) {
|
|
847
691
|
const normalized = normalizePath(pathValue);
|
|
848
|
-
const
|
|
849
|
-
return `./${
|
|
692
|
+
const relative = normalized.slice(".codex-plugin/".length);
|
|
693
|
+
return `./${relative}`;
|
|
850
694
|
}
|
|
851
695
|
function sha256(content) {
|
|
852
696
|
return `sha256:${createHash("sha256").update(content).digest("hex")}`;
|
|
@@ -886,9 +730,9 @@ function artifactKindForSurface(surfaceId) {
|
|
|
886
730
|
if (surfaceId === "plugin-mcp-json") return "mcp-config";
|
|
887
731
|
return surfaceId === "plugin-hooks-json" ? "hook-config" : "skill";
|
|
888
732
|
}
|
|
889
|
-
function codexHookPackageDiagnostic(code, message) {
|
|
733
|
+
function codexHookPackageDiagnostic(code, message, severity = "warning") {
|
|
890
734
|
return {
|
|
891
|
-
severity
|
|
735
|
+
severity,
|
|
892
736
|
code,
|
|
893
737
|
message,
|
|
894
738
|
harness: "codex",
|
|
@@ -919,7 +763,8 @@ function renderHookDefinitions(hookDefinitions) {
|
|
|
919
763
|
diagnostics.push(
|
|
920
764
|
codexHookPackageDiagnostic(
|
|
921
765
|
"codex.plugin.hooks.none_packaged",
|
|
922
|
-
"No Codex plugin hooks were packaged because no hook definitions passed existing Codex hook validation."
|
|
766
|
+
"No Codex plugin hooks were packaged because no hook definitions passed existing Codex hook validation.",
|
|
767
|
+
"info"
|
|
923
768
|
)
|
|
924
769
|
);
|
|
925
770
|
return { diagnostics };
|
|
@@ -1050,7 +895,8 @@ function renderCodexPluginPackage(input) {
|
|
|
1050
895
|
if (asset.surfaceId === "plugin-hooks-json" && !hookContent.content) {
|
|
1051
896
|
continue;
|
|
1052
897
|
}
|
|
1053
|
-
const
|
|
898
|
+
const rawAssetContent = hookContent.content ?? asset.content;
|
|
899
|
+
const assetContent = asset.surfaceId === "plugin-mcp-json" ? stripProviderMcp(rawAssetContent) : rawAssetContent;
|
|
1054
900
|
const reference = pluginRootReference(assetPath);
|
|
1055
901
|
manifest[asset.manifestField] = reference;
|
|
1056
902
|
if (assetContent !== void 0 && !assetPath.endsWith("/")) {
|
|
@@ -1119,6 +965,13 @@ function renderField(key, value) {
|
|
|
1119
965
|
function isRecord2(value) {
|
|
1120
966
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1121
967
|
}
|
|
968
|
+
function stripProviderMcpValues(surfaceId, values) {
|
|
969
|
+
if (surfaceId !== "mcp-server-config") return values;
|
|
970
|
+
const servers = values.mcp_servers;
|
|
971
|
+
if (!isRecord2(servers)) return values;
|
|
972
|
+
const { thoth_mem: _provider, ...mcpServers } = servers;
|
|
973
|
+
return { ...values, mcp_servers: mcpServers };
|
|
974
|
+
}
|
|
1122
975
|
function fieldOrder(fields, keys) {
|
|
1123
976
|
const orderedRoots = fields.map((field) => field.split(".")[0]);
|
|
1124
977
|
return [...keys].sort((left, right) => {
|
|
@@ -1161,8 +1014,9 @@ function renderCodexToml(input) {
|
|
|
1161
1014
|
const diagnostics = [];
|
|
1162
1015
|
const lines = [];
|
|
1163
1016
|
const tables = [];
|
|
1164
|
-
|
|
1165
|
-
|
|
1017
|
+
const values = stripProviderMcpValues(input.surfaceId, input.values);
|
|
1018
|
+
for (const key of fieldOrder(fields, Object.keys(values))) {
|
|
1019
|
+
const value = values[key];
|
|
1166
1020
|
if (!isAllowedRoot(fields, key)) {
|
|
1167
1021
|
diagnostics.push({
|
|
1168
1022
|
severity: "warning",
|
|
@@ -1191,144 +1045,6 @@ function renderCodexToml(input) {
|
|
|
1191
1045
|
};
|
|
1192
1046
|
}
|
|
1193
1047
|
|
|
1194
|
-
// src/harness/writers/skill-layout.ts
|
|
1195
|
-
import * as fs2 from "fs";
|
|
1196
|
-
import * as path2 from "path";
|
|
1197
|
-
|
|
1198
|
-
// src/harness/writers/fs-skill-collect.ts
|
|
1199
|
-
import { createHash as createHash2 } from "crypto";
|
|
1200
|
-
import * as fs from "fs";
|
|
1201
|
-
import * as path from "path";
|
|
1202
|
-
function normalizeSkillPath(value) {
|
|
1203
|
-
return value.split(path.sep).join("/");
|
|
1204
|
-
}
|
|
1205
|
-
function collectSkillFiles(directory) {
|
|
1206
|
-
if (!fs.existsSync(directory)) return [];
|
|
1207
|
-
const entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
1208
|
-
const files = [];
|
|
1209
|
-
for (const entry of entries) {
|
|
1210
|
-
const entryPath = path.join(directory, entry.name);
|
|
1211
|
-
if (entry.isDirectory()) {
|
|
1212
|
-
files.push(...collectSkillFiles(entryPath));
|
|
1213
|
-
} else if (entry.isFile()) {
|
|
1214
|
-
files.push(entryPath);
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
return files.sort((left, right) => left.localeCompare(right));
|
|
1218
|
-
}
|
|
1219
|
-
function sha256Hash(content) {
|
|
1220
|
-
return `sha256:${createHash2("sha256").update(content).digest("hex")}`;
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
// src/harness/writers/skill-layout.ts
|
|
1224
|
-
var OUTPUT_MODE_CONFIG = {
|
|
1225
|
-
"plugin-package": {
|
|
1226
|
-
basePath: ".codex-plugin/skills",
|
|
1227
|
-
manifestPath: ".codex-plugin/skills/.thoth-agents-manifest.json",
|
|
1228
|
-
surfaceId: "plugin-skills-directory",
|
|
1229
|
-
label: "plugin-bundled"
|
|
1230
|
-
},
|
|
1231
|
-
"repo-local-fallback": {
|
|
1232
|
-
basePath: ".agents/skills",
|
|
1233
|
-
manifestPath: ".agents/skills/.thoth-agents-manifest.json",
|
|
1234
|
-
surfaceId: "repo-skills-directory",
|
|
1235
|
-
label: "fallback .agents/skills"
|
|
1236
|
-
}
|
|
1237
|
-
};
|
|
1238
|
-
function resolveOutputModes(input) {
|
|
1239
|
-
const requested = input.outputModes ?? (input.outputMode ? [input.outputMode] : []);
|
|
1240
|
-
const modes = requested.length > 0 ? requested : ["plugin-package"];
|
|
1241
|
-
return [...new Set(modes)];
|
|
1242
|
-
}
|
|
1243
|
-
function duplicateScopeDiagnostic(skillNames, surfaceId) {
|
|
1244
|
-
if (skillNames.length === 0) return void 0;
|
|
1245
|
-
return {
|
|
1246
|
-
severity: "warning",
|
|
1247
|
-
code: "codex.skill.duplicate_scope_precedence_unverified",
|
|
1248
|
-
message: `Codex skills are selected for both plugin-bundled and fallback .agents/skills scopes: ${skillNames.join(", ")}. Plugin-bundled skills are the intended primary package content, but runtime precedence with fallback scopes is unresolved and must be validated before relying on ordering.`,
|
|
1249
|
-
harness: "codex",
|
|
1250
|
-
surface: surfaceId,
|
|
1251
|
-
fallback: "diagnostic-only"
|
|
1252
|
-
};
|
|
1253
|
-
}
|
|
1254
|
-
function renderCodexSkillLayout(input) {
|
|
1255
|
-
const surface = assertCodexSurfaceCanGenerate(input.surfaceId);
|
|
1256
|
-
if (!surface.ok) {
|
|
1257
|
-
return { artifacts: [], diagnostics: [surface.diagnostic] };
|
|
1258
|
-
}
|
|
1259
|
-
const artifacts = [];
|
|
1260
|
-
const diagnostics = [];
|
|
1261
|
-
const outputModes = resolveOutputModes(input);
|
|
1262
|
-
for (const mode of outputModes) {
|
|
1263
|
-
const modeSurface = assertCodexSurfaceCanGenerate(
|
|
1264
|
-
OUTPUT_MODE_CONFIG[mode].surfaceId
|
|
1265
|
-
);
|
|
1266
|
-
if (!modeSurface.ok) diagnostics.push(modeSurface.diagnostic);
|
|
1267
|
-
}
|
|
1268
|
-
const duplicateDiagnostic = duplicateScopeDiagnostic(
|
|
1269
|
-
outputModes.length > 1 ? input.skills.map((skill) => skill.name).sort() : [],
|
|
1270
|
-
input.surfaceId
|
|
1271
|
-
);
|
|
1272
|
-
if (duplicateDiagnostic) diagnostics.push(duplicateDiagnostic);
|
|
1273
|
-
const manifests = new Map(
|
|
1274
|
-
outputModes.map((mode) => [mode, []])
|
|
1275
|
-
);
|
|
1276
|
-
for (const skill of [...input.skills].sort(
|
|
1277
|
-
(left, right) => left.name.localeCompare(right.name)
|
|
1278
|
-
)) {
|
|
1279
|
-
const sourceBaseRoot = input.packageRoot ?? input.projectRoot;
|
|
1280
|
-
const sourceRoot = path2.join(sourceBaseRoot, skill.sourcePath);
|
|
1281
|
-
const files = collectSkillFiles(sourceRoot);
|
|
1282
|
-
if (files.length === 0) {
|
|
1283
|
-
diagnostics.push({
|
|
1284
|
-
severity: "warning",
|
|
1285
|
-
code: "codex.skill.source_missing",
|
|
1286
|
-
message: `Skipping Codex skill "${skill.name}" because source path "${skill.sourcePath}" was not found.`,
|
|
1287
|
-
harness: "codex",
|
|
1288
|
-
surface: input.surfaceId,
|
|
1289
|
-
fallback: "diagnostic-only"
|
|
1290
|
-
});
|
|
1291
|
-
continue;
|
|
1292
|
-
}
|
|
1293
|
-
for (const file of files) {
|
|
1294
|
-
const relative4 = normalizeSkillPath(path2.relative(sourceRoot, file));
|
|
1295
|
-
const content = fs2.readFileSync(file, "utf8");
|
|
1296
|
-
const sourcePath = normalizeSkillPath(
|
|
1297
|
-
path2.relative(sourceBaseRoot, file)
|
|
1298
|
-
);
|
|
1299
|
-
for (const mode of outputModes) {
|
|
1300
|
-
const config = OUTPUT_MODE_CONFIG[mode];
|
|
1301
|
-
const outputPath = `${config.basePath}/${skill.name}/${relative4}`;
|
|
1302
|
-
artifacts.push({
|
|
1303
|
-
harness: "codex",
|
|
1304
|
-
kind: "skill",
|
|
1305
|
-
path: outputPath,
|
|
1306
|
-
description: `Codex ${config.label} skill artifact for ${skill.name}`,
|
|
1307
|
-
content
|
|
1308
|
-
});
|
|
1309
|
-
manifests.get(mode)?.push({
|
|
1310
|
-
name: skill.name,
|
|
1311
|
-
sourcePath,
|
|
1312
|
-
outputPath,
|
|
1313
|
-
sha256: sha256Hash(content)
|
|
1314
|
-
});
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
for (const mode of outputModes) {
|
|
1319
|
-
const config = OUTPUT_MODE_CONFIG[mode];
|
|
1320
|
-
artifacts.push({
|
|
1321
|
-
harness: "codex",
|
|
1322
|
-
kind: "manifest",
|
|
1323
|
-
path: config.manifestPath,
|
|
1324
|
-
description: `Generated Codex ${config.label} skill source manifest with source hashes.`,
|
|
1325
|
-
content: `${JSON.stringify({ generatedBy: "thoth-agents", skills: manifests.get(mode) ?? [] }, null, 2)}
|
|
1326
|
-
`
|
|
1327
|
-
});
|
|
1328
|
-
}
|
|
1329
|
-
return { artifacts, diagnostics };
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
1048
|
// src/harness/adapters/codex.ts
|
|
1333
1049
|
function readRootPackageVersion(context2) {
|
|
1334
1050
|
const packageJsonPath = findRootPackageJsonPath([
|
|
@@ -1343,20 +1059,13 @@ function createCodexPluginPackageManifest(context2) {
|
|
|
1343
1059
|
return {
|
|
1344
1060
|
name: "thoth-agents",
|
|
1345
1061
|
version: readRootPackageVersion(context2),
|
|
1346
|
-
description: "
|
|
1062
|
+
description: "Bundled Spec Kit-compatible SDD skills and MCP configuration; the seven-role global Codex layer requires thoth-agents CLI setup."
|
|
1347
1063
|
};
|
|
1348
1064
|
}
|
|
1349
1065
|
function stableJson2(value) {
|
|
1350
1066
|
return `${JSON.stringify(value, null, 2)}
|
|
1351
1067
|
`;
|
|
1352
1068
|
}
|
|
1353
|
-
function codexCommandConfig(commandParts) {
|
|
1354
|
-
const [command = "", ...args] = commandParts;
|
|
1355
|
-
return {
|
|
1356
|
-
command,
|
|
1357
|
-
...args.length > 0 ? { args } : {}
|
|
1358
|
-
};
|
|
1359
|
-
}
|
|
1360
1069
|
function codexMcpConfig(config) {
|
|
1361
1070
|
if ("url" in config) {
|
|
1362
1071
|
return { url: config.url };
|
|
@@ -1372,8 +1081,7 @@ function createCodexBuiltinMcpServers() {
|
|
|
1372
1081
|
return {
|
|
1373
1082
|
exa: codexMcpConfig(exa),
|
|
1374
1083
|
context7: { url: CONTEXT7_MCP_URL },
|
|
1375
|
-
grep_app: { url: GREP_APP_MCP_URL }
|
|
1376
|
-
thoth_mem: codexCommandConfig(DEFAULT_THOTH_COMMAND)
|
|
1084
|
+
grep_app: { url: GREP_APP_MCP_URL }
|
|
1377
1085
|
};
|
|
1378
1086
|
}
|
|
1379
1087
|
function createCodexBuiltinMcpJsonConfig() {
|
|
@@ -1392,65 +1100,19 @@ function isCodexSubagentName(name) {
|
|
|
1392
1100
|
var CODEX_ROOT_START = "<!-- thoth-agents:codex-root:start -->";
|
|
1393
1101
|
var CODEX_ROOT_END = "<!-- thoth-agents:codex-root:end -->";
|
|
1394
1102
|
var CODEX_CAPABILITIES = CODEX_PROMPT_DIALECT.capabilities.capabilities;
|
|
1395
|
-
function
|
|
1396
|
-
switch (roleName) {
|
|
1397
|
-
case "orchestrator":
|
|
1398
|
-
return createOrchestratorPromptSections();
|
|
1399
|
-
case "explorer":
|
|
1400
|
-
case "librarian":
|
|
1401
|
-
case "oracle":
|
|
1402
|
-
return createReadOnlySpecialistPromptSections(roleName);
|
|
1403
|
-
case "designer":
|
|
1404
|
-
case "quick":
|
|
1405
|
-
case "deep":
|
|
1406
|
-
return createWriteCapableSpecialistPromptSections(roleName);
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
function codexModelFamilyPromptSection(roleName, model) {
|
|
1410
|
-
const section = createModelFamilySection(roleName, model);
|
|
1411
|
-
return section ? renderPromptSection(section, CODEX_PROMPT_DIALECT) : void 0;
|
|
1412
|
-
}
|
|
1413
|
-
function codexStepBudgetPromptSection(steps) {
|
|
1414
|
-
const section = createStepBudgetSection(steps);
|
|
1415
|
-
return section ? renderPromptSection(section, CODEX_PROMPT_DIALECT) : void 0;
|
|
1416
|
-
}
|
|
1417
|
-
function renderCodexRolePrompt(roleName, config, model) {
|
|
1418
|
-
const promptOverrides = loadAgentPrompt(roleName, config?.preset);
|
|
1419
|
-
const override = getAgentOverride(config, roleName);
|
|
1420
|
-
const basePrompt = renderRolePrompt(
|
|
1421
|
-
codexPromptSections(roleName),
|
|
1422
|
-
CODEX_PROMPT_DIALECT
|
|
1423
|
-
);
|
|
1424
|
-
const prompt = composeAgentPrompt({
|
|
1425
|
-
basePrompt,
|
|
1426
|
-
customPrompt: promptOverrides.prompt,
|
|
1427
|
-
customAppendPrompt: appendPromptSections(
|
|
1428
|
-
codexModelFamilyPromptSection(roleName, model),
|
|
1429
|
-
promptOverrides.appendPrompt
|
|
1430
|
-
)
|
|
1431
|
-
});
|
|
1432
|
-
return appendPromptSections(
|
|
1433
|
-
prompt,
|
|
1434
|
-
codexStepBudgetPromptSection(override?.steps)
|
|
1435
|
-
);
|
|
1436
|
-
}
|
|
1437
|
-
function codexInternalHandoffGuidance() {
|
|
1103
|
+
function codexRuntimeGuidance() {
|
|
1438
1104
|
return [
|
|
1439
|
-
"<codex-
|
|
1440
|
-
"- The
|
|
1105
|
+
"<codex-runtime>",
|
|
1106
|
+
"- The ambient Codex session is the adaptive root; no orchestrator child TOML is generated.",
|
|
1107
|
+
"- Delegate with `collaboration.spawn_agent` only when the root determines that specialization, context isolation, review, or independent parallel work creates a net gain.",
|
|
1441
1108
|
"- Collaboration tools are direct tools and must not be called from inside `functions.exec`.",
|
|
1442
|
-
"-
|
|
1443
|
-
"-
|
|
1444
|
-
"-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
"-
|
|
1448
|
-
"-
|
|
1449
|
-
"- `collaboration.send_message` delivers a message without triggering a turn; `collaboration.followup_task` triggers a turn when an idle task must continue.",
|
|
1450
|
-
"- Use `collaboration.interrupt_agent` only for explicit cancellation, a deadline, or supersession.",
|
|
1451
|
-
"- Do not invent result payloads or numeric wait, poll, or timeout rules.",
|
|
1452
|
-
"- Memory ownership, handoff recovery, permissions, and prompt-body exclusion are instruction-level unless the active Codex runtime documents stronger enforcement.",
|
|
1453
|
-
"</codex-delegation-guidance>"
|
|
1109
|
+
"- Use a role-prefixed `task_name` and a self-contained English `message` with scope, anchors, constraints, verification, and the compact return contract. The current collaboration surface has no hard custom-role selector, so role selection remains instruction-level.",
|
|
1110
|
+
"- Keep maximum depth 1: children do not delegate. Use one writer per mutable surface and parallelize only independent work.",
|
|
1111
|
+
"- A `collaboration.wait_agent` timeout is nonterminal; inspect `collaboration.list_agents` for the same task before rerouting or interrupting it.",
|
|
1112
|
+
"- Use `request_user_input` only for blocking material choices and always omit `autoResolutionMs` entirely.",
|
|
1113
|
+
"- Generated custom-agent TOMLs carry model and sandbox defaults, but permissions and role matching may remain instruction-level in the active host.",
|
|
1114
|
+
"- Installed provider guidance owns memory, persistence, hooks, MCP lifecycle, and recovery mechanics.",
|
|
1115
|
+
"</codex-runtime>"
|
|
1454
1116
|
].join("\n");
|
|
1455
1117
|
}
|
|
1456
1118
|
function codexRoleInstructions(role) {
|
|
@@ -1471,37 +1133,28 @@ function codexRoleInstructions(role) {
|
|
|
1471
1133
|
function roleInstructions(role, config) {
|
|
1472
1134
|
const model = getCodexAgentModel(role, config) ?? DEFAULT_MODELS[role.name];
|
|
1473
1135
|
return [
|
|
1474
|
-
|
|
1136
|
+
renderConfiguredRolePrompt({
|
|
1137
|
+
role: role.name,
|
|
1138
|
+
dialect: CODEX_PROMPT_DIALECT,
|
|
1139
|
+
config,
|
|
1140
|
+
model
|
|
1141
|
+
}),
|
|
1475
1142
|
codexRoleInstructions(role),
|
|
1476
1143
|
renderMemoryGovernanceInstructions(role, CODEX_PROMPT_DIALECT)
|
|
1477
1144
|
].join("\n\n");
|
|
1478
1145
|
}
|
|
1479
1146
|
function renderCodexRootInstructions(config) {
|
|
1480
1147
|
const rootOverride = getAgentOverride(config, "orchestrator");
|
|
1481
|
-
const rootPrompt =
|
|
1482
|
-
"orchestrator",
|
|
1148
|
+
const rootPrompt = renderConfiguredRolePrompt({
|
|
1149
|
+
role: "orchestrator",
|
|
1150
|
+
dialect: CODEX_PROMPT_DIALECT,
|
|
1483
1151
|
config,
|
|
1484
|
-
rootOverride?.model ?? DEFAULT_MODELS.orchestrator
|
|
1485
|
-
);
|
|
1152
|
+
model: rootOverride?.model ?? DEFAULT_MODELS.orchestrator
|
|
1153
|
+
});
|
|
1486
1154
|
return [
|
|
1487
1155
|
CODEX_ROOT_START,
|
|
1488
1156
|
rootPrompt,
|
|
1489
|
-
|
|
1490
|
-
"<codex-runtime>",
|
|
1491
|
-
"- The ambient Codex root session is the root/main orchestrator; orchestrator-only and root-owned instructions apply to it because Codex does not generate a selectable orchestrator agent TOML.",
|
|
1492
|
-
'- On each new root session, first resolve the stable root session identity from Codex request metadata in this order: `nodeRepl.requestMeta["x-codex-turn-metadata"].session_id`, then `nodeRepl.requestMeta["x-codex-turn-metadata"].thread_id`, then `nodeRepl.requestMeta.threadId`. Do not use `nodeRepl.requestMeta["x-codex-turn-metadata"].turn_id` as the stable session id.',
|
|
1493
|
-
'- `nodeRepl.requestMeta["x-codex-turn-metadata"].turn_id` is per-turn metadata only, not the stable root session id.',
|
|
1494
|
-
"- If `nodeRepl.requestMeta` is not yet visible and `tool_search` is available, load/discover the `node_repl` MCP tool before concluding metadata is unavailable.",
|
|
1495
|
-
'- When thoth-mem tools are installed and stable root session identity is resolved, 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.',
|
|
1496
|
-
"- If metadata tooling is unavailable, or the required fields are missing, disclose that memory bootstrap could not run and continue without claiming memory was saved.",
|
|
1497
|
-
'- Before delegating after meaningful context changes, save or refresh the handoff body with root-owned mem_session(action="summary") or mem_save(kind="session_summary") when available; if unavailable, disclose that root-owned compaction could not be persisted.',
|
|
1498
|
-
"- Use the ambient Codex root session as the delegate-first root coordinator; do not generate or select an orchestrator TOML.",
|
|
1499
|
-
"- Delegate by invoking `collaboration.spawn_agent`; use role-prefixed task names and explicit role instructions for the installed Codex role agents: explorer, librarian, oracle, designer, quick, and deep.",
|
|
1500
|
-
"- Use packaged thoth-agents plugin capabilities through Codex plugin, skill, MCP, and hook review surfaces after enabling them with /plugins and /hooks.",
|
|
1501
|
-
"- For blocking user decisions in Codex Default mode, use request_user_input after features.default_mode_request_user_input is enabled; do not ask those questions in plain prose.",
|
|
1502
|
-
"- Whenever the root orchestrator calls `request_user_input`, it MUST NEVER set or pass `autoResolutionMs`; omit the field entirely.",
|
|
1503
|
-
"- Memory governance, role permissions, provider-per-agent controls, and hooks are instruction-level unless the active Codex runtime documents stronger enforcement.",
|
|
1504
|
-
"</codex-runtime>",
|
|
1157
|
+
codexRuntimeGuidance(),
|
|
1505
1158
|
CODEX_ROOT_END,
|
|
1506
1159
|
""
|
|
1507
1160
|
].join("\n");
|
|
@@ -1578,15 +1231,19 @@ function renderConfigArtifacts() {
|
|
|
1578
1231
|
harness: "codex",
|
|
1579
1232
|
kind: "mcp-config",
|
|
1580
1233
|
path: ".codex/config.toml",
|
|
1581
|
-
description: "Codex MCP configuration snippet for
|
|
1234
|
+
description: "Codex MCP configuration snippet for unrelated integrations.",
|
|
1582
1235
|
content: mcp.content
|
|
1583
1236
|
}
|
|
1584
1237
|
],
|
|
1585
1238
|
diagnostics: [...config.diagnostics, ...mcp.diagnostics]
|
|
1586
1239
|
};
|
|
1587
1240
|
}
|
|
1588
|
-
function capabilityDiagnostics(
|
|
1589
|
-
|
|
1241
|
+
function capabilityDiagnostics({
|
|
1242
|
+
includeHooks
|
|
1243
|
+
}) {
|
|
1244
|
+
const surfaceDiagnostics = getCodexSurfaceRecords().filter(
|
|
1245
|
+
(surface) => surface.status !== "validated" && (includeHooks || surface.target !== "hook-config")
|
|
1246
|
+
).map(codexSurfaceDiagnostic);
|
|
1590
1247
|
const governanceDiagnostics = memoryGovernanceDiagnostics({
|
|
1591
1248
|
harness: "codex",
|
|
1592
1249
|
permissionControls: CODEX_CAPABILITIES.rolePermissions,
|
|
@@ -1623,9 +1280,6 @@ function hookReadinessDiagnostics() {
|
|
|
1623
1280
|
}
|
|
1624
1281
|
];
|
|
1625
1282
|
}
|
|
1626
|
-
function resolveSkillOutputModes(context2) {
|
|
1627
|
-
return context2.options?.codexSkillOutputModes ?? ["plugin-package"];
|
|
1628
|
-
}
|
|
1629
1283
|
var codexAdapter = {
|
|
1630
1284
|
id: "codex",
|
|
1631
1285
|
displayName: "Codex",
|
|
@@ -1634,14 +1288,6 @@ var codexAdapter = {
|
|
|
1634
1288
|
const config = hasCodexConfig(context2) ? context2.config : void 0;
|
|
1635
1289
|
const agentArtifacts = renderAgentArtifacts({ config });
|
|
1636
1290
|
const configArtifacts = renderConfigArtifacts();
|
|
1637
|
-
const skillOutputModes = resolveSkillOutputModes(context2);
|
|
1638
|
-
const skillLayout = renderCodexSkillLayout({
|
|
1639
|
-
projectRoot: context2.projectRoot,
|
|
1640
|
-
...hasCodexPackageRoot(context2) ? { packageRoot: context2.packageRoot } : {},
|
|
1641
|
-
skills: getSkillRegistry(),
|
|
1642
|
-
surfaceId: "plugin-skills-directory",
|
|
1643
|
-
outputModes: skillOutputModes
|
|
1644
|
-
});
|
|
1645
1291
|
const pluginPackage = renderCodexPluginPackage({
|
|
1646
1292
|
manifest: createCodexPluginPackageManifest(context2),
|
|
1647
1293
|
assets: [
|
|
@@ -1654,60 +1300,39 @@ var codexAdapter = {
|
|
|
1654
1300
|
{
|
|
1655
1301
|
surfaceId: "plugin-mcp-json",
|
|
1656
1302
|
manifestField: "mcpServers",
|
|
1657
|
-
path: ".codex-plugin
|
|
1303
|
+
path: ".codex-plugin/codex.mcp.json",
|
|
1658
1304
|
description: "Codex plugin-bundled MCP server definitions.",
|
|
1659
1305
|
content: stableJson2(createCodexBuiltinMcpJsonConfig())
|
|
1660
|
-
},
|
|
1661
|
-
{
|
|
1662
|
-
surfaceId: "plugin-hooks-json",
|
|
1663
|
-
manifestField: "hooks",
|
|
1664
|
-
path: ".codex-plugin/hooks/hooks.json",
|
|
1665
|
-
description: "Codex plugin-bundled hook configuration.",
|
|
1666
|
-
hookDefinitions: []
|
|
1667
1306
|
}
|
|
1668
1307
|
]
|
|
1669
1308
|
});
|
|
1309
|
+
const hasPackagedHooks = pluginPackage.artifacts.some(
|
|
1310
|
+
(artifact) => artifact.kind === "hook-config"
|
|
1311
|
+
);
|
|
1670
1312
|
return {
|
|
1671
1313
|
harness: "codex",
|
|
1672
1314
|
artifacts: [
|
|
1673
1315
|
...agentArtifacts.artifacts,
|
|
1674
1316
|
...configArtifacts.artifacts,
|
|
1675
|
-
...pluginPackage.artifacts
|
|
1676
|
-
...skillLayout.artifacts
|
|
1317
|
+
...pluginPackage.artifacts
|
|
1677
1318
|
],
|
|
1678
1319
|
diagnostics: [
|
|
1679
1320
|
...agentArtifacts.diagnostics,
|
|
1680
1321
|
...configArtifacts.diagnostics,
|
|
1681
1322
|
...pluginPackage.diagnostics,
|
|
1682
|
-
...
|
|
1683
|
-
...
|
|
1684
|
-
...capabilityDiagnostics()
|
|
1323
|
+
...hasPackagedHooks ? hookReadinessDiagnostics() : [],
|
|
1324
|
+
...capabilityDiagnostics({ includeHooks: hasPackagedHooks })
|
|
1685
1325
|
]
|
|
1686
1326
|
};
|
|
1687
1327
|
}
|
|
1688
1328
|
};
|
|
1689
1329
|
|
|
1690
|
-
// src/harness/codex-plugin-paths.ts
|
|
1691
|
-
import { join as join4 } from "path";
|
|
1692
|
-
var CODEX_PLUGIN_ARTIFACT_PREFIX = ".codex-plugin/";
|
|
1693
|
-
function codexPluginRootArtifactPath(artifactPath) {
|
|
1694
|
-
if (!artifactPath.startsWith(CODEX_PLUGIN_ARTIFACT_PREFIX)) {
|
|
1695
|
-
throw new Error(`Expected Codex plugin artifact path: ${artifactPath}`);
|
|
1696
|
-
}
|
|
1697
|
-
const relativePath = artifactPath.slice(CODEX_PLUGIN_ARTIFACT_PREFIX.length);
|
|
1698
|
-
if (relativePath === ".mcp.json") return relativePath;
|
|
1699
|
-
if (relativePath === "plugin.json" || relativePath.startsWith(".")) {
|
|
1700
|
-
return join4(".codex-plugin", relativePath);
|
|
1701
|
-
}
|
|
1702
|
-
return relativePath;
|
|
1703
|
-
}
|
|
1704
|
-
|
|
1705
1330
|
// src/cli/codex-config-io.ts
|
|
1706
1331
|
import {
|
|
1707
1332
|
copyFileSync,
|
|
1708
|
-
existsSync as
|
|
1333
|
+
existsSync as existsSync2,
|
|
1709
1334
|
mkdirSync,
|
|
1710
|
-
readFileSync as
|
|
1335
|
+
readFileSync as readFileSync2,
|
|
1711
1336
|
renameSync,
|
|
1712
1337
|
writeFileSync
|
|
1713
1338
|
} from "fs";
|
|
@@ -1801,7 +1426,7 @@ function buildCodexManagedConfigPatch(content, options) {
|
|
|
1801
1426
|
}
|
|
1802
1427
|
function writeCodexConfigMerge(options) {
|
|
1803
1428
|
try {
|
|
1804
|
-
const before =
|
|
1429
|
+
const before = existsSync2(options.configPath) ? readFileSync2(options.configPath, "utf8") : "";
|
|
1805
1430
|
const merged = buildCodexManagedConfigPatch(before, {
|
|
1806
1431
|
pluginId: options.pluginId
|
|
1807
1432
|
});
|
|
@@ -1817,7 +1442,7 @@ function writeCodexConfigMerge(options) {
|
|
|
1817
1442
|
}
|
|
1818
1443
|
mkdirSync(dirname2(options.configPath), { recursive: true });
|
|
1819
1444
|
const backupPath = `${options.configPath}.bak`;
|
|
1820
|
-
if (
|
|
1445
|
+
if (existsSync2(options.configPath))
|
|
1821
1446
|
copyFileSync(options.configPath, backupPath);
|
|
1822
1447
|
const tmpPath = `${options.configPath}.tmp`;
|
|
1823
1448
|
writeFileSync(tmpPath, merged.content);
|
|
@@ -1825,7 +1450,7 @@ function writeCodexConfigMerge(options) {
|
|
|
1825
1450
|
return {
|
|
1826
1451
|
success: true,
|
|
1827
1452
|
configPath: options.configPath,
|
|
1828
|
-
backupPath:
|
|
1453
|
+
backupPath: existsSync2(backupPath) ? backupPath : void 0,
|
|
1829
1454
|
changed,
|
|
1830
1455
|
diffSummary: merged.diffSummary,
|
|
1831
1456
|
warnings: merged.warnings
|
|
@@ -1845,18 +1470,18 @@ function writeCodexConfigMerge(options) {
|
|
|
1845
1470
|
// src/cli/managed-state-io.ts
|
|
1846
1471
|
import {
|
|
1847
1472
|
copyFileSync as copyFileSync2,
|
|
1848
|
-
existsSync as
|
|
1473
|
+
existsSync as existsSync3,
|
|
1849
1474
|
mkdirSync as mkdirSync2,
|
|
1850
|
-
readFileSync as
|
|
1475
|
+
readFileSync as readFileSync3,
|
|
1851
1476
|
writeFileSync as writeFileSync2
|
|
1852
1477
|
} from "fs";
|
|
1853
1478
|
import { dirname as dirname3 } from "path";
|
|
1854
1479
|
import { z } from "zod";
|
|
1855
|
-
function writeTextWithBackup(
|
|
1856
|
-
mkdirSync2(dirname3(
|
|
1857
|
-
if (
|
|
1858
|
-
if (
|
|
1859
|
-
writeFileSync2(
|
|
1480
|
+
function writeTextWithBackup(path, content) {
|
|
1481
|
+
mkdirSync2(dirname3(path), { recursive: true });
|
|
1482
|
+
if (existsSync3(path) && readFileSync3(path, "utf8") === content) return false;
|
|
1483
|
+
if (existsSync3(path)) copyFileSync2(path, `${path}.bak`);
|
|
1484
|
+
writeFileSync2(path, content);
|
|
1860
1485
|
return true;
|
|
1861
1486
|
}
|
|
1862
1487
|
function stableJson3(value) {
|
|
@@ -1910,9 +1535,30 @@ function parseManagedModelStateJson(text, version) {
|
|
|
1910
1535
|
return emptyManagedModelState(version);
|
|
1911
1536
|
}
|
|
1912
1537
|
}
|
|
1913
|
-
function readManagedModelState(
|
|
1914
|
-
if (!
|
|
1915
|
-
return parseManagedModelStateJson(
|
|
1538
|
+
function readManagedModelState(path, version) {
|
|
1539
|
+
if (!existsSync3(path)) return emptyManagedModelState(version);
|
|
1540
|
+
return parseManagedModelStateJson(readFileSync3(path, "utf8"), version);
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
// src/cli/package-root.ts
|
|
1544
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1545
|
+
import { dirname as dirname4, join as join2, parse } from "path";
|
|
1546
|
+
function findPackageRoot(startDir) {
|
|
1547
|
+
let currentDir = startDir;
|
|
1548
|
+
const filesystemRoot = parse(startDir).root;
|
|
1549
|
+
while (true) {
|
|
1550
|
+
if (existsSync4(join2(currentDir, "package.json"))) {
|
|
1551
|
+
return currentDir;
|
|
1552
|
+
}
|
|
1553
|
+
if (currentDir === filesystemRoot) {
|
|
1554
|
+
return null;
|
|
1555
|
+
}
|
|
1556
|
+
const parentDir = dirname4(currentDir);
|
|
1557
|
+
if (parentDir === currentDir) {
|
|
1558
|
+
return null;
|
|
1559
|
+
}
|
|
1560
|
+
currentDir = parentDir;
|
|
1561
|
+
}
|
|
1916
1562
|
}
|
|
1917
1563
|
|
|
1918
1564
|
// src/cli/codex-install.ts
|
|
@@ -1928,43 +1574,15 @@ function mergeManagedBlock(existing, managedBlock) {
|
|
|
1928
1574
|
return `${existing}${existing.endsWith("\n") || existing.length === 0 ? "" : "\n"}
|
|
1929
1575
|
${managedBlock}`;
|
|
1930
1576
|
}
|
|
1931
|
-
function packageArtifactTarget(packageRoot, artifact) {
|
|
1932
|
-
return join5(packageRoot, codexPluginRootArtifactPath(artifact.path));
|
|
1933
|
-
}
|
|
1934
1577
|
function resolvePackageRoot(packageRoot) {
|
|
1935
1578
|
if (packageRoot) return packageRoot;
|
|
1936
1579
|
return findPackageRoot(fileURLToPath2(new URL(".", import.meta.url))) ?? void 0;
|
|
1937
1580
|
}
|
|
1938
|
-
function normalizeRelativeMarketplacePath(path4) {
|
|
1939
|
-
const normalized = path4.replaceAll("\\", "/");
|
|
1940
|
-
if (isAbsolute(path4) || /^[A-Za-z]:\//.test(normalized)) return normalized;
|
|
1941
|
-
if (normalized.startsWith("./")) return normalized;
|
|
1942
|
-
return `./${normalized}`;
|
|
1943
|
-
}
|
|
1944
|
-
function marketplaceSourcePath(homeDir, personalPluginRoot) {
|
|
1945
|
-
return normalizeRelativeMarketplacePath(
|
|
1946
|
-
relative2(homeDir, personalPluginRoot)
|
|
1947
|
-
);
|
|
1948
|
-
}
|
|
1949
|
-
function managedMarketplaceEntry(homeDir, personalPluginRoot) {
|
|
1950
|
-
return {
|
|
1951
|
-
name: "thoth-agents",
|
|
1952
|
-
source: {
|
|
1953
|
-
source: "local",
|
|
1954
|
-
path: marketplaceSourcePath(homeDir, personalPluginRoot)
|
|
1955
|
-
},
|
|
1956
|
-
policy: {
|
|
1957
|
-
installation: "AVAILABLE",
|
|
1958
|
-
authentication: "ON_INSTALL"
|
|
1959
|
-
},
|
|
1960
|
-
category: "Productivity"
|
|
1961
|
-
};
|
|
1962
|
-
}
|
|
1963
1581
|
function emptyManagedModelState2() {
|
|
1964
1582
|
return emptyManagedModelState(MANAGED_MODEL_STATE_VERSION);
|
|
1965
1583
|
}
|
|
1966
|
-
function readManagedModelState2(
|
|
1967
|
-
return readManagedModelState(
|
|
1584
|
+
function readManagedModelState2(path) {
|
|
1585
|
+
return readManagedModelState(path, MANAGED_MODEL_STATE_VERSION);
|
|
1968
1586
|
}
|
|
1969
1587
|
function parseRoleTomlModel(content) {
|
|
1970
1588
|
const match = /^model\s*=\s*"((?:\\.|[^"\\])*)"\s*$/m.exec(content);
|
|
@@ -2002,8 +1620,8 @@ function replaceRoleTomlEffort(content, effort) {
|
|
|
2002
1620
|
return `${rendered}
|
|
2003
1621
|
${content}`;
|
|
2004
1622
|
}
|
|
2005
|
-
function roleManagedModelStateKey(
|
|
2006
|
-
return basename(
|
|
1623
|
+
function roleManagedModelStateKey(path) {
|
|
1624
|
+
return basename(path);
|
|
2007
1625
|
}
|
|
2008
1626
|
function normalizeCodexRuntimeModel(model) {
|
|
2009
1627
|
const separator = model.indexOf("/");
|
|
@@ -2054,7 +1672,7 @@ function applyCodexManagedModelOverrides(config, overrides) {
|
|
|
2054
1672
|
`Missing Codex role TOML content for ${override.role}.`
|
|
2055
1673
|
);
|
|
2056
1674
|
}
|
|
2057
|
-
const before = existsSync5(roleItem.targetPath) ?
|
|
1675
|
+
const before = existsSync5(roleItem.targetPath) ? readFileSync4(roleItem.targetPath, "utf8") : roleItem.content;
|
|
2058
1676
|
const model = normalizeCodexRuntimeModel(override.model);
|
|
2059
1677
|
let updated = replaceRoleTomlModel(before, model);
|
|
2060
1678
|
if (override.clearEffort) {
|
|
@@ -2116,7 +1734,7 @@ function resolveRoleTomlContent(options) {
|
|
|
2116
1734
|
}
|
|
2117
1735
|
return options.renderedContent;
|
|
2118
1736
|
}
|
|
2119
|
-
const installedContent =
|
|
1737
|
+
const installedContent = readFileSync4(options.targetPath, "utf8");
|
|
2120
1738
|
const currentModel = parseRoleTomlModel(installedContent);
|
|
2121
1739
|
const currentEffort = parseRoleTomlEffort(installedContent);
|
|
2122
1740
|
if (configuredModel !== void 0) {
|
|
@@ -2144,25 +1762,11 @@ function resolveRoleTomlContent(options) {
|
|
|
2144
1762
|
options.nextState.models[key] = renderedModel;
|
|
2145
1763
|
return replaceRoleTomlEffort(options.renderedContent, currentEffort);
|
|
2146
1764
|
}
|
|
2147
|
-
function mergePersonalMarketplace(existing, homeDir, personalPluginRoot) {
|
|
2148
|
-
const parsed = existing.trim() ? JSON.parse(existing) : {};
|
|
2149
|
-
const plugins = Array.isArray(parsed.plugins) ? parsed.plugins : [];
|
|
2150
|
-
const managedEntry = managedMarketplaceEntry(homeDir, personalPluginRoot);
|
|
2151
|
-
const nextPlugins = plugins.filter(
|
|
2152
|
-
(entry) => !(entry && typeof entry === "object" && "name" in entry && entry.name === "thoth-agents")
|
|
2153
|
-
).concat(managedEntry);
|
|
2154
|
-
return stableJson3({
|
|
2155
|
-
...parsed,
|
|
2156
|
-
name: typeof parsed.name === "string" ? parsed.name : "personal-marketplace",
|
|
2157
|
-
interface: parsed.interface && typeof parsed.interface === "object" ? parsed.interface : { displayName: "Personal Plugin Marketplace" },
|
|
2158
|
-
plugins: nextPlugins
|
|
2159
|
-
});
|
|
2160
|
-
}
|
|
2161
1765
|
function roleArtifactContent(role, artifacts) {
|
|
2162
|
-
const
|
|
2163
|
-
const artifact = artifacts.find((candidate) => candidate.path ===
|
|
1766
|
+
const path = `.codex/agents/thoth-agents-${role}.toml`;
|
|
1767
|
+
const artifact = artifacts.find((candidate) => candidate.path === path);
|
|
2164
1768
|
if (!artifact?.content)
|
|
2165
|
-
throw new Error(`Missing Codex role artifact: ${
|
|
1769
|
+
throw new Error(`Missing Codex role artifact: ${path}`);
|
|
2166
1770
|
return String(artifact.content);
|
|
2167
1771
|
}
|
|
2168
1772
|
function buildCodexSetupPlan(config) {
|
|
@@ -2177,9 +1781,6 @@ function buildCodexSetupPlan(config) {
|
|
|
2177
1781
|
projectRoot: config.projectRoot,
|
|
2178
1782
|
...packageRoot ? { packageRoot } : {}
|
|
2179
1783
|
});
|
|
2180
|
-
const packageArtifacts = render.artifacts.filter(
|
|
2181
|
-
(artifact) => artifact.path.startsWith(".codex-plugin/")
|
|
2182
|
-
);
|
|
2183
1784
|
const rootBlock = renderCodexRootInstructions();
|
|
2184
1785
|
const managedModelState2 = readManagedModelState2(targets.managedModelsPath);
|
|
2185
1786
|
const nextManagedModelState = emptyManagedModelState2();
|
|
@@ -2220,24 +1821,6 @@ function buildCodexSetupPlan(config) {
|
|
|
2220
1821
|
requiresBackup: existsSync5(targets.managedModelsPath),
|
|
2221
1822
|
content: stableJson3(nextManagedModelState)
|
|
2222
1823
|
},
|
|
2223
|
-
...packageArtifacts.map(
|
|
2224
|
-
(artifact) => ({
|
|
2225
|
-
kind: "personal-plugin-source",
|
|
2226
|
-
action: "refresh-package",
|
|
2227
|
-
targetPath: packageArtifactTarget(targets.personalPluginRoot, artifact),
|
|
2228
|
-
description: `Refresh Personal Codex plugin source asset ${artifact.path}.`,
|
|
2229
|
-
requiresBackup: false,
|
|
2230
|
-
content: String(artifact.content ?? "")
|
|
2231
|
-
})
|
|
2232
|
-
),
|
|
2233
|
-
{
|
|
2234
|
-
kind: "personal-marketplace",
|
|
2235
|
-
action: "merge-marketplace",
|
|
2236
|
-
targetPath: targets.personalMarketplacePath,
|
|
2237
|
-
description: "Register Personal Codex marketplace entry for the local thoth-agents plugin source.",
|
|
2238
|
-
requiresBackup: existsSync5(targets.personalMarketplacePath),
|
|
2239
|
-
content: targets.personalPluginRoot
|
|
2240
|
-
},
|
|
2241
1824
|
{
|
|
2242
1825
|
kind: "user-config",
|
|
2243
1826
|
action: "merge-toml",
|
|
@@ -2249,7 +1832,7 @@ function buildCodexSetupPlan(config) {
|
|
|
2249
1832
|
kind: "diagnostic",
|
|
2250
1833
|
action: "diagnose-only",
|
|
2251
1834
|
targetPath: targets.codexHome,
|
|
2252
|
-
description: "Report /plugins,
|
|
1835
|
+
description: "Report /plugins, precedence, and consumer capability review steps.",
|
|
2253
1836
|
requiresBackup: false
|
|
2254
1837
|
}
|
|
2255
1838
|
];
|
|
@@ -2260,58 +1843,25 @@ function buildCodexSetupPlan(config) {
|
|
|
2260
1843
|
configPath: targets.configPath,
|
|
2261
1844
|
pluginId: config.pluginId,
|
|
2262
1845
|
diagnostics: [
|
|
2263
|
-
"
|
|
2264
|
-
"
|
|
1846
|
+
"Register the published marketplace with `codex plugin marketplace add EremesNG/thoth-agents`, restart Codex, then open /plugins to install and enable thoth-agents.",
|
|
1847
|
+
"The CLI does not copy a plugin into ~/.codex/plugins or merge ~/.agents/plugins/marketplace.json; Codex owns marketplace snapshots and installed plugin cache state.",
|
|
1848
|
+
"The /hooks surface may show hooks from independently installed plugins; this thoth-agents setup plan does not install provider hooks.",
|
|
2265
1849
|
"Codex Default mode user-input requests require features.default_mode_request_user_input = true and use the request_user_input tool; other modes may not expose it.",
|
|
2266
|
-
"Higher-precedence Codex config (project, profile, CLI, system, or admin) may override user config feature flags."
|
|
1850
|
+
"Higher-precedence Codex config (project, profile, CLI, system, or admin) may override user config feature flags.",
|
|
1851
|
+
"Provider capability is owned by the external provider and is not established by this thoth-agents setup plan."
|
|
2267
1852
|
],
|
|
2268
1853
|
disclaimers: [
|
|
2269
|
-
"Role permissions
|
|
2270
|
-
"Codex
|
|
1854
|
+
"Role permissions and provider-per-agent settings are instruction-level or user-managed unless documented Codex runtime controls are available.",
|
|
1855
|
+
"Codex reset is managed-only; no broad destructive --force behavior is implemented."
|
|
2271
1856
|
]
|
|
2272
1857
|
};
|
|
2273
1858
|
}
|
|
2274
1859
|
function formatCodexSetupPlan(plan) {
|
|
2275
|
-
const
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
const group = refreshPackageGroups.get(item.kind) ?? [];
|
|
2279
|
-
group.push(item);
|
|
2280
|
-
refreshPackageGroups.set(item.kind, group);
|
|
2281
|
-
}
|
|
2282
|
-
const renderedRefreshKinds = /* @__PURE__ */ new Set();
|
|
2283
|
-
const lines = [];
|
|
2284
|
-
for (const item of plan.items) {
|
|
2285
|
-
if (item.action !== "refresh-package") {
|
|
2286
|
-
lines.push(`- ${item.action}: ${item.targetPath} (${item.description})`);
|
|
2287
|
-
continue;
|
|
2288
|
-
}
|
|
2289
|
-
if (renderedRefreshKinds.has(item.kind)) continue;
|
|
2290
|
-
renderedRefreshKinds.add(item.kind);
|
|
2291
|
-
lines.push(formatRefreshPackageGroup(item.kind, refreshPackageGroups));
|
|
2292
|
-
}
|
|
1860
|
+
const lines = plan.items.map(
|
|
1861
|
+
(item) => `- ${item.action}: ${item.targetPath} (${item.description})`
|
|
1862
|
+
);
|
|
2293
1863
|
return ["Codex setup plan:", ...lines].join("\n");
|
|
2294
1864
|
}
|
|
2295
|
-
function formatRefreshPackageGroup(kind, groups) {
|
|
2296
|
-
const items = groups.get(kind) ?? [];
|
|
2297
|
-
const description = kind === "personal-plugin-source" ? "Refresh Personal Codex plugin source" : "Refresh documented .codex-plugin package";
|
|
2298
|
-
return `- refresh-package: ${commonTargetDirectory(items)} (${description}, ${items.length} files.)`;
|
|
2299
|
-
}
|
|
2300
|
-
function commonTargetDirectory(items) {
|
|
2301
|
-
if (items.length === 0) return "";
|
|
2302
|
-
let common = dirname4(items[0]?.targetPath ?? "");
|
|
2303
|
-
for (const item of items.slice(1)) {
|
|
2304
|
-
while (!isSameOrChildPath(item.targetPath, common)) {
|
|
2305
|
-
const parent = dirname4(common);
|
|
2306
|
-
if (parent === common) return common;
|
|
2307
|
-
common = parent;
|
|
2308
|
-
}
|
|
2309
|
-
}
|
|
2310
|
-
return common;
|
|
2311
|
-
}
|
|
2312
|
-
function isSameOrChildPath(path4, parent) {
|
|
2313
|
-
return path4 === parent || path4.startsWith(`${parent}\\`) || path4.startsWith(`${parent}/`);
|
|
2314
|
-
}
|
|
2315
1865
|
function applyCodexSetup(plan) {
|
|
2316
1866
|
const changed = [];
|
|
2317
1867
|
const diagnostics = uniqueMessages([
|
|
@@ -2320,9 +1870,6 @@ function applyCodexSetup(plan) {
|
|
|
2320
1870
|
]);
|
|
2321
1871
|
if (plan.dryRun) return { success: true, changed, diagnostics };
|
|
2322
1872
|
try {
|
|
2323
|
-
for (const targetPath of managedRefreshRoots(plan)) {
|
|
2324
|
-
rmSync(targetPath, { recursive: true, force: true });
|
|
2325
|
-
}
|
|
2326
1873
|
for (const item of plan.items) {
|
|
2327
1874
|
if (item.action === "diagnose-only") continue;
|
|
2328
1875
|
if (item.action === "merge-toml") {
|
|
@@ -2336,20 +1883,9 @@ function applyCodexSetup(plan) {
|
|
|
2336
1883
|
if (result.changed) changed.push(item.targetPath);
|
|
2337
1884
|
continue;
|
|
2338
1885
|
}
|
|
2339
|
-
if (item.action === "merge-marketplace") {
|
|
2340
|
-
if (item.content === void 0) continue;
|
|
2341
|
-
const content2 = mergePersonalMarketplace(
|
|
2342
|
-
existsSync5(item.targetPath) ? readFileSync5(item.targetPath, "utf8") : "",
|
|
2343
|
-
dirname4(dirname4(dirname4(item.targetPath))),
|
|
2344
|
-
item.content
|
|
2345
|
-
);
|
|
2346
|
-
if (writeTextWithBackup(item.targetPath, content2))
|
|
2347
|
-
changed.push(item.targetPath);
|
|
2348
|
-
continue;
|
|
2349
|
-
}
|
|
2350
1886
|
if (item.content === void 0) continue;
|
|
2351
1887
|
const content = item.action === "merge-managed-block" ? mergeManagedBlock(
|
|
2352
|
-
existsSync5(item.targetPath) ?
|
|
1888
|
+
existsSync5(item.targetPath) ? readFileSync4(item.targetPath, "utf8") : "",
|
|
2353
1889
|
item.content
|
|
2354
1890
|
) : item.content;
|
|
2355
1891
|
if (writeTextWithBackup(item.targetPath, content))
|
|
@@ -2365,25 +1901,15 @@ function applyCodexSetup(plan) {
|
|
|
2365
1901
|
};
|
|
2366
1902
|
}
|
|
2367
1903
|
}
|
|
2368
|
-
function managedRefreshRoots(plan) {
|
|
2369
|
-
const refreshGroups = /* @__PURE__ */ new Map();
|
|
2370
|
-
for (const item of plan.items) {
|
|
2371
|
-
if (item.action !== "refresh-package") continue;
|
|
2372
|
-
const group = refreshGroups.get(item.kind) ?? [];
|
|
2373
|
-
group.push(item);
|
|
2374
|
-
refreshGroups.set(item.kind, group);
|
|
2375
|
-
}
|
|
2376
|
-
return [...refreshGroups].filter(([kind]) => kind === "personal-plugin-source").map(([, items]) => commonTargetDirectory(items)).filter((path4) => path4.length > 0);
|
|
2377
|
-
}
|
|
2378
1904
|
|
|
2379
1905
|
// src/cli/operations/claude-code.ts
|
|
2380
|
-
import { existsSync as existsSync7
|
|
2381
|
-
import { basename as basename2 } from "path";
|
|
1906
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2382
1907
|
|
|
2383
1908
|
// src/harness/adapters/claude-code.ts
|
|
2384
1909
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2385
1910
|
|
|
2386
1911
|
// src/harness/writers/claude-code-plugin-package.ts
|
|
1912
|
+
import { createHash as createHash2 } from "crypto";
|
|
2387
1913
|
var CLAUDE_PLUGIN_MANIFEST_FIELDS = [
|
|
2388
1914
|
"name",
|
|
2389
1915
|
"version",
|
|
@@ -2399,6 +1925,21 @@ function stableJson4(value) {
|
|
|
2399
1925
|
return `${JSON.stringify(value, null, 2)}
|
|
2400
1926
|
`;
|
|
2401
1927
|
}
|
|
1928
|
+
function sha256Hash(content) {
|
|
1929
|
+
return `sha256:${createHash2("sha256").update(content).digest("hex")}`;
|
|
1930
|
+
}
|
|
1931
|
+
function withoutProviderAsset(artifact) {
|
|
1932
|
+
if (!normalizePath2(artifact.path).endsWith(".mcp.json")) return artifact;
|
|
1933
|
+
const content = typeof artifact.content === "string" ? artifact.content : Buffer.from(artifact.content).toString("utf8");
|
|
1934
|
+
try {
|
|
1935
|
+
const parsed = JSON.parse(content);
|
|
1936
|
+
if (!parsed.mcpServers) return artifact;
|
|
1937
|
+
const { thoth_mem: _provider, ...mcpServers } = parsed.mcpServers;
|
|
1938
|
+
return { ...artifact, content: stableJson4({ ...parsed, mcpServers }) };
|
|
1939
|
+
} catch {
|
|
1940
|
+
return artifact;
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
2402
1943
|
function orderedManifest(manifest) {
|
|
2403
1944
|
const ordered = {};
|
|
2404
1945
|
for (const field of CLAUDE_PLUGIN_MANIFEST_FIELDS) {
|
|
@@ -2408,13 +1949,13 @@ function orderedManifest(manifest) {
|
|
|
2408
1949
|
return ordered;
|
|
2409
1950
|
}
|
|
2410
1951
|
function provenanceFor(artifact) {
|
|
2411
|
-
const
|
|
2412
|
-
if (
|
|
1952
|
+
const path = normalizePath2(artifact.path);
|
|
1953
|
+
if (path.endsWith("/")) return void 0;
|
|
2413
1954
|
const content = typeof artifact.content === "string" ? artifact.content : Buffer.from(artifact.content).toString("utf8");
|
|
2414
|
-
return { path
|
|
1955
|
+
return { path, kind: artifact.kind, sha256: sha256Hash(content) };
|
|
2415
1956
|
}
|
|
2416
1957
|
function renderClaudeCodePluginPackage(input) {
|
|
2417
|
-
const components =
|
|
1958
|
+
const components = input.componentArtifacts.map(withoutProviderAsset).sort(
|
|
2418
1959
|
(left, right) => normalizePath2(left.path).localeCompare(normalizePath2(right.path))
|
|
2419
1960
|
);
|
|
2420
1961
|
const provenance = components.map(provenanceFor).filter((entry) => entry !== void 0).sort((left, right) => left.path.localeCompare(right.path));
|
|
@@ -2438,65 +1979,6 @@ function renderClaudeCodePluginPackage(input) {
|
|
|
2438
1979
|
};
|
|
2439
1980
|
}
|
|
2440
1981
|
|
|
2441
|
-
// src/harness/writers/claude-code-skill-layout.ts
|
|
2442
|
-
import * as fs3 from "fs";
|
|
2443
|
-
import * as path3 from "path";
|
|
2444
|
-
var SKILLS_BASE_PATH = "skills";
|
|
2445
|
-
var SKILLS_MANIFEST_PATH = "skills/.thoth-agents-manifest.json";
|
|
2446
|
-
function renderClaudeCodeSkillLayout(input) {
|
|
2447
|
-
const artifacts = [];
|
|
2448
|
-
const diagnostics = [];
|
|
2449
|
-
const manifest = [];
|
|
2450
|
-
const sourceBaseRoot = input.packageRoot ?? input.projectRoot;
|
|
2451
|
-
for (const skill of [...input.skills].sort(
|
|
2452
|
-
(left, right) => left.name.localeCompare(right.name)
|
|
2453
|
-
)) {
|
|
2454
|
-
const sourceRoot = path3.join(sourceBaseRoot, skill.sourcePath);
|
|
2455
|
-
const files = collectSkillFiles(sourceRoot);
|
|
2456
|
-
if (files.length === 0) {
|
|
2457
|
-
diagnostics.push({
|
|
2458
|
-
severity: "warning",
|
|
2459
|
-
code: "claude-code.skill.source_missing",
|
|
2460
|
-
message: `Skipping Claude Code skill "${skill.name}" because source path "${skill.sourcePath}" was not found.`,
|
|
2461
|
-
harness: "claude",
|
|
2462
|
-
surface: "plugin-skills-directory",
|
|
2463
|
-
fallback: "diagnostic-only"
|
|
2464
|
-
});
|
|
2465
|
-
continue;
|
|
2466
|
-
}
|
|
2467
|
-
for (const file of files) {
|
|
2468
|
-
const relative4 = normalizeSkillPath(path3.relative(sourceRoot, file));
|
|
2469
|
-
const content = fs3.readFileSync(file, "utf8");
|
|
2470
|
-
const sourcePath = normalizeSkillPath(
|
|
2471
|
-
path3.relative(sourceBaseRoot, file)
|
|
2472
|
-
);
|
|
2473
|
-
const outputPath = `${SKILLS_BASE_PATH}/${skill.name}/${relative4}`;
|
|
2474
|
-
artifacts.push({
|
|
2475
|
-
harness: "claude",
|
|
2476
|
-
kind: "skill",
|
|
2477
|
-
path: outputPath,
|
|
2478
|
-
description: `Claude Code plugin-bundled skill artifact for ${skill.name}`,
|
|
2479
|
-
content
|
|
2480
|
-
});
|
|
2481
|
-
manifest.push({
|
|
2482
|
-
name: skill.name,
|
|
2483
|
-
sourcePath,
|
|
2484
|
-
outputPath,
|
|
2485
|
-
sha256: sha256Hash(content)
|
|
2486
|
-
});
|
|
2487
|
-
}
|
|
2488
|
-
}
|
|
2489
|
-
artifacts.push({
|
|
2490
|
-
harness: "claude",
|
|
2491
|
-
kind: "manifest",
|
|
2492
|
-
path: SKILLS_MANIFEST_PATH,
|
|
2493
|
-
description: "Generated Claude Code plugin-bundled skill source manifest with source hashes.",
|
|
2494
|
-
content: `${JSON.stringify({ generatedBy: "thoth-agents", skills: manifest }, null, 2)}
|
|
2495
|
-
`
|
|
2496
|
-
});
|
|
2497
|
-
return { artifacts, diagnostics };
|
|
2498
|
-
}
|
|
2499
|
-
|
|
2500
1982
|
// src/harness/writers/claude-code-subagent.ts
|
|
2501
1983
|
var CLAUDE_CODE_MODELS = [
|
|
2502
1984
|
"sonnet",
|
|
@@ -2521,6 +2003,7 @@ function renderClaudeCodeSubagent(input) {
|
|
|
2521
2003
|
`model: ${input.model}`,
|
|
2522
2004
|
...input.effort !== void 0 ? [`effort: ${input.effort}`] : [],
|
|
2523
2005
|
...input.tools !== void 0 ? [`tools: ${yamlScalar(input.tools)}`] : [],
|
|
2006
|
+
...input.disallowedTools !== void 0 ? [`disallowedTools: ${yamlScalar(input.disallowedTools)}`] : [],
|
|
2524
2007
|
"---"
|
|
2525
2008
|
].join("\n");
|
|
2526
2009
|
const body = input.instructions.trimEnd();
|
|
@@ -2549,48 +2032,6 @@ function getClaudeCodeAgentModel(role, config) {
|
|
|
2549
2032
|
if (override && isClaudeCodeModel(override)) return override;
|
|
2550
2033
|
return CLAUDE_CODE_SUBAGENT_DEFAULT_MODELS[role.name];
|
|
2551
2034
|
}
|
|
2552
|
-
function claudeCodePromptSections(roleName) {
|
|
2553
|
-
switch (roleName) {
|
|
2554
|
-
case "orchestrator":
|
|
2555
|
-
return createOrchestratorPromptSections();
|
|
2556
|
-
case "explorer":
|
|
2557
|
-
case "librarian":
|
|
2558
|
-
case "oracle":
|
|
2559
|
-
return createReadOnlySpecialistPromptSections(roleName);
|
|
2560
|
-
case "designer":
|
|
2561
|
-
case "quick":
|
|
2562
|
-
case "deep":
|
|
2563
|
-
return createWriteCapableSpecialistPromptSections(roleName);
|
|
2564
|
-
}
|
|
2565
|
-
}
|
|
2566
|
-
function claudeCodeModelFamilyPromptSection(roleName, model) {
|
|
2567
|
-
const section = createModelFamilySection(roleName, model);
|
|
2568
|
-
return section ? renderPromptSection(section, CLAUDE_CODE_PROMPT_DIALECT) : void 0;
|
|
2569
|
-
}
|
|
2570
|
-
function claudeCodeStepBudgetPromptSection(steps) {
|
|
2571
|
-
const section = createStepBudgetSection(steps);
|
|
2572
|
-
return section ? renderPromptSection(section, CLAUDE_CODE_PROMPT_DIALECT) : void 0;
|
|
2573
|
-
}
|
|
2574
|
-
function renderClaudeCodeRolePrompt(roleName, config, model) {
|
|
2575
|
-
const promptOverrides = loadAgentPrompt(roleName, config?.preset);
|
|
2576
|
-
const override = getAgentOverride(config, roleName);
|
|
2577
|
-
const basePrompt = renderRolePrompt(
|
|
2578
|
-
claudeCodePromptSections(roleName),
|
|
2579
|
-
CLAUDE_CODE_PROMPT_DIALECT
|
|
2580
|
-
);
|
|
2581
|
-
const prompt = composeAgentPrompt({
|
|
2582
|
-
basePrompt,
|
|
2583
|
-
customPrompt: promptOverrides.prompt,
|
|
2584
|
-
customAppendPrompt: appendPromptSections(
|
|
2585
|
-
claudeCodeModelFamilyPromptSection(roleName, model),
|
|
2586
|
-
promptOverrides.appendPrompt
|
|
2587
|
-
)
|
|
2588
|
-
});
|
|
2589
|
-
return appendPromptSections(
|
|
2590
|
-
prompt,
|
|
2591
|
-
claudeCodeStepBudgetPromptSection(override?.steps)
|
|
2592
|
-
);
|
|
2593
|
-
}
|
|
2594
2035
|
function claudeCodeRoleInstructions(role) {
|
|
2595
2036
|
return [
|
|
2596
2037
|
"<role-operational-contract>",
|
|
@@ -2599,8 +2040,10 @@ function claudeCodeRoleInstructions(role) {
|
|
|
2599
2040
|
`- Scope: ${role.scope}`,
|
|
2600
2041
|
`- Responsibility: ${role.responsibility}`,
|
|
2601
2042
|
"- Use AskUserQuestion for local blocking decisions.",
|
|
2602
|
-
`- ${role.name} runs as an auto-discovered Claude Code plugin subagent invoked via
|
|
2603
|
-
|
|
2043
|
+
`- ${role.name} runs as an auto-discovered Claude Code plugin subagent invoked via Agent(subagent_type: ${claudeCodeSubagentType(role.name)}); plugin subagents are namespaced with the plugin name. The orchestrator is the main Claude Code session.`,
|
|
2044
|
+
...role.mode === "read-only" ? [
|
|
2045
|
+
"- Write and Edit are denied in frontmatter while all other inherited tools, including MCP tools, remain available."
|
|
2046
|
+
] : [],
|
|
2604
2047
|
...role.toolGovernance.map((rule) => `- ${rule}`),
|
|
2605
2048
|
...role.verification.map((rule) => `- ${rule}`),
|
|
2606
2049
|
"</role-operational-contract>"
|
|
@@ -2609,38 +2052,39 @@ function claudeCodeRoleInstructions(role) {
|
|
|
2609
2052
|
function roleInstructions2(role, config) {
|
|
2610
2053
|
const model = getClaudeCodeAgentModel(role, config);
|
|
2611
2054
|
return [
|
|
2612
|
-
|
|
2055
|
+
renderConfiguredRolePrompt({
|
|
2056
|
+
role: role.name,
|
|
2057
|
+
dialect: CLAUDE_CODE_PROMPT_DIALECT,
|
|
2058
|
+
config,
|
|
2059
|
+
model
|
|
2060
|
+
}),
|
|
2613
2061
|
claudeCodeRoleInstructions(role),
|
|
2614
2062
|
renderMemoryGovernanceInstructions(role, CLAUDE_CODE_PROMPT_DIALECT)
|
|
2615
2063
|
].join("\n\n");
|
|
2616
2064
|
}
|
|
2617
2065
|
function renderClaudeCodeRootInstructions(config) {
|
|
2618
2066
|
const rootOverride = getAgentOverride(config, "orchestrator");
|
|
2619
|
-
const rootPrompt =
|
|
2620
|
-
"orchestrator",
|
|
2067
|
+
const rootPrompt = renderConfiguredRolePrompt({
|
|
2068
|
+
role: "orchestrator",
|
|
2069
|
+
dialect: CLAUDE_CODE_PROMPT_DIALECT,
|
|
2621
2070
|
config,
|
|
2622
|
-
rootOverride?.model ?? DEFAULT_MODELS.orchestrator
|
|
2623
|
-
);
|
|
2071
|
+
model: rootOverride?.model ?? DEFAULT_MODELS.orchestrator
|
|
2072
|
+
});
|
|
2624
2073
|
const specialists = getAgentPackContract().roles.filter((role) => role.name !== "orchestrator").map((role) => claudeCodeSubagentType(role.name)).join(", ");
|
|
2625
2074
|
return [
|
|
2626
2075
|
rootPrompt,
|
|
2627
2076
|
"<claude-code-runtime>",
|
|
2628
|
-
"- You
|
|
2629
|
-
|
|
2630
|
-
"-
|
|
2631
|
-
"-
|
|
2632
|
-
|
|
2633
|
-
"-
|
|
2634
|
-
'- 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.',
|
|
2635
|
-
"- Use AskUserQuestion for blocking user decisions; do not ask those questions in plain prose.",
|
|
2636
|
-
"- Track progress with TodoWrite; subagents do not own progress checkboxes or root-only memory.",
|
|
2637
|
-
"- 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.",
|
|
2077
|
+
"- You are the Claude Code adaptive root activated by plugin settings.json.",
|
|
2078
|
+
`- Delegate only for net gain through Agent with \`subagent_type\` set to one of these plugin-namespaced specialists: ${specialists}. Always keep the ${CLAUDE_CODE_SUBAGENT_NAMESPACE}: prefix.`,
|
|
2079
|
+
"- Subagents cannot delegate further. Parallelize only independent work and maintain one writer per mutable surface.",
|
|
2080
|
+
"- Read-only roles deny Write and Edit while retaining other inherited tools, including MCP tools. Coordination-agent path scope remains instruction-level.",
|
|
2081
|
+
"- Use AskUserQuestion only for blocking material choices and TodoWrite only for genuine multi-step progress.",
|
|
2082
|
+
"- Installed provider guidance owns memory, persistence, hooks, MCP lifecycle, and recovery mechanics.",
|
|
2638
2083
|
"</claude-code-runtime>"
|
|
2639
2084
|
].join("\n");
|
|
2640
2085
|
}
|
|
2641
2086
|
function claudeCodeMcpServers() {
|
|
2642
2087
|
const [exaCommand = "", ...exaArgs] = exa.command;
|
|
2643
|
-
const [thothCommand = "", ...thothArgs] = DEFAULT_THOTH_COMMAND;
|
|
2644
2088
|
return {
|
|
2645
2089
|
exa: {
|
|
2646
2090
|
command: exaCommand,
|
|
@@ -2648,11 +2092,7 @@ function claudeCodeMcpServers() {
|
|
|
2648
2092
|
...exa.environment && Object.keys(exa.environment).length > 0 ? { env: exa.environment } : {}
|
|
2649
2093
|
},
|
|
2650
2094
|
context7: { type: "http", url: CONTEXT7_MCP_URL },
|
|
2651
|
-
grep_app: { type: "http", url: GREP_APP_MCP_URL }
|
|
2652
|
-
thoth_mem: {
|
|
2653
|
-
command: thothCommand,
|
|
2654
|
-
...thothArgs.length > 0 ? { args: thothArgs } : {}
|
|
2655
|
-
}
|
|
2095
|
+
grep_app: { type: "http", url: GREP_APP_MCP_URL }
|
|
2656
2096
|
};
|
|
2657
2097
|
}
|
|
2658
2098
|
function stableJson5(value) {
|
|
@@ -2678,7 +2118,7 @@ function createPluginManifest(context2) {
|
|
|
2678
2118
|
return {
|
|
2679
2119
|
name: "thoth-agents",
|
|
2680
2120
|
version: readRootPackageVersion2(context2),
|
|
2681
|
-
description: "
|
|
2121
|
+
description: "Adaptive multi-harness agent pack with seven roles and a runtime-autonomous Spec Kit-compatible SDD bundle for Claude Code.",
|
|
2682
2122
|
author: { name: "thoth-agents" }
|
|
2683
2123
|
};
|
|
2684
2124
|
}
|
|
@@ -2691,6 +2131,7 @@ function renderSubagentArtifacts(config) {
|
|
|
2691
2131
|
name: role.name,
|
|
2692
2132
|
description: role.responsibility,
|
|
2693
2133
|
model: getClaudeCodeAgentModel(role, config),
|
|
2134
|
+
...role.mode === "read-only" ? { disallowedTools: "Write, Edit" } : {},
|
|
2694
2135
|
instructions: roleInstructions2(role, config)
|
|
2695
2136
|
});
|
|
2696
2137
|
artifacts.push({
|
|
@@ -2709,7 +2150,7 @@ function renderOrchestratorArtifact(config) {
|
|
|
2709
2150
|
);
|
|
2710
2151
|
const content = renderClaudeCodeSubagent({
|
|
2711
2152
|
name: "orchestrator",
|
|
2712
|
-
description: orchestrator?.responsibility ?? "
|
|
2153
|
+
description: orchestrator?.responsibility ?? "Adaptive root coordinator for direct work, SDD routing, and specialist dispatch.",
|
|
2713
2154
|
model: "inherit",
|
|
2714
2155
|
instructions: renderClaudeCodeRootInstructions(config)
|
|
2715
2156
|
});
|
|
@@ -2745,12 +2186,6 @@ var claudeCodeAdapter = {
|
|
|
2745
2186
|
content: stableJson5({ agent: "orchestrator" })
|
|
2746
2187
|
}
|
|
2747
2188
|
];
|
|
2748
|
-
const skillLayout = renderClaudeCodeSkillLayout({
|
|
2749
|
-
projectRoot: context2.projectRoot,
|
|
2750
|
-
...hasPackageRoot(context2) ? { packageRoot: context2.packageRoot } : {},
|
|
2751
|
-
skills: getSkillRegistry()
|
|
2752
|
-
});
|
|
2753
|
-
componentArtifacts.push(...skillLayout.artifacts);
|
|
2754
2189
|
const pluginPackage = renderClaudeCodePluginPackage({
|
|
2755
2190
|
manifest: createPluginManifest(context2),
|
|
2756
2191
|
componentArtifacts
|
|
@@ -2758,19 +2193,15 @@ var claudeCodeAdapter = {
|
|
|
2758
2193
|
return {
|
|
2759
2194
|
harness: "claude",
|
|
2760
2195
|
artifacts: pluginPackage.artifacts,
|
|
2761
|
-
diagnostics:
|
|
2196
|
+
diagnostics: pluginPackage.diagnostics
|
|
2762
2197
|
};
|
|
2763
2198
|
}
|
|
2764
2199
|
};
|
|
2765
2200
|
|
|
2766
2201
|
// src/cli/claude-code-install.ts
|
|
2767
|
-
import {
|
|
2768
|
-
import { join as join8 } from "path";
|
|
2769
|
-
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
2202
|
+
import { spawnSync } from "child_process";
|
|
2770
2203
|
|
|
2771
2204
|
// src/cli/claude-code-paths.ts
|
|
2772
|
-
import { homedir as homedir2 } from "os";
|
|
2773
|
-
import { join as join7 } from "path";
|
|
2774
2205
|
var CLAUDE_CODE_ROLE_NAMES = [
|
|
2775
2206
|
"explorer",
|
|
2776
2207
|
"librarian",
|
|
@@ -2779,320 +2210,386 @@ var CLAUDE_CODE_ROLE_NAMES = [
|
|
|
2779
2210
|
"quick",
|
|
2780
2211
|
"deep"
|
|
2781
2212
|
];
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2213
|
+
|
|
2214
|
+
// src/cli/claude-code-install.ts
|
|
2215
|
+
var MARKETPLACE_NAME = "thoth-agents";
|
|
2216
|
+
var MARKETPLACE_SOURCE = "EremesNG/thoth-agents";
|
|
2217
|
+
var PLUGIN_ID = "thoth-agents@thoth-agents";
|
|
2218
|
+
var MARKETPLACE_TARGET = "claude://marketplaces/thoth-agents";
|
|
2219
|
+
var PLUGIN_TARGET = "claude://plugins/thoth-agents@thoth-agents";
|
|
2220
|
+
function defaultCommandExecutor(command, args, options) {
|
|
2221
|
+
const result = spawnSync(command, [...args], {
|
|
2222
|
+
cwd: options.cwd,
|
|
2223
|
+
encoding: "utf8",
|
|
2224
|
+
shell: false,
|
|
2225
|
+
windowsHide: true
|
|
2226
|
+
});
|
|
2785
2227
|
return {
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
agentPaths: CLAUDE_CODE_ROLE_NAMES.map((role) => ({
|
|
2790
|
-
role,
|
|
2791
|
-
path: join7(pluginRoot, "agents", `${role}.md`)
|
|
2792
|
-
})),
|
|
2793
|
-
mcpPath: join7(pluginRoot, ".mcp.json"),
|
|
2794
|
-
hooksPath: join7(pluginRoot, "hooks", "hooks.json"),
|
|
2795
|
-
skillsDir: join7(pluginRoot, "skills"),
|
|
2796
|
-
managedModelsPath: join7(pluginRoot, ".thoth-agents-managed-models.json")
|
|
2228
|
+
exitCode: result.status ?? (result.error ? 1 : 0),
|
|
2229
|
+
stdout: result.stdout ?? "",
|
|
2230
|
+
stderr: result.stderr ?? result.error?.message ?? ""
|
|
2797
2231
|
};
|
|
2798
2232
|
}
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
var CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION = 1;
|
|
2802
|
-
var isClaudeCodeModelAlias = isClaudeCodeModel;
|
|
2803
|
-
function parseSubagentModel(content) {
|
|
2804
|
-
return /^model:\s*(\S+)\s*$/m.exec(content)?.[1];
|
|
2233
|
+
function isRecord3(value) {
|
|
2234
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2805
2235
|
}
|
|
2806
|
-
function
|
|
2807
|
-
|
|
2808
|
-
|
|
2236
|
+
function parseJsonRecords(content) {
|
|
2237
|
+
try {
|
|
2238
|
+
const parsed = JSON.parse(content);
|
|
2239
|
+
if (Array.isArray(parsed) && parsed.every(isRecord3)) return parsed;
|
|
2240
|
+
if (isRecord3(parsed)) return [parsed];
|
|
2241
|
+
return null;
|
|
2242
|
+
} catch {
|
|
2243
|
+
return null;
|
|
2809
2244
|
}
|
|
2810
|
-
return content;
|
|
2811
2245
|
}
|
|
2812
|
-
function
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
if (effort === void 0) {
|
|
2817
|
-
return content.replace(/^effort:\s*\S+\s*\n/m, "");
|
|
2246
|
+
function isCanonicalMarketplace(entry) {
|
|
2247
|
+
if (entry.name !== MARKETPLACE_NAME) return false;
|
|
2248
|
+
if (entry.repo === MARKETPLACE_SOURCE || entry.source === MARKETPLACE_SOURCE) {
|
|
2249
|
+
return true;
|
|
2818
2250
|
}
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
}
|
|
2822
|
-
return content.replace(/^(model:\s*\S+\s*)$/m, `$1
|
|
2823
|
-
effort: ${effort}`);
|
|
2824
|
-
}
|
|
2825
|
-
function emptyManagedModelState3() {
|
|
2826
|
-
return emptyManagedModelState(CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION);
|
|
2251
|
+
const source = entry.source;
|
|
2252
|
+
return isRecord3(source) && (source.repo === MARKETPLACE_SOURCE || source.source === MARKETPLACE_SOURCE);
|
|
2827
2253
|
}
|
|
2828
|
-
function
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2254
|
+
function inspectClaudeManager(executor, cwd, scope) {
|
|
2255
|
+
const marketplaceResult = executor(
|
|
2256
|
+
"claude",
|
|
2257
|
+
["plugin", "marketplace", "list", "--json"],
|
|
2258
|
+
{ cwd }
|
|
2832
2259
|
);
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
if (
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2260
|
+
const pluginResult = executor("claude", ["plugin", "list", "--json"], {
|
|
2261
|
+
cwd
|
|
2262
|
+
});
|
|
2263
|
+
const diagnostics = [];
|
|
2264
|
+
if (marketplaceResult.exitCode !== 0 || pluginResult.exitCode !== 0) {
|
|
2265
|
+
diagnostics.push(
|
|
2266
|
+
"Claude Code native plugin state could not be inspected; no manager mutation will be attempted."
|
|
2267
|
+
);
|
|
2268
|
+
return {
|
|
2269
|
+
ready: false,
|
|
2270
|
+
marketplace: "unknown",
|
|
2271
|
+
plugin: "unknown",
|
|
2272
|
+
diagnostics
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2275
|
+
const marketplaces = parseJsonRecords(marketplaceResult.stdout);
|
|
2276
|
+
const plugins = parseJsonRecords(pluginResult.stdout);
|
|
2277
|
+
if (!marketplaces || !plugins) {
|
|
2278
|
+
diagnostics.push(
|
|
2279
|
+
"Claude Code returned unparseable plugin manager JSON; no manager mutation will be attempted."
|
|
2280
|
+
);
|
|
2281
|
+
return {
|
|
2282
|
+
ready: false,
|
|
2283
|
+
marketplace: "unknown",
|
|
2284
|
+
plugin: "unknown",
|
|
2285
|
+
diagnostics
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
const namedMarketplaces = marketplaces.filter(
|
|
2289
|
+
(entry) => entry.name === MARKETPLACE_NAME
|
|
2856
2290
|
);
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
function applyConfiguredModel(content, targetPath, role, state, nextState, reset) {
|
|
2861
|
-
const renderedModel = parseSubagentModel(content);
|
|
2862
|
-
if (!role || !renderedModel) return content;
|
|
2863
|
-
nextState.models[role] = renderedModel;
|
|
2864
|
-
const configured = reset ? void 0 : state.configuredModels?.[role];
|
|
2865
|
-
let updated = content;
|
|
2866
|
-
if (configured !== void 0) {
|
|
2867
|
-
nextState.configuredModels ??= {};
|
|
2868
|
-
nextState.configuredModels[role] = configured;
|
|
2869
|
-
updated = replaceSubagentModel(updated, configured);
|
|
2870
|
-
}
|
|
2871
|
-
const configuredEffort = reset ? void 0 : state.configuredEfforts?.[role];
|
|
2872
|
-
if (configuredEffort !== void 0) {
|
|
2873
|
-
nextState.configuredEfforts ??= {};
|
|
2874
|
-
nextState.configuredEfforts[role] = configuredEffort;
|
|
2291
|
+
let marketplace = "absent";
|
|
2292
|
+
if (namedMarketplaces.length > 0) {
|
|
2293
|
+
marketplace = namedMarketplaces.every(isCanonicalMarketplace) ? "installed" : "conflict";
|
|
2875
2294
|
}
|
|
2876
|
-
if (
|
|
2877
|
-
|
|
2878
|
-
|
|
2295
|
+
if (marketplace === "conflict") {
|
|
2296
|
+
diagnostics.push(
|
|
2297
|
+
"A Claude marketplace named thoth-agents is registered from a different source; resolve it through Claude Code before retrying."
|
|
2879
2298
|
);
|
|
2880
|
-
updated = replaceSubagentEffort(updated, installedEffort);
|
|
2881
2299
|
}
|
|
2882
|
-
|
|
2300
|
+
const matchingPlugins = plugins.filter(
|
|
2301
|
+
(entry) => entry.id === PLUGIN_ID && entry.scope === scope
|
|
2302
|
+
);
|
|
2303
|
+
let plugin = "absent";
|
|
2304
|
+
if (matchingPlugins.some((entry) => entry.enabled === true)) {
|
|
2305
|
+
plugin = "installed";
|
|
2306
|
+
} else if (matchingPlugins.length > 0) {
|
|
2307
|
+
plugin = "disabled";
|
|
2308
|
+
}
|
|
2309
|
+
return {
|
|
2310
|
+
ready: marketplace !== "conflict",
|
|
2311
|
+
marketplace,
|
|
2312
|
+
plugin,
|
|
2313
|
+
diagnostics
|
|
2314
|
+
};
|
|
2883
2315
|
}
|
|
2884
|
-
function
|
|
2885
|
-
|
|
2886
|
-
scope: config.scope,
|
|
2887
|
-
projectRoot: config.projectRoot,
|
|
2888
|
-
homeDir: config.homeDir
|
|
2889
|
-
});
|
|
2890
|
-
const packageRoot = resolvePackageRoot2(config.packageRoot);
|
|
2891
|
-
const render = claudeCodeAdapter.render({
|
|
2892
|
-
projectRoot: config.projectRoot,
|
|
2893
|
-
...packageRoot ? { packageRoot } : {}
|
|
2894
|
-
});
|
|
2895
|
-
const state = readManagedModelState3(targets.managedModelsPath);
|
|
2896
|
-
const nextState = emptyManagedModelState3();
|
|
2897
|
-
const items = render.artifacts.map((artifact) => {
|
|
2898
|
-
const role = roleForArtifact(artifact);
|
|
2899
|
-
const rendered = String(artifact.content ?? "");
|
|
2900
|
-
const targetPath = join8(targets.pluginRoot, artifact.path);
|
|
2901
|
-
const content = role !== void 0 ? applyConfiguredModel(
|
|
2902
|
-
rendered,
|
|
2903
|
-
targetPath,
|
|
2904
|
-
role,
|
|
2905
|
-
state,
|
|
2906
|
-
nextState,
|
|
2907
|
-
config.reset
|
|
2908
|
-
) : rendered;
|
|
2316
|
+
function commandItem(action, scope, projectRoot) {
|
|
2317
|
+
if (action === "register-marketplace") {
|
|
2909
2318
|
return {
|
|
2910
|
-
kind:
|
|
2911
|
-
action
|
|
2912
|
-
targetPath,
|
|
2913
|
-
description:
|
|
2914
|
-
requiresBackup:
|
|
2915
|
-
|
|
2916
|
-
|
|
2319
|
+
kind: "native-marketplace",
|
|
2320
|
+
action,
|
|
2321
|
+
targetPath: MARKETPLACE_TARGET,
|
|
2322
|
+
description: "Register the package-owned thoth-agents Claude marketplace.",
|
|
2323
|
+
requiresBackup: false,
|
|
2324
|
+
command: {
|
|
2325
|
+
executable: "claude",
|
|
2326
|
+
args: [
|
|
2327
|
+
"plugin",
|
|
2328
|
+
"marketplace",
|
|
2329
|
+
"add",
|
|
2330
|
+
MARKETPLACE_SOURCE,
|
|
2331
|
+
"--scope",
|
|
2332
|
+
scope
|
|
2333
|
+
],
|
|
2334
|
+
cwd: projectRoot
|
|
2335
|
+
}
|
|
2917
2336
|
};
|
|
2918
|
-
}
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
description
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2337
|
+
}
|
|
2338
|
+
let command = "install";
|
|
2339
|
+
let description = "Install the thoth-agents Claude plugin from its native marketplace.";
|
|
2340
|
+
if (action === "enable-plugin") {
|
|
2341
|
+
command = "enable";
|
|
2342
|
+
description = "Enable the manager-owned thoth-agents Claude plugin.";
|
|
2343
|
+
} else if (action === "update-plugin") {
|
|
2344
|
+
command = "update";
|
|
2345
|
+
description = "Update the manager-owned thoth-agents Claude plugin.";
|
|
2346
|
+
}
|
|
2347
|
+
return {
|
|
2348
|
+
kind: "native-plugin",
|
|
2349
|
+
action,
|
|
2350
|
+
targetPath: PLUGIN_TARGET,
|
|
2351
|
+
description,
|
|
2352
|
+
requiresBackup: false,
|
|
2353
|
+
command: {
|
|
2354
|
+
executable: "claude",
|
|
2355
|
+
args: ["plugin", command, PLUGIN_ID, "--scope", scope],
|
|
2356
|
+
cwd: projectRoot
|
|
2357
|
+
}
|
|
2358
|
+
};
|
|
2359
|
+
}
|
|
2360
|
+
function buildClaudeCodeSetupPlan(config) {
|
|
2361
|
+
const commandExecutor = config.commandExecutor ?? defaultCommandExecutor;
|
|
2362
|
+
const inspection = inspectClaudeManager(
|
|
2363
|
+
commandExecutor,
|
|
2364
|
+
config.projectRoot,
|
|
2365
|
+
config.scope
|
|
2366
|
+
);
|
|
2367
|
+
const items = [];
|
|
2368
|
+
if (inspection.ready) {
|
|
2369
|
+
if (inspection.marketplace === "absent") {
|
|
2370
|
+
items.push(
|
|
2371
|
+
commandItem("register-marketplace", config.scope, config.projectRoot)
|
|
2372
|
+
);
|
|
2373
|
+
}
|
|
2374
|
+
if (inspection.plugin === "absent") {
|
|
2375
|
+
items.push(
|
|
2376
|
+
commandItem("install-plugin", config.scope, config.projectRoot)
|
|
2377
|
+
);
|
|
2378
|
+
} else if (inspection.plugin === "disabled") {
|
|
2379
|
+
items.push(
|
|
2380
|
+
commandItem("enable-plugin", config.scope, config.projectRoot)
|
|
2381
|
+
);
|
|
2382
|
+
if (config.refresh === true || config.reset) {
|
|
2383
|
+
items.push(
|
|
2384
|
+
commandItem("update-plugin", config.scope, config.projectRoot)
|
|
2385
|
+
);
|
|
2386
|
+
}
|
|
2387
|
+
} else if (config.refresh === true || config.reset) {
|
|
2388
|
+
items.push(
|
|
2389
|
+
commandItem("update-plugin", config.scope, config.projectRoot)
|
|
2390
|
+
);
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2927
2393
|
return {
|
|
2928
2394
|
dryRun: config.dryRun === true,
|
|
2929
2395
|
reset: config.reset,
|
|
2930
2396
|
items,
|
|
2931
|
-
pluginRoot:
|
|
2397
|
+
pluginRoot: PLUGIN_TARGET,
|
|
2398
|
+
ready: inspection.ready,
|
|
2399
|
+
scope: config.scope,
|
|
2400
|
+
projectRoot: config.projectRoot,
|
|
2401
|
+
commandExecutor,
|
|
2932
2402
|
diagnostics: [
|
|
2933
|
-
|
|
2934
|
-
"
|
|
2935
|
-
"
|
|
2403
|
+
...inspection.diagnostics,
|
|
2404
|
+
"Claude Code installs thoth-agents from EremesNG/thoth-agents through its native marketplace and owns the cached plugin files.",
|
|
2405
|
+
"Restart Claude Code or run /reload-plugins after installation; use /plugin to inspect marketplace and plugin state.",
|
|
2406
|
+
"Provider capability is owned by the external provider and is not established by this thoth-agents setup plan."
|
|
2936
2407
|
],
|
|
2937
2408
|
disclaimers: [
|
|
2938
|
-
"The orchestrator agent
|
|
2939
|
-
"
|
|
2940
|
-
"
|
|
2941
|
-
"User-scope skills-directory plugins load hooks and MCP servers without extra approval; project-scope requires accepting the workspace trust dialog."
|
|
2409
|
+
"The native plugin activates its orchestrator agent as the main thread through plugin-root settings.json.",
|
|
2410
|
+
"Read-only subagents remain instruction/tool-denylist constrained; capability parity with other harnesses is not implied.",
|
|
2411
|
+
"Per-role Claude model rewrites are unavailable because native marketplace cache contents are manager-owned and immutable to thoth-agents."
|
|
2942
2412
|
]
|
|
2943
2413
|
};
|
|
2944
2414
|
}
|
|
2415
|
+
function uniqueMessages2(messages) {
|
|
2416
|
+
return [...new Set(messages)];
|
|
2417
|
+
}
|
|
2418
|
+
function boundedCommandFailure(item, result) {
|
|
2419
|
+
const detail = result.stderr.trim().replace(/\s+/g, " ").slice(0, 240);
|
|
2420
|
+
return `${item.description} Claude exited with code ${result.exitCode}${detail ? `: ${detail}` : "."}`;
|
|
2421
|
+
}
|
|
2945
2422
|
function applyClaudeCodeSetup(plan) {
|
|
2946
2423
|
const changed = [];
|
|
2947
|
-
const diagnostics =
|
|
2424
|
+
const diagnostics = uniqueMessages2([
|
|
2948
2425
|
...plan.diagnostics,
|
|
2949
2426
|
...plan.disclaimers
|
|
2950
2427
|
]);
|
|
2951
|
-
if (plan.
|
|
2952
|
-
try {
|
|
2953
|
-
for (const item of plan.items) {
|
|
2954
|
-
if (writeTextWithBackup(item.targetPath, item.content)) {
|
|
2955
|
-
changed.push(item.targetPath);
|
|
2956
|
-
}
|
|
2957
|
-
}
|
|
2958
|
-
return { success: true, changed, diagnostics };
|
|
2959
|
-
} catch (error) {
|
|
2428
|
+
if (!plan.ready) {
|
|
2960
2429
|
return {
|
|
2961
2430
|
success: false,
|
|
2962
2431
|
changed,
|
|
2963
2432
|
diagnostics,
|
|
2964
|
-
error:
|
|
2433
|
+
error: "Claude Code native plugin manager state is not safe to mutate."
|
|
2965
2434
|
};
|
|
2966
2435
|
}
|
|
2967
|
-
}
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2436
|
+
if (plan.dryRun) return { success: true, changed, diagnostics };
|
|
2437
|
+
for (const item of plan.items) {
|
|
2438
|
+
const result = plan.commandExecutor(
|
|
2439
|
+
item.command.executable,
|
|
2440
|
+
item.command.args,
|
|
2441
|
+
{ cwd: item.command.cwd }
|
|
2442
|
+
);
|
|
2443
|
+
if (result.exitCode !== 0) {
|
|
2444
|
+
return {
|
|
2445
|
+
success: false,
|
|
2446
|
+
changed,
|
|
2447
|
+
diagnostics,
|
|
2448
|
+
error: boundedCommandFailure(item, result)
|
|
2449
|
+
};
|
|
2450
|
+
}
|
|
2451
|
+
changed.push(item.targetPath);
|
|
2977
2452
|
}
|
|
2978
|
-
const
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
});
|
|
2983
|
-
const stateItem = plan.items.find(
|
|
2984
|
-
(item) => item.action === "write-managed-model-state"
|
|
2453
|
+
const inspection = inspectClaudeManager(
|
|
2454
|
+
plan.commandExecutor,
|
|
2455
|
+
plan.projectRoot,
|
|
2456
|
+
plan.scope
|
|
2985
2457
|
);
|
|
2986
|
-
|
|
2987
|
-
if (
|
|
2988
|
-
return {
|
|
2989
|
-
success: false,
|
|
2990
|
-
changed: [],
|
|
2991
|
-
diagnostics: plan.diagnostics,
|
|
2992
|
-
error: "Claude Code managed model state target was not found."
|
|
2993
|
-
};
|
|
2994
|
-
}
|
|
2995
|
-
const changed = [];
|
|
2996
|
-
const diagnostics = uniqueMessages([
|
|
2997
|
-
...plan.diagnostics,
|
|
2998
|
-
...plan.disclaimers
|
|
2999
|
-
]);
|
|
3000
|
-
const state = parseManagedModelStateJson2(stateItem?.content);
|
|
3001
|
-
const nextState = {
|
|
3002
|
-
version: CLAUDE_CODE_MANAGED_MODEL_STATE_VERSION,
|
|
3003
|
-
models: { ...state.models },
|
|
3004
|
-
...state.configuredModels ? { configuredModels: { ...state.configuredModels } } : {},
|
|
3005
|
-
...state.configuredEfforts ? { configuredEfforts: { ...state.configuredEfforts } } : {}
|
|
3006
|
-
};
|
|
3007
|
-
try {
|
|
3008
|
-
for (const override of overrides) {
|
|
3009
|
-
if (!isClaudeCodeModelAlias(override.model) && override.catalogId !== override.model) {
|
|
3010
|
-
throw new Error(
|
|
3011
|
-
`Unsupported Claude Code model "${override.model}" for ${override.role}; use sonnet, opus, haiku, or inherit.`
|
|
3012
|
-
);
|
|
3013
|
-
}
|
|
3014
|
-
const roleItem = plan.items.find(
|
|
3015
|
-
(item) => item.kind === "subagent" && item.role === override.role
|
|
3016
|
-
);
|
|
3017
|
-
if (!roleItem) {
|
|
3018
|
-
throw new Error(`Missing Claude Code subagent for ${override.role}.`);
|
|
3019
|
-
}
|
|
3020
|
-
const before = existsSync6(roleItem.targetPath) ? readFileSync7(roleItem.targetPath, "utf8") : roleItem.content;
|
|
3021
|
-
let updated = replaceSubagentModel(before, override.model);
|
|
3022
|
-
if (override.clearEffort) {
|
|
3023
|
-
updated = replaceSubagentEffort(updated, void 0);
|
|
3024
|
-
} else if (override.effort !== void 0) {
|
|
3025
|
-
updated = replaceSubagentEffort(updated, override.effort);
|
|
3026
|
-
}
|
|
3027
|
-
if (writeTextWithBackup(roleItem.targetPath, updated)) {
|
|
3028
|
-
changed.push(roleItem.targetPath);
|
|
3029
|
-
}
|
|
3030
|
-
nextState.configuredModels ??= {};
|
|
3031
|
-
nextState.configuredModels[override.role] = override.model;
|
|
3032
|
-
if (override.clearEffort) {
|
|
3033
|
-
if (nextState.configuredEfforts) {
|
|
3034
|
-
delete nextState.configuredEfforts[override.role];
|
|
3035
|
-
}
|
|
3036
|
-
} else if (override.effort !== void 0) {
|
|
3037
|
-
nextState.configuredEfforts ??= {};
|
|
3038
|
-
nextState.configuredEfforts[override.role] = override.effort;
|
|
3039
|
-
}
|
|
3040
|
-
}
|
|
3041
|
-
if (writeTextWithBackup(statePath, stableJson3(nextState))) {
|
|
3042
|
-
changed.push(statePath);
|
|
3043
|
-
}
|
|
3044
|
-
return { success: true, changed, diagnostics };
|
|
3045
|
-
} catch (error) {
|
|
2458
|
+
diagnostics.push(...inspection.diagnostics);
|
|
2459
|
+
if (inspection.marketplace !== "installed" || inspection.plugin !== "installed") {
|
|
3046
2460
|
return {
|
|
3047
2461
|
success: false,
|
|
3048
2462
|
changed,
|
|
3049
|
-
diagnostics,
|
|
3050
|
-
error:
|
|
2463
|
+
diagnostics: uniqueMessages2(diagnostics),
|
|
2464
|
+
error: "Claude Code did not verify the expected marketplace and enabled plugin after mutation."
|
|
3051
2465
|
};
|
|
3052
2466
|
}
|
|
2467
|
+
return { success: true, changed, diagnostics: uniqueMessages2(diagnostics) };
|
|
3053
2468
|
}
|
|
3054
2469
|
function formatClaudeCodeSetupPlan(plan) {
|
|
3055
2470
|
const lines = plan.items.map(
|
|
3056
|
-
(item) => `- ${item.action}: ${item.
|
|
2471
|
+
(item) => `- ${item.action}: ${item.command.executable} ${item.command.args.join(" ")}`
|
|
3057
2472
|
);
|
|
3058
2473
|
return ["Claude Code setup plan:", ...lines].join("\n");
|
|
3059
2474
|
}
|
|
3060
2475
|
|
|
3061
|
-
// src/cli/
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
2476
|
+
// src/cli/skills.ts
|
|
2477
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
2478
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2479
|
+
import { homedir as homedir2 } from "os";
|
|
2480
|
+
import { join as join3 } from "path";
|
|
2481
|
+
var SKILLS_CLI_AGENT = {
|
|
2482
|
+
opencode: "opencode",
|
|
2483
|
+
codex: "codex",
|
|
2484
|
+
claude: "claude-code"
|
|
2485
|
+
};
|
|
2486
|
+
var GLOBAL_SKILL_ROOT = {
|
|
2487
|
+
opencode: [".config", "opencode", "skills"],
|
|
2488
|
+
codex: [".codex", "skills"],
|
|
2489
|
+
claude: [".claude", "skills"]
|
|
2490
|
+
};
|
|
2491
|
+
var REQUIRED_SKILLS = [
|
|
2492
|
+
{
|
|
2493
|
+
name: "simplify",
|
|
2494
|
+
repo: "https://github.com/EremesNG/skills",
|
|
2495
|
+
skillName: "simplify",
|
|
2496
|
+
description: "YAGNI code simplification expert"
|
|
2497
|
+
},
|
|
2498
|
+
{
|
|
2499
|
+
name: "tdd",
|
|
2500
|
+
repo: "https://github.com/mattpocock/skills",
|
|
2501
|
+
skillName: "tdd",
|
|
2502
|
+
description: "Test-driven development workflow"
|
|
2503
|
+
},
|
|
2504
|
+
{
|
|
2505
|
+
name: "progressive-context-router",
|
|
2506
|
+
repo: "https://github.com/EremesNG/skills",
|
|
2507
|
+
skillName: "progressive-context-router",
|
|
2508
|
+
description: "Repository instruction and on-demand context routing"
|
|
2509
|
+
},
|
|
2510
|
+
{
|
|
2511
|
+
name: "architectural-grilling",
|
|
2512
|
+
repo: "https://github.com/EremesNG/skills",
|
|
2513
|
+
skillName: "architectural-grilling",
|
|
2514
|
+
description: "Conditional product and architecture decision interview"
|
|
3069
2515
|
}
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
2516
|
+
];
|
|
2517
|
+
function getRequiredSkillPath(skill, harness, homeDir = homedir2()) {
|
|
2518
|
+
return join3(
|
|
2519
|
+
homeDir,
|
|
2520
|
+
...GLOBAL_SKILL_ROOT[harness],
|
|
2521
|
+
skill.skillName,
|
|
2522
|
+
"SKILL.md"
|
|
2523
|
+
);
|
|
2524
|
+
}
|
|
2525
|
+
function isRequiredSkillInstalled(skill, harness, options = {}) {
|
|
2526
|
+
return existsSync6(getRequiredSkillPath(skill, harness, options.homeDir));
|
|
2527
|
+
}
|
|
2528
|
+
function getRequiredSkillInstallCommand(skill, harness) {
|
|
2529
|
+
return {
|
|
2530
|
+
command: "npx",
|
|
2531
|
+
args: [
|
|
2532
|
+
"skills",
|
|
2533
|
+
"add",
|
|
2534
|
+
skill.repo,
|
|
2535
|
+
"--skill",
|
|
2536
|
+
skill.skillName,
|
|
2537
|
+
"--global",
|
|
2538
|
+
"--agent",
|
|
2539
|
+
SKILLS_CLI_AGENT[harness],
|
|
2540
|
+
"--yes"
|
|
2541
|
+
]
|
|
2542
|
+
};
|
|
2543
|
+
}
|
|
2544
|
+
function installRequiredSkill(skill, harness, options = {}) {
|
|
2545
|
+
const skillPath = getRequiredSkillPath(skill, harness, options.homeDir);
|
|
2546
|
+
if (isRequiredSkillInstalled(skill, harness, options)) {
|
|
2547
|
+
return { skill, harness, status: "already-installed", skillPath };
|
|
2548
|
+
}
|
|
2549
|
+
const { command, args } = getRequiredSkillInstallCommand(skill, harness);
|
|
2550
|
+
try {
|
|
2551
|
+
const result = spawnSync2(command, args, { stdio: "inherit" });
|
|
2552
|
+
if (result.status === 0) {
|
|
2553
|
+
return { skill, harness, status: "installed", skillPath };
|
|
2554
|
+
}
|
|
2555
|
+
if (isRequiredSkillInstalled(skill, harness, options)) {
|
|
2556
|
+
return { skill, harness, status: "already-installed", skillPath };
|
|
2557
|
+
}
|
|
2558
|
+
return { skill, harness, status: "failed", skillPath, error: result.error };
|
|
2559
|
+
} catch (error) {
|
|
2560
|
+
return { skill, harness, status: "failed", skillPath, error };
|
|
3077
2561
|
}
|
|
3078
|
-
|
|
3079
|
-
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
// src/cli/operations/types.ts
|
|
2565
|
+
var UNSUPPORTED_PROVIDER_CAPABILITY = {
|
|
2566
|
+
state: "unsupported",
|
|
2567
|
+
source: "none",
|
|
2568
|
+
basis: []
|
|
2569
|
+
};
|
|
2570
|
+
function classifyProviderCapabilityEvidence(input = {}) {
|
|
2571
|
+
const evidence = input.providerEvidence;
|
|
2572
|
+
if (!evidence) {
|
|
2573
|
+
return { ...UNSUPPORTED_PROVIDER_CAPABILITY, basis: [] };
|
|
3080
2574
|
}
|
|
3081
|
-
|
|
3082
|
-
|
|
2575
|
+
const basis = evidence.basis.filter((item) => item.trim().length > 0);
|
|
2576
|
+
const hasDocumentedSource = evidence.source === "provider" || evidence.source === "harness";
|
|
2577
|
+
if (!hasDocumentedSource || basis.length === 0) {
|
|
2578
|
+
return { ...UNSUPPORTED_PROVIDER_CAPABILITY, basis: [] };
|
|
3083
2579
|
}
|
|
3084
|
-
return {
|
|
3085
|
-
ok: false,
|
|
3086
|
-
code: "claude-code-effort-catalog-unsupported",
|
|
3087
|
-
message: `Effort ${effort} is not supported by ${input.catalogId ?? input.model} in the models.dev catalog.`
|
|
3088
|
-
};
|
|
2580
|
+
return { ...evidence, basis };
|
|
3089
2581
|
}
|
|
2582
|
+
|
|
2583
|
+
// src/cli/operations/claude-code.ts
|
|
2584
|
+
var CLAUDE_CODE_DISPLAY_NAME = "Claude Code";
|
|
2585
|
+
var claudeCodePlanSources = /* @__PURE__ */ new WeakMap();
|
|
2586
|
+
var MODEL_CONFIG_DISABLED_REASON = "Native Claude marketplace cache files are manager-owned; configure packaged agent defaults in the repository and publish a new plugin version.";
|
|
3090
2587
|
var claudeCodeActions = [
|
|
3091
2588
|
{
|
|
3092
2589
|
id: "claude-code-status",
|
|
3093
2590
|
kind: "status",
|
|
3094
2591
|
label: "Status",
|
|
3095
|
-
description: "Inspect
|
|
2592
|
+
description: "Inspect native Claude Code marketplace and plugin state",
|
|
3096
2593
|
dryRun: false,
|
|
3097
2594
|
requiresConfirmation: false,
|
|
3098
2595
|
supported: true
|
|
@@ -3110,7 +2607,7 @@ var claudeCodeActions = [
|
|
|
3110
2607
|
id: "claude-code-install",
|
|
3111
2608
|
kind: "install",
|
|
3112
2609
|
label: "Install",
|
|
3113
|
-
description: "Preview Claude
|
|
2610
|
+
description: "Preview native Claude marketplace registration and install",
|
|
3114
2611
|
dryRun: true,
|
|
3115
2612
|
requiresConfirmation: true,
|
|
3116
2613
|
supported: true
|
|
@@ -3119,7 +2616,7 @@ var claudeCodeActions = [
|
|
|
3119
2616
|
id: "claude-code-update",
|
|
3120
2617
|
kind: "update",
|
|
3121
2618
|
label: "Update",
|
|
3122
|
-
description: "Preview Claude
|
|
2619
|
+
description: "Preview native Claude plugin reconciliation",
|
|
3123
2620
|
dryRun: true,
|
|
3124
2621
|
requiresConfirmation: true,
|
|
3125
2622
|
supported: true
|
|
@@ -3128,7 +2625,7 @@ var claudeCodeActions = [
|
|
|
3128
2625
|
id: "claude-code-sync",
|
|
3129
2626
|
kind: "sync",
|
|
3130
2627
|
label: "Sync",
|
|
3131
|
-
description: "Preview Claude
|
|
2628
|
+
description: "Preview native Claude plugin and required-skill reconciliation",
|
|
3132
2629
|
dryRun: true,
|
|
3133
2630
|
requiresConfirmation: true,
|
|
3134
2631
|
supported: true
|
|
@@ -3137,40 +2634,39 @@ var claudeCodeActions = [
|
|
|
3137
2634
|
id: "claude-code-model-config",
|
|
3138
2635
|
kind: "model-config",
|
|
3139
2636
|
label: "Model",
|
|
3140
|
-
description: "
|
|
2637
|
+
description: "Claude agent models are package-owned defaults",
|
|
3141
2638
|
dryRun: true,
|
|
3142
|
-
requiresConfirmation:
|
|
3143
|
-
supported:
|
|
2639
|
+
requiresConfirmation: false,
|
|
2640
|
+
supported: false,
|
|
2641
|
+
disabledReason: MODEL_CONFIG_DISABLED_REASON
|
|
3144
2642
|
}
|
|
3145
2643
|
];
|
|
3146
2644
|
var claudeCodeOperationAdapter = {
|
|
3147
2645
|
id: "claude",
|
|
3148
2646
|
displayName: CLAUDE_CODE_DISPLAY_NAME,
|
|
3149
2647
|
available: true,
|
|
3150
|
-
description: "Claude Code plugin
|
|
2648
|
+
description: "Native Claude Code marketplace plugin and required skills.",
|
|
3151
2649
|
actions: claudeCodeActions
|
|
3152
2650
|
};
|
|
3153
|
-
function claudeCodeConfig(context2 = { cwd: process.cwd() }, dryRun) {
|
|
2651
|
+
function claudeCodeConfig(context2 = { cwd: process.cwd() }, dryRun, refresh = false) {
|
|
3154
2652
|
return {
|
|
3155
2653
|
dryRun,
|
|
3156
2654
|
reset: false,
|
|
3157
|
-
// Match the installer (install.ts uses 'user') so post-install status,
|
|
3158
|
-
// update, sync, and model commands resolve the same plugin root.
|
|
3159
2655
|
scope: context2.scope ?? "user",
|
|
3160
2656
|
projectRoot: context2.cwd,
|
|
3161
|
-
|
|
3162
|
-
|
|
2657
|
+
commandExecutor: context2.commandExecutor,
|
|
2658
|
+
refresh
|
|
3163
2659
|
};
|
|
3164
2660
|
}
|
|
3165
2661
|
function claudeCodeDisclaimers() {
|
|
3166
2662
|
return [
|
|
3167
2663
|
{
|
|
3168
|
-
message: "
|
|
3169
|
-
code: "claude-code-
|
|
2664
|
+
message: "Claude Code owns marketplace snapshots and installed plugin cache files; thoth-agents never edits that cache directly.",
|
|
2665
|
+
code: "claude-code-manager-owned-cache"
|
|
3170
2666
|
},
|
|
3171
2667
|
{
|
|
3172
|
-
message: "
|
|
3173
|
-
code: "claude-code-
|
|
2668
|
+
message: "Read-only roles remain instruction/tool-denylist constrained; the orchestrator is activated as the main plugin agent.",
|
|
2669
|
+
code: "claude-code-first-class"
|
|
3174
2670
|
}
|
|
3175
2671
|
];
|
|
3176
2672
|
}
|
|
@@ -3179,9 +2675,9 @@ function warning(message, code) {
|
|
|
3179
2675
|
}
|
|
3180
2676
|
function targetForItem(item, state, observed) {
|
|
3181
2677
|
return {
|
|
3182
|
-
kind:
|
|
2678
|
+
kind: "package",
|
|
3183
2679
|
path: item.targetPath,
|
|
3184
|
-
label: item.
|
|
2680
|
+
label: item.kind === "native-marketplace" ? "Claude Code marketplace" : "Claude Code plugin",
|
|
3185
2681
|
state,
|
|
3186
2682
|
expected: item.action,
|
|
3187
2683
|
observed,
|
|
@@ -3190,47 +2686,45 @@ function targetForItem(item, state, observed) {
|
|
|
3190
2686
|
}
|
|
3191
2687
|
function surfaceForItem(item) {
|
|
3192
2688
|
return {
|
|
3193
|
-
id: `${item.kind}:${item.
|
|
3194
|
-
label: item.
|
|
2689
|
+
id: `${item.kind}:${item.action}`,
|
|
2690
|
+
label: item.kind === "native-marketplace" ? "Claude Code marketplace" : "Claude Code plugin",
|
|
3195
2691
|
path: item.targetPath,
|
|
3196
2692
|
description: item.description
|
|
3197
2693
|
};
|
|
3198
2694
|
}
|
|
3199
|
-
function
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
if (observed === item.content)
|
|
3211
|
-
return { state: "installed", observed: "current" };
|
|
3212
|
-
try {
|
|
3213
|
-
const expectedVersion = JSON.parse(item.content).version;
|
|
3214
|
-
const observedVersion = JSON.parse(observed).version;
|
|
3215
|
-
if (typeof expectedVersion === "string" && typeof observedVersion === "string" && expectedVersion !== observedVersion) {
|
|
3216
|
-
return {
|
|
3217
|
-
state: "outdated",
|
|
3218
|
-
observed: `version ${observedVersion}; expected ${expectedVersion}`
|
|
3219
|
-
};
|
|
3220
|
-
}
|
|
3221
|
-
} catch {
|
|
3222
|
-
return { state: "unknown", observed: "unparseable plugin manifest" };
|
|
2695
|
+
function managerTargets(plan) {
|
|
2696
|
+
if (!plan.ready) {
|
|
2697
|
+
return [
|
|
2698
|
+
{
|
|
2699
|
+
kind: "package",
|
|
2700
|
+
path: plan.pluginRoot,
|
|
2701
|
+
label: "Claude Code native plugin",
|
|
2702
|
+
state: "unknown",
|
|
2703
|
+
observed: "native manager state is unsafe to mutate"
|
|
2704
|
+
}
|
|
2705
|
+
];
|
|
3223
2706
|
}
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
2707
|
+
if (plan.items.length === 0) {
|
|
2708
|
+
return [
|
|
2709
|
+
{
|
|
2710
|
+
kind: "package",
|
|
2711
|
+
path: plan.pluginRoot,
|
|
2712
|
+
label: "Claude Code native plugin",
|
|
2713
|
+
state: "installed",
|
|
2714
|
+
expected: "registered marketplace and enabled plugin",
|
|
2715
|
+
observed: "registered and enabled"
|
|
2716
|
+
}
|
|
2717
|
+
];
|
|
2718
|
+
}
|
|
2719
|
+
return plan.items.map((item) => {
|
|
2720
|
+
if (item.action === "enable-plugin") {
|
|
2721
|
+
return targetForItem(item, "drift", "plugin disabled");
|
|
2722
|
+
}
|
|
2723
|
+
if (item.action === "update-plugin") {
|
|
2724
|
+
return targetForItem(item, "installed", "native update requested");
|
|
2725
|
+
}
|
|
2726
|
+
return targetForItem(item, "missing", "absent");
|
|
2727
|
+
});
|
|
3234
2728
|
}
|
|
3235
2729
|
function aggregateState(states) {
|
|
3236
2730
|
if (states.includes("unknown")) return "unknown";
|
|
@@ -3242,36 +2736,81 @@ function aggregateState(states) {
|
|
|
3242
2736
|
function statusSummary(state) {
|
|
3243
2737
|
switch (state) {
|
|
3244
2738
|
case "installed":
|
|
3245
|
-
return "Claude Code
|
|
2739
|
+
return "Claude Code native plugin and required skills are installed.";
|
|
3246
2740
|
case "missing":
|
|
3247
|
-
return "Claude Code
|
|
2741
|
+
return "Claude Code native plugin or required skills are missing.";
|
|
3248
2742
|
case "drift":
|
|
3249
|
-
return "Claude Code
|
|
2743
|
+
return "Claude Code native plugin is installed but disabled or incomplete.";
|
|
3250
2744
|
case "outdated":
|
|
3251
|
-
return "Claude Code
|
|
2745
|
+
return "Claude Code native plugin is outdated.";
|
|
3252
2746
|
case "unknown":
|
|
3253
|
-
return "Claude Code
|
|
2747
|
+
return "Claude Code native plugin state could not be classified safely.";
|
|
3254
2748
|
}
|
|
3255
2749
|
}
|
|
3256
|
-
function
|
|
3257
|
-
const
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
2750
|
+
function claudeCodeRequiredSkillStatus(context2) {
|
|
2751
|
+
const targets = REQUIRED_SKILLS.map((skill) => {
|
|
2752
|
+
const path = getRequiredSkillPath(skill, "claude", context2.homeDir);
|
|
2753
|
+
const installed = existsSync7(path);
|
|
2754
|
+
return {
|
|
2755
|
+
kind: "skill",
|
|
2756
|
+
path,
|
|
2757
|
+
label: `Claude Code required skill: ${skill.name}`,
|
|
2758
|
+
state: installed ? "installed" : "missing",
|
|
2759
|
+
expected: "required global Claude Code skill",
|
|
2760
|
+
observed: installed ? "installed" : "missing"
|
|
2761
|
+
};
|
|
2762
|
+
});
|
|
2763
|
+
return {
|
|
2764
|
+
targets,
|
|
2765
|
+
diagnostics: targets.some((target) => target.state === "missing") ? [
|
|
2766
|
+
{
|
|
2767
|
+
severity: "important",
|
|
2768
|
+
code: "claude-code-required-skills-missing",
|
|
2769
|
+
message: "Required Claude Code skills are missing; run install, update, or sync to restore them."
|
|
2770
|
+
}
|
|
2771
|
+
] : []
|
|
2772
|
+
};
|
|
2773
|
+
}
|
|
2774
|
+
function claudeCodeRequiredSkillPlanItem() {
|
|
2775
|
+
return {
|
|
2776
|
+
title: "Install required external skills for Claude Code",
|
|
2777
|
+
target: {
|
|
2778
|
+
kind: "skill",
|
|
2779
|
+
label: "Required Claude Code skills",
|
|
2780
|
+
expected: REQUIRED_SKILLS.map(({ name }) => name).join(", ")
|
|
2781
|
+
},
|
|
2782
|
+
preview: JSON.stringify(
|
|
2783
|
+
REQUIRED_SKILLS.map((skill) => ({
|
|
2784
|
+
name: skill.name,
|
|
2785
|
+
...getRequiredSkillInstallCommand(skill, "claude")
|
|
2786
|
+
})),
|
|
2787
|
+
null,
|
|
2788
|
+
2
|
|
2789
|
+
)
|
|
2790
|
+
};
|
|
2791
|
+
}
|
|
2792
|
+
function statusFromSetupPlan(plan, context2) {
|
|
2793
|
+
const manager = managerTargets(plan);
|
|
2794
|
+
const requiredSkills = claudeCodeRequiredSkillStatus(context2);
|
|
2795
|
+
const state = aggregateState(
|
|
2796
|
+
[...manager, ...requiredSkills.targets].map(
|
|
2797
|
+
(target) => target.state ?? "unknown"
|
|
2798
|
+
)
|
|
2799
|
+
);
|
|
3262
2800
|
return {
|
|
3263
2801
|
harness: "claude",
|
|
3264
2802
|
displayName: CLAUDE_CODE_DISPLAY_NAME,
|
|
3265
2803
|
state,
|
|
3266
2804
|
summary: statusSummary(state),
|
|
3267
|
-
targets:
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
2805
|
+
targets: [...manager, ...requiredSkills.targets],
|
|
2806
|
+
diagnostics: [
|
|
2807
|
+
...plan.diagnostics.map((message) => ({
|
|
2808
|
+
severity: "minor",
|
|
2809
|
+
message,
|
|
2810
|
+
code: "claude-code-diagnostic"
|
|
2811
|
+
})),
|
|
2812
|
+
...requiredSkills.diagnostics
|
|
2813
|
+
],
|
|
3275
2814
|
actions: claudeCodeActions,
|
|
3276
2815
|
disclaimers: [
|
|
3277
2816
|
...claudeCodeDisclaimers(),
|
|
@@ -3279,10 +2818,14 @@ function statusFromSetupPlan(plan) {
|
|
|
3279
2818
|
]
|
|
3280
2819
|
};
|
|
3281
2820
|
}
|
|
3282
|
-
function getClaudeCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
3283
|
-
|
|
2821
|
+
function getClaudeCodeStatus(context2 = { cwd: process.cwd() }, evidence = {}) {
|
|
2822
|
+
const providerCapability = classifyProviderCapabilityEvidence(evidence);
|
|
3284
2823
|
try {
|
|
3285
|
-
plan = buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true));
|
|
2824
|
+
const plan = buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true));
|
|
2825
|
+
return {
|
|
2826
|
+
...statusFromSetupPlan(plan, context2),
|
|
2827
|
+
providerCapability
|
|
2828
|
+
};
|
|
3286
2829
|
} catch (error) {
|
|
3287
2830
|
const message = error instanceof Error ? error.message : String(error);
|
|
3288
2831
|
return {
|
|
@@ -3299,22 +2842,24 @@ function getClaudeCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
|
3299
2842
|
}
|
|
3300
2843
|
],
|
|
3301
2844
|
actions: claudeCodeActions,
|
|
2845
|
+
providerCapability,
|
|
3302
2846
|
disclaimers: claudeCodeDisclaimers()
|
|
3303
2847
|
};
|
|
3304
2848
|
}
|
|
3305
|
-
return statusFromSetupPlan(plan);
|
|
3306
2849
|
}
|
|
3307
2850
|
function planItemFromSetup(item) {
|
|
3308
2851
|
return {
|
|
3309
2852
|
title: item.description,
|
|
3310
2853
|
target: targetForItem(item),
|
|
3311
|
-
preview: item.
|
|
3312
|
-
backup:
|
|
2854
|
+
preview: `${item.command.executable} ${item.command.args.join(" ")}`,
|
|
2855
|
+
backup: { required: false, strategy: "external" }
|
|
3313
2856
|
};
|
|
3314
2857
|
}
|
|
3315
|
-
function planFromSetup(id, action, title, summary, setupPlan) {
|
|
3316
|
-
const status = statusFromSetupPlan(setupPlan);
|
|
3317
|
-
const
|
|
2858
|
+
function planFromSetup(id, action, title, summary, setupPlan, context2) {
|
|
2859
|
+
const status = statusFromSetupPlan(setupPlan, context2);
|
|
2860
|
+
const missingSkills = status.targets.some(
|
|
2861
|
+
(target) => target.kind === "skill" && target.state === "missing"
|
|
2862
|
+
);
|
|
3318
2863
|
const plan = {
|
|
3319
2864
|
id,
|
|
3320
2865
|
harness: "claude",
|
|
@@ -3322,161 +2867,100 @@ function planFromSetup(id, action, title, summary, setupPlan) {
|
|
|
3322
2867
|
title,
|
|
3323
2868
|
summary,
|
|
3324
2869
|
dryRun: true,
|
|
3325
|
-
canApply,
|
|
2870
|
+
canApply: setupPlan.ready && (setupPlan.items.length > 0 || missingSkills),
|
|
3326
2871
|
targets: status.targets,
|
|
3327
|
-
surfaces: setupPlan.items.map(surfaceForItem)
|
|
2872
|
+
surfaces: setupPlan.items.length > 0 ? setupPlan.items.map(surfaceForItem) : [
|
|
2873
|
+
{
|
|
2874
|
+
id: "native-plugin:installed",
|
|
2875
|
+
label: "Claude Code native plugin",
|
|
2876
|
+
path: setupPlan.pluginRoot,
|
|
2877
|
+
state: setupPlan.ready ? "installed" : "unknown"
|
|
2878
|
+
}
|
|
2879
|
+
],
|
|
3328
2880
|
backup: {
|
|
3329
|
-
required:
|
|
3330
|
-
strategy: "
|
|
3331
|
-
description: "
|
|
2881
|
+
required: false,
|
|
2882
|
+
strategy: "external",
|
|
2883
|
+
description: "Claude Code owns its marketplace snapshots and plugin cache."
|
|
3332
2884
|
},
|
|
3333
|
-
items:
|
|
2885
|
+
items: [
|
|
2886
|
+
...setupPlan.items.map(planItemFromSetup),
|
|
2887
|
+
claudeCodeRequiredSkillPlanItem()
|
|
2888
|
+
],
|
|
3334
2889
|
warnings: status.diagnostics,
|
|
3335
2890
|
disclaimers: [
|
|
3336
2891
|
...claudeCodeDisclaimers(),
|
|
3337
2892
|
...setupPlan.disclaimers.map((message) => ({ message }))
|
|
3338
2893
|
]
|
|
3339
2894
|
};
|
|
3340
|
-
claudeCodePlanSources.set(plan, setupPlan);
|
|
2895
|
+
claudeCodePlanSources.set(plan, { setupPlan, context: context2 });
|
|
3341
2896
|
return plan;
|
|
3342
2897
|
}
|
|
3343
2898
|
function buildClaudeCodeInstallPlan(context2 = { cwd: process.cwd() }) {
|
|
3344
2899
|
return planFromSetup(
|
|
3345
2900
|
"claude-code-install-preview",
|
|
3346
2901
|
"install",
|
|
3347
|
-
"Install Claude Code plugin
|
|
3348
|
-
"Preview
|
|
3349
|
-
buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true))
|
|
2902
|
+
"Install Claude Code native plugin",
|
|
2903
|
+
"Preview native marketplace registration, plugin installation, and required skills.",
|
|
2904
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true)),
|
|
2905
|
+
context2
|
|
3350
2906
|
);
|
|
3351
2907
|
}
|
|
3352
2908
|
function buildClaudeCodeUpdatePlan(context2 = { cwd: process.cwd() }) {
|
|
3353
2909
|
return planFromSetup(
|
|
3354
2910
|
"claude-code-update-preview",
|
|
3355
2911
|
"update",
|
|
3356
|
-
"
|
|
3357
|
-
"Preview
|
|
3358
|
-
buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true))
|
|
2912
|
+
"Reconcile Claude Code native plugin",
|
|
2913
|
+
"Preview native marketplace, enabled-plugin, and required-skill reconciliation.",
|
|
2914
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true, true)),
|
|
2915
|
+
context2
|
|
3359
2916
|
);
|
|
3360
2917
|
}
|
|
3361
2918
|
function buildClaudeCodeSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
3362
2919
|
return planFromSetup(
|
|
3363
2920
|
"claude-code-sync-preview",
|
|
3364
2921
|
"sync",
|
|
3365
|
-
"Sync Claude Code plugin
|
|
3366
|
-
"Preview
|
|
3367
|
-
buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true))
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
(entry) => !entry.validRole || !entry.validModel
|
|
3390
|
-
);
|
|
3391
|
-
const effortErrors = resolvedRoles.filter(
|
|
3392
|
-
(entry) => entry.validRole && entry.validModel && !entry.effort.ok
|
|
3393
|
-
);
|
|
3394
|
-
const warnings = [
|
|
3395
|
-
...status.diagnostics,
|
|
3396
|
-
...input.warnings ?? [],
|
|
3397
|
-
...rejectedRoles.map(
|
|
3398
|
-
({ role }) => warning(
|
|
3399
|
-
`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.`,
|
|
3400
|
-
"claude-code-unsupported-model-role"
|
|
3401
|
-
)
|
|
3402
|
-
),
|
|
3403
|
-
...effortErrors.map(
|
|
3404
|
-
({ effort }) => warning(
|
|
3405
|
-
effort.ok ? "Unknown Claude Code effort error." : effort.message,
|
|
3406
|
-
effort.ok ? "claude-code-effort-unknown" : effort.code
|
|
3407
|
-
)
|
|
3408
|
-
)
|
|
3409
|
-
];
|
|
3410
|
-
if (input.harness !== "claude") {
|
|
3411
|
-
warnings.push(
|
|
3412
|
-
warning(
|
|
3413
|
-
"Model plan target harness must be claude.",
|
|
3414
|
-
"claude-code-model-harness-mismatch"
|
|
3415
|
-
)
|
|
3416
|
-
);
|
|
3417
|
-
}
|
|
3418
|
-
const targets = supportedRoles.map(({ role }) => {
|
|
3419
|
-
const target = status.targets.find(
|
|
3420
|
-
(candidate) => candidate.path?.endsWith(`agents${pathSep()}${role}.md`)
|
|
3421
|
-
);
|
|
3422
|
-
return target ?? {
|
|
3423
|
-
kind: "generated-artifact",
|
|
3424
|
-
label: `Claude Code ${role} subagent`,
|
|
3425
|
-
state: "missing"
|
|
3426
|
-
};
|
|
3427
|
-
});
|
|
3428
|
-
const stateTarget = status.targets.find(
|
|
3429
|
-
(target) => target.path?.endsWith(".thoth-agents-managed-models.json")
|
|
3430
|
-
);
|
|
3431
|
-
const plan = {
|
|
3432
|
-
id: "claude-code-model-config-preview",
|
|
3433
|
-
harness: "claude",
|
|
3434
|
-
action: "model-config",
|
|
3435
|
-
title: "Configure Claude Code subagent model lines",
|
|
3436
|
-
summary: "Preview model changes for generated Claude Code subagent files and managed model state only.",
|
|
3437
|
-
dryRun: true,
|
|
3438
|
-
canApply: input.harness === "claude" && supportedRoles.length > 0 && effortErrors.length === 0 && status.state !== "unknown",
|
|
3439
|
-
targets: [...targets, ...stateTarget ? [stateTarget] : []],
|
|
3440
|
-
surfaces: targets.map((target) => ({
|
|
3441
|
-
id: `claude-code-model:${target.label}`,
|
|
3442
|
-
label: target.label ?? "Claude Code subagent",
|
|
3443
|
-
path: target.path,
|
|
3444
|
-
state: target.state
|
|
3445
|
-
})),
|
|
3446
|
-
backup: {
|
|
3447
|
-
required: true,
|
|
3448
|
-
strategy: "managed-backup-file",
|
|
3449
|
-
description: "Existing subagent files and managed model state are backed up by the managed write helper."
|
|
3450
|
-
},
|
|
3451
|
-
items: supportedRoles.map(({ role, model, effort }) => ({
|
|
3452
|
-
title: `Set ${role} Claude Code subagent model line`,
|
|
3453
|
-
target: targets.find(
|
|
3454
|
-
(target) => target.path?.endsWith(`agents${pathSep()}${role}.md`)
|
|
3455
|
-
) ?? {
|
|
3456
|
-
kind: "generated-artifact",
|
|
3457
|
-
label: `Claude Code ${role} subagent`
|
|
2922
|
+
"Sync Claude Code native plugin",
|
|
2923
|
+
"Preview native marketplace, enabled-plugin, and required-skill reconciliation.",
|
|
2924
|
+
buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true)),
|
|
2925
|
+
context2
|
|
2926
|
+
);
|
|
2927
|
+
}
|
|
2928
|
+
function buildClaudeCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
2929
|
+
const mismatch = input.harness !== "claude";
|
|
2930
|
+
return {
|
|
2931
|
+
id: "claude-code-model-config-unsupported",
|
|
2932
|
+
harness: "claude",
|
|
2933
|
+
action: "model-config",
|
|
2934
|
+
title: "Claude Code package-owned agent models",
|
|
2935
|
+
summary: MODEL_CONFIG_DISABLED_REASON,
|
|
2936
|
+
dryRun: true,
|
|
2937
|
+
canApply: false,
|
|
2938
|
+
targets: input.target ? [input.target] : [],
|
|
2939
|
+
surfaces: [],
|
|
2940
|
+
backup: { required: false, strategy: "none" },
|
|
2941
|
+
items: input.roles.map((role) => ({
|
|
2942
|
+
title: `Requested ${role.role} Claude Code model`,
|
|
2943
|
+
target: {
|
|
2944
|
+
kind: "package",
|
|
2945
|
+
label: `Claude Code ${role.role} packaged agent`
|
|
3458
2946
|
},
|
|
3459
|
-
preview: JSON.stringify({
|
|
3460
|
-
|
|
2947
|
+
preview: JSON.stringify({
|
|
2948
|
+
role: role.role,
|
|
2949
|
+
model: role.model,
|
|
2950
|
+
effort: role.effort ?? null,
|
|
2951
|
+
catalogId: role.catalogId ?? null
|
|
2952
|
+
}),
|
|
2953
|
+
backup: { required: false, strategy: "none" }
|
|
3461
2954
|
})),
|
|
3462
|
-
warnings
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
]
|
|
2955
|
+
warnings: [
|
|
2956
|
+
...input.warnings ?? [],
|
|
2957
|
+
warning(
|
|
2958
|
+
mismatch ? "Model plan target harness must be claude." : MODEL_CONFIG_DISABLED_REASON,
|
|
2959
|
+
mismatch ? "claude-code-model-harness-mismatch" : "claude-code-model-cache-owned"
|
|
2960
|
+
)
|
|
2961
|
+
],
|
|
2962
|
+
disclaimers: [...claudeCodeDisclaimers(), ...input.disclaimers ?? []]
|
|
3471
2963
|
};
|
|
3472
|
-
claudeCodeModelSources.set(plan, {
|
|
3473
|
-
config: claudeCodeConfig(context2, false),
|
|
3474
|
-
roles: supportedRoles
|
|
3475
|
-
});
|
|
3476
|
-
return plan;
|
|
3477
|
-
}
|
|
3478
|
-
function pathSep() {
|
|
3479
|
-
return process.platform === "win32" ? "\\" : "/";
|
|
3480
2964
|
}
|
|
3481
2965
|
function rejectPlan(plan, message, severity = "critical") {
|
|
3482
2966
|
return {
|
|
@@ -3500,75 +2984,80 @@ function validateClaudeCodePlan(plan) {
|
|
|
3500
2984
|
"Claude Code plan cannot be applied because canApply is false."
|
|
3501
2985
|
);
|
|
3502
2986
|
}
|
|
3503
|
-
if (!["install", "update", "sync"
|
|
2987
|
+
if (!["install", "update", "sync"].includes(plan.action)) {
|
|
3504
2988
|
return rejectPlan(
|
|
3505
2989
|
plan,
|
|
3506
2990
|
`Unsupported Claude Code apply action: ${plan.action}.`
|
|
3507
2991
|
);
|
|
3508
2992
|
}
|
|
3509
|
-
if (plan.items.length === 0) {
|
|
3510
|
-
return rejectPlan(plan, "Claude Code plan has no items to apply.");
|
|
3511
|
-
}
|
|
3512
2993
|
return null;
|
|
3513
2994
|
}
|
|
3514
2995
|
function applyClaudeCodePlan(plan) {
|
|
3515
2996
|
const rejection = validateClaudeCodePlan(plan);
|
|
3516
2997
|
if (rejection) return rejection;
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
if (!source) {
|
|
3520
|
-
return rejectPlan(
|
|
3521
|
-
plan,
|
|
3522
|
-
"Claude Code model plan was not produced by buildClaudeCodeModelPlan in this process."
|
|
3523
|
-
);
|
|
3524
|
-
}
|
|
3525
|
-
const result2 = applyClaudeCodeManagedModelOverrides(
|
|
3526
|
-
source.config,
|
|
3527
|
-
source.roles.map((role) => ({
|
|
3528
|
-
role: role.role,
|
|
3529
|
-
model: role.model,
|
|
3530
|
-
...role.catalogId ? { catalogId: role.catalogId } : {},
|
|
3531
|
-
...role.effort ? { effort: role.effort } : {},
|
|
3532
|
-
...role.clearEffort ? { clearEffort: true } : {}
|
|
3533
|
-
}))
|
|
3534
|
-
);
|
|
3535
|
-
return {
|
|
3536
|
-
harness: "claude",
|
|
3537
|
-
action: "model-config",
|
|
3538
|
-
applied: result2.success,
|
|
3539
|
-
summary: result2.success ? "Applied Claude Code subagent model overrides." : result2.error ?? "Failed to apply Claude Code subagent model overrides.",
|
|
3540
|
-
changedTargets: result2.changed.map((path4) => ({
|
|
3541
|
-
kind: path4.endsWith(".json") ? "memory-state" : "generated-artifact",
|
|
3542
|
-
path: path4,
|
|
3543
|
-
label: basename2(path4),
|
|
3544
|
-
state: "installed"
|
|
3545
|
-
})),
|
|
3546
|
-
backups: result2.changed.filter((path4) => existsSync7(`${path4}.bak`)).map((path4) => ({ path: `${path4}.bak`, label: "managed backup" })),
|
|
3547
|
-
warnings: result2.success ? [] : [{ severity: "critical", message: result2.error ?? "apply failed." }],
|
|
3548
|
-
disclaimers: claudeCodeDisclaimers()
|
|
3549
|
-
};
|
|
3550
|
-
}
|
|
3551
|
-
const setupPlan = claudeCodePlanSources.get(plan);
|
|
3552
|
-
if (!setupPlan) {
|
|
2998
|
+
const source = claudeCodePlanSources.get(plan);
|
|
2999
|
+
if (!source) {
|
|
3553
3000
|
return rejectPlan(
|
|
3554
3001
|
plan,
|
|
3555
3002
|
"Claude Code setup plan was not produced by a Claude Code operation plan builder in this process."
|
|
3556
3003
|
);
|
|
3557
3004
|
}
|
|
3558
|
-
const result = applyClaudeCodeSetup({ ...setupPlan, dryRun: false });
|
|
3005
|
+
const result = applyClaudeCodeSetup({ ...source.setupPlan, dryRun: false });
|
|
3006
|
+
const requiredSkillWarnings = [];
|
|
3007
|
+
const requiredSkillTargets = [];
|
|
3008
|
+
if (result.success) {
|
|
3009
|
+
for (const skill of REQUIRED_SKILLS) {
|
|
3010
|
+
const installed = installRequiredSkill(skill, "claude", {
|
|
3011
|
+
homeDir: source.context.homeDir
|
|
3012
|
+
});
|
|
3013
|
+
const success2 = installed.status !== "failed";
|
|
3014
|
+
requiredSkillTargets.push({
|
|
3015
|
+
kind: "skill",
|
|
3016
|
+
path: installed.skillPath,
|
|
3017
|
+
label: `Claude Code required skill: ${skill.name}`,
|
|
3018
|
+
state: success2 ? "installed" : "drift",
|
|
3019
|
+
observed: success2 ? installed.status : "installation failed"
|
|
3020
|
+
});
|
|
3021
|
+
if (!success2) {
|
|
3022
|
+
requiredSkillWarnings.push({
|
|
3023
|
+
severity: "critical",
|
|
3024
|
+
code: "claude-code-required-skill-failed",
|
|
3025
|
+
message: `Failed to install required Claude Code skill: ${skill.name}.`
|
|
3026
|
+
});
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
const success = result.success && requiredSkillWarnings.length === 0;
|
|
3031
|
+
let summary = result.error ?? `Failed to apply Claude Code ${plan.action} plan.`;
|
|
3032
|
+
if (success) {
|
|
3033
|
+
summary = `Applied Claude Code native ${plan.action} plan.`;
|
|
3034
|
+
} else if (requiredSkillWarnings.length > 0) {
|
|
3035
|
+
summary = "Claude Code plugin was installed, but required skills failed to install.";
|
|
3036
|
+
}
|
|
3559
3037
|
return {
|
|
3560
3038
|
harness: "claude",
|
|
3561
3039
|
action: plan.action,
|
|
3562
|
-
applied:
|
|
3563
|
-
summary
|
|
3564
|
-
changedTargets:
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3040
|
+
applied: success,
|
|
3041
|
+
summary,
|
|
3042
|
+
changedTargets: [
|
|
3043
|
+
...result.changed.map((path) => ({
|
|
3044
|
+
kind: "package",
|
|
3045
|
+
path,
|
|
3046
|
+
label: path.includes("/marketplaces/") ? "Claude Code marketplace" : "Claude Code plugin",
|
|
3047
|
+
state: "installed"
|
|
3048
|
+
})),
|
|
3049
|
+
...requiredSkillTargets
|
|
3050
|
+
],
|
|
3051
|
+
backups: [],
|
|
3052
|
+
warnings: [
|
|
3053
|
+
...requiredSkillWarnings,
|
|
3054
|
+
...result.success ? [] : [
|
|
3055
|
+
{
|
|
3056
|
+
severity: "critical",
|
|
3057
|
+
message: result.error ?? "apply failed."
|
|
3058
|
+
}
|
|
3059
|
+
]
|
|
3060
|
+
],
|
|
3572
3061
|
disclaimers: claudeCodeDisclaimers()
|
|
3573
3062
|
};
|
|
3574
3063
|
}
|
|
@@ -3580,8 +3069,8 @@ function defaultClaudeCodeModelRoles() {
|
|
|
3580
3069
|
}
|
|
3581
3070
|
|
|
3582
3071
|
// src/cli/operations/codex.ts
|
|
3583
|
-
import { existsSync as existsSync8, readFileSync as
|
|
3584
|
-
import { basename as
|
|
3072
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
|
|
3073
|
+
import { basename as basename2 } from "path";
|
|
3585
3074
|
var CODEX_DISPLAY_NAME = "Codex";
|
|
3586
3075
|
var codexPlanSources = /* @__PURE__ */ new WeakMap();
|
|
3587
3076
|
var codexModelSources = /* @__PURE__ */ new WeakMap();
|
|
@@ -3667,7 +3156,7 @@ function codexDisclaimers() {
|
|
|
3667
3156
|
code: "codex-root-guidance-only"
|
|
3668
3157
|
},
|
|
3669
3158
|
{
|
|
3670
|
-
message: "Provider-per-role behavior
|
|
3159
|
+
message: "Provider-per-role behavior and permissions are instruction-level or user-managed unless Codex exposes runtime controls.",
|
|
3671
3160
|
code: "codex-runtime-limits"
|
|
3672
3161
|
}
|
|
3673
3162
|
];
|
|
@@ -3688,13 +3177,13 @@ function targetForItem2(item, state, observed) {
|
|
|
3688
3177
|
}
|
|
3689
3178
|
function surfaceForItem2(item) {
|
|
3690
3179
|
return {
|
|
3691
|
-
id: `${item.kind}:${item.role ??
|
|
3180
|
+
id: `${item.kind}:${item.role ?? basename2(item.targetPath)}`,
|
|
3692
3181
|
label: item.role ? `Codex ${item.role} subagent TOML` : item.kind.replaceAll("-", " "),
|
|
3693
3182
|
path: item.targetPath,
|
|
3694
3183
|
description: item.description
|
|
3695
3184
|
};
|
|
3696
3185
|
}
|
|
3697
|
-
function
|
|
3186
|
+
function backupForItem(item) {
|
|
3698
3187
|
return {
|
|
3699
3188
|
required: item.requiresBackup,
|
|
3700
3189
|
strategy: item.requiresBackup ? "managed-backup-file" : "none",
|
|
@@ -3704,7 +3193,7 @@ function backupForItem2(item) {
|
|
|
3704
3193
|
function rootBlockState(item) {
|
|
3705
3194
|
if (!existsSync8(item.targetPath))
|
|
3706
3195
|
return { state: "missing", observed: "absent" };
|
|
3707
|
-
const content =
|
|
3196
|
+
const content = readFileSync5(item.targetPath, "utf8");
|
|
3708
3197
|
if (item.content && content.includes(item.content)) {
|
|
3709
3198
|
return { state: "installed", observed: "managed root block present" };
|
|
3710
3199
|
}
|
|
@@ -3717,11 +3206,11 @@ function managedModelState(item) {
|
|
|
3717
3206
|
if (!existsSync8(item.targetPath))
|
|
3718
3207
|
return { state: "missing", observed: "absent" };
|
|
3719
3208
|
try {
|
|
3720
|
-
const parsed = JSON.parse(
|
|
3209
|
+
const parsed = JSON.parse(readFileSync5(item.targetPath, "utf8"));
|
|
3721
3210
|
if (parsed.version !== MANAGED_MODEL_STATE_VERSION || !parsed.models || typeof parsed.models !== "object" || Array.isArray(parsed.models)) {
|
|
3722
3211
|
return { state: "unknown", observed: "invalid managed model state" };
|
|
3723
3212
|
}
|
|
3724
|
-
return item.content ===
|
|
3213
|
+
return item.content === readFileSync5(item.targetPath, "utf8") ? { state: "installed", observed: "managed model state current" } : { state: "drift", observed: "managed model state differs" };
|
|
3725
3214
|
} catch {
|
|
3726
3215
|
return { state: "unknown", observed: "unparseable managed model state" };
|
|
3727
3216
|
}
|
|
@@ -3729,7 +3218,7 @@ function managedModelState(item) {
|
|
|
3729
3218
|
function userConfigState(item) {
|
|
3730
3219
|
if (!existsSync8(item.targetPath))
|
|
3731
3220
|
return { state: "missing", observed: "absent" };
|
|
3732
|
-
const content =
|
|
3221
|
+
const content = readFileSync5(item.targetPath, "utf8");
|
|
3733
3222
|
if (/^\s*default_mode_request_user_input\s*=\s*true\b/m.test(content)) {
|
|
3734
3223
|
return {
|
|
3735
3224
|
state: "installed",
|
|
@@ -3741,41 +3230,10 @@ function userConfigState(item) {
|
|
|
3741
3230
|
}
|
|
3742
3231
|
return { state: "missing", observed: "managed feature flag absent" };
|
|
3743
3232
|
}
|
|
3744
|
-
function
|
|
3745
|
-
if (!existsSync8(item.targetPath))
|
|
3746
|
-
return { state: "missing", observed: "absent" };
|
|
3747
|
-
try {
|
|
3748
|
-
const parsed = JSON.parse(readFileSync9(item.targetPath, "utf8"));
|
|
3749
|
-
const plugins = Array.isArray(parsed.plugins) ? parsed.plugins : [];
|
|
3750
|
-
const entry = plugins.find(
|
|
3751
|
-
(plugin) => plugin && typeof plugin === "object" && "name" in plugin && plugin.name === "thoth-agents"
|
|
3752
|
-
);
|
|
3753
|
-
if (!entry)
|
|
3754
|
-
return { state: "missing", observed: "marketplace entry absent" };
|
|
3755
|
-
const sourcePath = typeof entry === "object" && entry && "source" in entry && entry.source && typeof entry.source === "object" && "path" in entry.source ? String(entry.source.path) : "";
|
|
3756
|
-
return sourcePath.includes(".codex/plugins/thoth-agents") ? { state: "installed", observed: "marketplace entry present" } : { state: "drift", observed: `marketplace source ${sourcePath}` };
|
|
3757
|
-
} catch {
|
|
3758
|
-
return { state: "unknown", observed: "unparseable marketplace JSON" };
|
|
3759
|
-
}
|
|
3760
|
-
}
|
|
3761
|
-
function contentState2(item) {
|
|
3233
|
+
function contentState(item) {
|
|
3762
3234
|
if (!existsSync8(item.targetPath))
|
|
3763
3235
|
return { state: "missing", observed: "absent" };
|
|
3764
|
-
const observed =
|
|
3765
|
-
if (basename3(item.targetPath) === "plugin.json" && item.targetPath.replaceAll("\\", "/").includes("/.codex-plugin/")) {
|
|
3766
|
-
try {
|
|
3767
|
-
const expectedVersion = JSON.parse(item.content ?? "{}").version;
|
|
3768
|
-
const observedVersion = JSON.parse(observed).version;
|
|
3769
|
-
if (typeof expectedVersion === "string" && typeof observedVersion === "string" && expectedVersion !== observedVersion) {
|
|
3770
|
-
return {
|
|
3771
|
-
state: "outdated",
|
|
3772
|
-
observed: `version ${observedVersion}; expected ${expectedVersion}`
|
|
3773
|
-
};
|
|
3774
|
-
}
|
|
3775
|
-
} catch {
|
|
3776
|
-
return { state: "unknown", observed: "unparseable plugin manifest" };
|
|
3777
|
-
}
|
|
3778
|
-
}
|
|
3236
|
+
const observed = readFileSync5(item.targetPath, "utf8");
|
|
3779
3237
|
if (item.content === observed)
|
|
3780
3238
|
return { state: "installed", observed: "current" };
|
|
3781
3239
|
if (item.role) {
|
|
@@ -3790,7 +3248,7 @@ function contentState2(item) {
|
|
|
3790
3248
|
}
|
|
3791
3249
|
return { state: "drift", observed: "content differs" };
|
|
3792
3250
|
}
|
|
3793
|
-
function
|
|
3251
|
+
function classifyItem(item) {
|
|
3794
3252
|
if (item.action === "diagnose-only") {
|
|
3795
3253
|
return { state: "unknown", observed: "diagnostic guidance only" };
|
|
3796
3254
|
}
|
|
@@ -3799,8 +3257,7 @@ function classifyItem2(item) {
|
|
|
3799
3257
|
return managedModelState(item);
|
|
3800
3258
|
}
|
|
3801
3259
|
if (item.action === "merge-toml") return userConfigState(item);
|
|
3802
|
-
|
|
3803
|
-
return contentState2(item);
|
|
3260
|
+
return contentState(item);
|
|
3804
3261
|
}
|
|
3805
3262
|
function aggregateState2(states) {
|
|
3806
3263
|
if (states.includes("unknown")) return "unknown";
|
|
@@ -3823,7 +3280,50 @@ function statusSummary2(state) {
|
|
|
3823
3280
|
return "Codex managed setup could not be classified safely.";
|
|
3824
3281
|
}
|
|
3825
3282
|
}
|
|
3826
|
-
function
|
|
3283
|
+
function codexRequiredSkillStatus(context2) {
|
|
3284
|
+
const targets = REQUIRED_SKILLS.map((skill) => {
|
|
3285
|
+
const path = getRequiredSkillPath(skill, "codex", context2.homeDir);
|
|
3286
|
+
const installed = existsSync8(path);
|
|
3287
|
+
return {
|
|
3288
|
+
kind: "skill",
|
|
3289
|
+
path,
|
|
3290
|
+
label: `Codex required skill: ${skill.name}`,
|
|
3291
|
+
state: installed ? "installed" : "missing",
|
|
3292
|
+
expected: "required global Codex skill",
|
|
3293
|
+
observed: installed ? "installed" : "missing"
|
|
3294
|
+
};
|
|
3295
|
+
});
|
|
3296
|
+
return {
|
|
3297
|
+
targets,
|
|
3298
|
+
diagnostics: targets.some((target) => target.state === "missing") ? [
|
|
3299
|
+
{
|
|
3300
|
+
severity: "important",
|
|
3301
|
+
code: "codex-required-skills-missing",
|
|
3302
|
+
message: "Required Codex skills are missing; run install, update, or sync to restore them."
|
|
3303
|
+
}
|
|
3304
|
+
] : []
|
|
3305
|
+
};
|
|
3306
|
+
}
|
|
3307
|
+
function codexRequiredSkillPlanItem() {
|
|
3308
|
+
return {
|
|
3309
|
+
title: "Install required external skills for Codex",
|
|
3310
|
+
target: {
|
|
3311
|
+
kind: "skill",
|
|
3312
|
+
label: "Required Codex skills",
|
|
3313
|
+
expected: REQUIRED_SKILLS.map(({ name }) => name).join(", ")
|
|
3314
|
+
},
|
|
3315
|
+
preview: JSON.stringify(
|
|
3316
|
+
REQUIRED_SKILLS.map((skill) => ({
|
|
3317
|
+
name: skill.name,
|
|
3318
|
+
...getRequiredSkillInstallCommand(skill, "codex")
|
|
3319
|
+
})),
|
|
3320
|
+
null,
|
|
3321
|
+
2
|
|
3322
|
+
)
|
|
3323
|
+
};
|
|
3324
|
+
}
|
|
3325
|
+
function getCodexStatus(context2 = { cwd: process.cwd() }, evidence = {}) {
|
|
3326
|
+
const providerCapability = classifyProviderCapabilityEvidence(evidence);
|
|
3827
3327
|
let plan;
|
|
3828
3328
|
try {
|
|
3829
3329
|
plan = buildCodexSetupPlan(codexConfig(context2, true));
|
|
@@ -3843,26 +3343,36 @@ function getCodexStatus(context2 = { cwd: process.cwd() }) {
|
|
|
3843
3343
|
}
|
|
3844
3344
|
],
|
|
3845
3345
|
actions: codexActions,
|
|
3346
|
+
providerCapability,
|
|
3846
3347
|
disclaimers: codexDisclaimers()
|
|
3847
3348
|
};
|
|
3848
3349
|
}
|
|
3849
|
-
const classified = plan.items.filter((item) => item.action !== "diagnose-only").map((item) => ({ item, ...
|
|
3850
|
-
const
|
|
3350
|
+
const classified = plan.items.filter((item) => item.action !== "diagnose-only").map((item) => ({ item, ...classifyItem(item) }));
|
|
3351
|
+
const requiredSkills = codexRequiredSkillStatus(context2);
|
|
3352
|
+
const state = aggregateState2([
|
|
3353
|
+
...classified.map((item) => item.state),
|
|
3354
|
+
...requiredSkills.targets.map((target) => target.state ?? "unknown")
|
|
3355
|
+
]);
|
|
3851
3356
|
const diagnostics = plan.diagnostics.map((message) => ({
|
|
3852
3357
|
severity: "minor",
|
|
3853
3358
|
message,
|
|
3854
3359
|
code: "codex-diagnostic"
|
|
3855
3360
|
}));
|
|
3361
|
+
diagnostics.push(...requiredSkills.diagnostics);
|
|
3856
3362
|
return {
|
|
3857
3363
|
harness: "codex",
|
|
3858
3364
|
displayName: CODEX_DISPLAY_NAME,
|
|
3859
3365
|
state,
|
|
3860
3366
|
summary: statusSummary2(state),
|
|
3861
|
-
targets:
|
|
3862
|
-
(
|
|
3863
|
-
|
|
3367
|
+
targets: [
|
|
3368
|
+
...classified.map(
|
|
3369
|
+
({ item, state: state2, observed }) => targetForItem2(item, state2, observed)
|
|
3370
|
+
),
|
|
3371
|
+
...requiredSkills.targets
|
|
3372
|
+
],
|
|
3864
3373
|
diagnostics,
|
|
3865
3374
|
actions: codexActions,
|
|
3375
|
+
providerCapability,
|
|
3866
3376
|
disclaimers: [
|
|
3867
3377
|
...codexDisclaimers(),
|
|
3868
3378
|
...plan.disclaimers.map((message) => ({ message }))
|
|
@@ -3874,7 +3384,7 @@ function planItemFromSetup2(item) {
|
|
|
3874
3384
|
title: item.description,
|
|
3875
3385
|
target: targetForItem2(item),
|
|
3876
3386
|
preview: item.content,
|
|
3877
|
-
backup:
|
|
3387
|
+
backup: backupForItem(item)
|
|
3878
3388
|
};
|
|
3879
3389
|
}
|
|
3880
3390
|
function planFromSetup2(id, action, title, summary, setupPlan, context2) {
|
|
@@ -3895,7 +3405,10 @@ function planFromSetup2(id, action, title, summary, setupPlan, context2) {
|
|
|
3895
3405
|
strategy: "existing-helper",
|
|
3896
3406
|
description: "Codex setup apply uses the existing installer backup behavior for files that already exist."
|
|
3897
3407
|
},
|
|
3898
|
-
items:
|
|
3408
|
+
items: [
|
|
3409
|
+
...setupPlan.items.map(planItemFromSetup2),
|
|
3410
|
+
codexRequiredSkillPlanItem()
|
|
3411
|
+
],
|
|
3899
3412
|
warnings: [
|
|
3900
3413
|
...status.diagnostics,
|
|
3901
3414
|
...canApply ? [] : [
|
|
@@ -3910,7 +3423,7 @@ function planFromSetup2(id, action, title, summary, setupPlan, context2) {
|
|
|
3910
3423
|
...setupPlan.disclaimers.map((message) => ({ message }))
|
|
3911
3424
|
]
|
|
3912
3425
|
};
|
|
3913
|
-
codexPlanSources.set(plan, setupPlan);
|
|
3426
|
+
codexPlanSources.set(plan, { setupPlan, context: context2 });
|
|
3914
3427
|
return plan;
|
|
3915
3428
|
}
|
|
3916
3429
|
function buildCodexUpdatePlan(context2 = { cwd: process.cwd() }) {
|
|
@@ -3930,7 +3443,7 @@ function buildCodexSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
|
3930
3443
|
"codex-sync-preview",
|
|
3931
3444
|
"sync",
|
|
3932
3445
|
"Sync Codex managed configuration",
|
|
3933
|
-
"Preview Codex managed root instructions, subagents,
|
|
3446
|
+
"Preview Codex managed root instructions, subagents, feature gates, and native marketplace guidance.",
|
|
3934
3447
|
setupPlan,
|
|
3935
3448
|
context2
|
|
3936
3449
|
);
|
|
@@ -4120,26 +3633,26 @@ function applyCodexPlan(plan) {
|
|
|
4120
3633
|
const rejection = validateCodexPlan(plan);
|
|
4121
3634
|
if (rejection) return rejection;
|
|
4122
3635
|
if (plan.action === "model-config") {
|
|
4123
|
-
const
|
|
4124
|
-
if (!
|
|
3636
|
+
const source2 = codexModelSources.get(plan);
|
|
3637
|
+
if (!source2) {
|
|
4125
3638
|
return rejectPlan2(
|
|
4126
3639
|
plan,
|
|
4127
3640
|
"Codex model plan was not produced by buildCodexModelPlan in this process."
|
|
4128
3641
|
);
|
|
4129
3642
|
}
|
|
4130
|
-
const result2 = applyCodexManagedModelOverrides(
|
|
3643
|
+
const result2 = applyCodexManagedModelOverrides(source2.config, source2.roles);
|
|
4131
3644
|
return {
|
|
4132
3645
|
harness: "codex",
|
|
4133
3646
|
action: "model-config",
|
|
4134
3647
|
applied: result2.success,
|
|
4135
3648
|
summary: result2.success ? "Applied Codex subagent model overrides." : result2.error ?? "Failed to apply Codex subagent model overrides.",
|
|
4136
|
-
changedTargets: result2.changed.map((
|
|
4137
|
-
kind:
|
|
4138
|
-
path
|
|
4139
|
-
label:
|
|
3649
|
+
changedTargets: result2.changed.map((path) => ({
|
|
3650
|
+
kind: path.endsWith(".json") ? "memory-state" : "generated-artifact",
|
|
3651
|
+
path,
|
|
3652
|
+
label: basename2(path),
|
|
4140
3653
|
state: "installed"
|
|
4141
3654
|
})),
|
|
4142
|
-
backups: result2.changed.filter((
|
|
3655
|
+
backups: result2.changed.filter((path) => existsSync8(`${path}.bak`)).map((path) => ({ path: `${path}.bak`, label: "managed backup" })),
|
|
4143
3656
|
warnings: result2.success ? [] : [
|
|
4144
3657
|
{
|
|
4145
3658
|
severity: "critical",
|
|
@@ -4149,31 +3662,62 @@ function applyCodexPlan(plan) {
|
|
|
4149
3662
|
disclaimers: codexDisclaimers()
|
|
4150
3663
|
};
|
|
4151
3664
|
}
|
|
4152
|
-
const
|
|
4153
|
-
if (!
|
|
3665
|
+
const source = codexPlanSources.get(plan);
|
|
3666
|
+
if (!source) {
|
|
4154
3667
|
return rejectPlan2(
|
|
4155
3668
|
plan,
|
|
4156
3669
|
"Codex setup plan was not produced by a Codex operation plan builder in this process."
|
|
4157
3670
|
);
|
|
4158
3671
|
}
|
|
4159
|
-
const result = applyCodexSetup({ ...setupPlan, dryRun: false });
|
|
3672
|
+
const result = applyCodexSetup({ ...source.setupPlan, dryRun: false });
|
|
3673
|
+
const requiredSkillWarnings = [];
|
|
3674
|
+
const requiredSkillTargets = [];
|
|
3675
|
+
if (result.success) {
|
|
3676
|
+
for (const skill of REQUIRED_SKILLS) {
|
|
3677
|
+
const installed = installRequiredSkill(skill, "codex", {
|
|
3678
|
+
homeDir: source.context.homeDir
|
|
3679
|
+
});
|
|
3680
|
+
const success2 = installed.status !== "failed";
|
|
3681
|
+
requiredSkillTargets.push({
|
|
3682
|
+
kind: "skill",
|
|
3683
|
+
path: installed.skillPath,
|
|
3684
|
+
label: `Codex required skill: ${skill.name}`,
|
|
3685
|
+
state: success2 ? "installed" : "drift",
|
|
3686
|
+
observed: success2 ? installed.status : "installation failed"
|
|
3687
|
+
});
|
|
3688
|
+
if (!success2) {
|
|
3689
|
+
requiredSkillWarnings.push({
|
|
3690
|
+
severity: "critical",
|
|
3691
|
+
code: "codex-required-skill-failed",
|
|
3692
|
+
message: `Failed to install required Codex skill: ${skill.name}.`
|
|
3693
|
+
});
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
}
|
|
3697
|
+
const success = result.success && requiredSkillWarnings.length === 0;
|
|
4160
3698
|
return {
|
|
4161
3699
|
harness: "codex",
|
|
4162
3700
|
action: plan.action,
|
|
4163
|
-
applied:
|
|
4164
|
-
summary:
|
|
4165
|
-
changedTargets:
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
3701
|
+
applied: success,
|
|
3702
|
+
summary: success ? `Applied Codex managed ${plan.action} plan.` : requiredSkillWarnings.length > 0 ? `Codex setup was written, but required skills failed to install.` : result.error ?? `Failed to apply Codex ${plan.action} plan.`,
|
|
3703
|
+
changedTargets: [
|
|
3704
|
+
...result.changed.map((path) => ({
|
|
3705
|
+
kind: path.endsWith(".json") ? "memory-state" : "generated-artifact",
|
|
3706
|
+
path,
|
|
3707
|
+
label: basename2(path),
|
|
3708
|
+
state: "installed"
|
|
3709
|
+
})),
|
|
3710
|
+
...requiredSkillTargets
|
|
3711
|
+
],
|
|
3712
|
+
backups: result.changed.filter((path) => existsSync8(`${path}.bak`)).map((path) => ({ path: `${path}.bak`, label: "managed backup" })),
|
|
3713
|
+
warnings: [
|
|
3714
|
+
...requiredSkillWarnings,
|
|
3715
|
+
...result.success ? [] : [
|
|
3716
|
+
{
|
|
3717
|
+
severity: "critical",
|
|
3718
|
+
message: result.error ?? "Codex setup apply failed."
|
|
3719
|
+
}
|
|
3720
|
+
]
|
|
4177
3721
|
],
|
|
4178
3722
|
disclaimers: codexDisclaimers()
|
|
4179
3723
|
};
|
|
@@ -4181,8 +3725,8 @@ function applyCodexPlan(plan) {
|
|
|
4181
3725
|
|
|
4182
3726
|
// src/cli/operations/opencode.ts
|
|
4183
3727
|
import { createHash as createHash3 } from "crypto";
|
|
4184
|
-
import { existsSync as
|
|
4185
|
-
import { join as
|
|
3728
|
+
import { existsSync as existsSync9 } from "fs";
|
|
3729
|
+
import { join as join4 } from "path";
|
|
4186
3730
|
|
|
4187
3731
|
// src/cli/opencode-effort.ts
|
|
4188
3732
|
var OPENAI_GPT_RUNTIME_VARIANTS = /* @__PURE__ */ new Set([
|
|
@@ -4214,80 +3758,6 @@ function resolveOpenCodeEffort(input) {
|
|
|
4214
3758
|
};
|
|
4215
3759
|
}
|
|
4216
3760
|
|
|
4217
|
-
// src/cli/skills.ts
|
|
4218
|
-
import { spawnSync } from "child_process";
|
|
4219
|
-
import { existsSync as existsSync9 } from "fs";
|
|
4220
|
-
import { homedir as homedir3 } from "os";
|
|
4221
|
-
import { join as join9 } from "path";
|
|
4222
|
-
var RECOMMENDED_SKILLS = [
|
|
4223
|
-
{
|
|
4224
|
-
name: "simplify",
|
|
4225
|
-
repo: "https://github.com/brianlovin/claude-config",
|
|
4226
|
-
skillName: "simplify",
|
|
4227
|
-
description: "YAGNI code simplification expert"
|
|
4228
|
-
},
|
|
4229
|
-
{
|
|
4230
|
-
name: "playwright-cli",
|
|
4231
|
-
repo: "https://github.com/microsoft/playwright-cli",
|
|
4232
|
-
skillName: "playwright-cli",
|
|
4233
|
-
description: "Browser automation for visual checks and testing"
|
|
4234
|
-
}
|
|
4235
|
-
];
|
|
4236
|
-
function getRecommendedSkillPath(skill, homeDir = homedir3()) {
|
|
4237
|
-
return join9(homeDir, ".agents", "skills", skill.skillName, "SKILL.md");
|
|
4238
|
-
}
|
|
4239
|
-
function isRecommendedSkillInstalled(skill, options = {}) {
|
|
4240
|
-
return existsSync9(getRecommendedSkillPath(skill, options.homeDir));
|
|
4241
|
-
}
|
|
4242
|
-
function runSkillInstallCommand(skill) {
|
|
4243
|
-
const args = [
|
|
4244
|
-
"skills",
|
|
4245
|
-
"add",
|
|
4246
|
-
skill.repo,
|
|
4247
|
-
"--skill",
|
|
4248
|
-
skill.skillName,
|
|
4249
|
-
"-a",
|
|
4250
|
-
"opencode",
|
|
4251
|
-
"-y",
|
|
4252
|
-
"--global"
|
|
4253
|
-
];
|
|
4254
|
-
try {
|
|
4255
|
-
const result = spawnSync("npx", args, { stdio: "inherit" });
|
|
4256
|
-
if (result.status !== 0) {
|
|
4257
|
-
return { success: false };
|
|
4258
|
-
}
|
|
4259
|
-
if (skill.postInstallCommands && skill.postInstallCommands.length > 0) {
|
|
4260
|
-
console.log(`Running post-install commands for ${skill.name}...`);
|
|
4261
|
-
for (const cmd of skill.postInstallCommands) {
|
|
4262
|
-
console.log(`> ${cmd}`);
|
|
4263
|
-
const [command, ...cmdArgs] = cmd.split(" ");
|
|
4264
|
-
const cmdResult = spawnSync(command, cmdArgs, { stdio: "inherit" });
|
|
4265
|
-
if (cmdResult.status !== 0) {
|
|
4266
|
-
console.warn(`Post-install command failed: ${cmd}`);
|
|
4267
|
-
}
|
|
4268
|
-
}
|
|
4269
|
-
}
|
|
4270
|
-
return { success: true };
|
|
4271
|
-
} catch (error) {
|
|
4272
|
-
console.error(`Failed to install skill: ${skill.name}`, error);
|
|
4273
|
-
return { success: false, error };
|
|
4274
|
-
}
|
|
4275
|
-
}
|
|
4276
|
-
function installRecommendedSkill(skill, options = {}) {
|
|
4277
|
-
const skillPath = getRecommendedSkillPath(skill, options.homeDir);
|
|
4278
|
-
if (isRecommendedSkillInstalled(skill, options)) {
|
|
4279
|
-
return { skill, status: "already-installed", skillPath };
|
|
4280
|
-
}
|
|
4281
|
-
const result = runSkillInstallCommand(skill);
|
|
4282
|
-
if (result.success) {
|
|
4283
|
-
return { skill, status: "installed", skillPath };
|
|
4284
|
-
}
|
|
4285
|
-
if (isRecommendedSkillInstalled(skill, options)) {
|
|
4286
|
-
return { skill, status: "already-installed", skillPath };
|
|
4287
|
-
}
|
|
4288
|
-
return { skill, status: "failed", skillPath, error: result.error };
|
|
4289
|
-
}
|
|
4290
|
-
|
|
4291
3761
|
// src/cli/operations/opencode.ts
|
|
4292
3762
|
var PACKAGE_NAME = "thoth-agents";
|
|
4293
3763
|
var EXPECTED_PLUGIN = `${PACKAGE_NAME}@latest`;
|
|
@@ -4453,7 +3923,7 @@ var openCodeActions = [
|
|
|
4453
3923
|
id: "opencode-install",
|
|
4454
3924
|
kind: "install",
|
|
4455
3925
|
label: "Install",
|
|
4456
|
-
description: "Preview OpenCode install
|
|
3926
|
+
description: "Preview OpenCode install with required external skills",
|
|
4457
3927
|
dryRun: true,
|
|
4458
3928
|
requiresConfirmation: true,
|
|
4459
3929
|
supported: true
|
|
@@ -4508,7 +3978,7 @@ function targetForLiteConfig(state) {
|
|
|
4508
3978
|
path: getExistingLiteConfigPath(),
|
|
4509
3979
|
label: "thoth-agents config",
|
|
4510
3980
|
...state ? { state } : {},
|
|
4511
|
-
expected: `seven-
|
|
3981
|
+
expected: `seven-role ${OPENAI_PRESET} roster`
|
|
4512
3982
|
};
|
|
4513
3983
|
}
|
|
4514
3984
|
function titleCaseSkillName(value) {
|
|
@@ -4522,89 +3992,32 @@ function titleCaseSkillName(value) {
|
|
|
4522
3992
|
}).join("-");
|
|
4523
3993
|
}
|
|
4524
3994
|
function homeDirFromContext(context2) {
|
|
4525
|
-
return context2.env?.HOME ?? context2.env?.USERPROFILE ?? (context2.cwd !== process.cwd() ?
|
|
3995
|
+
return context2.env?.HOME ?? context2.env?.USERPROFILE ?? (context2.cwd !== process.cwd() ? join4(context2.cwd, "home") : void 0);
|
|
4526
3996
|
}
|
|
4527
3997
|
function openCodeSkillTargets(context2) {
|
|
4528
3998
|
const homeDir = homeDirFromContext(context2);
|
|
4529
|
-
const
|
|
4530
|
-
(skill
|
|
4531
|
-
|
|
4532
|
-
const installed = existsSync10(path4);
|
|
4533
|
-
return {
|
|
4534
|
-
kind: "skill",
|
|
4535
|
-
path: path4,
|
|
4536
|
-
label: titleCaseSkillName(skill.skillName),
|
|
4537
|
-
state: installed ? "installed" : "missing",
|
|
4538
|
-
expected: "recommended global OpenCode skill",
|
|
4539
|
-
observed: installed ? "recommended global skill installed" : "recommended global skill missing"
|
|
4540
|
-
};
|
|
4541
|
-
}
|
|
4542
|
-
);
|
|
4543
|
-
let staleSkillNames = /* @__PURE__ */ new Set();
|
|
4544
|
-
let removedSkillNames = [];
|
|
4545
|
-
let skillHealthError;
|
|
4546
|
-
try {
|
|
4547
|
-
const updateCheck = checkCustomSkillsNeedUpdate();
|
|
4548
|
-
staleSkillNames = new Set(
|
|
4549
|
-
updateCheck.skillsNeedingUpdate.map(({ skill }) => skill.name)
|
|
4550
|
-
);
|
|
4551
|
-
removedSkillNames = updateCheck.removedSkills;
|
|
4552
|
-
} catch (error) {
|
|
4553
|
-
skillHealthError = String(error);
|
|
4554
|
-
}
|
|
4555
|
-
const bundledTargets = CUSTOM_SKILLS.map((skill) => {
|
|
4556
|
-
const path4 = join10(getCustomSkillsDir(), skill.name, "SKILL.md");
|
|
4557
|
-
const installed = existsSync10(path4);
|
|
4558
|
-
const stale = installed && staleSkillNames.has(skill.name);
|
|
4559
|
-
let state = "installed";
|
|
4560
|
-
let observed = "bundled OpenCode skill installed and current";
|
|
4561
|
-
if (!installed) {
|
|
4562
|
-
state = "missing";
|
|
4563
|
-
observed = "bundled OpenCode skill missing";
|
|
4564
|
-
} else if (stale) {
|
|
4565
|
-
state = "drift";
|
|
4566
|
-
observed = "bundled OpenCode skill is outdated";
|
|
4567
|
-
}
|
|
3999
|
+
const requiredTargets = REQUIRED_SKILLS.map((skill) => {
|
|
4000
|
+
const path = getRequiredSkillPath(skill, "opencode", homeDir);
|
|
4001
|
+
const installed = existsSync9(path);
|
|
4568
4002
|
return {
|
|
4569
4003
|
kind: "skill",
|
|
4570
|
-
path
|
|
4571
|
-
label: titleCaseSkillName(skill.
|
|
4572
|
-
state,
|
|
4573
|
-
expected: "
|
|
4574
|
-
observed
|
|
4004
|
+
path,
|
|
4005
|
+
label: titleCaseSkillName(skill.skillName),
|
|
4006
|
+
state: installed ? "installed" : "missing",
|
|
4007
|
+
expected: "required global OpenCode skill",
|
|
4008
|
+
observed: installed ? "required global skill installed" : "required global skill missing"
|
|
4575
4009
|
};
|
|
4576
4010
|
});
|
|
4577
4011
|
const diagnostics = [];
|
|
4578
|
-
if (
|
|
4579
|
-
diagnostics.push({
|
|
4580
|
-
severity: "minor",
|
|
4581
|
-
message: "Recommended OpenCode global skills are missing; run the OpenCode install flow with skills enabled.",
|
|
4582
|
-
code: "opencode-recommended-skills-missing"
|
|
4583
|
-
});
|
|
4584
|
-
}
|
|
4585
|
-
if (bundledTargets.some((target) => target.state === "missing")) {
|
|
4012
|
+
if (requiredTargets.some((target) => target.state === "missing")) {
|
|
4586
4013
|
diagnostics.push({
|
|
4587
4014
|
severity: "important",
|
|
4588
|
-
message: "
|
|
4589
|
-
code: "opencode-
|
|
4590
|
-
});
|
|
4591
|
-
}
|
|
4592
|
-
if (bundledTargets.some((target) => target.state === "drift") || removedSkillNames.length > 0) {
|
|
4593
|
-
diagnostics.push({
|
|
4594
|
-
severity: "important",
|
|
4595
|
-
message: "Bundled thoth-agents OpenCode skills are outdated; run OpenCode install or sync to refresh managed skills.",
|
|
4596
|
-
code: "opencode-bundled-skills-outdated"
|
|
4597
|
-
});
|
|
4598
|
-
}
|
|
4599
|
-
if (skillHealthError) {
|
|
4600
|
-
diagnostics.push({
|
|
4601
|
-
severity: "critical",
|
|
4602
|
-
message: `Bundled skill health could not be classified: ${skillHealthError}`,
|
|
4603
|
-
code: "opencode-bundled-skills-health-unknown"
|
|
4015
|
+
message: "Required OpenCode global skills are missing; run install or sync to restore them.",
|
|
4016
|
+
code: "opencode-required-skills-missing"
|
|
4604
4017
|
});
|
|
4605
4018
|
}
|
|
4606
4019
|
return {
|
|
4607
|
-
targets:
|
|
4020
|
+
targets: requiredTargets,
|
|
4608
4021
|
diagnostics
|
|
4609
4022
|
};
|
|
4610
4023
|
}
|
|
@@ -4677,12 +4090,10 @@ var REPAIRABLE_DIAGNOSTIC_CODES = /* @__PURE__ */ new Set([
|
|
|
4677
4090
|
"opencode-lite-config-missing",
|
|
4678
4091
|
"opencode-roster-drift",
|
|
4679
4092
|
ACTIVE_PRESET_SELECTED_CODE,
|
|
4680
|
-
"opencode-
|
|
4681
|
-
"opencode-bundled-skills-outdated"
|
|
4093
|
+
"opencode-required-skills-missing"
|
|
4682
4094
|
]);
|
|
4683
4095
|
var MODEL_PRECONFIG_SKILL_CODES = /* @__PURE__ */ new Set([
|
|
4684
|
-
"opencode-
|
|
4685
|
-
"opencode-bundled-skills-outdated"
|
|
4096
|
+
"opencode-required-skills-missing"
|
|
4686
4097
|
]);
|
|
4687
4098
|
function uniqueTargets(targets) {
|
|
4688
4099
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -4714,9 +4125,9 @@ function targetsForBlockingDiagnostic(diagnostic, status) {
|
|
|
4714
4125
|
if (code === "opencode-roster-drift" || code === ACTIVE_PRESET_SELECTED_CODE || code === "opencode-lite-config-missing" || code === "thoth-config-parse-error" || code === "opencode-roster-unrecognized") {
|
|
4715
4126
|
return lite ? [lite] : [];
|
|
4716
4127
|
}
|
|
4717
|
-
if (code === "opencode-
|
|
4128
|
+
if (code === "opencode-required-skills-missing") {
|
|
4718
4129
|
return status.targets.filter(
|
|
4719
|
-
(target) => target.expected === "
|
|
4130
|
+
(target) => target.expected === "required global OpenCode skill" && target.state !== "installed"
|
|
4720
4131
|
);
|
|
4721
4132
|
}
|
|
4722
4133
|
return [
|
|
@@ -4759,7 +4170,7 @@ function blockingDetails(action, status, includeRepairable = false) {
|
|
|
4759
4170
|
function canApplyToManagedHealth(action, status) {
|
|
4760
4171
|
return blockingDetails(action, status).diagnostics.length === 0;
|
|
4761
4172
|
}
|
|
4762
|
-
function
|
|
4173
|
+
function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
4763
4174
|
const mainPath = getExistingConfigPath();
|
|
4764
4175
|
const litePath = getExistingLiteConfigPath();
|
|
4765
4176
|
const main = parseConfig(mainPath);
|
|
@@ -4801,8 +4212,8 @@ function getOpenCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
|
4801
4212
|
actions: openCodeActions
|
|
4802
4213
|
};
|
|
4803
4214
|
}
|
|
4804
|
-
const mainExists =
|
|
4805
|
-
const liteExists =
|
|
4215
|
+
const mainExists = existsSync9(mainPath);
|
|
4216
|
+
const liteExists = existsSync9(litePath);
|
|
4806
4217
|
const mainTarget = {
|
|
4807
4218
|
...targetForMainConfig(),
|
|
4808
4219
|
observed: configPluginMarker(main.config)
|
|
@@ -5028,7 +4439,7 @@ function getOpenCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
|
5028
4439
|
harness: "opencode",
|
|
5029
4440
|
displayName: "OpenCode",
|
|
5030
4441
|
state: "drift",
|
|
5031
|
-
summary: selectedNamedPreset ? "A valid named OpenCode preset is active outside the managed seven-
|
|
4442
|
+
summary: selectedNamedPreset ? "A valid named OpenCode preset is active outside the managed seven-role roster." : "thoth-agents config does not match the expected seven-role roster.",
|
|
5032
4443
|
targets: [
|
|
5033
4444
|
{ ...mainTarget, state: "installed" },
|
|
5034
4445
|
{ ...liteTarget, state: "drift" },
|
|
@@ -5045,11 +4456,14 @@ function getOpenCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
|
5045
4456
|
actions: openCodeActions
|
|
5046
4457
|
};
|
|
5047
4458
|
}
|
|
4459
|
+
const requiredSkillsMissing = skillStatus.targets.some(
|
|
4460
|
+
(target) => target.state === "missing"
|
|
4461
|
+
);
|
|
5048
4462
|
return {
|
|
5049
4463
|
harness: "opencode",
|
|
5050
4464
|
displayName: "OpenCode",
|
|
5051
|
-
state: "installed",
|
|
5052
|
-
summary: "OpenCode managed thoth-agents configuration is installed.",
|
|
4465
|
+
state: requiredSkillsMissing ? "drift" : "installed",
|
|
4466
|
+
summary: requiredSkillsMissing ? "OpenCode configuration is installed, but required external skills are missing." : "OpenCode managed thoth-agents configuration is installed.",
|
|
5053
4467
|
targets: [
|
|
5054
4468
|
{ ...mainTarget, state: "installed" },
|
|
5055
4469
|
{ ...liteTarget, state: "installed" },
|
|
@@ -5059,11 +4473,17 @@ function getOpenCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
|
5059
4473
|
actions: openCodeActions
|
|
5060
4474
|
};
|
|
5061
4475
|
}
|
|
5062
|
-
function
|
|
4476
|
+
function getOpenCodeStatus(context2 = { cwd: process.cwd() }, evidence = {}) {
|
|
4477
|
+
return {
|
|
4478
|
+
...getOpenCodeManagedStatus(context2),
|
|
4479
|
+
providerCapability: classifyProviderCapabilityEvidence(evidence)
|
|
4480
|
+
};
|
|
4481
|
+
}
|
|
4482
|
+
function defaultBackup(path) {
|
|
5063
4483
|
return {
|
|
5064
4484
|
required: true,
|
|
5065
4485
|
strategy: "managed-backup-file",
|
|
5066
|
-
destinations: [{ path: `${
|
|
4486
|
+
destinations: [{ path: `${path}.bak`, label: "managed backup" }]
|
|
5067
4487
|
};
|
|
5068
4488
|
}
|
|
5069
4489
|
function backupFromItems(items) {
|
|
@@ -5085,18 +4505,6 @@ function backupFromItems(items) {
|
|
|
5085
4505
|
...primary?.description ? { description: primary.description } : {}
|
|
5086
4506
|
};
|
|
5087
4507
|
}
|
|
5088
|
-
function bundledSkillsRefreshItem() {
|
|
5089
|
-
const names = CUSTOM_SKILLS.map(({ name }) => name);
|
|
5090
|
-
return {
|
|
5091
|
-
title: "Refresh bundled thoth-agents OpenCode skills",
|
|
5092
|
-
target: {
|
|
5093
|
-
kind: "skill",
|
|
5094
|
-
label: "Bundled thoth-agents OpenCode skills",
|
|
5095
|
-
expected: `${names.length} managed bundled skills`
|
|
5096
|
-
},
|
|
5097
|
-
preview: JSON.stringify({ count: names.length, names }, null, 2)
|
|
5098
|
-
};
|
|
5099
|
-
}
|
|
5100
4508
|
function defaultDisclaimers() {
|
|
5101
4509
|
return [
|
|
5102
4510
|
{
|
|
@@ -5151,7 +4559,7 @@ function planFromItems(id, action, title, summary, items, context2) {
|
|
|
5151
4559
|
};
|
|
5152
4560
|
}
|
|
5153
4561
|
function buildOpenCodeUpdatePlan(_context = { cwd: process.cwd() }) {
|
|
5154
|
-
const
|
|
4562
|
+
const path = getExistingConfigPath();
|
|
5155
4563
|
const { plan, status } = planFromItems(
|
|
5156
4564
|
"opencode-update-preview",
|
|
5157
4565
|
"update",
|
|
@@ -5163,7 +4571,7 @@ function buildOpenCodeUpdatePlan(_context = { cwd: process.cwd() }) {
|
|
|
5163
4571
|
target: targetForMainConfig(),
|
|
5164
4572
|
state: getOpenCodeStatus(_context).state,
|
|
5165
4573
|
preview: `plugin: ["${EXPECTED_PLUGIN}"]`,
|
|
5166
|
-
backup: defaultBackup(
|
|
4574
|
+
backup: defaultBackup(path)
|
|
5167
4575
|
}
|
|
5168
4576
|
],
|
|
5169
4577
|
_context
|
|
@@ -5177,8 +4585,6 @@ function buildOpenCodeSyncPlan(_context = { cwd: process.cwd() }) {
|
|
|
5177
4585
|
const generatedConfig = generateLiteConfig({
|
|
5178
4586
|
agent: "opencode",
|
|
5179
4587
|
hasTmux: false,
|
|
5180
|
-
installSkills: false,
|
|
5181
|
-
installCustomSkills: true,
|
|
5182
4588
|
dryRun: true,
|
|
5183
4589
|
reset: false
|
|
5184
4590
|
});
|
|
@@ -5203,12 +4609,27 @@ function buildOpenCodeSyncPlan(_context = { cwd: process.cwd() }) {
|
|
|
5203
4609
|
backup: defaultBackup(getExistingConfigPath())
|
|
5204
4610
|
},
|
|
5205
4611
|
{
|
|
5206
|
-
title: "Write thoth-agents seven-
|
|
4612
|
+
title: "Write thoth-agents seven-role config",
|
|
5207
4613
|
target: targetForLiteConfig(),
|
|
5208
4614
|
preview: JSON.stringify(generatedConfig, null, 2),
|
|
5209
4615
|
backup: defaultBackup(litePath)
|
|
5210
4616
|
},
|
|
5211
|
-
|
|
4617
|
+
{
|
|
4618
|
+
title: "Install required external skills",
|
|
4619
|
+
target: {
|
|
4620
|
+
kind: "skill",
|
|
4621
|
+
label: "Required OpenCode skills",
|
|
4622
|
+
expected: REQUIRED_SKILLS.map(({ name }) => name).join(", ")
|
|
4623
|
+
},
|
|
4624
|
+
preview: JSON.stringify(
|
|
4625
|
+
REQUIRED_SKILLS.map((skill) => ({
|
|
4626
|
+
name: skill.name,
|
|
4627
|
+
...getRequiredSkillInstallCommand(skill, "opencode")
|
|
4628
|
+
})),
|
|
4629
|
+
null,
|
|
4630
|
+
2
|
|
4631
|
+
)
|
|
4632
|
+
}
|
|
5212
4633
|
],
|
|
5213
4634
|
_context
|
|
5214
4635
|
);
|
|
@@ -5221,8 +4642,6 @@ function tuiInstallConfig() {
|
|
|
5221
4642
|
return {
|
|
5222
4643
|
agent: "opencode",
|
|
5223
4644
|
hasTmux: false,
|
|
5224
|
-
installSkills: true,
|
|
5225
|
-
installCustomSkills: true,
|
|
5226
4645
|
dryRun: true,
|
|
5227
4646
|
reset: false
|
|
5228
4647
|
};
|
|
@@ -5232,16 +4651,15 @@ function buildOpenCodeInstallPlan(_context = { cwd: process.cwd() }) {
|
|
|
5232
4651
|
const installPreview = {
|
|
5233
4652
|
noTui: true,
|
|
5234
4653
|
hasTmux: false,
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
equivalentCommand: "install --agent=opencode --no-tui --tmux=no --skills=yes"
|
|
4654
|
+
requiredSkills: REQUIRED_SKILLS.map((skill) => skill.name),
|
|
4655
|
+
equivalentCommand: "install --agent=opencode --no-tui --tmux=no"
|
|
5238
4656
|
};
|
|
5239
4657
|
const litePath = getExistingLiteConfigPath();
|
|
5240
4658
|
const { plan, status } = planFromItems(
|
|
5241
4659
|
"opencode-install-preview",
|
|
5242
4660
|
"install",
|
|
5243
4661
|
"Preview install",
|
|
5244
|
-
"Preview OpenCode install
|
|
4662
|
+
"Preview OpenCode install with the required external skills.",
|
|
5245
4663
|
[
|
|
5246
4664
|
{
|
|
5247
4665
|
title: "Apply OpenCode TUI install options",
|
|
@@ -5250,7 +4668,7 @@ function buildOpenCodeInstallPlan(_context = { cwd: process.cwd() }) {
|
|
|
5250
4668
|
path: getExistingConfigPath(),
|
|
5251
4669
|
label: "OpenCode install options",
|
|
5252
4670
|
state: getOpenCodeStatus(_context).state,
|
|
5253
|
-
expected: "--no-tui --tmux=no
|
|
4671
|
+
expected: "--no-tui --tmux=no plus required external skills"
|
|
5254
4672
|
},
|
|
5255
4673
|
preview: JSON.stringify(installPreview, null, 2)
|
|
5256
4674
|
},
|
|
@@ -5268,20 +4686,26 @@ function buildOpenCodeInstallPlan(_context = { cwd: process.cwd() }) {
|
|
|
5268
4686
|
backup: defaultBackup(getExistingConfigPath())
|
|
5269
4687
|
},
|
|
5270
4688
|
{
|
|
5271
|
-
title: "Write thoth-agents seven-
|
|
4689
|
+
title: "Write thoth-agents seven-role config",
|
|
5272
4690
|
target: targetForLiteConfig(),
|
|
5273
4691
|
preview: JSON.stringify(generatedConfig, null, 2),
|
|
5274
4692
|
backup: defaultBackup(litePath)
|
|
5275
4693
|
},
|
|
5276
|
-
bundledSkillsRefreshItem(),
|
|
5277
4694
|
{
|
|
5278
|
-
title: "Install
|
|
4695
|
+
title: "Install required external skills",
|
|
5279
4696
|
target: {
|
|
5280
4697
|
kind: "skill",
|
|
5281
|
-
label: "
|
|
5282
|
-
expected: "
|
|
4698
|
+
label: "Required OpenCode skills",
|
|
4699
|
+
expected: REQUIRED_SKILLS.map(({ name }) => name).join(", ")
|
|
5283
4700
|
},
|
|
5284
|
-
preview: JSON.stringify(
|
|
4701
|
+
preview: JSON.stringify(
|
|
4702
|
+
REQUIRED_SKILLS.map((skill) => ({
|
|
4703
|
+
name: skill.name,
|
|
4704
|
+
...getRequiredSkillInstallCommand(skill, "opencode")
|
|
4705
|
+
})),
|
|
4706
|
+
null,
|
|
4707
|
+
2
|
|
4708
|
+
)
|
|
5285
4709
|
}
|
|
5286
4710
|
],
|
|
5287
4711
|
_context
|
|
@@ -5528,8 +4952,6 @@ function applyModelPlan(roles) {
|
|
|
5528
4952
|
const base = parsed.config ?? generateLiteConfig({
|
|
5529
4953
|
agent: "opencode",
|
|
5530
4954
|
hasTmux: false,
|
|
5531
|
-
installSkills: false,
|
|
5532
|
-
installCustomSkills: true,
|
|
5533
4955
|
dryRun: false,
|
|
5534
4956
|
reset: false
|
|
5535
4957
|
});
|
|
@@ -5583,85 +5005,33 @@ function applyModelPlan(roles) {
|
|
|
5583
5005
|
observed: "agents role overrides updated"
|
|
5584
5006
|
}
|
|
5585
5007
|
],
|
|
5586
|
-
backups:
|
|
5008
|
+
backups: existsSync9(`${targetPath}.bak`) ? [{ path: `${targetPath}.bak`, label: "managed backup" }] : [],
|
|
5587
5009
|
warnings,
|
|
5588
5010
|
disclaimers: defaultDisclaimers()
|
|
5589
5011
|
};
|
|
5590
5012
|
}
|
|
5591
|
-
function
|
|
5013
|
+
function applyRequiredSkills(context2) {
|
|
5592
5014
|
const warnings = [];
|
|
5593
|
-
|
|
5015
|
+
const homeDir = homeDirFromContext(context2);
|
|
5016
|
+
for (const skill of REQUIRED_SKILLS) {
|
|
5594
5017
|
try {
|
|
5595
|
-
const result =
|
|
5018
|
+
const result = installRequiredSkill(skill, "opencode", { homeDir });
|
|
5596
5019
|
if (result.status !== "failed") continue;
|
|
5597
5020
|
warnings.push({
|
|
5598
|
-
severity: "
|
|
5599
|
-
code: "opencode-
|
|
5600
|
-
message: `Failed to install
|
|
5021
|
+
severity: "critical",
|
|
5022
|
+
code: "opencode-required-skill-failed",
|
|
5023
|
+
message: `Failed to install required OpenCode skill: ${skill.name}.`
|
|
5601
5024
|
});
|
|
5602
5025
|
} catch (error) {
|
|
5603
5026
|
const reason = error instanceof Error ? error.message : String(error);
|
|
5604
5027
|
warnings.push({
|
|
5605
|
-
severity: "
|
|
5606
|
-
code: "opencode-
|
|
5607
|
-
message: `Failed to install
|
|
5028
|
+
severity: "critical",
|
|
5029
|
+
code: "opencode-required-skill-failed",
|
|
5030
|
+
message: `Failed to install required OpenCode skill ${skill.name}: ${reason}.`
|
|
5608
5031
|
});
|
|
5609
5032
|
}
|
|
5610
5033
|
}
|
|
5611
|
-
return warnings;
|
|
5612
|
-
}
|
|
5613
|
-
function bundledSkillsFailure(plan, changedTargets, backups, warnings, detail) {
|
|
5614
|
-
const message = `Failed to install bundled thoth-agents OpenCode skills: ${detail}.`;
|
|
5615
|
-
return {
|
|
5616
|
-
harness: plan.harness,
|
|
5617
|
-
action: plan.action,
|
|
5618
|
-
applied: false,
|
|
5619
|
-
summary: message,
|
|
5620
|
-
changedTargets,
|
|
5621
|
-
backups,
|
|
5622
|
-
warnings: [
|
|
5623
|
-
...warnings,
|
|
5624
|
-
{
|
|
5625
|
-
severity: "critical",
|
|
5626
|
-
code: "opencode-bundled-skills-failed",
|
|
5627
|
-
message
|
|
5628
|
-
}
|
|
5629
|
-
],
|
|
5630
|
-
disclaimers: defaultDisclaimers()
|
|
5631
|
-
};
|
|
5632
|
-
}
|
|
5633
|
-
function applyBundledSkills(plan, changedTargets, backups, warnings) {
|
|
5634
|
-
let bundled;
|
|
5635
|
-
try {
|
|
5636
|
-
bundled = installCustomSkills();
|
|
5637
|
-
} catch (error) {
|
|
5638
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
5639
|
-
return bundledSkillsFailure(
|
|
5640
|
-
plan,
|
|
5641
|
-
changedTargets,
|
|
5642
|
-
backups,
|
|
5643
|
-
warnings,
|
|
5644
|
-
detail
|
|
5645
|
-
);
|
|
5646
|
-
}
|
|
5647
|
-
if (!bundled.success) {
|
|
5648
|
-
const failedNames = bundled.failedSkills.map(({ skill }) => skill.name);
|
|
5649
|
-
const detail = failedNames.length > 0 ? failedNames.join(", ") : "unknown bundled skills";
|
|
5650
|
-
return bundledSkillsFailure(
|
|
5651
|
-
plan,
|
|
5652
|
-
changedTargets,
|
|
5653
|
-
backups,
|
|
5654
|
-
warnings,
|
|
5655
|
-
detail
|
|
5656
|
-
);
|
|
5657
|
-
}
|
|
5658
|
-
changedTargets.push({
|
|
5659
|
-
kind: "skill",
|
|
5660
|
-
label: "Bundled thoth-agents OpenCode skills",
|
|
5661
|
-
state: "installed",
|
|
5662
|
-
observed: "managed bundled skills refreshed"
|
|
5663
|
-
});
|
|
5664
|
-
return null;
|
|
5034
|
+
return { success: warnings.length === 0, warnings };
|
|
5665
5035
|
}
|
|
5666
5036
|
function applyOpenCodePlan(plan) {
|
|
5667
5037
|
const rejection = validateApplyPlan(plan);
|
|
@@ -5718,7 +5088,7 @@ function applyOpenCodePlan(plan) {
|
|
|
5718
5088
|
...targetForMainConfig("installed"),
|
|
5719
5089
|
observed: `plugin includes ${EXPECTED_PLUGIN}`
|
|
5720
5090
|
});
|
|
5721
|
-
if (
|
|
5091
|
+
if (existsSync9(`${mainConfigResult.configPath}.bak`)) {
|
|
5722
5092
|
backups.push({
|
|
5723
5093
|
path: `${mainConfigResult.configPath}.bak`,
|
|
5724
5094
|
label: "OpenCode config backup"
|
|
@@ -5729,8 +5099,6 @@ function applyOpenCodePlan(plan) {
|
|
|
5729
5099
|
{
|
|
5730
5100
|
agent: "opencode",
|
|
5731
5101
|
hasTmux: false,
|
|
5732
|
-
installSkills: issued.payload.action === "install",
|
|
5733
|
-
installCustomSkills: true,
|
|
5734
5102
|
dryRun: false,
|
|
5735
5103
|
reset: false
|
|
5736
5104
|
},
|
|
@@ -5744,32 +5112,36 @@ function applyOpenCodePlan(plan) {
|
|
|
5744
5112
|
}
|
|
5745
5113
|
changedTargets.push({
|
|
5746
5114
|
...targetForLiteConfig("installed"),
|
|
5747
|
-
observed: "seven-
|
|
5115
|
+
observed: "seven-role roster written"
|
|
5748
5116
|
});
|
|
5749
|
-
if (
|
|
5117
|
+
if (existsSync9(`${liteResult.configPath}.bak`)) {
|
|
5750
5118
|
backups.push({
|
|
5751
5119
|
path: `${liteResult.configPath}.bak`,
|
|
5752
5120
|
label: "thoth-agents config backup"
|
|
5753
5121
|
});
|
|
5754
5122
|
}
|
|
5755
|
-
const skillsRejection = applyBundledSkills(
|
|
5756
|
-
plan,
|
|
5757
|
-
changedTargets,
|
|
5758
|
-
backups,
|
|
5759
|
-
warnings
|
|
5760
|
-
);
|
|
5761
|
-
if (skillsRejection) return skillsRejection;
|
|
5762
5123
|
}
|
|
5763
|
-
if (issued.payload.action === "install") {
|
|
5764
|
-
|
|
5124
|
+
if (issued.payload.action === "install" || issued.payload.action === "sync") {
|
|
5125
|
+
const requiredSkills = applyRequiredSkills(issued.context);
|
|
5126
|
+
warnings.push(...requiredSkills.warnings);
|
|
5765
5127
|
changedTargets.push({
|
|
5766
5128
|
kind: "skill",
|
|
5767
|
-
label: "
|
|
5768
|
-
state:
|
|
5769
|
-
|
|
5770
|
-
) ? "drift" : "installed",
|
|
5771
|
-
observed: "optional skills=yes processed"
|
|
5129
|
+
label: "Required OpenCode skills",
|
|
5130
|
+
state: requiredSkills.success ? "installed" : "drift",
|
|
5131
|
+
observed: requiredSkills.success ? "required external skills installed" : "required external skill installation failed"
|
|
5772
5132
|
});
|
|
5133
|
+
if (!requiredSkills.success) {
|
|
5134
|
+
return {
|
|
5135
|
+
harness: "opencode",
|
|
5136
|
+
action: plan.action,
|
|
5137
|
+
applied: false,
|
|
5138
|
+
summary: "OpenCode configuration was written, but required skills failed to install.",
|
|
5139
|
+
changedTargets,
|
|
5140
|
+
backups,
|
|
5141
|
+
warnings,
|
|
5142
|
+
disclaimers: defaultDisclaimers()
|
|
5143
|
+
};
|
|
5144
|
+
}
|
|
5773
5145
|
}
|
|
5774
5146
|
return {
|
|
5775
5147
|
harness: "opencode",
|
|
@@ -5806,14 +5178,14 @@ import { execFileSync } from "child_process";
|
|
|
5806
5178
|
|
|
5807
5179
|
// src/cli/model-catalog/cache.ts
|
|
5808
5180
|
import {
|
|
5809
|
-
existsSync as
|
|
5181
|
+
existsSync as existsSync10,
|
|
5810
5182
|
mkdirSync as mkdirSync3,
|
|
5811
|
-
readFileSync as
|
|
5183
|
+
readFileSync as readFileSync6,
|
|
5812
5184
|
renameSync as renameSync2,
|
|
5813
5185
|
unlinkSync,
|
|
5814
5186
|
writeFileSync as writeFileSync3
|
|
5815
5187
|
} from "fs";
|
|
5816
|
-
import { homedir as
|
|
5188
|
+
import { homedir as homedir3 } from "os";
|
|
5817
5189
|
import { dirname as dirname5, posix, win32 } from "path";
|
|
5818
5190
|
import { z as z2 } from "zod";
|
|
5819
5191
|
var modelCatalogSourceSchema = z2.enum(["remote", "lkg", "manual"]);
|
|
@@ -5834,19 +5206,19 @@ var modelsDevCacheSchema = z2.object({
|
|
|
5834
5206
|
function resolveModelsDevCachePath(options = {}) {
|
|
5835
5207
|
const platform = options.platform ?? process.platform;
|
|
5836
5208
|
const env = options.env ?? process.env;
|
|
5837
|
-
const home = options.homeDir ??
|
|
5209
|
+
const home = options.homeDir ?? homedir3();
|
|
5838
5210
|
if (platform === "win32") {
|
|
5839
|
-
const
|
|
5840
|
-
const base2 = env.LOCALAPPDATA ?? env.APPDATA ??
|
|
5841
|
-
return
|
|
5211
|
+
const path = win32;
|
|
5212
|
+
const base2 = env.LOCALAPPDATA ?? env.APPDATA ?? path.join(home, "AppData", "Local");
|
|
5213
|
+
return path.join(base2, "thoth-agents", "models-dev-v1.json");
|
|
5842
5214
|
}
|
|
5843
5215
|
const base = env.XDG_CACHE_HOME ?? posix.join(home, ".cache");
|
|
5844
5216
|
return posix.join(base, "thoth-agents", "models-dev-v1.json");
|
|
5845
5217
|
}
|
|
5846
5218
|
function readModelsDevCache(cachePath) {
|
|
5847
|
-
if (!
|
|
5219
|
+
if (!existsSync10(cachePath)) return void 0;
|
|
5848
5220
|
try {
|
|
5849
|
-
const input = JSON.parse(
|
|
5221
|
+
const input = JSON.parse(readFileSync6(cachePath, "utf8"));
|
|
5850
5222
|
const parsed = modelsDevCacheSchema.safeParse(input);
|
|
5851
5223
|
return parsed.success ? parsed.data : void 0;
|
|
5852
5224
|
} catch {
|
|
@@ -5868,7 +5240,7 @@ function writeModelsDevCacheAtomic(cachePath, cache) {
|
|
|
5868
5240
|
);
|
|
5869
5241
|
renameSync2(temporaryPath, cachePath);
|
|
5870
5242
|
} catch (error) {
|
|
5871
|
-
if (
|
|
5243
|
+
if (existsSync10(temporaryPath)) unlinkSync(temporaryPath);
|
|
5872
5244
|
throw error;
|
|
5873
5245
|
}
|
|
5874
5246
|
}
|
|
@@ -6060,7 +5432,7 @@ var CODEX_DOCUMENTED_EFFORTS2 = /* @__PURE__ */ new Set([
|
|
|
6060
5432
|
"max",
|
|
6061
5433
|
"ultra"
|
|
6062
5434
|
]);
|
|
6063
|
-
var
|
|
5435
|
+
var CLAUDE_CODE_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
6064
5436
|
function parseOpenCodeModels(output) {
|
|
6065
5437
|
const seen = /* @__PURE__ */ new Set();
|
|
6066
5438
|
const options = [];
|
|
@@ -6093,7 +5465,7 @@ function writableEfforts(harness, option) {
|
|
|
6093
5465
|
}
|
|
6094
5466
|
if (harness === "claude") {
|
|
6095
5467
|
return option.efforts.filter(
|
|
6096
|
-
(effort) =>
|
|
5468
|
+
(effort) => CLAUDE_CODE_EFFORTS.includes(effort)
|
|
6097
5469
|
);
|
|
6098
5470
|
}
|
|
6099
5471
|
return option.efforts.filter(
|
|
@@ -6144,7 +5516,7 @@ var CLAUDE_CODE_MODEL_OPTIONS = [
|
|
|
6144
5516
|
}
|
|
6145
5517
|
].map((option) => ({
|
|
6146
5518
|
...option,
|
|
6147
|
-
efforts: option.id === "inherit" ? [] : [...
|
|
5519
|
+
efforts: option.id === "inherit" ? [] : [...CLAUDE_CODE_EFFORTS],
|
|
6148
5520
|
source: "manual"
|
|
6149
5521
|
}));
|
|
6150
5522
|
async function getModelOptions(harness) {
|
|
@@ -6181,7 +5553,7 @@ async function getModelOptions(harness) {
|
|
|
6181
5553
|
}
|
|
6182
5554
|
|
|
6183
5555
|
// src/cli/tui/operations.ts
|
|
6184
|
-
import { existsSync as
|
|
5556
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
|
|
6185
5557
|
var context = { cwd: process.cwd() };
|
|
6186
5558
|
var codexContext = { cwd: process.cwd() };
|
|
6187
5559
|
var claudeCodeContext = {
|
|
@@ -6191,7 +5563,8 @@ var claudeCodeContext = {
|
|
|
6191
5563
|
var opencodeModelRoles = ALL_AGENT_NAMES.map(
|
|
6192
5564
|
(role) => ({
|
|
6193
5565
|
role,
|
|
6194
|
-
model:
|
|
5566
|
+
model: getDefaultOpenCodeModel(role),
|
|
5567
|
+
effort: { kind: "effort", value: getDefaultOpenCodeVariant(role) }
|
|
6195
5568
|
})
|
|
6196
5569
|
);
|
|
6197
5570
|
var codexModelRoles = CODEX_ROLE_NAMES.map(
|
|
@@ -6222,7 +5595,7 @@ function getCodexModelRoles(source = codexContext) {
|
|
|
6222
5595
|
const item = plan.items.find(
|
|
6223
5596
|
(candidate) => candidate.action === "write-role-toml" && candidate.role === role
|
|
6224
5597
|
);
|
|
6225
|
-
const content = item &&
|
|
5598
|
+
const content = item && existsSync11(item.targetPath) ? readFileSync7(item.targetPath, "utf8") : item?.content;
|
|
6226
5599
|
const effort = content ? parseRoleTomlEffort(content) : void 0;
|
|
6227
5600
|
return {
|
|
6228
5601
|
role,
|
|
@@ -6234,32 +5607,8 @@ function getCodexModelRoles(source = codexContext) {
|
|
|
6234
5607
|
return codexModelRoles.map((role) => ({ ...role }));
|
|
6235
5608
|
}
|
|
6236
5609
|
}
|
|
6237
|
-
function getClaudeCodeModelRoles(
|
|
6238
|
-
|
|
6239
|
-
const plan = buildClaudeCodeSetupPlan({
|
|
6240
|
-
dryRun: true,
|
|
6241
|
-
reset: false,
|
|
6242
|
-
scope: source.scope ?? "user",
|
|
6243
|
-
projectRoot: source.cwd,
|
|
6244
|
-
homeDir: source.homeDir,
|
|
6245
|
-
packageRoot: source.packageRoot
|
|
6246
|
-
});
|
|
6247
|
-
return CLAUDE_CODE_ROLE_NAMES.map((role) => {
|
|
6248
|
-
const item = plan.items.find(
|
|
6249
|
-
(candidate) => candidate.kind === "subagent" && candidate.role === role
|
|
6250
|
-
);
|
|
6251
|
-
const content = item && existsSync12(item.targetPath) ? readFileSync11(item.targetPath, "utf8") : item?.content;
|
|
6252
|
-
const model = content ? parseSubagentModel(content) : void 0;
|
|
6253
|
-
const effort = content ? parseSubagentEffort(content) : void 0;
|
|
6254
|
-
return {
|
|
6255
|
-
role,
|
|
6256
|
-
model: model ?? "inherit",
|
|
6257
|
-
effort: effort ? { kind: "effort", value: effort } : { kind: "inherit" }
|
|
6258
|
-
};
|
|
6259
|
-
});
|
|
6260
|
-
} catch {
|
|
6261
|
-
return defaultClaudeCodeModelRoles();
|
|
6262
|
-
}
|
|
5610
|
+
function getClaudeCodeModelRoles(_source = claudeCodeContext) {
|
|
5611
|
+
return defaultClaudeCodeModelRoles();
|
|
6263
5612
|
}
|
|
6264
5613
|
function readRoleField(config, role, field) {
|
|
6265
5614
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
@@ -6281,10 +5630,13 @@ function getOpenCodeModelRoles() {
|
|
|
6281
5630
|
const selectedPreset = presetName !== void 0 ? presets[presetName] : void 0;
|
|
6282
5631
|
const activePreset = selectedPreset && typeof selectedPreset === "object" && !Array.isArray(selectedPreset) ? selectedPreset : void 0;
|
|
6283
5632
|
return ALL_AGENT_NAMES.map((role) => {
|
|
6284
|
-
const
|
|
5633
|
+
const defaultModel = getDefaultOpenCodeModel(role);
|
|
5634
|
+
const model = readRoleField(agents, role, "model") ?? readRoleField(activePreset, role, "model") ?? defaultModel;
|
|
5635
|
+
const configuredVariant = readRoleField(agents, role, "variant") ?? readRoleField(activePreset, role, "variant");
|
|
5636
|
+
const variant = configuredVariant ?? (model === defaultModel ? getDefaultOpenCodeVariant(role) : void 0);
|
|
6285
5637
|
return {
|
|
6286
5638
|
role,
|
|
6287
|
-
model
|
|
5639
|
+
model,
|
|
6288
5640
|
effort: variant ? { kind: "effort", value: variant } : { kind: "inherit" }
|
|
6289
5641
|
};
|
|
6290
5642
|
});
|
|
@@ -6302,12 +5654,12 @@ function buildTuiModelPlan(harness, roles) {
|
|
|
6302
5654
|
return buildCodexModelPlan({ harness, dryRun: true, roles }, codexContext);
|
|
6303
5655
|
}
|
|
6304
5656
|
var defaultTuiOperations = {
|
|
6305
|
-
status(harness) {
|
|
6306
|
-
if (harness === "opencode") return getOpenCodeStatus(context);
|
|
5657
|
+
status(harness, evidence) {
|
|
5658
|
+
if (harness === "opencode") return getOpenCodeStatus(context, evidence);
|
|
6307
5659
|
if (harness === "claude") {
|
|
6308
|
-
return getClaudeCodeStatus(claudeCodeContext);
|
|
5660
|
+
return getClaudeCodeStatus(claudeCodeContext, evidence);
|
|
6309
5661
|
}
|
|
6310
|
-
return getCodexStatus(codexContext);
|
|
5662
|
+
return getCodexStatus(codexContext, evidence);
|
|
6311
5663
|
},
|
|
6312
5664
|
modelRoles(harness) {
|
|
6313
5665
|
if (harness === "opencode") return getOpenCodeModelRoles();
|
|
@@ -6365,8 +5717,9 @@ export {
|
|
|
6365
5717
|
buildClaudeCodeSetupPlan,
|
|
6366
5718
|
applyClaudeCodeSetup,
|
|
6367
5719
|
formatClaudeCodeSetupPlan,
|
|
6368
|
-
|
|
6369
|
-
|
|
5720
|
+
REQUIRED_SKILLS,
|
|
5721
|
+
getRequiredSkillInstallCommand,
|
|
5722
|
+
installRequiredSkill,
|
|
6370
5723
|
getClaudeCodeStatus,
|
|
6371
5724
|
buildClaudeCodeUpdatePlan,
|
|
6372
5725
|
buildClaudeCodeSyncPlan,
|