thoth-agents 0.3.8 → 0.3.9
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/.claude-plugin/marketplace.json +1 -1
- package/README.md +27 -0
- package/dist/{chunk-F2K4CS5J.js → chunk-2TLT6WWY.js} +147 -22
- package/dist/{chunk-6GQYMWWT.js → chunk-MY2GIOOH.js} +1 -1
- package/dist/{chunk-3WNTCPVK.js → chunk-XEEYNMPC.js} +1291 -290
- package/dist/cli/commands.d.ts +1 -0
- package/dist/cli/config-io.d.ts +2 -1
- package/dist/cli/index.js +126 -428
- package/dist/cli/install-completion.d.ts +34 -0
- package/dist/cli/install-ledger.d.ts +44 -0
- package/dist/cli/install.d.ts +9 -1
- package/dist/cli/operations/claude-code.d.ts +12 -1
- package/dist/cli/operations/codex.d.ts +16 -0
- package/dist/cli/operations/opencode.d.ts +20 -3
- package/dist/cli/operations/types.d.ts +13 -0
- package/dist/cli/package-version.d.ts +19 -0
- package/dist/cli/tui/index.js +63 -24
- package/dist/hooks/auto-update-checker/checker.d.ts +0 -5
- package/dist/hooks/auto-update-checker/index.d.ts +1 -2
- package/dist/hooks/auto-update-checker/types.d.ts +0 -1
- package/dist/index.js +98 -251
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/dist/hooks/auto-update-checker/cache.d.ts +0 -7
package/dist/cli/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
getOpenCodeVersion,
|
|
5
5
|
isOpenCodeInstalled,
|
|
6
6
|
opencodeAdapter
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-MY2GIOOH.js";
|
|
8
8
|
import {
|
|
9
9
|
CODEX_ROLE_NAMES,
|
|
10
10
|
REQUIRED_SKILLS,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
applyClaudeCodePlan,
|
|
13
13
|
applyClaudeCodeSetup,
|
|
14
14
|
applyCodexPlan,
|
|
15
|
+
applyCodexPluginSetup,
|
|
15
16
|
applyCodexSetup,
|
|
16
17
|
applyOpenCodePlan,
|
|
17
18
|
buildClaudeCodeModelPlan,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
buildClaudeCodeSyncPlan,
|
|
20
21
|
buildClaudeCodeUpdatePlan,
|
|
21
22
|
buildCodexModelPlan,
|
|
23
|
+
buildCodexPluginSetupPlan,
|
|
22
24
|
buildCodexSetupPlan,
|
|
23
25
|
buildCodexSyncPlan,
|
|
24
26
|
buildCodexUpdatePlan,
|
|
@@ -28,14 +30,15 @@ import {
|
|
|
28
30
|
claudeCodeAdapter,
|
|
29
31
|
codexAdapter,
|
|
30
32
|
defaultClaudeCodeModelRoles,
|
|
33
|
+
finalizeHarnessInstall,
|
|
31
34
|
formatClaudeCodeSetupPlan,
|
|
35
|
+
formatCodexPluginSetupPlan,
|
|
32
36
|
formatCodexSetupPlan,
|
|
33
37
|
getClaudeCodeModelRoles,
|
|
34
38
|
getClaudeCodeStatus,
|
|
35
39
|
getCodexModelRoles,
|
|
36
40
|
getCodexStatus,
|
|
37
41
|
getModelOptions,
|
|
38
|
-
getNpxCommand,
|
|
39
42
|
getOpenCodeModelRoles,
|
|
40
43
|
getOpenCodeStatus,
|
|
41
44
|
getOperationHarness,
|
|
@@ -44,7 +47,7 @@ import {
|
|
|
44
47
|
listOperationHarnesses,
|
|
45
48
|
normalizeEffortSelection,
|
|
46
49
|
syncOpenCodeOwnedSkills
|
|
47
|
-
} from "../chunk-
|
|
50
|
+
} from "../chunk-XEEYNMPC.js";
|
|
48
51
|
import {
|
|
49
52
|
ALL_AGENT_NAMES,
|
|
50
53
|
detectCurrentConfig,
|
|
@@ -52,9 +55,10 @@ import {
|
|
|
52
55
|
getDefaultOpenCodeModel,
|
|
53
56
|
getDefaultOpenCodeVariant,
|
|
54
57
|
getExistingLiteConfigPath,
|
|
58
|
+
resolveExecutingPackageVersion,
|
|
55
59
|
updateOpenCodeMainConfig,
|
|
56
60
|
writeLiteConfig
|
|
57
|
-
} from "../chunk-
|
|
61
|
+
} from "../chunk-2TLT6WWY.js";
|
|
58
62
|
|
|
59
63
|
// src/cli/index.ts
|
|
60
64
|
import { pathToFileURL } from "url";
|
|
@@ -74,402 +78,6 @@ function getHarnessAdapter(harness) {
|
|
|
74
78
|
import { existsSync } from "fs";
|
|
75
79
|
import { homedir } from "os";
|
|
76
80
|
import { cwd } from "process";
|
|
77
|
-
|
|
78
|
-
// src/cli/codex-plugin-install.ts
|
|
79
|
-
import { spawnSync } from "child_process";
|
|
80
|
-
var MARKETPLACE_NAME = "thoth-agents";
|
|
81
|
-
var MARKETPLACE_SOURCE = "EremesNG/thoth-agents";
|
|
82
|
-
var PLUGIN_ID = "thoth-agents@thoth-agents";
|
|
83
|
-
var MARKETPLACE_TARGET = "codex://marketplaces/thoth-agents";
|
|
84
|
-
var PLUGIN_TARGET = "codex://plugins/thoth-agents@thoth-agents";
|
|
85
|
-
function getCodexCommand(args, options = {}) {
|
|
86
|
-
if ((options.platform ?? process.platform) !== "win32") {
|
|
87
|
-
return { command: "codex", args: [...args] };
|
|
88
|
-
}
|
|
89
|
-
return {
|
|
90
|
-
command: options.commandShell ?? process.env.ComSpec ?? "cmd.exe",
|
|
91
|
-
args: ["/d", "/s", "/c", "codex", ...args]
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
function defaultCommandExecutor(command, args, options) {
|
|
95
|
-
const invocation = command === "codex" ? getCodexCommand(args) : { command, args: [...args] };
|
|
96
|
-
const result = spawnSync(invocation.command, invocation.args, {
|
|
97
|
-
cwd: options.cwd,
|
|
98
|
-
encoding: "utf8",
|
|
99
|
-
shell: false,
|
|
100
|
-
windowsHide: true
|
|
101
|
-
});
|
|
102
|
-
return {
|
|
103
|
-
exitCode: result.status ?? (result.error ? 1 : 0),
|
|
104
|
-
stdout: result.stdout ?? "",
|
|
105
|
-
stderr: result.stderr ?? result.error?.message ?? ""
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
function isRecord(value) {
|
|
109
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
110
|
-
}
|
|
111
|
-
function parseRecordArray(value) {
|
|
112
|
-
return Array.isArray(value) && value.every(isRecord) ? value : null;
|
|
113
|
-
}
|
|
114
|
-
function parseManagerRecord(content) {
|
|
115
|
-
try {
|
|
116
|
-
const parsed = JSON.parse(content);
|
|
117
|
-
return isRecord(parsed) ? parsed : null;
|
|
118
|
-
} catch {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
function normalizeMarketplaceSource(value) {
|
|
123
|
-
return value.trim().replace(/^git@github\.com:/i, "").replace(/^(?:https?|ssh):\/\/(?:git@)?github\.com\//i, "").replace(/^github\.com\//i, "").replace(/\/?\.git\/?$/i, "").replace(/\/$/, "").toLowerCase();
|
|
124
|
-
}
|
|
125
|
-
function marketplaceSources(entry) {
|
|
126
|
-
const sources = [entry.repo, entry.source];
|
|
127
|
-
if (isRecord(entry.marketplaceSource)) {
|
|
128
|
-
sources.push(entry.marketplaceSource.repo, entry.marketplaceSource.source);
|
|
129
|
-
}
|
|
130
|
-
return sources.filter(
|
|
131
|
-
(source) => typeof source === "string"
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
function isCanonicalMarketplace(entry) {
|
|
135
|
-
if (entry.name !== MARKETPLACE_NAME) return false;
|
|
136
|
-
const canonical = MARKETPLACE_SOURCE.toLowerCase();
|
|
137
|
-
return marketplaceSources(entry).some(
|
|
138
|
-
(source) => normalizeMarketplaceSource(source) === canonical
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
function inspectCodexManager(executor, cwd2) {
|
|
142
|
-
const marketplaceResult = executor(
|
|
143
|
-
"codex",
|
|
144
|
-
["plugin", "marketplace", "list", "--json"],
|
|
145
|
-
{ cwd: cwd2 }
|
|
146
|
-
);
|
|
147
|
-
const pluginResult = executor(
|
|
148
|
-
"codex",
|
|
149
|
-
["plugin", "list", "--available", "--json"],
|
|
150
|
-
{ cwd: cwd2 }
|
|
151
|
-
);
|
|
152
|
-
const diagnostics = [];
|
|
153
|
-
if (marketplaceResult.exitCode !== 0 || pluginResult.exitCode !== 0) {
|
|
154
|
-
diagnostics.push(
|
|
155
|
-
"Codex native plugin state could not be inspected; no manager mutation will be attempted."
|
|
156
|
-
);
|
|
157
|
-
return {
|
|
158
|
-
ready: false,
|
|
159
|
-
marketplace: "unknown",
|
|
160
|
-
plugin: "unknown",
|
|
161
|
-
diagnostics
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
const marketplaceRecord = parseManagerRecord(marketplaceResult.stdout);
|
|
165
|
-
const pluginRecord = parseManagerRecord(pluginResult.stdout);
|
|
166
|
-
const marketplaces = marketplaceRecord ? parseRecordArray(marketplaceRecord.marketplaces) : null;
|
|
167
|
-
const installedPlugins = pluginRecord ? parseRecordArray(pluginRecord.installed) : null;
|
|
168
|
-
if (!marketplaces || !installedPlugins) {
|
|
169
|
-
diagnostics.push(
|
|
170
|
-
"Codex returned unparseable plugin manager JSON; no manager mutation will be attempted."
|
|
171
|
-
);
|
|
172
|
-
return {
|
|
173
|
-
ready: false,
|
|
174
|
-
marketplace: "unknown",
|
|
175
|
-
plugin: "unknown",
|
|
176
|
-
diagnostics
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
const namedMarketplaces = marketplaces.filter(
|
|
180
|
-
(entry) => entry.name === MARKETPLACE_NAME
|
|
181
|
-
);
|
|
182
|
-
let marketplace = "absent";
|
|
183
|
-
if (namedMarketplaces.length > 0) {
|
|
184
|
-
marketplace = namedMarketplaces.every(isCanonicalMarketplace) ? "installed" : "conflict";
|
|
185
|
-
}
|
|
186
|
-
if (marketplace === "conflict") {
|
|
187
|
-
diagnostics.push(
|
|
188
|
-
"A Codex marketplace named thoth-agents is registered from a different source; resolve it through Codex before retrying."
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
const matchingPlugins = installedPlugins.filter(
|
|
192
|
-
(entry) => entry.pluginId === PLUGIN_ID
|
|
193
|
-
);
|
|
194
|
-
let plugin = "absent";
|
|
195
|
-
if (matchingPlugins.some((entry) => entry.enabled === true)) {
|
|
196
|
-
plugin = "installed";
|
|
197
|
-
} else if (matchingPlugins.length > 0) {
|
|
198
|
-
plugin = "disabled";
|
|
199
|
-
}
|
|
200
|
-
return {
|
|
201
|
-
ready: marketplace !== "conflict",
|
|
202
|
-
marketplace,
|
|
203
|
-
plugin,
|
|
204
|
-
diagnostics
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
function commandItem(action, projectRoot) {
|
|
208
|
-
if (action === "register-marketplace") {
|
|
209
|
-
return {
|
|
210
|
-
action,
|
|
211
|
-
targetPath: MARKETPLACE_TARGET,
|
|
212
|
-
description: "Register the package-owned thoth-agents Codex marketplace.",
|
|
213
|
-
command: {
|
|
214
|
-
executable: "codex",
|
|
215
|
-
args: ["plugin", "marketplace", "add", MARKETPLACE_SOURCE, "--json"],
|
|
216
|
-
cwd: projectRoot
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
return {
|
|
221
|
-
action,
|
|
222
|
-
targetPath: PLUGIN_TARGET,
|
|
223
|
-
description: "Install and enable the thoth-agents Codex plugin from its native marketplace.",
|
|
224
|
-
command: {
|
|
225
|
-
executable: "codex",
|
|
226
|
-
args: ["plugin", "add", PLUGIN_ID, "--json"],
|
|
227
|
-
cwd: projectRoot
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
function buildCodexPluginSetupPlan(config) {
|
|
232
|
-
const commandExecutor = config.commandExecutor ?? defaultCommandExecutor;
|
|
233
|
-
const inspection = inspectCodexManager(commandExecutor, config.projectRoot);
|
|
234
|
-
const items = [];
|
|
235
|
-
if (inspection.ready) {
|
|
236
|
-
if (inspection.marketplace === "absent") {
|
|
237
|
-
items.push(commandItem("register-marketplace", config.projectRoot));
|
|
238
|
-
}
|
|
239
|
-
if (inspection.plugin === "absent" || inspection.plugin === "disabled") {
|
|
240
|
-
items.push(commandItem("install-plugin", config.projectRoot));
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
return {
|
|
244
|
-
dryRun: config.dryRun === true,
|
|
245
|
-
items,
|
|
246
|
-
ready: inspection.ready,
|
|
247
|
-
projectRoot: config.projectRoot,
|
|
248
|
-
commandExecutor,
|
|
249
|
-
diagnostics: [
|
|
250
|
-
...inspection.diagnostics,
|
|
251
|
-
"Codex installs thoth-agents from EremesNG/thoth-agents through its native plugin manager and owns the cached plugin files.",
|
|
252
|
-
"Restart Codex after installation; use /plugins to inspect marketplace and plugin state."
|
|
253
|
-
]
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
function uniqueMessages(messages) {
|
|
257
|
-
return [...new Set(messages)];
|
|
258
|
-
}
|
|
259
|
-
function boundedCommandFailure(item, result) {
|
|
260
|
-
const detail = result.stderr.trim().replace(/\s+/g, " ").slice(0, 240);
|
|
261
|
-
return `${item.description} Codex exited with code ${result.exitCode}${detail ? `: ${detail}` : "."}`;
|
|
262
|
-
}
|
|
263
|
-
function applyCodexPluginSetup(plan) {
|
|
264
|
-
const changed = [];
|
|
265
|
-
const diagnostics = uniqueMessages(plan.diagnostics);
|
|
266
|
-
if (!plan.ready) {
|
|
267
|
-
return {
|
|
268
|
-
success: false,
|
|
269
|
-
changed,
|
|
270
|
-
diagnostics,
|
|
271
|
-
error: "Codex native plugin manager state is not safe to mutate."
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
if (plan.dryRun) return { success: true, changed, diagnostics };
|
|
275
|
-
for (const item of plan.items) {
|
|
276
|
-
const result = plan.commandExecutor(
|
|
277
|
-
item.command.executable,
|
|
278
|
-
item.command.args,
|
|
279
|
-
{ cwd: item.command.cwd }
|
|
280
|
-
);
|
|
281
|
-
if (result.exitCode !== 0) {
|
|
282
|
-
return {
|
|
283
|
-
success: false,
|
|
284
|
-
changed,
|
|
285
|
-
diagnostics,
|
|
286
|
-
error: boundedCommandFailure(item, result)
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
changed.push(item.targetPath);
|
|
290
|
-
}
|
|
291
|
-
const inspection = inspectCodexManager(
|
|
292
|
-
plan.commandExecutor,
|
|
293
|
-
plan.projectRoot
|
|
294
|
-
);
|
|
295
|
-
diagnostics.push(...inspection.diagnostics);
|
|
296
|
-
if (inspection.marketplace !== "installed" || inspection.plugin !== "installed") {
|
|
297
|
-
return {
|
|
298
|
-
success: false,
|
|
299
|
-
changed,
|
|
300
|
-
diagnostics: uniqueMessages(diagnostics),
|
|
301
|
-
error: "Codex did not verify the expected marketplace and enabled plugin after mutation."
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
return { success: true, changed, diagnostics: uniqueMessages(diagnostics) };
|
|
305
|
-
}
|
|
306
|
-
function formatCodexPluginSetupPlan(plan) {
|
|
307
|
-
const lines = plan.items.map(
|
|
308
|
-
(item) => `- ${item.action}: ${item.command.executable} ${item.command.args.join(" ")}`
|
|
309
|
-
);
|
|
310
|
-
return ["Codex plugin setup plan:", ...lines].join("\n");
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// src/cli/thoth-mem-install.ts
|
|
314
|
-
import { spawnSync as spawnSync2 } from "child_process";
|
|
315
|
-
var THOTH_MEM_SETUP_TIMEOUT_MS = 12e4;
|
|
316
|
-
var STATUS_EXIT_CODE = {
|
|
317
|
-
complete: 0,
|
|
318
|
-
failed: 1,
|
|
319
|
-
partial: 2,
|
|
320
|
-
requires_user_action: 3
|
|
321
|
-
};
|
|
322
|
-
var SETUP_STATUSES = new Set(
|
|
323
|
-
Object.keys(STATUS_EXIT_CODE)
|
|
324
|
-
);
|
|
325
|
-
var STEP_OUTCOMES = /* @__PURE__ */ new Set([
|
|
326
|
-
"planned",
|
|
327
|
-
"skipped",
|
|
328
|
-
"confirmed",
|
|
329
|
-
"failed",
|
|
330
|
-
"unavailable"
|
|
331
|
-
]);
|
|
332
|
-
function isRecord2(value) {
|
|
333
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
334
|
-
}
|
|
335
|
-
function isStringArray(value) {
|
|
336
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
337
|
-
}
|
|
338
|
-
function isSetupStep(value) {
|
|
339
|
-
return isRecord2(value) && typeof value.name === "string" && value.name.length > 0 && typeof value.outcome === "string" && STEP_OUTCOMES.has(value.outcome);
|
|
340
|
-
}
|
|
341
|
-
function isProviderSetupResult(value, expectedHarness) {
|
|
342
|
-
return isRecord2(value) && typeof value.status === "string" && SETUP_STATUSES.has(value.status) && typeof value.changed === "boolean" && value.harness === expectedHarness && value.scope === "global" && typeof value.target === "string" && value.target.length > 0 && Array.isArray(value.steps) && value.steps.every(isSetupStep) && isStringArray(value.diagnostics) && isStringArray(value.manual_actions) && (value.receipt === null || typeof value.receipt === "string");
|
|
343
|
-
}
|
|
344
|
-
function parseProviderSetupResult(stdout, expectedHarness) {
|
|
345
|
-
let value;
|
|
346
|
-
try {
|
|
347
|
-
value = JSON.parse(stdout.trim());
|
|
348
|
-
} catch {
|
|
349
|
-
return void 0;
|
|
350
|
-
}
|
|
351
|
-
return isProviderSetupResult(value, expectedHarness) ? value : void 0;
|
|
352
|
-
}
|
|
353
|
-
function defaultCommandExecutor2(command, args, options) {
|
|
354
|
-
const result = spawnSync2(command, [...args], {
|
|
355
|
-
cwd: options.cwd,
|
|
356
|
-
encoding: "utf8",
|
|
357
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
358
|
-
timeout: options.timeoutMs
|
|
359
|
-
});
|
|
360
|
-
return {
|
|
361
|
-
exitCode: result.status,
|
|
362
|
-
stdout: result.stdout ?? "",
|
|
363
|
-
stderr: result.stderr ?? "",
|
|
364
|
-
error: result.error
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
function describeError(error) {
|
|
368
|
-
return error instanceof Error ? error.message : String(error);
|
|
369
|
-
}
|
|
370
|
-
function isTimeoutError(error) {
|
|
371
|
-
return isRecord2(error) && error.code === "ETIMEDOUT";
|
|
372
|
-
}
|
|
373
|
-
function invalidResult(harness, command, args, exitCode, error) {
|
|
374
|
-
return {
|
|
375
|
-
success: false,
|
|
376
|
-
evidenceValid: false,
|
|
377
|
-
status: "invalid",
|
|
378
|
-
changed: false,
|
|
379
|
-
harness,
|
|
380
|
-
steps: [],
|
|
381
|
-
diagnostics: [],
|
|
382
|
-
manualActions: [],
|
|
383
|
-
receipt: null,
|
|
384
|
-
command,
|
|
385
|
-
args,
|
|
386
|
-
exitCode,
|
|
387
|
-
error
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
function getThothMemSetupCommand(harness, dryRun, options = {}) {
|
|
391
|
-
return getNpxCommand(
|
|
392
|
-
[
|
|
393
|
-
"-y",
|
|
394
|
-
"thoth-mem@latest",
|
|
395
|
-
"setup",
|
|
396
|
-
harness,
|
|
397
|
-
"--scope",
|
|
398
|
-
"global",
|
|
399
|
-
...dryRun ? ["--plan"] : [],
|
|
400
|
-
"--json"
|
|
401
|
-
],
|
|
402
|
-
options
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
function runThothMemSetup(options) {
|
|
406
|
-
const { command, args } = getThothMemSetupCommand(
|
|
407
|
-
options.harness,
|
|
408
|
-
options.dryRun ?? false
|
|
409
|
-
);
|
|
410
|
-
const executor = options.commandExecutor ?? defaultCommandExecutor2;
|
|
411
|
-
let commandResult;
|
|
412
|
-
try {
|
|
413
|
-
commandResult = executor(command, args, {
|
|
414
|
-
cwd: options.cwd,
|
|
415
|
-
timeoutMs: THOTH_MEM_SETUP_TIMEOUT_MS
|
|
416
|
-
});
|
|
417
|
-
} catch (error) {
|
|
418
|
-
return invalidResult(
|
|
419
|
-
options.harness,
|
|
420
|
-
command,
|
|
421
|
-
args,
|
|
422
|
-
null,
|
|
423
|
-
`Unable to launch thoth-mem setup: ${describeError(error)}`
|
|
424
|
-
);
|
|
425
|
-
}
|
|
426
|
-
if (commandResult.error) {
|
|
427
|
-
const stderr = commandResult.stderr.trim().slice(0, 2e3);
|
|
428
|
-
return invalidResult(
|
|
429
|
-
options.harness,
|
|
430
|
-
command,
|
|
431
|
-
args,
|
|
432
|
-
commandResult.exitCode,
|
|
433
|
-
isTimeoutError(commandResult.error) ? `thoth-mem setup timed out after ${THOTH_MEM_SETUP_TIMEOUT_MS} ms${stderr ? `: ${stderr}` : "."}` : `Unable to launch thoth-mem setup: ${describeError(commandResult.error)}`
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
const provider = parseProviderSetupResult(
|
|
437
|
-
commandResult.stdout,
|
|
438
|
-
options.harness
|
|
439
|
-
);
|
|
440
|
-
if (!provider) {
|
|
441
|
-
const stderr = commandResult.stderr.trim();
|
|
442
|
-
return invalidResult(
|
|
443
|
-
options.harness,
|
|
444
|
-
command,
|
|
445
|
-
args,
|
|
446
|
-
commandResult.exitCode,
|
|
447
|
-
stderr ? `thoth-mem returned invalid JSON evidence: ${stderr.slice(0, 2e3)}` : "thoth-mem returned invalid JSON evidence."
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
const expectedExitCode = STATUS_EXIT_CODE[provider.status];
|
|
451
|
-
const evidenceValid = commandResult.exitCode === expectedExitCode;
|
|
452
|
-
return {
|
|
453
|
-
success: evidenceValid && provider.status === "complete",
|
|
454
|
-
evidenceValid,
|
|
455
|
-
status: provider.status,
|
|
456
|
-
changed: provider.changed,
|
|
457
|
-
harness: provider.harness,
|
|
458
|
-
target: provider.target,
|
|
459
|
-
steps: provider.steps,
|
|
460
|
-
diagnostics: provider.diagnostics,
|
|
461
|
-
manualActions: provider.manual_actions,
|
|
462
|
-
receipt: provider.receipt,
|
|
463
|
-
command,
|
|
464
|
-
args,
|
|
465
|
-
exitCode: commandResult.exitCode,
|
|
466
|
-
...!evidenceValid ? {
|
|
467
|
-
error: `thoth-mem exit code ${String(commandResult.exitCode)} contradicts reported status ${provider.status} (expected ${expectedExitCode}).`
|
|
468
|
-
} : {}
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// src/cli/install.ts
|
|
473
81
|
var GREEN = "\x1B[32m";
|
|
474
82
|
var BLUE = "\x1B[34m";
|
|
475
83
|
var YELLOW = "\x1B[33m";
|
|
@@ -628,14 +236,32 @@ function printThothMemSetupResult(result, dryRun) {
|
|
|
628
236
|
);
|
|
629
237
|
return true;
|
|
630
238
|
}
|
|
631
|
-
function
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
239
|
+
function finalizeInstallForHarness(harness, dryRun, version, dependencies) {
|
|
240
|
+
const finalize = dependencies.finalizeHarnessInstall ?? finalizeHarnessInstall;
|
|
241
|
+
const result = finalize({
|
|
242
|
+
harness,
|
|
243
|
+
version,
|
|
244
|
+
dryRun,
|
|
245
|
+
cwd: cwd(),
|
|
246
|
+
runThothMemSetup: dependencies.runThothMemSetup,
|
|
247
|
+
ledgerOptions: dependencies.installLedgerOptions ?? {
|
|
248
|
+
homeDir: dependencies.homeDir ?? homedir()
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
const providerComplete = printThothMemSetupResult(result.provider, dryRun);
|
|
252
|
+
if (!providerComplete) return false;
|
|
253
|
+
if (!result.success) {
|
|
254
|
+
printError(result.error ?? "Failed to record the completed CLI install.");
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
if (result.ledger.status === "planned") {
|
|
258
|
+
printInfo(`CLI-managed install record planned: ${result.ledger.path}`);
|
|
259
|
+
} else if (result.ledger.status === "recorded") {
|
|
260
|
+
printSuccess(`CLI-managed install version recorded: ${result.ledger.path}`);
|
|
261
|
+
}
|
|
262
|
+
return true;
|
|
637
263
|
}
|
|
638
|
-
async function runInstall(config, dependencies) {
|
|
264
|
+
async function runInstall(config, dependencies, pluginVersion) {
|
|
639
265
|
const detected = detectCurrentConfig();
|
|
640
266
|
const isUpdate = detected.isInstalled;
|
|
641
267
|
printHeader(isUpdate);
|
|
@@ -656,8 +282,10 @@ async function runInstall(config, dependencies) {
|
|
|
656
282
|
if (config.dryRun) {
|
|
657
283
|
printInfo("Dry run mode - skipping main config update");
|
|
658
284
|
} else {
|
|
659
|
-
const
|
|
285
|
+
const updateMainConfig = dependencies.updateOpenCodeMainConfig ?? updateOpenCodeMainConfig;
|
|
286
|
+
const mainConfigResult = updateMainConfig({
|
|
660
287
|
ensurePlugin: true,
|
|
288
|
+
pluginVersion,
|
|
661
289
|
disableDefaults: true
|
|
662
290
|
});
|
|
663
291
|
if (!handleStepResult(mainConfigResult, "Main config updated")) return 1;
|
|
@@ -702,7 +330,12 @@ ${JSON.stringify(liteConfig, null, 2)}
|
|
|
702
330
|
))
|
|
703
331
|
return 1;
|
|
704
332
|
printStep(step++, totalSteps, "Configuring provider-owned thoth-mem...");
|
|
705
|
-
if (!
|
|
333
|
+
if (!finalizeInstallForHarness(
|
|
334
|
+
"opencode",
|
|
335
|
+
config.dryRun,
|
|
336
|
+
pluginVersion,
|
|
337
|
+
dependencies
|
|
338
|
+
)) {
|
|
706
339
|
return 1;
|
|
707
340
|
}
|
|
708
341
|
console.log();
|
|
@@ -733,6 +366,14 @@ function createInstallConfig(args) {
|
|
|
733
366
|
}
|
|
734
367
|
async function install(args, dependencies = {}) {
|
|
735
368
|
const config = createInstallConfig(args);
|
|
369
|
+
const resolvePackageVersion = dependencies.resolveExecutingPackageVersion ?? resolveExecutingPackageVersion;
|
|
370
|
+
const packageVersion = resolvePackageVersion();
|
|
371
|
+
if (!packageVersion.ok) {
|
|
372
|
+
printError(
|
|
373
|
+
`Could not resolve the executing thoth-agents package version: ${packageVersion.error.message}`
|
|
374
|
+
);
|
|
375
|
+
return 1;
|
|
376
|
+
}
|
|
736
377
|
if (config.agent === "codex") {
|
|
737
378
|
const projectRoot = cwd();
|
|
738
379
|
const pluginPlan = buildCodexPluginSetupPlan({
|
|
@@ -751,7 +392,7 @@ async function install(args, dependencies = {}) {
|
|
|
751
392
|
reset: config.reset,
|
|
752
393
|
scope: "user",
|
|
753
394
|
projectRoot,
|
|
754
|
-
homeDir: homedir()
|
|
395
|
+
homeDir: dependencies.homeDir ?? homedir()
|
|
755
396
|
});
|
|
756
397
|
console.log(formatCodexSetupPlan(plan));
|
|
757
398
|
const result = applyCodexSetup(plan);
|
|
@@ -760,8 +401,18 @@ async function install(args, dependencies = {}) {
|
|
|
760
401
|
printError(`Codex install failed: ${result.error}`);
|
|
761
402
|
return 1;
|
|
762
403
|
}
|
|
763
|
-
if (!installRequiredSkillsForHarness(
|
|
764
|
-
|
|
404
|
+
if (!installRequiredSkillsForHarness(
|
|
405
|
+
"codex",
|
|
406
|
+
config.dryRun,
|
|
407
|
+
dependencies.homeDir ?? homedir()
|
|
408
|
+
))
|
|
409
|
+
return 1;
|
|
410
|
+
if (!finalizeInstallForHarness(
|
|
411
|
+
"codex",
|
|
412
|
+
config.dryRun,
|
|
413
|
+
packageVersion.version,
|
|
414
|
+
dependencies
|
|
415
|
+
)) {
|
|
765
416
|
return 1;
|
|
766
417
|
}
|
|
767
418
|
printSuccess(
|
|
@@ -783,8 +434,18 @@ async function install(args, dependencies = {}) {
|
|
|
783
434
|
printError(`Claude Code install failed: ${result.error}`);
|
|
784
435
|
return 1;
|
|
785
436
|
}
|
|
786
|
-
if (!installRequiredSkillsForHarness(
|
|
787
|
-
|
|
437
|
+
if (!installRequiredSkillsForHarness(
|
|
438
|
+
"claude",
|
|
439
|
+
config.dryRun,
|
|
440
|
+
dependencies.homeDir ?? homedir()
|
|
441
|
+
))
|
|
442
|
+
return 1;
|
|
443
|
+
if (!finalizeInstallForHarness(
|
|
444
|
+
"claude",
|
|
445
|
+
config.dryRun,
|
|
446
|
+
packageVersion.version,
|
|
447
|
+
dependencies
|
|
448
|
+
)) {
|
|
788
449
|
return 1;
|
|
789
450
|
}
|
|
790
451
|
printSuccess(
|
|
@@ -792,7 +453,7 @@ async function install(args, dependencies = {}) {
|
|
|
792
453
|
);
|
|
793
454
|
return 0;
|
|
794
455
|
}
|
|
795
|
-
return runInstall(config, dependencies);
|
|
456
|
+
return runInstall(config, dependencies, packageVersion.version);
|
|
796
457
|
}
|
|
797
458
|
|
|
798
459
|
// src/cli/commands.ts
|
|
@@ -808,6 +469,20 @@ function formatTarget(target) {
|
|
|
808
469
|
const observed = target.observed ? ` observed ${target.observed}` : "";
|
|
809
470
|
return `- ${label}${location}${state}${expected}${observed}`;
|
|
810
471
|
}
|
|
472
|
+
function formatCliManagedInstall(targets) {
|
|
473
|
+
const target = targets.find(
|
|
474
|
+
({ label }) => label === "CLI-managed install version"
|
|
475
|
+
);
|
|
476
|
+
if (!target) return [];
|
|
477
|
+
const executing = target.expected?.replace(/^executing\s+/, "") ?? "unknown";
|
|
478
|
+
const recorded = target.observed?.replace(/^recorded\s+/, "") ?? "unknown";
|
|
479
|
+
return [
|
|
480
|
+
"Official CLI-managed install:",
|
|
481
|
+
`Executing CLI version: ${executing}`,
|
|
482
|
+
`Recorded complete-install version: ${recorded}`,
|
|
483
|
+
"Native marketplace versions do not advance this record."
|
|
484
|
+
];
|
|
485
|
+
}
|
|
811
486
|
function formatPaths(paths) {
|
|
812
487
|
return paths.map((path) => {
|
|
813
488
|
const label = path.label ? `${path.label}: ` : "";
|
|
@@ -843,6 +518,7 @@ function formatHarnessStatusReport(reports) {
|
|
|
843
518
|
`${report.displayName ?? getOperationHarness(report.harness).displayName} (${report.harness})`,
|
|
844
519
|
`State: ${report.state}`,
|
|
845
520
|
`Summary: ${report.summary}`,
|
|
521
|
+
...formatCliManagedInstall(report.targets),
|
|
846
522
|
...report.providerCapability ? [
|
|
847
523
|
"Provider evidence:",
|
|
848
524
|
`Provider capability: ${report.providerCapability.state}`,
|
|
@@ -924,6 +600,8 @@ function formatOperationApplyResult(result) {
|
|
|
924
600
|
]);
|
|
925
601
|
}
|
|
926
602
|
function printHelp() {
|
|
603
|
+
const packageVersion = resolveExecutingPackageVersion();
|
|
604
|
+
const exactPluginEntry = packageVersion.ok ? `thoth-agents@${packageVersion.version}` : "thoth-agents@<executing-version-unavailable>";
|
|
927
605
|
console.log(`
|
|
928
606
|
thoth-agents CLI (npm binary: thoth-agents)
|
|
929
607
|
|
|
@@ -937,9 +615,9 @@ Commands:
|
|
|
937
615
|
(no command) Open the interactive TUI in a TTY; fall back to OpenCode install in CI/non-TTY
|
|
938
616
|
install Install OpenCode, Codex, or Claude Code agent assets
|
|
939
617
|
generate Generate harness-specific artifacts
|
|
940
|
-
status Show managed install status
|
|
618
|
+
status Show official CLI-managed versions and managed install status
|
|
941
619
|
list List managed surfaces and actions
|
|
942
|
-
update Preview
|
|
620
|
+
update Preview a complete selected-harness CLI refresh
|
|
943
621
|
sync Preview managed configuration sync
|
|
944
622
|
model Preview role model/provider settings
|
|
945
623
|
|
|
@@ -947,6 +625,7 @@ Options:
|
|
|
947
625
|
--tmux=yes|no Enable tmux integration (yes/no)
|
|
948
626
|
--no-tui Non-interactive mode
|
|
949
627
|
--dry-run Simulate install without writing files
|
|
628
|
+
--apply Apply a reviewed update, sync, or model plan
|
|
950
629
|
--reset Repair managed installer-owned targets
|
|
951
630
|
--agent=opencode|codex|claude
|
|
952
631
|
Select OpenCode plugin install (default), Codex agent-pack, or Claude Code plugin setup
|
|
@@ -961,10 +640,18 @@ Generate options:
|
|
|
961
640
|
|
|
962
641
|
OpenCode plugin config and the npm binary are separate surfaces.
|
|
963
642
|
OpenCode loads the plugin with config such as:
|
|
964
|
-
plugin: ["
|
|
643
|
+
plugin: ["${exactPluginEntry}"]
|
|
965
644
|
|
|
966
645
|
That plugin entry does not create a global thoth-agents command.
|
|
967
646
|
Run this CLI through a global install, npx, or pnpm dlx.
|
|
647
|
+
@latest selects the CLI release; OpenCode receives that exact version pin.
|
|
648
|
+
|
|
649
|
+
Update performs the complete selected-harness CLI refresh and records success last.
|
|
650
|
+
The official record is $XDG_CONFIG_HOME/thoth-agents/install-state.json,
|
|
651
|
+
or ~/.config/thoth-agents/install-state.json when XDG_CONFIG_HOME is unset.
|
|
652
|
+
Codex and Claude marketplace versions remain native-manager-owned and do not prove
|
|
653
|
+
that CLI-managed agents, skills, configuration, or provider setup are aligned.
|
|
654
|
+
Runtime release checks notify only; use the latest CLI install or interactive CLI Update.
|
|
968
655
|
|
|
969
656
|
OpenCode install configures the adaptive seven-role roster and native task delegation.
|
|
970
657
|
Provider capability is external and reported only from caller-supplied evidence.
|
|
@@ -997,18 +684,16 @@ function operationContext() {
|
|
|
997
684
|
function selectedHarness(args, fallback = "opencode") {
|
|
998
685
|
return args.harness ?? fallback;
|
|
999
686
|
}
|
|
1000
|
-
function statusReports(args) {
|
|
687
|
+
function statusReports(args, context = operationContext()) {
|
|
1001
688
|
const harnesses = args.all || !args.harness ? SUPPORTED_OPERATION_HARNESSES : [args.harness];
|
|
1002
|
-
const context = operationContext();
|
|
1003
689
|
return harnesses.map((harness) => {
|
|
1004
690
|
if (harness === "opencode") return getOpenCodeStatus(context);
|
|
1005
691
|
if (harness === "claude") return getClaudeCodeStatus(context);
|
|
1006
692
|
return getCodexStatus(context);
|
|
1007
693
|
});
|
|
1008
694
|
}
|
|
1009
|
-
function buildOperationPlan(command, args) {
|
|
695
|
+
function buildOperationPlan(command, args, context = operationContext()) {
|
|
1010
696
|
const harness = selectedHarness(args);
|
|
1011
|
-
const context = operationContext();
|
|
1012
697
|
if (harness === "opencode") {
|
|
1013
698
|
return command === "update" ? buildOpenCodeUpdatePlan(context) : buildOpenCodeSyncPlan(context);
|
|
1014
699
|
}
|
|
@@ -1099,7 +784,7 @@ function applyOperationPlan(plan) {
|
|
|
1099
784
|
if (plan.harness === "claude") return applyClaudeCodePlan(plan);
|
|
1100
785
|
return applyCodexPlan(plan);
|
|
1101
786
|
}
|
|
1102
|
-
function printPlanOrApply(plan, args) {
|
|
787
|
+
function printPlanOrApply(plan, args, applyPlan) {
|
|
1103
788
|
if (args.apply && args.dryRun) {
|
|
1104
789
|
console.error("--apply cannot be combined with --dry-run.");
|
|
1105
790
|
return 1;
|
|
@@ -1108,12 +793,17 @@ function printPlanOrApply(plan, args) {
|
|
|
1108
793
|
console.log(formatOperationPlan(plan));
|
|
1109
794
|
return 0;
|
|
1110
795
|
}
|
|
1111
|
-
|
|
1112
|
-
|
|
796
|
+
const result = applyPlan(plan);
|
|
797
|
+
console.log(formatOperationApplyResult(result));
|
|
798
|
+
return result.applied ? 0 : 1;
|
|
1113
799
|
}
|
|
1114
800
|
async function runOperationCommand(command, args, services) {
|
|
1115
801
|
if (command === "status") {
|
|
1116
|
-
console.log(
|
|
802
|
+
console.log(
|
|
803
|
+
formatHarnessStatusReport(
|
|
804
|
+
statusReports(args, services.operationContext())
|
|
805
|
+
)
|
|
806
|
+
);
|
|
1117
807
|
return 0;
|
|
1118
808
|
}
|
|
1119
809
|
if (command === "list") {
|
|
@@ -1121,11 +811,19 @@ async function runOperationCommand(command, args, services) {
|
|
|
1121
811
|
return 0;
|
|
1122
812
|
}
|
|
1123
813
|
if (command === "update" || command === "sync") {
|
|
1124
|
-
return printPlanOrApply(
|
|
814
|
+
return printPlanOrApply(
|
|
815
|
+
buildOperationPlan(command, args, services.operationContext()),
|
|
816
|
+
args,
|
|
817
|
+
services.applyOperationPlan ?? applyOperationPlan
|
|
818
|
+
);
|
|
1125
819
|
}
|
|
1126
820
|
const plan = await buildModelPlan(args, services);
|
|
1127
821
|
if (!plan) return printModelGuidance();
|
|
1128
|
-
return printPlanOrApply(
|
|
822
|
+
return printPlanOrApply(
|
|
823
|
+
plan,
|
|
824
|
+
args,
|
|
825
|
+
services.applyOperationPlan ?? applyOperationPlan
|
|
826
|
+
);
|
|
1129
827
|
}
|
|
1130
828
|
function printHarnessGeneration(args) {
|
|
1131
829
|
if (!args.dryRun) {
|