thoth-agents 0.3.7 → 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-HIXSRHEG.js → chunk-XEEYNMPC.js} +1292 -291
- 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
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
createStepBudgetSection,
|
|
16
16
|
ensureConfigDir,
|
|
17
17
|
exa,
|
|
18
|
+
findPackageRoot,
|
|
18
19
|
generateLiteConfig,
|
|
19
20
|
getAgentOverride,
|
|
20
21
|
getAgentPackContract,
|
|
@@ -24,14 +25,16 @@ import {
|
|
|
24
25
|
getExistingLiteConfigPath,
|
|
25
26
|
getOpenCodeManagedModelStatePath,
|
|
26
27
|
getPrimaryModelId,
|
|
28
|
+
isValidPackageVersion,
|
|
27
29
|
loadAgentPrompt,
|
|
28
30
|
parseConfig,
|
|
29
31
|
renderPromptSection,
|
|
30
32
|
renderRolePrompt,
|
|
33
|
+
resolveExecutingPackageVersion,
|
|
31
34
|
updateOpenCodeMainConfig,
|
|
32
35
|
writeConfig,
|
|
33
36
|
writeLiteConfig
|
|
34
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-2TLT6WWY.js";
|
|
35
38
|
|
|
36
39
|
// src/cli/codex-paths.ts
|
|
37
40
|
import { homedir } from "os";
|
|
@@ -83,7 +86,7 @@ function isExplicitEffort(selection) {
|
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
// src/cli/codex-install.ts
|
|
86
|
-
import { existsSync as
|
|
89
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
87
90
|
import { basename } from "path";
|
|
88
91
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
89
92
|
|
|
@@ -1540,27 +1543,6 @@ function readManagedModelState(path, version) {
|
|
|
1540
1543
|
return parseManagedModelStateJson(readFileSync3(path, "utf8"), version);
|
|
1541
1544
|
}
|
|
1542
1545
|
|
|
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
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
1546
|
// src/cli/codex-install.ts
|
|
1565
1547
|
var ROOT_START = "<!-- thoth-agents:codex-root:start -->";
|
|
1566
1548
|
var ROOT_END = "<!-- thoth-agents:codex-root:end -->";
|
|
@@ -1672,7 +1654,7 @@ function applyCodexManagedModelOverrides(config, overrides) {
|
|
|
1672
1654
|
`Missing Codex role TOML content for ${override.role}.`
|
|
1673
1655
|
);
|
|
1674
1656
|
}
|
|
1675
|
-
const before =
|
|
1657
|
+
const before = existsSync4(roleItem.targetPath) ? readFileSync4(roleItem.targetPath, "utf8") : roleItem.content;
|
|
1676
1658
|
const model = normalizeCodexRuntimeModel(override.model);
|
|
1677
1659
|
let updated = replaceRoleTomlModel(before, model);
|
|
1678
1660
|
if (override.clearEffort) {
|
|
@@ -1725,7 +1707,7 @@ function resolveRoleTomlContent(options) {
|
|
|
1725
1707
|
options.nextState.configuredEfforts ??= {};
|
|
1726
1708
|
options.nextState.configuredEfforts[key] = configuredEffort;
|
|
1727
1709
|
}
|
|
1728
|
-
if (options.reset || !
|
|
1710
|
+
if (options.reset || !existsSync4(options.targetPath)) {
|
|
1729
1711
|
options.nextState.models[key] = renderedModel;
|
|
1730
1712
|
if (configuredModel !== void 0) {
|
|
1731
1713
|
options.nextState.configuredModels ??= {};
|
|
@@ -1799,7 +1781,7 @@ function buildCodexSetupPlan(config) {
|
|
|
1799
1781
|
action: "write-role-toml",
|
|
1800
1782
|
targetPath: target.path,
|
|
1801
1783
|
description: `Materialize Codex role subagent ${target.role}.`,
|
|
1802
|
-
requiresBackup:
|
|
1784
|
+
requiresBackup: existsSync4(target.path),
|
|
1803
1785
|
role: target.role,
|
|
1804
1786
|
renderedModel: parseRoleTomlModel(
|
|
1805
1787
|
roleArtifactContent(target.role, render.artifacts)
|
|
@@ -1818,7 +1800,7 @@ function buildCodexSetupPlan(config) {
|
|
|
1818
1800
|
action: "write-managed-model-state",
|
|
1819
1801
|
targetPath: targets.managedModelsPath,
|
|
1820
1802
|
description: "Record thoth-agents-managed Codex role model ownership state.",
|
|
1821
|
-
requiresBackup:
|
|
1803
|
+
requiresBackup: existsSync4(targets.managedModelsPath),
|
|
1822
1804
|
content: stableJson3(nextManagedModelState)
|
|
1823
1805
|
},
|
|
1824
1806
|
{
|
|
@@ -1885,7 +1867,7 @@ function applyCodexSetup(plan) {
|
|
|
1885
1867
|
}
|
|
1886
1868
|
if (item.content === void 0) continue;
|
|
1887
1869
|
const content = item.action === "merge-managed-block" ? mergeManagedBlock(
|
|
1888
|
-
|
|
1870
|
+
existsSync4(item.targetPath) ? readFileSync4(item.targetPath, "utf8") : "",
|
|
1889
1871
|
item.content
|
|
1890
1872
|
) : item.content;
|
|
1891
1873
|
if (writeTextWithBackup(item.targetPath, content))
|
|
@@ -2473,11 +2455,123 @@ function formatClaudeCodeSetupPlan(plan) {
|
|
|
2473
2455
|
return ["Claude Code setup plan:", ...lines].join("\n");
|
|
2474
2456
|
}
|
|
2475
2457
|
|
|
2476
|
-
// src/cli/
|
|
2477
|
-
import {
|
|
2478
|
-
|
|
2458
|
+
// src/cli/install-ledger.ts
|
|
2459
|
+
import {
|
|
2460
|
+
copyFileSync as copyFileSync3,
|
|
2461
|
+
existsSync as existsSync5,
|
|
2462
|
+
mkdirSync as mkdirSync3,
|
|
2463
|
+
readFileSync as readFileSync5,
|
|
2464
|
+
renameSync as renameSync2,
|
|
2465
|
+
rmSync,
|
|
2466
|
+
writeFileSync as writeFileSync3
|
|
2467
|
+
} from "fs";
|
|
2479
2468
|
import { homedir as homedir2 } from "os";
|
|
2480
|
-
import { join as
|
|
2469
|
+
import { dirname as dirname4, join as join2 } from "path";
|
|
2470
|
+
var INSTALL_LEDGER_SCHEMA_VERSION = 1;
|
|
2471
|
+
var INSTALL_HARNESSES = /* @__PURE__ */ new Set([
|
|
2472
|
+
"opencode",
|
|
2473
|
+
"codex",
|
|
2474
|
+
"claude"
|
|
2475
|
+
]);
|
|
2476
|
+
function hasOnlyKeys(value, keys) {
|
|
2477
|
+
const actual = Object.keys(value);
|
|
2478
|
+
return actual.length === keys.length && actual.every((key) => keys.includes(key));
|
|
2479
|
+
}
|
|
2480
|
+
function parseInstallLedger(serialized) {
|
|
2481
|
+
let parsed;
|
|
2482
|
+
try {
|
|
2483
|
+
parsed = JSON.parse(serialized);
|
|
2484
|
+
} catch (error) {
|
|
2485
|
+
return { success: false, error: `Malformed install ledger JSON: ${error}` };
|
|
2486
|
+
}
|
|
2487
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2488
|
+
return { success: false, error: "Install ledger must be a JSON object." };
|
|
2489
|
+
}
|
|
2490
|
+
const root = parsed;
|
|
2491
|
+
if (!hasOnlyKeys(root, ["schemaVersion", "harnesses"]) || root.schemaVersion !== INSTALL_LEDGER_SCHEMA_VERSION || !root.harnesses || typeof root.harnesses !== "object" || Array.isArray(root.harnesses)) {
|
|
2492
|
+
return { success: false, error: "Install ledger schema is unsupported." };
|
|
2493
|
+
}
|
|
2494
|
+
const harnesses = root.harnesses;
|
|
2495
|
+
for (const [harness, value] of Object.entries(harnesses)) {
|
|
2496
|
+
if (!INSTALL_HARNESSES.has(harness) || !value || typeof value !== "object" || Array.isArray(value) || !hasOnlyKeys(value, ["version"]) || !isValidPackageVersion(value.version)) {
|
|
2497
|
+
return {
|
|
2498
|
+
success: false,
|
|
2499
|
+
error: `Install ledger record for ${harness} is invalid.`
|
|
2500
|
+
};
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
return { success: true, ledger: parsed };
|
|
2504
|
+
}
|
|
2505
|
+
function getInstallLedgerPath(options = {}) {
|
|
2506
|
+
const env = options.env ?? process.env;
|
|
2507
|
+
const configuredRoot = env.XDG_CONFIG_HOME?.trim();
|
|
2508
|
+
const homeDir = options.homeDir ?? env.HOME ?? env.USERPROFILE ?? homedir2();
|
|
2509
|
+
const configRoot = options.configRoot ?? (configuredRoot || join2(homeDir, ".config"));
|
|
2510
|
+
return join2(configRoot, "thoth-agents", "install-state.json");
|
|
2511
|
+
}
|
|
2512
|
+
function readInstallLedger(options = {}) {
|
|
2513
|
+
const path = getInstallLedgerPath(options);
|
|
2514
|
+
if (!existsSync5(path)) return { status: "missing", path };
|
|
2515
|
+
let serialized;
|
|
2516
|
+
try {
|
|
2517
|
+
serialized = readFileSync5(path, "utf8");
|
|
2518
|
+
} catch (error) {
|
|
2519
|
+
return {
|
|
2520
|
+
status: "invalid",
|
|
2521
|
+
path,
|
|
2522
|
+
error: `Could not read install ledger: ${error}`
|
|
2523
|
+
};
|
|
2524
|
+
}
|
|
2525
|
+
const parsed = parseInstallLedger(serialized);
|
|
2526
|
+
return parsed.success ? { status: "valid", path, ledger: parsed.ledger } : { status: "invalid", path, error: parsed.error };
|
|
2527
|
+
}
|
|
2528
|
+
function recordCompletedInstall(options) {
|
|
2529
|
+
const { harness, version } = options;
|
|
2530
|
+
const path = getInstallLedgerPath(options);
|
|
2531
|
+
if (!INSTALL_HARNESSES.has(harness) || !isValidPackageVersion(version)) {
|
|
2532
|
+
return {
|
|
2533
|
+
success: false,
|
|
2534
|
+
path,
|
|
2535
|
+
error: "A supported harness and valid package version are required."
|
|
2536
|
+
};
|
|
2537
|
+
}
|
|
2538
|
+
const current = readInstallLedger(options);
|
|
2539
|
+
const ledger = {
|
|
2540
|
+
schemaVersion: INSTALL_LEDGER_SCHEMA_VERSION,
|
|
2541
|
+
harnesses: current.status === "valid" ? { ...current.ledger.harnesses } : {}
|
|
2542
|
+
};
|
|
2543
|
+
ledger.harnesses[harness] = { version };
|
|
2544
|
+
const temporaryPath = `${path}.tmp`;
|
|
2545
|
+
const repairedInvalidState = current.status === "invalid";
|
|
2546
|
+
const backupPath = repairedInvalidState ? `${path}.bak` : void 0;
|
|
2547
|
+
try {
|
|
2548
|
+
mkdirSync3(dirname4(path), { recursive: true });
|
|
2549
|
+
writeFileSync3(temporaryPath, `${JSON.stringify(ledger, null, 2)}
|
|
2550
|
+
`);
|
|
2551
|
+
if (backupPath) copyFileSync3(path, backupPath);
|
|
2552
|
+
(options.renameFile ?? renameSync2)(temporaryPath, path);
|
|
2553
|
+
return {
|
|
2554
|
+
success: true,
|
|
2555
|
+
path,
|
|
2556
|
+
ledger,
|
|
2557
|
+
repairedInvalidState,
|
|
2558
|
+
...backupPath ? { backupPath } : {}
|
|
2559
|
+
};
|
|
2560
|
+
} catch (error) {
|
|
2561
|
+
try {
|
|
2562
|
+
rmSync(temporaryPath, { recursive: true, force: true });
|
|
2563
|
+
} catch {
|
|
2564
|
+
}
|
|
2565
|
+
return {
|
|
2566
|
+
success: false,
|
|
2567
|
+
path,
|
|
2568
|
+
error: `Could not record completed install: ${error}`
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
// src/cli/thoth-mem-install.ts
|
|
2574
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
2481
2575
|
|
|
2482
2576
|
// src/cli/npx-command.ts
|
|
2483
2577
|
function getNpxCommand(args, options = {}) {
|
|
@@ -2486,11 +2580,230 @@ function getNpxCommand(args, options = {}) {
|
|
|
2486
2580
|
}
|
|
2487
2581
|
return {
|
|
2488
2582
|
command: options.commandShell ?? process.env.ComSpec ?? "cmd.exe",
|
|
2489
|
-
args: ["/d", "/s", "/c", "npx
|
|
2583
|
+
args: ["/d", "/s", "/c", ["npx", ...args].join(" ")]
|
|
2584
|
+
};
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2587
|
+
// src/cli/thoth-mem-install.ts
|
|
2588
|
+
var THOTH_MEM_SETUP_TIMEOUT_MS = 12e4;
|
|
2589
|
+
var STATUS_EXIT_CODE = {
|
|
2590
|
+
complete: 0,
|
|
2591
|
+
failed: 1,
|
|
2592
|
+
partial: 2,
|
|
2593
|
+
requires_user_action: 3
|
|
2594
|
+
};
|
|
2595
|
+
var SETUP_STATUSES = new Set(
|
|
2596
|
+
Object.keys(STATUS_EXIT_CODE)
|
|
2597
|
+
);
|
|
2598
|
+
var STEP_OUTCOMES = /* @__PURE__ */ new Set([
|
|
2599
|
+
"planned",
|
|
2600
|
+
"skipped",
|
|
2601
|
+
"confirmed",
|
|
2602
|
+
"failed",
|
|
2603
|
+
"unavailable"
|
|
2604
|
+
]);
|
|
2605
|
+
function isRecord4(value) {
|
|
2606
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2607
|
+
}
|
|
2608
|
+
function isStringArray(value) {
|
|
2609
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
2610
|
+
}
|
|
2611
|
+
function isSetupStep(value) {
|
|
2612
|
+
return isRecord4(value) && typeof value.name === "string" && value.name.length > 0 && typeof value.outcome === "string" && STEP_OUTCOMES.has(value.outcome);
|
|
2613
|
+
}
|
|
2614
|
+
function isProviderSetupResult(value, expectedHarness) {
|
|
2615
|
+
return isRecord4(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");
|
|
2616
|
+
}
|
|
2617
|
+
function parseProviderSetupResult(stdout, expectedHarness) {
|
|
2618
|
+
let value;
|
|
2619
|
+
try {
|
|
2620
|
+
value = JSON.parse(stdout.trim());
|
|
2621
|
+
} catch {
|
|
2622
|
+
return void 0;
|
|
2623
|
+
}
|
|
2624
|
+
return isProviderSetupResult(value, expectedHarness) ? value : void 0;
|
|
2625
|
+
}
|
|
2626
|
+
function defaultCommandExecutor2(command, args, options) {
|
|
2627
|
+
const result = spawnSync2(command, [...args], {
|
|
2628
|
+
cwd: options.cwd,
|
|
2629
|
+
encoding: "utf8",
|
|
2630
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2631
|
+
timeout: options.timeoutMs
|
|
2632
|
+
});
|
|
2633
|
+
return {
|
|
2634
|
+
exitCode: result.status,
|
|
2635
|
+
stdout: result.stdout ?? "",
|
|
2636
|
+
stderr: result.stderr ?? "",
|
|
2637
|
+
error: result.error
|
|
2638
|
+
};
|
|
2639
|
+
}
|
|
2640
|
+
function describeError(error) {
|
|
2641
|
+
return error instanceof Error ? error.message : String(error);
|
|
2642
|
+
}
|
|
2643
|
+
function isTimeoutError(error) {
|
|
2644
|
+
return isRecord4(error) && error.code === "ETIMEDOUT";
|
|
2645
|
+
}
|
|
2646
|
+
function invalidResult(harness, command, args, exitCode, error) {
|
|
2647
|
+
return {
|
|
2648
|
+
success: false,
|
|
2649
|
+
evidenceValid: false,
|
|
2650
|
+
status: "invalid",
|
|
2651
|
+
changed: false,
|
|
2652
|
+
harness,
|
|
2653
|
+
steps: [],
|
|
2654
|
+
diagnostics: [],
|
|
2655
|
+
manualActions: [],
|
|
2656
|
+
receipt: null,
|
|
2657
|
+
command,
|
|
2658
|
+
args,
|
|
2659
|
+
exitCode,
|
|
2660
|
+
error
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2663
|
+
function getThothMemSetupCommand(harness, dryRun, options = {}) {
|
|
2664
|
+
return getNpxCommand(
|
|
2665
|
+
[
|
|
2666
|
+
"-y",
|
|
2667
|
+
"thoth-mem@latest",
|
|
2668
|
+
"setup",
|
|
2669
|
+
harness,
|
|
2670
|
+
"--scope",
|
|
2671
|
+
"global",
|
|
2672
|
+
...dryRun ? ["--plan"] : [],
|
|
2673
|
+
"--json"
|
|
2674
|
+
],
|
|
2675
|
+
options
|
|
2676
|
+
);
|
|
2677
|
+
}
|
|
2678
|
+
function runThothMemSetup(options) {
|
|
2679
|
+
const { command, args } = getThothMemSetupCommand(
|
|
2680
|
+
options.harness,
|
|
2681
|
+
options.dryRun ?? false
|
|
2682
|
+
);
|
|
2683
|
+
const executor = options.commandExecutor ?? defaultCommandExecutor2;
|
|
2684
|
+
let commandResult;
|
|
2685
|
+
try {
|
|
2686
|
+
commandResult = executor(command, args, {
|
|
2687
|
+
cwd: options.cwd,
|
|
2688
|
+
timeoutMs: THOTH_MEM_SETUP_TIMEOUT_MS
|
|
2689
|
+
});
|
|
2690
|
+
} catch (error) {
|
|
2691
|
+
return invalidResult(
|
|
2692
|
+
options.harness,
|
|
2693
|
+
command,
|
|
2694
|
+
args,
|
|
2695
|
+
null,
|
|
2696
|
+
`Unable to launch thoth-mem setup: ${describeError(error)}`
|
|
2697
|
+
);
|
|
2698
|
+
}
|
|
2699
|
+
if (commandResult.error) {
|
|
2700
|
+
const stderr = commandResult.stderr.trim().slice(0, 2e3);
|
|
2701
|
+
return invalidResult(
|
|
2702
|
+
options.harness,
|
|
2703
|
+
command,
|
|
2704
|
+
args,
|
|
2705
|
+
commandResult.exitCode,
|
|
2706
|
+
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)}`
|
|
2707
|
+
);
|
|
2708
|
+
}
|
|
2709
|
+
const provider = parseProviderSetupResult(
|
|
2710
|
+
commandResult.stdout,
|
|
2711
|
+
options.harness
|
|
2712
|
+
);
|
|
2713
|
+
if (!provider) {
|
|
2714
|
+
const stderr = commandResult.stderr.trim();
|
|
2715
|
+
return invalidResult(
|
|
2716
|
+
options.harness,
|
|
2717
|
+
command,
|
|
2718
|
+
args,
|
|
2719
|
+
commandResult.exitCode,
|
|
2720
|
+
stderr ? `thoth-mem returned invalid JSON evidence: ${stderr.slice(0, 2e3)}` : "thoth-mem returned invalid JSON evidence."
|
|
2721
|
+
);
|
|
2722
|
+
}
|
|
2723
|
+
const expectedExitCode = STATUS_EXIT_CODE[provider.status];
|
|
2724
|
+
const evidenceValid = commandResult.exitCode === expectedExitCode;
|
|
2725
|
+
return {
|
|
2726
|
+
success: evidenceValid && provider.status === "complete",
|
|
2727
|
+
evidenceValid,
|
|
2728
|
+
status: provider.status,
|
|
2729
|
+
changed: provider.changed,
|
|
2730
|
+
harness: provider.harness,
|
|
2731
|
+
target: provider.target,
|
|
2732
|
+
steps: provider.steps,
|
|
2733
|
+
diagnostics: provider.diagnostics,
|
|
2734
|
+
manualActions: provider.manual_actions,
|
|
2735
|
+
receipt: provider.receipt,
|
|
2736
|
+
command,
|
|
2737
|
+
args,
|
|
2738
|
+
exitCode: commandResult.exitCode,
|
|
2739
|
+
...!evidenceValid ? {
|
|
2740
|
+
error: `thoth-mem exit code ${String(commandResult.exitCode)} contradicts reported status ${provider.status} (expected ${expectedExitCode}).`
|
|
2741
|
+
} : {}
|
|
2742
|
+
};
|
|
2743
|
+
}
|
|
2744
|
+
|
|
2745
|
+
// src/cli/install-completion.ts
|
|
2746
|
+
function isConsistentProviderSuccess(result, harness) {
|
|
2747
|
+
return result.success && result.evidenceValid && result.status === "complete" && result.exitCode === 0 && result.harness === harness;
|
|
2748
|
+
}
|
|
2749
|
+
function finalizeHarnessInstall(options) {
|
|
2750
|
+
const ledgerPath = getInstallLedgerPath(options.ledgerOptions);
|
|
2751
|
+
const setup = options.runThothMemSetup ?? runThothMemSetup;
|
|
2752
|
+
const provider = setup({
|
|
2753
|
+
harness: options.harness,
|
|
2754
|
+
dryRun: options.dryRun,
|
|
2755
|
+
cwd: options.cwd
|
|
2756
|
+
});
|
|
2757
|
+
if (!isConsistentProviderSuccess(provider, options.harness)) {
|
|
2758
|
+
return {
|
|
2759
|
+
success: false,
|
|
2760
|
+
provider,
|
|
2761
|
+
ledger: { status: "not-attempted", path: ledgerPath },
|
|
2762
|
+
error: provider.error ?? "thoth-mem setup did not return consistent complete evidence."
|
|
2763
|
+
};
|
|
2764
|
+
}
|
|
2765
|
+
if (options.dryRun) {
|
|
2766
|
+
return {
|
|
2767
|
+
success: true,
|
|
2768
|
+
provider,
|
|
2769
|
+
ledger: { status: "planned", path: ledgerPath }
|
|
2770
|
+
};
|
|
2771
|
+
}
|
|
2772
|
+
const record = options.recordCompletedInstall ?? recordCompletedInstall;
|
|
2773
|
+
const ledger = record({
|
|
2774
|
+
...options.ledgerOptions,
|
|
2775
|
+
harness: options.harness,
|
|
2776
|
+
version: options.version
|
|
2777
|
+
});
|
|
2778
|
+
if (!ledger.success) {
|
|
2779
|
+
return {
|
|
2780
|
+
success: false,
|
|
2781
|
+
provider,
|
|
2782
|
+
ledger: {
|
|
2783
|
+
status: "failed",
|
|
2784
|
+
path: ledger.path,
|
|
2785
|
+
error: ledger.error
|
|
2786
|
+
},
|
|
2787
|
+
error: ledger.error
|
|
2788
|
+
};
|
|
2789
|
+
}
|
|
2790
|
+
return {
|
|
2791
|
+
success: true,
|
|
2792
|
+
provider,
|
|
2793
|
+
ledger: {
|
|
2794
|
+
status: "recorded",
|
|
2795
|
+
path: ledger.path,
|
|
2796
|
+
repairedInvalidState: ledger.repairedInvalidState,
|
|
2797
|
+
...ledger.backupPath ? { backupPath: ledger.backupPath } : {}
|
|
2798
|
+
}
|
|
2490
2799
|
};
|
|
2491
2800
|
}
|
|
2492
2801
|
|
|
2493
2802
|
// src/cli/skills.ts
|
|
2803
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
2804
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2805
|
+
import { homedir as homedir3 } from "os";
|
|
2806
|
+
import { join as join3 } from "path";
|
|
2494
2807
|
var SKILLS_CLI_AGENT = {
|
|
2495
2808
|
opencode: "opencode",
|
|
2496
2809
|
codex: "codex",
|
|
@@ -2530,19 +2843,19 @@ var REQUIRED_SKILLS = [
|
|
|
2530
2843
|
description: "Conditional product and architecture decision interview"
|
|
2531
2844
|
}
|
|
2532
2845
|
];
|
|
2533
|
-
function getRequiredSkillPaths(skill, harness, homeDir =
|
|
2846
|
+
function getRequiredSkillPaths(skill, harness, homeDir = homedir3()) {
|
|
2534
2847
|
return GLOBAL_SKILL_ROOTS[harness].map(
|
|
2535
2848
|
(root) => join3(homeDir, ...root, skill.skillName, "SKILL.md")
|
|
2536
2849
|
);
|
|
2537
2850
|
}
|
|
2538
|
-
function getRequiredSkillPath(skill, harness, homeDir =
|
|
2851
|
+
function getRequiredSkillPath(skill, harness, homeDir = homedir3()) {
|
|
2539
2852
|
const [primaryPath] = getRequiredSkillPaths(skill, harness, homeDir);
|
|
2540
2853
|
if (!primaryPath) {
|
|
2541
2854
|
throw new Error(`No global skill root is configured for ${harness}.`);
|
|
2542
2855
|
}
|
|
2543
2856
|
return primaryPath;
|
|
2544
2857
|
}
|
|
2545
|
-
function getInstalledRequiredSkillPath(skill, harness, homeDir =
|
|
2858
|
+
function getInstalledRequiredSkillPath(skill, harness, homeDir = homedir3()) {
|
|
2546
2859
|
return getRequiredSkillPaths(skill, harness, homeDir).find(
|
|
2547
2860
|
(path) => existsSync6(path)
|
|
2548
2861
|
);
|
|
@@ -2585,7 +2898,7 @@ function installRequiredSkill(skill, harness, options = {}) {
|
|
|
2585
2898
|
options
|
|
2586
2899
|
);
|
|
2587
2900
|
try {
|
|
2588
|
-
const result =
|
|
2901
|
+
const result = spawnSync3(command, args, { stdio: "inherit" });
|
|
2589
2902
|
if (result.status === 0) {
|
|
2590
2903
|
return {
|
|
2591
2904
|
skill,
|
|
@@ -2614,6 +2927,77 @@ function installRequiredSkill(skill, harness, options = {}) {
|
|
|
2614
2927
|
}
|
|
2615
2928
|
|
|
2616
2929
|
// src/cli/operations/types.ts
|
|
2930
|
+
function getInstallCompletionEvidence(completion, options) {
|
|
2931
|
+
const targets = [
|
|
2932
|
+
{
|
|
2933
|
+
kind: "surface",
|
|
2934
|
+
label: "Provider-owned thoth-mem setup",
|
|
2935
|
+
state: completion.provider.success ? "installed" : "drift",
|
|
2936
|
+
observed: completion.provider.status
|
|
2937
|
+
}
|
|
2938
|
+
];
|
|
2939
|
+
const warnings = [
|
|
2940
|
+
...completion.provider.diagnostics.map((message) => ({
|
|
2941
|
+
severity: "minor",
|
|
2942
|
+
code: `${options.codePrefix}-provider-diagnostic`,
|
|
2943
|
+
message
|
|
2944
|
+
})),
|
|
2945
|
+
...completion.provider.manualActions.map((message) => ({
|
|
2946
|
+
severity: "important",
|
|
2947
|
+
code: `${options.codePrefix}-provider-manual-action`,
|
|
2948
|
+
message
|
|
2949
|
+
}))
|
|
2950
|
+
];
|
|
2951
|
+
if (completion.provider.receipt) {
|
|
2952
|
+
targets.push({
|
|
2953
|
+
kind: "file",
|
|
2954
|
+
path: completion.provider.receipt,
|
|
2955
|
+
label: "thoth-mem setup receipt",
|
|
2956
|
+
observed: "provider-owned receipt"
|
|
2957
|
+
});
|
|
2958
|
+
}
|
|
2959
|
+
if (completion.success) {
|
|
2960
|
+
targets.push({
|
|
2961
|
+
kind: "file",
|
|
2962
|
+
path: completion.ledger.path,
|
|
2963
|
+
label: "CLI-managed install version",
|
|
2964
|
+
state: "installed",
|
|
2965
|
+
observed: `recorded ${options.version}`
|
|
2966
|
+
});
|
|
2967
|
+
} else {
|
|
2968
|
+
warnings.push({
|
|
2969
|
+
severity: "critical",
|
|
2970
|
+
code: `${options.codePrefix}-install-finalization-failed`,
|
|
2971
|
+
message: completion.error ?? options.fallbackError
|
|
2972
|
+
});
|
|
2973
|
+
}
|
|
2974
|
+
return { targets, warnings };
|
|
2975
|
+
}
|
|
2976
|
+
function getCliManagedInstallVersionTarget(harness, context2 = {}) {
|
|
2977
|
+
const executing = resolveExecutingPackageVersion();
|
|
2978
|
+
const ledger = readInstallLedger({
|
|
2979
|
+
env: context2.env,
|
|
2980
|
+
homeDir: context2.homeDir
|
|
2981
|
+
});
|
|
2982
|
+
const recordedVersion = ledger.status === "valid" ? ledger.ledger.harnesses[harness]?.version : void 0;
|
|
2983
|
+
let state;
|
|
2984
|
+
if (!executing.ok || ledger.status === "invalid") {
|
|
2985
|
+
state = "unknown";
|
|
2986
|
+
} else if (!recordedVersion) {
|
|
2987
|
+
state = "missing";
|
|
2988
|
+
} else {
|
|
2989
|
+
state = recordedVersion === executing.version ? "installed" : "outdated";
|
|
2990
|
+
}
|
|
2991
|
+
return {
|
|
2992
|
+
kind: "file",
|
|
2993
|
+
path: ledger.path,
|
|
2994
|
+
label: "CLI-managed install version",
|
|
2995
|
+
state,
|
|
2996
|
+
expected: executing.ok ? `executing ${executing.version}` : "executing unknown",
|
|
2997
|
+
observed: ledger.status === "invalid" ? "recorded unknown (invalid ledger)" : recordedVersion ? `recorded ${recordedVersion}` : "recorded missing",
|
|
2998
|
+
description: "Authoritative last complete CLI-managed install version; native marketplace state is independent."
|
|
2999
|
+
};
|
|
3000
|
+
}
|
|
2617
3001
|
var UNSUPPORTED_PROVIDER_CAPABILITY = {
|
|
2618
3002
|
state: "unsupported",
|
|
2619
3003
|
source: "none",
|
|
@@ -2872,10 +3256,16 @@ function statusFromSetupPlan(plan, context2) {
|
|
|
2872
3256
|
}
|
|
2873
3257
|
function getClaudeCodeStatus(context2 = { cwd: process.cwd() }, evidence = {}) {
|
|
2874
3258
|
const providerCapability = classifyProviderCapabilityEvidence(evidence);
|
|
3259
|
+
const installVersionTarget = getCliManagedInstallVersionTarget("claude", {
|
|
3260
|
+
env: context2.env,
|
|
3261
|
+
homeDir: context2.homeDir
|
|
3262
|
+
});
|
|
2875
3263
|
try {
|
|
2876
3264
|
const plan = buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true));
|
|
3265
|
+
const status = statusFromSetupPlan(plan, context2);
|
|
2877
3266
|
return {
|
|
2878
|
-
...
|
|
3267
|
+
...status,
|
|
3268
|
+
targets: [...status.targets, installVersionTarget],
|
|
2879
3269
|
providerCapability
|
|
2880
3270
|
};
|
|
2881
3271
|
} catch (error) {
|
|
@@ -2885,7 +3275,7 @@ function getClaudeCodeStatus(context2 = { cwd: process.cwd() }, evidence = {}) {
|
|
|
2885
3275
|
displayName: CLAUDE_CODE_DISPLAY_NAME,
|
|
2886
3276
|
state: "unknown",
|
|
2887
3277
|
summary: `Claude Code setup plan could not be built: ${message}`,
|
|
2888
|
-
targets: [],
|
|
3278
|
+
targets: [installVersionTarget],
|
|
2889
3279
|
diagnostics: [
|
|
2890
3280
|
{
|
|
2891
3281
|
severity: "critical",
|
|
@@ -2907,11 +3297,50 @@ function planItemFromSetup(item) {
|
|
|
2907
3297
|
backup: { required: false, strategy: "external" }
|
|
2908
3298
|
};
|
|
2909
3299
|
}
|
|
2910
|
-
function
|
|
3300
|
+
function claudeLedgerOptions(context2) {
|
|
3301
|
+
return context2.installLedgerOptions ?? {
|
|
3302
|
+
env: context2.env,
|
|
3303
|
+
homeDir: context2.homeDir
|
|
3304
|
+
};
|
|
3305
|
+
}
|
|
3306
|
+
function planFromSetup(id, action, title, summary, setupPlan, context2, version) {
|
|
2911
3307
|
const status = statusFromSetupPlan(setupPlan, context2);
|
|
2912
3308
|
const missingSkills = status.targets.some(
|
|
2913
3309
|
(target) => target.kind === "skill" && target.state === "missing"
|
|
2914
3310
|
);
|
|
3311
|
+
const providerCommand = getThothMemSetupCommand("claude", true);
|
|
3312
|
+
const nativeSetupItems = setupPlan.items.length > 0 ? setupPlan.items.map(planItemFromSetup) : [
|
|
3313
|
+
{
|
|
3314
|
+
title: "Verify Claude Code native marketplace and plugin state",
|
|
3315
|
+
target: {
|
|
3316
|
+
kind: "package",
|
|
3317
|
+
path: setupPlan.pluginRoot,
|
|
3318
|
+
label: "Claude Code native thoth-agents plugin"
|
|
3319
|
+
},
|
|
3320
|
+
preview: "Inspect and verify the canonical marketplace and enabled native plugin.",
|
|
3321
|
+
backup: { required: false, strategy: "external" }
|
|
3322
|
+
}
|
|
3323
|
+
];
|
|
3324
|
+
const completionItems = version ? [
|
|
3325
|
+
{
|
|
3326
|
+
title: "Plan provider-owned thoth-mem setup for Claude Code",
|
|
3327
|
+
target: {
|
|
3328
|
+
kind: "surface",
|
|
3329
|
+
label: "Provider-owned thoth-mem setup"
|
|
3330
|
+
},
|
|
3331
|
+
preview: `${providerCommand.command} ${providerCommand.args.join(" ")}`
|
|
3332
|
+
},
|
|
3333
|
+
{
|
|
3334
|
+
title: "Record completed Claude Code CLI install",
|
|
3335
|
+
target: {
|
|
3336
|
+
kind: "file",
|
|
3337
|
+
path: getInstallLedgerPath(claudeLedgerOptions(context2)),
|
|
3338
|
+
label: "CLI-managed install version",
|
|
3339
|
+
expected: `recorded ${version}`
|
|
3340
|
+
},
|
|
3341
|
+
preview: JSON.stringify({ harness: "claude", version })
|
|
3342
|
+
}
|
|
3343
|
+
] : [];
|
|
2915
3344
|
const plan = {
|
|
2916
3345
|
id,
|
|
2917
3346
|
harness: "claude",
|
|
@@ -2935,8 +3364,9 @@ function planFromSetup(id, action, title, summary, setupPlan, context2) {
|
|
|
2935
3364
|
description: "Claude Code owns its marketplace snapshots and plugin cache."
|
|
2936
3365
|
},
|
|
2937
3366
|
items: [
|
|
2938
|
-
...
|
|
2939
|
-
claudeCodeRequiredSkillPlanItem()
|
|
3367
|
+
...nativeSetupItems,
|
|
3368
|
+
claudeCodeRequiredSkillPlanItem(),
|
|
3369
|
+
...completionItems
|
|
2940
3370
|
],
|
|
2941
3371
|
warnings: status.diagnostics,
|
|
2942
3372
|
disclaimers: [
|
|
@@ -2944,36 +3374,51 @@ function planFromSetup(id, action, title, summary, setupPlan, context2) {
|
|
|
2944
3374
|
...setupPlan.disclaimers.map((message) => ({ message }))
|
|
2945
3375
|
]
|
|
2946
3376
|
};
|
|
2947
|
-
claudeCodePlanSources.set(plan, {
|
|
3377
|
+
claudeCodePlanSources.set(plan, {
|
|
3378
|
+
setupPlan,
|
|
3379
|
+
context: context2,
|
|
3380
|
+
...version ? { version } : {}
|
|
3381
|
+
});
|
|
2948
3382
|
return plan;
|
|
2949
3383
|
}
|
|
2950
|
-
function
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
3384
|
+
function buildCompleteClaudeCodePlan(action, context2) {
|
|
3385
|
+
const resolveVersion = context2.resolveExecutingPackageVersion ?? resolveExecutingPackageVersion;
|
|
3386
|
+
const packageVersion = resolveVersion();
|
|
3387
|
+
const buildSetup = context2.buildClaudeCodeSetupPlan ?? buildClaudeCodeSetupPlan;
|
|
3388
|
+
const plan = planFromSetup(
|
|
3389
|
+
`claude-code-${action}-preview`,
|
|
3390
|
+
action,
|
|
3391
|
+
`${action === "install" ? "Install" : "Update"} complete Claude Code setup`,
|
|
3392
|
+
"Preview native marketplace/plugin refresh, required skills, provider setup, and CLI ledger commit.",
|
|
3393
|
+
buildSetup(claudeCodeConfig(context2, true, action === "update")),
|
|
3394
|
+
context2,
|
|
3395
|
+
packageVersion.ok ? packageVersion.version : void 0
|
|
2958
3396
|
);
|
|
3397
|
+
if (!packageVersion.ok) {
|
|
3398
|
+
plan.canApply = false;
|
|
3399
|
+
plan.warnings.push(
|
|
3400
|
+
warning(
|
|
3401
|
+
packageVersion.error.message,
|
|
3402
|
+
"claude-code-package-version-unresolved"
|
|
3403
|
+
)
|
|
3404
|
+
);
|
|
3405
|
+
}
|
|
3406
|
+
return plan;
|
|
3407
|
+
}
|
|
3408
|
+
function buildClaudeCodeInstallPlan(context2 = { cwd: process.cwd() }) {
|
|
3409
|
+
return buildCompleteClaudeCodePlan("install", context2);
|
|
2959
3410
|
}
|
|
2960
3411
|
function buildClaudeCodeUpdatePlan(context2 = { cwd: process.cwd() }) {
|
|
2961
|
-
return
|
|
2962
|
-
"claude-code-update-preview",
|
|
2963
|
-
"update",
|
|
2964
|
-
"Reconcile Claude Code native plugin",
|
|
2965
|
-
"Preview native marketplace, enabled-plugin, and required-skill reconciliation.",
|
|
2966
|
-
buildClaudeCodeSetupPlan(claudeCodeConfig(context2, true, true)),
|
|
2967
|
-
context2
|
|
2968
|
-
);
|
|
3412
|
+
return buildCompleteClaudeCodePlan("update", context2);
|
|
2969
3413
|
}
|
|
2970
3414
|
function buildClaudeCodeSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
3415
|
+
const buildSetup = context2.buildClaudeCodeSetupPlan ?? buildClaudeCodeSetupPlan;
|
|
2971
3416
|
return planFromSetup(
|
|
2972
3417
|
"claude-code-sync-preview",
|
|
2973
3418
|
"sync",
|
|
2974
3419
|
"Sync Claude Code native plugin",
|
|
2975
3420
|
"Preview native marketplace, enabled-plugin, and required-skill reconciliation.",
|
|
2976
|
-
|
|
3421
|
+
buildSetup(claudeCodeConfig(context2, true)),
|
|
2977
3422
|
context2
|
|
2978
3423
|
);
|
|
2979
3424
|
}
|
|
@@ -3054,12 +3499,24 @@ function applyClaudeCodePlan(plan) {
|
|
|
3054
3499
|
"Claude Code setup plan was not produced by a Claude Code operation plan builder in this process."
|
|
3055
3500
|
);
|
|
3056
3501
|
}
|
|
3057
|
-
|
|
3502
|
+
if (source.version) {
|
|
3503
|
+
const resolveVersion = source.context.resolveExecutingPackageVersion ?? resolveExecutingPackageVersion;
|
|
3504
|
+
const currentVersion = resolveVersion();
|
|
3505
|
+
if (!currentVersion.ok || currentVersion.version !== source.version) {
|
|
3506
|
+
return rejectPlan(
|
|
3507
|
+
plan,
|
|
3508
|
+
currentVersion.ok ? `Approved package version changed from ${source.version} to ${currentVersion.version} before apply.` : currentVersion.error.message
|
|
3509
|
+
);
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3512
|
+
const applySetup = source.context.applyClaudeCodeSetup ?? applyClaudeCodeSetup;
|
|
3513
|
+
const result = applySetup({ ...source.setupPlan, dryRun: false });
|
|
3058
3514
|
const requiredSkillWarnings = [];
|
|
3059
3515
|
const requiredSkillTargets = [];
|
|
3060
3516
|
if (result.success) {
|
|
3517
|
+
const installSkill = source.context.installRequiredSkill ?? installRequiredSkill;
|
|
3061
3518
|
for (const skill of REQUIRED_SKILLS) {
|
|
3062
|
-
const installed =
|
|
3519
|
+
const installed = installSkill(skill, "claude", {
|
|
3063
3520
|
homeDir: source.context.homeDir
|
|
3064
3521
|
});
|
|
3065
3522
|
const success2 = installed.status !== "failed";
|
|
@@ -3079,37 +3536,63 @@ function applyClaudeCodePlan(plan) {
|
|
|
3079
3536
|
}
|
|
3080
3537
|
}
|
|
3081
3538
|
}
|
|
3082
|
-
|
|
3539
|
+
let success = result.success && requiredSkillWarnings.length === 0;
|
|
3083
3540
|
let summary = result.error ?? `Failed to apply Claude Code ${plan.action} plan.`;
|
|
3084
3541
|
if (success) {
|
|
3085
3542
|
summary = `Applied Claude Code native ${plan.action} plan.`;
|
|
3086
3543
|
} else if (requiredSkillWarnings.length > 0) {
|
|
3087
3544
|
summary = "Claude Code plugin was installed, but required skills failed to install.";
|
|
3088
3545
|
}
|
|
3546
|
+
const changedTargets = [
|
|
3547
|
+
...result.changed.map((path) => ({
|
|
3548
|
+
kind: "package",
|
|
3549
|
+
path,
|
|
3550
|
+
label: path.includes("/marketplaces/") ? "Claude Code marketplace" : "Claude Code plugin",
|
|
3551
|
+
state: "installed"
|
|
3552
|
+
})),
|
|
3553
|
+
...requiredSkillTargets
|
|
3554
|
+
];
|
|
3555
|
+
const warnings = [
|
|
3556
|
+
...requiredSkillWarnings,
|
|
3557
|
+
...result.success ? [] : [
|
|
3558
|
+
{
|
|
3559
|
+
severity: "critical",
|
|
3560
|
+
message: result.error ?? "apply failed."
|
|
3561
|
+
}
|
|
3562
|
+
]
|
|
3563
|
+
];
|
|
3564
|
+
if (success && source.version && plan.action !== "sync") {
|
|
3565
|
+
const finalize = source.context.finalizeHarnessInstall ?? finalizeHarnessInstall;
|
|
3566
|
+
const completion = finalize({
|
|
3567
|
+
harness: "claude",
|
|
3568
|
+
version: source.version,
|
|
3569
|
+
dryRun: false,
|
|
3570
|
+
cwd: source.context.cwd,
|
|
3571
|
+
runThothMemSetup: source.context.runThothMemSetup,
|
|
3572
|
+
ledgerOptions: claudeLedgerOptions(source.context)
|
|
3573
|
+
});
|
|
3574
|
+
const completionEvidence = getInstallCompletionEvidence(completion, {
|
|
3575
|
+
codePrefix: "claude-code",
|
|
3576
|
+
version: source.version,
|
|
3577
|
+
fallbackError: "Claude Code install finalization failed."
|
|
3578
|
+
});
|
|
3579
|
+
warnings.push(...completionEvidence.warnings);
|
|
3580
|
+
changedTargets.push(...completionEvidence.targets);
|
|
3581
|
+
if (completion.success) {
|
|
3582
|
+
summary = `Applied complete Claude Code ${plan.action} plan.`;
|
|
3583
|
+
} else {
|
|
3584
|
+
success = false;
|
|
3585
|
+
summary = completion.error ?? "Claude Code install finalization failed.";
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3089
3588
|
return {
|
|
3090
3589
|
harness: "claude",
|
|
3091
3590
|
action: plan.action,
|
|
3092
3591
|
applied: success,
|
|
3093
3592
|
summary,
|
|
3094
|
-
changedTargets
|
|
3095
|
-
...result.changed.map((path) => ({
|
|
3096
|
-
kind: "package",
|
|
3097
|
-
path,
|
|
3098
|
-
label: path.includes("/marketplaces/") ? "Claude Code marketplace" : "Claude Code plugin",
|
|
3099
|
-
state: "installed"
|
|
3100
|
-
})),
|
|
3101
|
-
...requiredSkillTargets
|
|
3102
|
-
],
|
|
3593
|
+
changedTargets,
|
|
3103
3594
|
backups: [],
|
|
3104
|
-
warnings
|
|
3105
|
-
...requiredSkillWarnings,
|
|
3106
|
-
...result.success ? [] : [
|
|
3107
|
-
{
|
|
3108
|
-
severity: "critical",
|
|
3109
|
-
message: result.error ?? "apply failed."
|
|
3110
|
-
}
|
|
3111
|
-
]
|
|
3112
|
-
],
|
|
3595
|
+
warnings,
|
|
3113
3596
|
disclaimers: claudeCodeDisclaimers()
|
|
3114
3597
|
};
|
|
3115
3598
|
}
|
|
@@ -3121,8 +3604,245 @@ function defaultClaudeCodeModelRoles() {
|
|
|
3121
3604
|
}
|
|
3122
3605
|
|
|
3123
3606
|
// src/cli/operations/codex.ts
|
|
3124
|
-
import { existsSync as existsSync8, readFileSync as
|
|
3125
|
-
import { basename as basename2 } from "path";
|
|
3607
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
3608
|
+
import { basename as basename2 } from "path";
|
|
3609
|
+
|
|
3610
|
+
// src/cli/codex-plugin-install.ts
|
|
3611
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
3612
|
+
var MARKETPLACE_NAME2 = "thoth-agents";
|
|
3613
|
+
var MARKETPLACE_SOURCE2 = "EremesNG/thoth-agents";
|
|
3614
|
+
var PLUGIN_ID2 = "thoth-agents@thoth-agents";
|
|
3615
|
+
var MARKETPLACE_TARGET2 = "codex://marketplaces/thoth-agents";
|
|
3616
|
+
var PLUGIN_TARGET2 = "codex://plugins/thoth-agents@thoth-agents";
|
|
3617
|
+
function getCodexCommand(args, options = {}) {
|
|
3618
|
+
if ((options.platform ?? process.platform) !== "win32") {
|
|
3619
|
+
return { command: "codex", args: [...args] };
|
|
3620
|
+
}
|
|
3621
|
+
return {
|
|
3622
|
+
command: options.commandShell ?? process.env.ComSpec ?? "cmd.exe",
|
|
3623
|
+
args: ["/d", "/s", "/c", "codex", ...args]
|
|
3624
|
+
};
|
|
3625
|
+
}
|
|
3626
|
+
function defaultCommandExecutor3(command, args, options) {
|
|
3627
|
+
const invocation = command === "codex" ? getCodexCommand(args) : { command, args: [...args] };
|
|
3628
|
+
const result = spawnSync4(invocation.command, invocation.args, {
|
|
3629
|
+
cwd: options.cwd,
|
|
3630
|
+
encoding: "utf8",
|
|
3631
|
+
shell: false,
|
|
3632
|
+
windowsHide: true
|
|
3633
|
+
});
|
|
3634
|
+
return {
|
|
3635
|
+
exitCode: result.status ?? (result.error ? 1 : 0),
|
|
3636
|
+
stdout: result.stdout ?? "",
|
|
3637
|
+
stderr: result.stderr ?? result.error?.message ?? ""
|
|
3638
|
+
};
|
|
3639
|
+
}
|
|
3640
|
+
function isRecord5(value) {
|
|
3641
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3642
|
+
}
|
|
3643
|
+
function parseRecordArray(value) {
|
|
3644
|
+
return Array.isArray(value) && value.every(isRecord5) ? value : null;
|
|
3645
|
+
}
|
|
3646
|
+
function parseManagerRecord(content) {
|
|
3647
|
+
try {
|
|
3648
|
+
const parsed = JSON.parse(content);
|
|
3649
|
+
return isRecord5(parsed) ? parsed : null;
|
|
3650
|
+
} catch {
|
|
3651
|
+
return null;
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
function normalizeMarketplaceSource(value) {
|
|
3655
|
+
return value.trim().replace(/^git@github\.com:/i, "").replace(/^(?:https?|ssh):\/\/(?:git@)?github\.com\//i, "").replace(/^github\.com\//i, "").replace(/\/?\.git\/?$/i, "").replace(/\/$/, "").toLowerCase();
|
|
3656
|
+
}
|
|
3657
|
+
function marketplaceSources(entry) {
|
|
3658
|
+
const sources = [entry.repo, entry.source];
|
|
3659
|
+
if (isRecord5(entry.marketplaceSource)) {
|
|
3660
|
+
sources.push(entry.marketplaceSource.repo, entry.marketplaceSource.source);
|
|
3661
|
+
}
|
|
3662
|
+
return sources.filter(
|
|
3663
|
+
(source) => typeof source === "string"
|
|
3664
|
+
);
|
|
3665
|
+
}
|
|
3666
|
+
function isCanonicalMarketplace2(entry) {
|
|
3667
|
+
if (entry.name !== MARKETPLACE_NAME2) return false;
|
|
3668
|
+
const canonical = MARKETPLACE_SOURCE2.toLowerCase();
|
|
3669
|
+
return marketplaceSources(entry).some(
|
|
3670
|
+
(source) => normalizeMarketplaceSource(source) === canonical
|
|
3671
|
+
);
|
|
3672
|
+
}
|
|
3673
|
+
function inspectCodexManager(executor, cwd) {
|
|
3674
|
+
const marketplaceResult = executor(
|
|
3675
|
+
"codex",
|
|
3676
|
+
["plugin", "marketplace", "list", "--json"],
|
|
3677
|
+
{ cwd }
|
|
3678
|
+
);
|
|
3679
|
+
const pluginResult = executor(
|
|
3680
|
+
"codex",
|
|
3681
|
+
["plugin", "list", "--available", "--json"],
|
|
3682
|
+
{ cwd }
|
|
3683
|
+
);
|
|
3684
|
+
const diagnostics = [];
|
|
3685
|
+
if (marketplaceResult.exitCode !== 0 || pluginResult.exitCode !== 0) {
|
|
3686
|
+
diagnostics.push(
|
|
3687
|
+
"Codex native plugin state could not be inspected; no manager mutation will be attempted."
|
|
3688
|
+
);
|
|
3689
|
+
return {
|
|
3690
|
+
ready: false,
|
|
3691
|
+
marketplace: "unknown",
|
|
3692
|
+
plugin: "unknown",
|
|
3693
|
+
diagnostics
|
|
3694
|
+
};
|
|
3695
|
+
}
|
|
3696
|
+
const marketplaceRecord = parseManagerRecord(marketplaceResult.stdout);
|
|
3697
|
+
const pluginRecord = parseManagerRecord(pluginResult.stdout);
|
|
3698
|
+
const marketplaces = marketplaceRecord ? parseRecordArray(marketplaceRecord.marketplaces) : null;
|
|
3699
|
+
const installedPlugins = pluginRecord ? parseRecordArray(pluginRecord.installed) : null;
|
|
3700
|
+
if (!marketplaces || !installedPlugins) {
|
|
3701
|
+
diagnostics.push(
|
|
3702
|
+
"Codex returned unparseable plugin manager JSON; no manager mutation will be attempted."
|
|
3703
|
+
);
|
|
3704
|
+
return {
|
|
3705
|
+
ready: false,
|
|
3706
|
+
marketplace: "unknown",
|
|
3707
|
+
plugin: "unknown",
|
|
3708
|
+
diagnostics
|
|
3709
|
+
};
|
|
3710
|
+
}
|
|
3711
|
+
const namedMarketplaces = marketplaces.filter(
|
|
3712
|
+
(entry) => entry.name === MARKETPLACE_NAME2
|
|
3713
|
+
);
|
|
3714
|
+
let marketplace = "absent";
|
|
3715
|
+
if (namedMarketplaces.length > 0) {
|
|
3716
|
+
marketplace = namedMarketplaces.every(isCanonicalMarketplace2) ? "installed" : "conflict";
|
|
3717
|
+
}
|
|
3718
|
+
if (marketplace === "conflict") {
|
|
3719
|
+
diagnostics.push(
|
|
3720
|
+
"A Codex marketplace named thoth-agents is registered from a different source; resolve it through Codex before retrying."
|
|
3721
|
+
);
|
|
3722
|
+
}
|
|
3723
|
+
const matchingPlugins = installedPlugins.filter(
|
|
3724
|
+
(entry) => entry.pluginId === PLUGIN_ID2
|
|
3725
|
+
);
|
|
3726
|
+
let plugin = "absent";
|
|
3727
|
+
if (matchingPlugins.some((entry) => entry.enabled === true)) {
|
|
3728
|
+
plugin = "installed";
|
|
3729
|
+
} else if (matchingPlugins.length > 0) {
|
|
3730
|
+
plugin = "disabled";
|
|
3731
|
+
}
|
|
3732
|
+
return {
|
|
3733
|
+
ready: marketplace !== "conflict",
|
|
3734
|
+
marketplace,
|
|
3735
|
+
plugin,
|
|
3736
|
+
diagnostics
|
|
3737
|
+
};
|
|
3738
|
+
}
|
|
3739
|
+
function commandItem2(action, projectRoot) {
|
|
3740
|
+
if (action === "register-marketplace") {
|
|
3741
|
+
return {
|
|
3742
|
+
action,
|
|
3743
|
+
targetPath: MARKETPLACE_TARGET2,
|
|
3744
|
+
description: "Register the package-owned thoth-agents Codex marketplace.",
|
|
3745
|
+
command: {
|
|
3746
|
+
executable: "codex",
|
|
3747
|
+
args: ["plugin", "marketplace", "add", MARKETPLACE_SOURCE2, "--json"],
|
|
3748
|
+
cwd: projectRoot
|
|
3749
|
+
}
|
|
3750
|
+
};
|
|
3751
|
+
}
|
|
3752
|
+
return {
|
|
3753
|
+
action,
|
|
3754
|
+
targetPath: PLUGIN_TARGET2,
|
|
3755
|
+
description: "Install and enable the thoth-agents Codex plugin from its native marketplace.",
|
|
3756
|
+
command: {
|
|
3757
|
+
executable: "codex",
|
|
3758
|
+
args: ["plugin", "add", PLUGIN_ID2, "--json"],
|
|
3759
|
+
cwd: projectRoot
|
|
3760
|
+
}
|
|
3761
|
+
};
|
|
3762
|
+
}
|
|
3763
|
+
function buildCodexPluginSetupPlan(config) {
|
|
3764
|
+
const commandExecutor = config.commandExecutor ?? defaultCommandExecutor3;
|
|
3765
|
+
const inspection = inspectCodexManager(commandExecutor, config.projectRoot);
|
|
3766
|
+
const items = [];
|
|
3767
|
+
if (inspection.ready) {
|
|
3768
|
+
if (inspection.marketplace === "absent") {
|
|
3769
|
+
items.push(commandItem2("register-marketplace", config.projectRoot));
|
|
3770
|
+
}
|
|
3771
|
+
if (inspection.plugin === "absent" || inspection.plugin === "disabled") {
|
|
3772
|
+
items.push(commandItem2("install-plugin", config.projectRoot));
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3775
|
+
return {
|
|
3776
|
+
dryRun: config.dryRun === true,
|
|
3777
|
+
items,
|
|
3778
|
+
ready: inspection.ready,
|
|
3779
|
+
projectRoot: config.projectRoot,
|
|
3780
|
+
commandExecutor,
|
|
3781
|
+
diagnostics: [
|
|
3782
|
+
...inspection.diagnostics,
|
|
3783
|
+
"Codex installs thoth-agents from EremesNG/thoth-agents through its native plugin manager and owns the cached plugin files.",
|
|
3784
|
+
"Restart Codex after installation; use /plugins to inspect marketplace and plugin state."
|
|
3785
|
+
]
|
|
3786
|
+
};
|
|
3787
|
+
}
|
|
3788
|
+
function uniqueMessages3(messages) {
|
|
3789
|
+
return [...new Set(messages)];
|
|
3790
|
+
}
|
|
3791
|
+
function boundedCommandFailure2(item, result) {
|
|
3792
|
+
const detail = result.stderr.trim().replace(/\s+/g, " ").slice(0, 240);
|
|
3793
|
+
return `${item.description} Codex exited with code ${result.exitCode}${detail ? `: ${detail}` : "."}`;
|
|
3794
|
+
}
|
|
3795
|
+
function applyCodexPluginSetup(plan) {
|
|
3796
|
+
const changed = [];
|
|
3797
|
+
const diagnostics = uniqueMessages3(plan.diagnostics);
|
|
3798
|
+
if (!plan.ready) {
|
|
3799
|
+
return {
|
|
3800
|
+
success: false,
|
|
3801
|
+
changed,
|
|
3802
|
+
diagnostics,
|
|
3803
|
+
error: "Codex native plugin manager state is not safe to mutate."
|
|
3804
|
+
};
|
|
3805
|
+
}
|
|
3806
|
+
if (plan.dryRun) return { success: true, changed, diagnostics };
|
|
3807
|
+
for (const item of plan.items) {
|
|
3808
|
+
const result = plan.commandExecutor(
|
|
3809
|
+
item.command.executable,
|
|
3810
|
+
item.command.args,
|
|
3811
|
+
{ cwd: item.command.cwd }
|
|
3812
|
+
);
|
|
3813
|
+
if (result.exitCode !== 0) {
|
|
3814
|
+
return {
|
|
3815
|
+
success: false,
|
|
3816
|
+
changed,
|
|
3817
|
+
diagnostics,
|
|
3818
|
+
error: boundedCommandFailure2(item, result)
|
|
3819
|
+
};
|
|
3820
|
+
}
|
|
3821
|
+
changed.push(item.targetPath);
|
|
3822
|
+
}
|
|
3823
|
+
const inspection = inspectCodexManager(
|
|
3824
|
+
plan.commandExecutor,
|
|
3825
|
+
plan.projectRoot
|
|
3826
|
+
);
|
|
3827
|
+
diagnostics.push(...inspection.diagnostics);
|
|
3828
|
+
if (inspection.marketplace !== "installed" || inspection.plugin !== "installed") {
|
|
3829
|
+
return {
|
|
3830
|
+
success: false,
|
|
3831
|
+
changed,
|
|
3832
|
+
diagnostics: uniqueMessages3(diagnostics),
|
|
3833
|
+
error: "Codex did not verify the expected marketplace and enabled plugin after mutation."
|
|
3834
|
+
};
|
|
3835
|
+
}
|
|
3836
|
+
return { success: true, changed, diagnostics: uniqueMessages3(diagnostics) };
|
|
3837
|
+
}
|
|
3838
|
+
function formatCodexPluginSetupPlan(plan) {
|
|
3839
|
+
const lines = plan.items.map(
|
|
3840
|
+
(item) => `- ${item.action}: ${item.command.executable} ${item.command.args.join(" ")}`
|
|
3841
|
+
);
|
|
3842
|
+
return ["Codex plugin setup plan:", ...lines].join("\n");
|
|
3843
|
+
}
|
|
3844
|
+
|
|
3845
|
+
// src/cli/operations/codex.ts
|
|
3126
3846
|
var CODEX_DISPLAY_NAME = "Codex";
|
|
3127
3847
|
var codexPlanSources = /* @__PURE__ */ new WeakMap();
|
|
3128
3848
|
var codexModelSources = /* @__PURE__ */ new WeakMap();
|
|
@@ -3245,7 +3965,7 @@ function backupForItem(item) {
|
|
|
3245
3965
|
function rootBlockState(item) {
|
|
3246
3966
|
if (!existsSync8(item.targetPath))
|
|
3247
3967
|
return { state: "missing", observed: "absent" };
|
|
3248
|
-
const content =
|
|
3968
|
+
const content = readFileSync6(item.targetPath, "utf8");
|
|
3249
3969
|
if (item.content && content.includes(item.content)) {
|
|
3250
3970
|
return { state: "installed", observed: "managed root block present" };
|
|
3251
3971
|
}
|
|
@@ -3258,11 +3978,11 @@ function managedModelState(item) {
|
|
|
3258
3978
|
if (!existsSync8(item.targetPath))
|
|
3259
3979
|
return { state: "missing", observed: "absent" };
|
|
3260
3980
|
try {
|
|
3261
|
-
const parsed = JSON.parse(
|
|
3981
|
+
const parsed = JSON.parse(readFileSync6(item.targetPath, "utf8"));
|
|
3262
3982
|
if (parsed.version !== MANAGED_MODEL_STATE_VERSION || !parsed.models || typeof parsed.models !== "object" || Array.isArray(parsed.models)) {
|
|
3263
3983
|
return { state: "unknown", observed: "invalid managed model state" };
|
|
3264
3984
|
}
|
|
3265
|
-
return item.content ===
|
|
3985
|
+
return item.content === readFileSync6(item.targetPath, "utf8") ? { state: "installed", observed: "managed model state current" } : { state: "drift", observed: "managed model state differs" };
|
|
3266
3986
|
} catch {
|
|
3267
3987
|
return { state: "unknown", observed: "unparseable managed model state" };
|
|
3268
3988
|
}
|
|
@@ -3270,7 +3990,7 @@ function managedModelState(item) {
|
|
|
3270
3990
|
function userConfigState(item) {
|
|
3271
3991
|
if (!existsSync8(item.targetPath))
|
|
3272
3992
|
return { state: "missing", observed: "absent" };
|
|
3273
|
-
const content =
|
|
3993
|
+
const content = readFileSync6(item.targetPath, "utf8");
|
|
3274
3994
|
if (/^\s*default_mode_request_user_input\s*=\s*true\b/m.test(content)) {
|
|
3275
3995
|
return {
|
|
3276
3996
|
state: "installed",
|
|
@@ -3285,7 +4005,7 @@ function userConfigState(item) {
|
|
|
3285
4005
|
function contentState(item) {
|
|
3286
4006
|
if (!existsSync8(item.targetPath))
|
|
3287
4007
|
return { state: "missing", observed: "absent" };
|
|
3288
|
-
const observed =
|
|
4008
|
+
const observed = readFileSync6(item.targetPath, "utf8");
|
|
3289
4009
|
if (item.content === observed)
|
|
3290
4010
|
return { state: "installed", observed: "current" };
|
|
3291
4011
|
if (item.role) {
|
|
@@ -3376,6 +4096,10 @@ function codexRequiredSkillPlanItem() {
|
|
|
3376
4096
|
}
|
|
3377
4097
|
function getCodexStatus(context2 = { cwd: process.cwd() }, evidence = {}) {
|
|
3378
4098
|
const providerCapability = classifyProviderCapabilityEvidence(evidence);
|
|
4099
|
+
const installVersionTarget = getCliManagedInstallVersionTarget("codex", {
|
|
4100
|
+
env: context2.env,
|
|
4101
|
+
homeDir: context2.homeDir
|
|
4102
|
+
});
|
|
3379
4103
|
let plan;
|
|
3380
4104
|
try {
|
|
3381
4105
|
plan = buildCodexSetupPlan(codexConfig(context2, true));
|
|
@@ -3386,7 +4110,7 @@ function getCodexStatus(context2 = { cwd: process.cwd() }, evidence = {}) {
|
|
|
3386
4110
|
displayName: CODEX_DISPLAY_NAME,
|
|
3387
4111
|
state: "unknown",
|
|
3388
4112
|
summary: `Codex setup plan could not be built: ${message}`,
|
|
3389
|
-
targets: [],
|
|
4113
|
+
targets: [installVersionTarget],
|
|
3390
4114
|
diagnostics: [
|
|
3391
4115
|
{
|
|
3392
4116
|
severity: "critical",
|
|
@@ -3420,7 +4144,8 @@ function getCodexStatus(context2 = { cwd: process.cwd() }, evidence = {}) {
|
|
|
3420
4144
|
...classified.map(
|
|
3421
4145
|
({ item, state: state2, observed }) => targetForItem2(item, state2, observed)
|
|
3422
4146
|
),
|
|
3423
|
-
...requiredSkills.targets
|
|
4147
|
+
...requiredSkills.targets,
|
|
4148
|
+
installVersionTarget
|
|
3424
4149
|
],
|
|
3425
4150
|
diagnostics,
|
|
3426
4151
|
actions: codexActions,
|
|
@@ -3439,9 +4164,62 @@ function planItemFromSetup2(item) {
|
|
|
3439
4164
|
backup: backupForItem(item)
|
|
3440
4165
|
};
|
|
3441
4166
|
}
|
|
3442
|
-
function
|
|
4167
|
+
function planItemFromPluginSetup(item) {
|
|
4168
|
+
return {
|
|
4169
|
+
title: item.description,
|
|
4170
|
+
target: {
|
|
4171
|
+
kind: "package",
|
|
4172
|
+
path: item.targetPath,
|
|
4173
|
+
label: "Codex native plugin manager target"
|
|
4174
|
+
},
|
|
4175
|
+
preview: `${item.command.executable} ${item.command.args.join(" ")}`,
|
|
4176
|
+
backup: { required: false, strategy: "external" }
|
|
4177
|
+
};
|
|
4178
|
+
}
|
|
4179
|
+
function codexLedgerOptions(context2) {
|
|
4180
|
+
return context2.installLedgerOptions ?? {
|
|
4181
|
+
env: context2.env,
|
|
4182
|
+
homeDir: context2.homeDir
|
|
4183
|
+
};
|
|
4184
|
+
}
|
|
4185
|
+
function planFromSetup2(id, action, title, summary, setupPlan, context2, completion) {
|
|
3443
4186
|
const status = getCodexStatus(context2);
|
|
3444
|
-
const canApply = status.state === "installed" || status.state === "missing" || status.state === "outdated";
|
|
4187
|
+
const canApply = (status.state === "installed" || status.state === "missing" || status.state === "outdated") && (completion?.pluginPlan.ready ?? true);
|
|
4188
|
+
const providerCommand = getThothMemSetupCommand("codex", true);
|
|
4189
|
+
const nativePluginItems = completion ? completion.pluginPlan.items.length > 0 ? completion.pluginPlan.items.map(planItemFromPluginSetup) : [
|
|
4190
|
+
{
|
|
4191
|
+
title: "Verify Codex native plugin manager state",
|
|
4192
|
+
target: {
|
|
4193
|
+
kind: "package",
|
|
4194
|
+
label: "Codex native thoth-agents plugin"
|
|
4195
|
+
},
|
|
4196
|
+
preview: "Inspect and post-verify the canonical thoth-agents marketplace and enabled plugin.",
|
|
4197
|
+
backup: { required: false, strategy: "external" }
|
|
4198
|
+
}
|
|
4199
|
+
] : [];
|
|
4200
|
+
const completionItems = completion ? [
|
|
4201
|
+
{
|
|
4202
|
+
title: "Plan provider-owned thoth-mem setup for Codex",
|
|
4203
|
+
target: {
|
|
4204
|
+
kind: "surface",
|
|
4205
|
+
label: "Provider-owned thoth-mem setup"
|
|
4206
|
+
},
|
|
4207
|
+
preview: `${providerCommand.command} ${providerCommand.args.join(" ")}`
|
|
4208
|
+
},
|
|
4209
|
+
{
|
|
4210
|
+
title: "Record completed Codex CLI install",
|
|
4211
|
+
target: {
|
|
4212
|
+
kind: "file",
|
|
4213
|
+
path: getInstallLedgerPath(codexLedgerOptions(context2)),
|
|
4214
|
+
label: "CLI-managed install version",
|
|
4215
|
+
expected: `recorded ${completion.version}`
|
|
4216
|
+
},
|
|
4217
|
+
preview: JSON.stringify({
|
|
4218
|
+
harness: "codex",
|
|
4219
|
+
version: completion.version
|
|
4220
|
+
})
|
|
4221
|
+
}
|
|
4222
|
+
] : [];
|
|
3445
4223
|
const plan = {
|
|
3446
4224
|
id,
|
|
3447
4225
|
harness: "codex",
|
|
@@ -3458,8 +4236,10 @@ function planFromSetup2(id, action, title, summary, setupPlan, context2) {
|
|
|
3458
4236
|
description: "Codex setup apply uses the existing installer backup behavior for files that already exist."
|
|
3459
4237
|
},
|
|
3460
4238
|
items: [
|
|
4239
|
+
...nativePluginItems,
|
|
3461
4240
|
...setupPlan.items.map(planItemFromSetup2),
|
|
3462
|
-
codexRequiredSkillPlanItem()
|
|
4241
|
+
codexRequiredSkillPlanItem(),
|
|
4242
|
+
...completionItems
|
|
3463
4243
|
],
|
|
3464
4244
|
warnings: [
|
|
3465
4245
|
...status.diagnostics,
|
|
@@ -3475,22 +4255,59 @@ function planFromSetup2(id, action, title, summary, setupPlan, context2) {
|
|
|
3475
4255
|
...setupPlan.disclaimers.map((message) => ({ message }))
|
|
3476
4256
|
]
|
|
3477
4257
|
};
|
|
3478
|
-
codexPlanSources.set(plan, {
|
|
4258
|
+
codexPlanSources.set(plan, {
|
|
4259
|
+
setupPlan,
|
|
4260
|
+
context: context2,
|
|
4261
|
+
...completion ? {
|
|
4262
|
+
pluginPlan: completion.pluginPlan,
|
|
4263
|
+
version: completion.version
|
|
4264
|
+
} : {}
|
|
4265
|
+
});
|
|
3479
4266
|
return plan;
|
|
3480
4267
|
}
|
|
3481
|
-
function
|
|
3482
|
-
const
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
4268
|
+
function buildCompleteCodexPlan(action, context2) {
|
|
4269
|
+
const resolveVersion = context2.resolveExecutingPackageVersion ?? resolveExecutingPackageVersion;
|
|
4270
|
+
const packageVersion = resolveVersion();
|
|
4271
|
+
const buildPlugin = context2.buildCodexPluginSetupPlan ?? buildCodexPluginSetupPlan;
|
|
4272
|
+
const pluginPlan = buildPlugin({
|
|
4273
|
+
dryRun: true,
|
|
4274
|
+
projectRoot: context2.cwd,
|
|
4275
|
+
commandExecutor: context2.codexPluginCommandExecutor
|
|
4276
|
+
});
|
|
4277
|
+
const buildSetup = context2.buildCodexSetupPlan ?? buildCodexSetupPlan;
|
|
4278
|
+
const setupPlan = buildSetup(codexConfig(context2, true));
|
|
4279
|
+
const plan = planFromSetup2(
|
|
4280
|
+
`codex-${action}-preview`,
|
|
4281
|
+
action,
|
|
4282
|
+
`${action === "install" ? "Install" : "Update"} complete Codex setup`,
|
|
4283
|
+
`Preview Codex native plugin, global agent pack, required skills, provider setup, and CLI ledger ${action}.`,
|
|
3488
4284
|
setupPlan,
|
|
3489
|
-
context2
|
|
4285
|
+
context2,
|
|
4286
|
+
packageVersion.ok ? { pluginPlan, version: packageVersion.version } : void 0
|
|
3490
4287
|
);
|
|
4288
|
+
if (!packageVersion.ok) {
|
|
4289
|
+
plan.canApply = false;
|
|
4290
|
+
plan.warnings.push(
|
|
4291
|
+
warning2(packageVersion.error.message, "codex-package-version-unresolved")
|
|
4292
|
+
);
|
|
4293
|
+
}
|
|
4294
|
+
if (!pluginPlan.ready) {
|
|
4295
|
+
plan.canApply = false;
|
|
4296
|
+
plan.warnings.push(
|
|
4297
|
+
warning2(
|
|
4298
|
+
"Codex native plugin manager state is not safe to mutate.",
|
|
4299
|
+
"codex-native-plugin-not-ready"
|
|
4300
|
+
)
|
|
4301
|
+
);
|
|
4302
|
+
}
|
|
4303
|
+
return plan;
|
|
4304
|
+
}
|
|
4305
|
+
function buildCodexUpdatePlan(context2 = { cwd: process.cwd() }) {
|
|
4306
|
+
return buildCompleteCodexPlan("update", context2);
|
|
3491
4307
|
}
|
|
3492
4308
|
function buildCodexSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
3493
|
-
const
|
|
4309
|
+
const buildSetup = context2.buildCodexSetupPlan ?? buildCodexSetupPlan;
|
|
4310
|
+
const setupPlan = buildSetup(codexConfig(context2, true));
|
|
3494
4311
|
return planFromSetup2(
|
|
3495
4312
|
"codex-sync-preview",
|
|
3496
4313
|
"sync",
|
|
@@ -3501,15 +4318,7 @@ function buildCodexSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
|
3501
4318
|
);
|
|
3502
4319
|
}
|
|
3503
4320
|
function buildCodexInstallPlan(context2 = { cwd: process.cwd() }) {
|
|
3504
|
-
|
|
3505
|
-
return planFromSetup2(
|
|
3506
|
-
"codex-install-preview",
|
|
3507
|
-
"install",
|
|
3508
|
-
"Install Codex managed setup",
|
|
3509
|
-
"Preview Codex managed agent-pack setup using buildCodexSetupPlan().",
|
|
3510
|
-
setupPlan,
|
|
3511
|
-
context2
|
|
3512
|
-
);
|
|
4321
|
+
return buildCompleteCodexPlan("install", context2);
|
|
3513
4322
|
}
|
|
3514
4323
|
function normalizeCodexModel(input) {
|
|
3515
4324
|
return normalizeCodexRuntimeModel(input.model);
|
|
@@ -3721,12 +4530,66 @@ function applyCodexPlan(plan) {
|
|
|
3721
4530
|
"Codex setup plan was not produced by a Codex operation plan builder in this process."
|
|
3722
4531
|
);
|
|
3723
4532
|
}
|
|
3724
|
-
|
|
4533
|
+
if (source.version) {
|
|
4534
|
+
const resolveVersion = source.context.resolveExecutingPackageVersion ?? resolveExecutingPackageVersion;
|
|
4535
|
+
const currentVersion = resolveVersion();
|
|
4536
|
+
if (!currentVersion.ok || currentVersion.version !== source.version) {
|
|
4537
|
+
return rejectPlan2(
|
|
4538
|
+
plan,
|
|
4539
|
+
currentVersion.ok ? `Approved package version changed from ${source.version} to ${currentVersion.version} before apply.` : currentVersion.error.message
|
|
4540
|
+
);
|
|
4541
|
+
}
|
|
4542
|
+
}
|
|
4543
|
+
const changedTargets = [];
|
|
4544
|
+
const warnings = [];
|
|
4545
|
+
if (source.pluginPlan) {
|
|
4546
|
+
const applyPlugin = source.context.applyCodexPluginSetup ?? applyCodexPluginSetup;
|
|
4547
|
+
const nativeResult = applyPlugin({
|
|
4548
|
+
...source.pluginPlan,
|
|
4549
|
+
dryRun: false
|
|
4550
|
+
});
|
|
4551
|
+
warnings.push(
|
|
4552
|
+
...nativeResult.diagnostics.map((message) => ({
|
|
4553
|
+
severity: "minor",
|
|
4554
|
+
code: "codex-native-plugin-diagnostic",
|
|
4555
|
+
message
|
|
4556
|
+
}))
|
|
4557
|
+
);
|
|
4558
|
+
changedTargets.push(
|
|
4559
|
+
...nativeResult.changed.map((path) => ({
|
|
4560
|
+
kind: "package",
|
|
4561
|
+
path,
|
|
4562
|
+
label: "Codex native plugin manager target",
|
|
4563
|
+
state: "installed"
|
|
4564
|
+
}))
|
|
4565
|
+
);
|
|
4566
|
+
if (!nativeResult.success) {
|
|
4567
|
+
const message = nativeResult.error ?? "Codex native plugin setup failed.";
|
|
4568
|
+
warnings.push({
|
|
4569
|
+
severity: "critical",
|
|
4570
|
+
code: "codex-native-plugin-failed",
|
|
4571
|
+
message
|
|
4572
|
+
});
|
|
4573
|
+
return {
|
|
4574
|
+
harness: "codex",
|
|
4575
|
+
action: plan.action,
|
|
4576
|
+
applied: false,
|
|
4577
|
+
summary: message,
|
|
4578
|
+
changedTargets,
|
|
4579
|
+
backups: [],
|
|
4580
|
+
warnings,
|
|
4581
|
+
disclaimers: codexDisclaimers()
|
|
4582
|
+
};
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4585
|
+
const applySetup = source.context.applyCodexSetup ?? applyCodexSetup;
|
|
4586
|
+
const result = applySetup({ ...source.setupPlan, dryRun: false });
|
|
3725
4587
|
const requiredSkillWarnings = [];
|
|
3726
4588
|
const requiredSkillTargets = [];
|
|
3727
4589
|
if (result.success) {
|
|
4590
|
+
const installSkill = source.context.installRequiredSkill ?? installRequiredSkill;
|
|
3728
4591
|
for (const skill of REQUIRED_SKILLS) {
|
|
3729
|
-
const installed =
|
|
4592
|
+
const installed = installSkill(skill, "codex", {
|
|
3730
4593
|
homeDir: source.context.homeDir
|
|
3731
4594
|
});
|
|
3732
4595
|
const success2 = installed.status !== "failed";
|
|
@@ -3746,31 +4609,56 @@ function applyCodexPlan(plan) {
|
|
|
3746
4609
|
}
|
|
3747
4610
|
}
|
|
3748
4611
|
}
|
|
3749
|
-
|
|
4612
|
+
let success = result.success && requiredSkillWarnings.length === 0;
|
|
4613
|
+
let 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.`;
|
|
4614
|
+
warnings.push(...requiredSkillWarnings);
|
|
4615
|
+
if (!result.success) {
|
|
4616
|
+
warnings.push({
|
|
4617
|
+
severity: "critical",
|
|
4618
|
+
message: result.error ?? "Codex setup apply failed."
|
|
4619
|
+
});
|
|
4620
|
+
}
|
|
4621
|
+
changedTargets.push(
|
|
4622
|
+
...result.changed.map((path) => ({
|
|
4623
|
+
kind: path.endsWith(".json") ? "memory-state" : "generated-artifact",
|
|
4624
|
+
path,
|
|
4625
|
+
label: basename2(path),
|
|
4626
|
+
state: "installed"
|
|
4627
|
+
})),
|
|
4628
|
+
...requiredSkillTargets
|
|
4629
|
+
);
|
|
4630
|
+
if (success && source.version && plan.action !== "sync") {
|
|
4631
|
+
const finalize = source.context.finalizeHarnessInstall ?? finalizeHarnessInstall;
|
|
4632
|
+
const completion = finalize({
|
|
4633
|
+
harness: "codex",
|
|
4634
|
+
version: source.version,
|
|
4635
|
+
dryRun: false,
|
|
4636
|
+
cwd: source.context.cwd,
|
|
4637
|
+
runThothMemSetup: source.context.runThothMemSetup,
|
|
4638
|
+
ledgerOptions: codexLedgerOptions(source.context)
|
|
4639
|
+
});
|
|
4640
|
+
const completionEvidence = getInstallCompletionEvidence(completion, {
|
|
4641
|
+
codePrefix: "codex",
|
|
4642
|
+
version: source.version,
|
|
4643
|
+
fallbackError: "Codex install finalization failed."
|
|
4644
|
+
});
|
|
4645
|
+
warnings.push(...completionEvidence.warnings);
|
|
4646
|
+
changedTargets.push(...completionEvidence.targets);
|
|
4647
|
+
if (completion.success) {
|
|
4648
|
+
summary = `Applied complete Codex ${plan.action} plan.`;
|
|
4649
|
+
} else {
|
|
4650
|
+
success = false;
|
|
4651
|
+
summary = completion.error ?? "Codex install finalization failed.";
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
3750
4654
|
return {
|
|
3751
4655
|
harness: "codex",
|
|
3752
4656
|
action: plan.action,
|
|
3753
4657
|
applied: success,
|
|
3754
|
-
summary
|
|
3755
|
-
changedTargets
|
|
3756
|
-
...result.changed.map((path) => ({
|
|
3757
|
-
kind: path.endsWith(".json") ? "memory-state" : "generated-artifact",
|
|
3758
|
-
path,
|
|
3759
|
-
label: basename2(path),
|
|
3760
|
-
state: "installed"
|
|
3761
|
-
})),
|
|
3762
|
-
...requiredSkillTargets
|
|
3763
|
-
],
|
|
4658
|
+
summary,
|
|
4659
|
+
changedTargets,
|
|
3764
4660
|
backups: result.changed.filter((path) => existsSync8(`${path}.bak`)).map((path) => ({ path: `${path}.bak`, label: "managed backup" })),
|
|
3765
|
-
warnings
|
|
3766
|
-
...requiredSkillWarnings,
|
|
3767
|
-
...result.success ? [] : [
|
|
3768
|
-
{
|
|
3769
|
-
severity: "critical",
|
|
3770
|
-
message: result.error ?? "Codex setup apply failed."
|
|
3771
|
-
}
|
|
3772
|
-
]
|
|
3773
|
-
],
|
|
4661
|
+
warnings,
|
|
3774
4662
|
disclaimers: codexDisclaimers()
|
|
3775
4663
|
};
|
|
3776
4664
|
}
|
|
@@ -3814,13 +4702,13 @@ function resolveOpenCodeEffort(input) {
|
|
|
3814
4702
|
import {
|
|
3815
4703
|
cpSync,
|
|
3816
4704
|
existsSync as existsSync9,
|
|
3817
|
-
mkdirSync as
|
|
4705
|
+
mkdirSync as mkdirSync4,
|
|
3818
4706
|
mkdtempSync,
|
|
3819
|
-
renameSync as
|
|
3820
|
-
rmSync,
|
|
4707
|
+
renameSync as renameSync3,
|
|
4708
|
+
rmSync as rmSync2,
|
|
3821
4709
|
statSync
|
|
3822
4710
|
} from "fs";
|
|
3823
|
-
import { homedir as
|
|
4711
|
+
import { homedir as homedir4 } from "os";
|
|
3824
4712
|
import { dirname as dirname5, join as join4 } from "path";
|
|
3825
4713
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
3826
4714
|
|
|
@@ -3848,7 +4736,7 @@ function defaultPackageRoot() {
|
|
|
3848
4736
|
}
|
|
3849
4737
|
function getOpenCodeOwnedSkillEntries(options = {}) {
|
|
3850
4738
|
const packageRoot = options.packageRoot ?? defaultPackageRoot();
|
|
3851
|
-
const homeDir = options.homeDir ??
|
|
4739
|
+
const homeDir = options.homeDir ?? homedir4();
|
|
3852
4740
|
return THOTH_OWNED_SKILL_NAMES.map((name) => ({
|
|
3853
4741
|
name,
|
|
3854
4742
|
sourcePath: join4(packageRoot, "skills", name),
|
|
@@ -3870,12 +4758,12 @@ function installOwnedSkills(skills) {
|
|
|
3870
4758
|
const firstSkill = skills[0];
|
|
3871
4759
|
if (!firstSkill) return;
|
|
3872
4760
|
const globalRoot = dirname5(firstSkill.destinationPath);
|
|
3873
|
-
|
|
4761
|
+
mkdirSync4(globalRoot, { recursive: true });
|
|
3874
4762
|
const transactionRoot = mkdtempSync(join4(globalRoot, ".thoth-agents-owned-"));
|
|
3875
4763
|
const stagedRoot = join4(transactionRoot, "staged");
|
|
3876
4764
|
const backupRoot = join4(transactionRoot, "backup");
|
|
3877
|
-
|
|
3878
|
-
|
|
4765
|
+
mkdirSync4(stagedRoot, { recursive: true });
|
|
4766
|
+
mkdirSync4(backupRoot, { recursive: true });
|
|
3879
4767
|
try {
|
|
3880
4768
|
for (const skill of skills) {
|
|
3881
4769
|
cpSync(skill.sourcePath, join4(stagedRoot, skill.name), {
|
|
@@ -3888,25 +4776,25 @@ function installOwnedSkills(skills) {
|
|
|
3888
4776
|
const backupPath = join4(backupRoot, skill.name);
|
|
3889
4777
|
const hadExistingDestination = existsSync9(skill.destinationPath);
|
|
3890
4778
|
if (hadExistingDestination) {
|
|
3891
|
-
|
|
4779
|
+
renameSync3(skill.destinationPath, backupPath);
|
|
3892
4780
|
}
|
|
3893
4781
|
try {
|
|
3894
|
-
|
|
4782
|
+
renameSync3(stagedPath, skill.destinationPath);
|
|
3895
4783
|
} catch (error) {
|
|
3896
4784
|
if (existsSync9(skill.destinationPath)) {
|
|
3897
|
-
|
|
4785
|
+
rmSync2(skill.destinationPath, { recursive: true, force: true });
|
|
3898
4786
|
}
|
|
3899
4787
|
if (hadExistingDestination && existsSync9(backupPath)) {
|
|
3900
|
-
|
|
4788
|
+
renameSync3(backupPath, skill.destinationPath);
|
|
3901
4789
|
}
|
|
3902
4790
|
throw error;
|
|
3903
4791
|
}
|
|
3904
4792
|
if (existsSync9(backupPath)) {
|
|
3905
|
-
|
|
4793
|
+
rmSync2(backupPath, { recursive: true, force: true });
|
|
3906
4794
|
}
|
|
3907
4795
|
}
|
|
3908
4796
|
} finally {
|
|
3909
|
-
|
|
4797
|
+
rmSync2(transactionRoot, { recursive: true, force: true });
|
|
3910
4798
|
}
|
|
3911
4799
|
}
|
|
3912
4800
|
function syncOpenCodeOwnedSkills(options = {}) {
|
|
@@ -3928,7 +4816,8 @@ function syncOpenCodeOwnedSkills(options = {}) {
|
|
|
3928
4816
|
|
|
3929
4817
|
// src/cli/operations/opencode.ts
|
|
3930
4818
|
var PACKAGE_NAME = "thoth-agents";
|
|
3931
|
-
var
|
|
4819
|
+
var EXECUTING_PACKAGE_VERSION = resolveExecutingPackageVersion();
|
|
4820
|
+
var EXPECTED_PLUGIN = EXECUTING_PACKAGE_VERSION.ok ? `${PACKAGE_NAME}@${EXECUTING_PACKAGE_VERSION.version}` : null;
|
|
3932
4821
|
var OPENAI_PRESET = "openai";
|
|
3933
4822
|
var APPLIED_MODELS_PRESET = "agents";
|
|
3934
4823
|
var ROLE_NAMES = [...ALL_AGENT_NAMES];
|
|
@@ -4053,7 +4942,11 @@ function issueOpenCodePlan(plan, status, context2, payload) {
|
|
|
4053
4942
|
if (!planDigest.ok || !planDigest.digest || !statusDigest.ok || !statusDigest.digest) {
|
|
4054
4943
|
return plan;
|
|
4055
4944
|
}
|
|
4056
|
-
const immutablePayload = payload.kind === "fixed" ? Object.freeze({
|
|
4945
|
+
const immutablePayload = payload.kind === "fixed" ? Object.freeze({
|
|
4946
|
+
kind: "fixed",
|
|
4947
|
+
action: payload.action,
|
|
4948
|
+
version: payload.version
|
|
4949
|
+
}) : Object.freeze({
|
|
4057
4950
|
kind: "model",
|
|
4058
4951
|
roles: Object.freeze(
|
|
4059
4952
|
payload.roles.map((role) => Object.freeze({ ...role }))
|
|
@@ -4065,6 +4958,7 @@ function issueOpenCodePlan(plan, status, context2, payload) {
|
|
|
4065
4958
|
harness: "opencode",
|
|
4066
4959
|
action: payload.kind === "model" ? "model-config" : payload.action,
|
|
4067
4960
|
context: immutableContext(context2),
|
|
4961
|
+
runtimeContext: context2,
|
|
4068
4962
|
payload: immutablePayload
|
|
4069
4963
|
});
|
|
4070
4964
|
return plan;
|
|
@@ -4138,7 +5032,7 @@ function targetForMainConfig(state) {
|
|
|
4138
5032
|
path: getExistingConfigPath(),
|
|
4139
5033
|
label: "OpenCode config",
|
|
4140
5034
|
...state ? { state } : {},
|
|
4141
|
-
expected: `plugin includes ${EXPECTED_PLUGIN}`
|
|
5035
|
+
expected: EXPECTED_PLUGIN ? `plugin includes ${EXPECTED_PLUGIN}` : "plugin version unavailable"
|
|
4142
5036
|
};
|
|
4143
5037
|
}
|
|
4144
5038
|
function targetForLiteConfig(state) {
|
|
@@ -4216,7 +5110,7 @@ function configPluginMarker(config) {
|
|
|
4216
5110
|
return plugins.length > 0 ? `plugin: ${JSON.stringify(plugins)}` : "plugin: []";
|
|
4217
5111
|
}
|
|
4218
5112
|
function hasExpectedPlugin(config) {
|
|
4219
|
-
return Array.isArray(config?.plugin) && config.plugin.includes(EXPECTED_PLUGIN);
|
|
5113
|
+
return EXPECTED_PLUGIN !== null && Array.isArray(config?.plugin) && config.plugin.includes(EXPECTED_PLUGIN);
|
|
4220
5114
|
}
|
|
4221
5115
|
function hasManagedPluginDrift(config) {
|
|
4222
5116
|
return Array.isArray(config?.plugin) && config.plugin.some(
|
|
@@ -4356,6 +5250,13 @@ function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
|
4356
5250
|
const lite = parseConfig(litePath);
|
|
4357
5251
|
const diagnostics = [];
|
|
4358
5252
|
const skillStatus = openCodeSkillTargets(context2);
|
|
5253
|
+
if (!EXECUTING_PACKAGE_VERSION.ok) {
|
|
5254
|
+
diagnostics.push({
|
|
5255
|
+
severity: "critical",
|
|
5256
|
+
message: EXECUTING_PACKAGE_VERSION.error.message,
|
|
5257
|
+
code: "opencode-package-version-unresolved"
|
|
5258
|
+
});
|
|
5259
|
+
}
|
|
4359
5260
|
if (main.error) {
|
|
4360
5261
|
diagnostics.push({
|
|
4361
5262
|
severity: "critical",
|
|
@@ -4419,7 +5320,7 @@ function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
|
4419
5320
|
harness: "opencode",
|
|
4420
5321
|
displayName: "OpenCode",
|
|
4421
5322
|
state: "drift",
|
|
4422
|
-
summary: "OpenCode config has a managed thoth-agents plugin entry that
|
|
5323
|
+
summary: "OpenCode config has a managed thoth-agents plugin entry that does not match the executing CLI release.",
|
|
4423
5324
|
targets: [
|
|
4424
5325
|
{ ...mainTarget, state: "drift" },
|
|
4425
5326
|
{
|
|
@@ -4653,8 +5554,13 @@ function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
|
4653
5554
|
};
|
|
4654
5555
|
}
|
|
4655
5556
|
function getOpenCodeStatus(context2 = { cwd: process.cwd() }, evidence = {}) {
|
|
5557
|
+
const managedStatus = getOpenCodeManagedStatus(context2);
|
|
4656
5558
|
return {
|
|
4657
|
-
...
|
|
5559
|
+
...managedStatus,
|
|
5560
|
+
targets: [
|
|
5561
|
+
...managedStatus.targets,
|
|
5562
|
+
getCliManagedInstallVersionTarget("opencode", context2)
|
|
5563
|
+
],
|
|
4658
5564
|
providerCapability: classifyProviderCapabilityEvidence(evidence)
|
|
4659
5565
|
};
|
|
4660
5566
|
}
|
|
@@ -4737,52 +5643,77 @@ function planFromItems(id, action, title, summary, items, context2) {
|
|
|
4737
5643
|
}
|
|
4738
5644
|
};
|
|
4739
5645
|
}
|
|
4740
|
-
function
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
5646
|
+
function packageVersionForContext(context2) {
|
|
5647
|
+
return (context2.resolveExecutingPackageVersion ?? resolveExecutingPackageVersion)();
|
|
5648
|
+
}
|
|
5649
|
+
function ledgerOptionsForContext(context2) {
|
|
5650
|
+
return context2.installLedgerOptions ?? {
|
|
5651
|
+
env: context2.env,
|
|
5652
|
+
homeDir: homeDirFromContext(context2)
|
|
5653
|
+
};
|
|
5654
|
+
}
|
|
5655
|
+
function failedPackageVersionPlan(action, context2, error) {
|
|
5656
|
+
const { plan } = planFromItems(
|
|
5657
|
+
`opencode-${action}-version-unavailable`,
|
|
5658
|
+
action,
|
|
5659
|
+
`${action === "install" ? "Install" : action === "update" ? "Update" : "Sync"} OpenCode`,
|
|
5660
|
+
"The executing package version could not be approved.",
|
|
4747
5661
|
[
|
|
4748
5662
|
{
|
|
4749
|
-
title: "
|
|
4750
|
-
target:
|
|
4751
|
-
state:
|
|
4752
|
-
preview:
|
|
4753
|
-
backup: defaultBackup(path)
|
|
5663
|
+
title: "Resolve exact thoth-agents package version",
|
|
5664
|
+
target: { kind: "package", label: "Executing thoth-agents package" },
|
|
5665
|
+
state: "unknown",
|
|
5666
|
+
preview: error
|
|
4754
5667
|
}
|
|
4755
5668
|
],
|
|
4756
|
-
|
|
5669
|
+
context2
|
|
4757
5670
|
);
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
5671
|
+
plan.canApply = false;
|
|
5672
|
+
plan.warnings.push({
|
|
5673
|
+
severity: "critical",
|
|
5674
|
+
code: "opencode-package-version-unresolved",
|
|
5675
|
+
message: error
|
|
4761
5676
|
});
|
|
5677
|
+
return plan;
|
|
4762
5678
|
}
|
|
4763
|
-
function
|
|
5679
|
+
function buildOpenCodeCompletePlan(action, context2) {
|
|
5680
|
+
const packageVersion = packageVersionForContext(context2);
|
|
5681
|
+
if (!packageVersion.ok) {
|
|
5682
|
+
return failedPackageVersionPlan(
|
|
5683
|
+
action,
|
|
5684
|
+
context2,
|
|
5685
|
+
packageVersion.error.message
|
|
5686
|
+
);
|
|
5687
|
+
}
|
|
5688
|
+
const version = packageVersion.version;
|
|
5689
|
+
const expectedPlugin = `${PACKAGE_NAME}@${version}`;
|
|
4764
5690
|
const generatedConfig = generateLiteConfig({
|
|
4765
5691
|
agent: "opencode",
|
|
4766
5692
|
hasTmux: false,
|
|
4767
5693
|
dryRun: true,
|
|
4768
5694
|
reset: false
|
|
4769
5695
|
});
|
|
4770
|
-
const
|
|
4771
|
-
const ownedSkills =
|
|
5696
|
+
const syncOwned = context2.syncOpenCodeOwnedSkills ?? syncOpenCodeOwnedSkills;
|
|
5697
|
+
const ownedSkills = syncOwned({
|
|
4772
5698
|
dryRun: true,
|
|
4773
5699
|
homeDir: homeDirFromContext(context2)
|
|
4774
5700
|
});
|
|
5701
|
+
const providerCommand = getThothMemSetupCommand("opencode", true);
|
|
5702
|
+
const ledgerPath = getInstallLedgerPath(ledgerOptionsForContext(context2));
|
|
4775
5703
|
const { plan, status } = planFromItems(
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
"
|
|
4779
|
-
|
|
5704
|
+
`opencode-${action}-preview`,
|
|
5705
|
+
action,
|
|
5706
|
+
`${action === "install" ? "Install" : "Update"} complete OpenCode setup`,
|
|
5707
|
+
`Preview complete OpenCode refresh pinned to ${expectedPlugin}.`,
|
|
4780
5708
|
[
|
|
4781
5709
|
{
|
|
4782
|
-
title:
|
|
4783
|
-
target:
|
|
5710
|
+
title: `Ensure OpenCode plugin points at ${expectedPlugin}`,
|
|
5711
|
+
target: {
|
|
5712
|
+
...targetForMainConfig(),
|
|
5713
|
+
expected: `plugin includes ${expectedPlugin}`
|
|
5714
|
+
},
|
|
4784
5715
|
state: getOpenCodeStatus(context2).state,
|
|
4785
|
-
preview: `plugin: ["${
|
|
5716
|
+
preview: `plugin: ["${expectedPlugin}"]`,
|
|
4786
5717
|
backup: defaultBackup(getExistingConfigPath())
|
|
4787
5718
|
},
|
|
4788
5719
|
{
|
|
@@ -4795,7 +5726,7 @@ function buildOpenCodeSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
|
4795
5726
|
title: "Write thoth-agents seven-role config",
|
|
4796
5727
|
target: targetForLiteConfig(),
|
|
4797
5728
|
preview: JSON.stringify(generatedConfig, null, 2),
|
|
4798
|
-
backup: defaultBackup(
|
|
5729
|
+
backup: defaultBackup(getExistingLiteConfigPath())
|
|
4799
5730
|
},
|
|
4800
5731
|
{
|
|
4801
5732
|
title: "Synchronize global thoth-owned OpenCode skills",
|
|
@@ -4821,59 +5752,69 @@ function buildOpenCodeSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
|
4821
5752
|
null,
|
|
4822
5753
|
2
|
|
4823
5754
|
)
|
|
5755
|
+
},
|
|
5756
|
+
{
|
|
5757
|
+
title: "Plan provider-owned thoth-mem setup",
|
|
5758
|
+
target: {
|
|
5759
|
+
kind: "surface",
|
|
5760
|
+
label: "Provider-owned thoth-mem setup"
|
|
5761
|
+
},
|
|
5762
|
+
preview: `${providerCommand.command} ${providerCommand.args.join(" ")}`
|
|
5763
|
+
},
|
|
5764
|
+
{
|
|
5765
|
+
title: "Record completed OpenCode CLI install",
|
|
5766
|
+
target: {
|
|
5767
|
+
kind: "file",
|
|
5768
|
+
path: ledgerPath,
|
|
5769
|
+
label: "CLI-managed install version",
|
|
5770
|
+
expected: `recorded ${version}`
|
|
5771
|
+
},
|
|
5772
|
+
preview: JSON.stringify({ harness: "opencode", version })
|
|
4824
5773
|
}
|
|
4825
5774
|
],
|
|
4826
5775
|
context2
|
|
4827
5776
|
);
|
|
5777
|
+
plan.canApply = canApplyToManagedHealth("install", status);
|
|
4828
5778
|
return issueOpenCodePlan(plan, status, context2, {
|
|
4829
5779
|
kind: "fixed",
|
|
4830
|
-
action
|
|
5780
|
+
action,
|
|
5781
|
+
version
|
|
4831
5782
|
});
|
|
4832
5783
|
}
|
|
4833
|
-
function
|
|
4834
|
-
return
|
|
5784
|
+
function buildOpenCodeUpdatePlan(context2 = { cwd: process.cwd() }) {
|
|
5785
|
+
return buildOpenCodeCompletePlan("update", context2);
|
|
5786
|
+
}
|
|
5787
|
+
function buildOpenCodeSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
5788
|
+
const packageVersion = packageVersionForContext(context2);
|
|
5789
|
+
if (!packageVersion.ok) {
|
|
5790
|
+
return failedPackageVersionPlan(
|
|
5791
|
+
"sync",
|
|
5792
|
+
context2,
|
|
5793
|
+
packageVersion.error.message
|
|
5794
|
+
);
|
|
5795
|
+
}
|
|
5796
|
+
const generatedConfig = generateLiteConfig({
|
|
4835
5797
|
agent: "opencode",
|
|
4836
5798
|
hasTmux: false,
|
|
4837
5799
|
dryRun: true,
|
|
4838
5800
|
reset: false
|
|
4839
|
-
};
|
|
4840
|
-
|
|
4841
|
-
function buildOpenCodeInstallPlan(context2 = { cwd: process.cwd() }) {
|
|
5801
|
+
});
|
|
5802
|
+
const litePath = getExistingLiteConfigPath();
|
|
4842
5803
|
const ownedSkills = syncOpenCodeOwnedSkills({
|
|
4843
5804
|
dryRun: true,
|
|
4844
5805
|
homeDir: homeDirFromContext(context2)
|
|
4845
5806
|
});
|
|
4846
|
-
const generatedConfig = generateLiteConfig(tuiInstallConfig());
|
|
4847
|
-
const installPreview = {
|
|
4848
|
-
noTui: true,
|
|
4849
|
-
hasTmux: false,
|
|
4850
|
-
ownedSkills: ownedSkills.skills.map((skill) => skill.name),
|
|
4851
|
-
requiredSkills: REQUIRED_SKILLS.map((skill) => skill.name),
|
|
4852
|
-
equivalentCommand: "install --agent=opencode --no-tui --tmux=no"
|
|
4853
|
-
};
|
|
4854
|
-
const litePath = getExistingLiteConfigPath();
|
|
4855
5807
|
const { plan, status } = planFromItems(
|
|
4856
|
-
"opencode-
|
|
4857
|
-
"
|
|
4858
|
-
"
|
|
4859
|
-
"Preview OpenCode
|
|
5808
|
+
"opencode-sync-preview",
|
|
5809
|
+
"sync",
|
|
5810
|
+
"Sync OpenCode managed configuration",
|
|
5811
|
+
"Preview OpenCode plugin entry, default-agent disablement, and thoth-agents config sync.",
|
|
4860
5812
|
[
|
|
4861
5813
|
{
|
|
4862
|
-
title:
|
|
4863
|
-
target: {
|
|
4864
|
-
kind: "config",
|
|
4865
|
-
path: getExistingConfigPath(),
|
|
4866
|
-
label: "OpenCode install options",
|
|
4867
|
-
state: getOpenCodeStatus(context2).state,
|
|
4868
|
-
expected: "--no-tui --tmux=no plus required global skills"
|
|
4869
|
-
},
|
|
4870
|
-
preview: JSON.stringify(installPreview, null, 2)
|
|
4871
|
-
},
|
|
4872
|
-
{
|
|
4873
|
-
title: "Ensure OpenCode plugin points at thoth-agents@latest",
|
|
5814
|
+
title: `Ensure OpenCode plugin points at ${PACKAGE_NAME}@${packageVersion.version}`,
|
|
4874
5815
|
target: targetForMainConfig(),
|
|
4875
5816
|
state: getOpenCodeStatus(context2).state,
|
|
4876
|
-
preview: `plugin: ["${
|
|
5817
|
+
preview: `plugin: ["${PACKAGE_NAME}@${packageVersion.version}"]`,
|
|
4877
5818
|
backup: defaultBackup(getExistingConfigPath())
|
|
4878
5819
|
},
|
|
4879
5820
|
{
|
|
@@ -4918,9 +5859,13 @@ function buildOpenCodeInstallPlan(context2 = { cwd: process.cwd() }) {
|
|
|
4918
5859
|
);
|
|
4919
5860
|
return issueOpenCodePlan(plan, status, context2, {
|
|
4920
5861
|
kind: "fixed",
|
|
4921
|
-
action: "
|
|
5862
|
+
action: "sync",
|
|
5863
|
+
version: packageVersion.version
|
|
4922
5864
|
});
|
|
4923
5865
|
}
|
|
5866
|
+
function buildOpenCodeInstallPlan(context2 = { cwd: process.cwd() }) {
|
|
5867
|
+
return buildOpenCodeCompletePlan("install", context2);
|
|
5868
|
+
}
|
|
4924
5869
|
function normalizeRoleModel(input) {
|
|
4925
5870
|
if (input.provider && !input.model.includes("/")) {
|
|
4926
5871
|
return `${input.provider}/${input.model}`;
|
|
@@ -4974,7 +5919,7 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
4974
5919
|
plan.warnings.push(...effortWarnings);
|
|
4975
5920
|
}
|
|
4976
5921
|
if (input.harness !== "opencode") {
|
|
4977
|
-
|
|
5922
|
+
return {
|
|
4978
5923
|
...plan,
|
|
4979
5924
|
canApply: false,
|
|
4980
5925
|
warnings: [
|
|
@@ -4986,13 +5931,12 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
4986
5931
|
}
|
|
4987
5932
|
]
|
|
4988
5933
|
};
|
|
4989
|
-
return rejectedPlan;
|
|
4990
5934
|
}
|
|
4991
5935
|
const unsupportedRoles = input.roles.filter(
|
|
4992
5936
|
(role) => !ROLE_NAMES.includes(role.role)
|
|
4993
5937
|
);
|
|
4994
5938
|
if (input.roles.length === 0 || unsupportedRoles.length > 0) {
|
|
4995
|
-
|
|
5939
|
+
return {
|
|
4996
5940
|
...plan,
|
|
4997
5941
|
canApply: false,
|
|
4998
5942
|
warnings: [
|
|
@@ -5004,7 +5948,6 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
5004
5948
|
}
|
|
5005
5949
|
]
|
|
5006
5950
|
};
|
|
5007
|
-
return rejectedPlan;
|
|
5008
5951
|
}
|
|
5009
5952
|
if (effortWarnings.length > 0) {
|
|
5010
5953
|
return plan;
|
|
@@ -5261,9 +6204,10 @@ function applyModelPlan(roles) {
|
|
|
5261
6204
|
function applyRequiredSkills(context2) {
|
|
5262
6205
|
const warnings = [];
|
|
5263
6206
|
const homeDir = homeDirFromContext(context2);
|
|
6207
|
+
const installSkill = context2.installRequiredSkill ?? installRequiredSkill;
|
|
5264
6208
|
for (const skill of REQUIRED_SKILLS) {
|
|
5265
6209
|
try {
|
|
5266
|
-
const result =
|
|
6210
|
+
const result = installSkill(skill, "opencode", { homeDir });
|
|
5267
6211
|
if (result.status !== "failed") continue;
|
|
5268
6212
|
warnings.push({
|
|
5269
6213
|
severity: "critical",
|
|
@@ -5282,7 +6226,8 @@ function applyRequiredSkills(context2) {
|
|
|
5282
6226
|
return { success: warnings.length === 0, warnings };
|
|
5283
6227
|
}
|
|
5284
6228
|
function applyOwnedSkills(context2) {
|
|
5285
|
-
const
|
|
6229
|
+
const syncOwned = context2.syncOpenCodeOwnedSkills ?? syncOpenCodeOwnedSkills;
|
|
6230
|
+
const result = syncOwned({
|
|
5286
6231
|
homeDir: homeDirFromContext(context2)
|
|
5287
6232
|
});
|
|
5288
6233
|
if (result.success) return { success: true, warnings: [] };
|
|
@@ -5309,8 +6254,32 @@ function applyOpenCodePlan(plan) {
|
|
|
5309
6254
|
"opencode-plan-unissued"
|
|
5310
6255
|
);
|
|
5311
6256
|
}
|
|
6257
|
+
if (issued.payload.kind === "fixed") {
|
|
6258
|
+
const currentPackageVersion = packageVersionForContext(
|
|
6259
|
+
issued.runtimeContext
|
|
6260
|
+
);
|
|
6261
|
+
if (!currentPackageVersion.ok) {
|
|
6262
|
+
return rejectPlan3(
|
|
6263
|
+
plan,
|
|
6264
|
+
currentPackageVersion.error.message,
|
|
6265
|
+
"critical",
|
|
6266
|
+
"opencode-package-version-unresolved",
|
|
6267
|
+
issued.context
|
|
6268
|
+
);
|
|
6269
|
+
}
|
|
6270
|
+
if (currentPackageVersion.version !== issued.payload.version) {
|
|
6271
|
+
return rejectPlan3(
|
|
6272
|
+
plan,
|
|
6273
|
+
`Approved package version changed from ${issued.payload.version} to ${currentPackageVersion.version} before apply.`,
|
|
6274
|
+
"critical",
|
|
6275
|
+
"opencode-package-version-changed",
|
|
6276
|
+
issued.context
|
|
6277
|
+
);
|
|
6278
|
+
}
|
|
6279
|
+
}
|
|
5312
6280
|
const status = getOpenCodeStatus(issued.context);
|
|
5313
|
-
|
|
6281
|
+
const healthAction = issued.payload.kind === "fixed" && (issued.payload.action === "install" || issued.payload.action === "update") ? "install" : plan.action;
|
|
6282
|
+
if (!canApplyToManagedHealth(healthAction, status)) {
|
|
5314
6283
|
return rejectPlan3(
|
|
5315
6284
|
plan,
|
|
5316
6285
|
`OpenCode state is ${status.state}; refusing to apply plan without a safe status.`,
|
|
@@ -5335,9 +6304,11 @@ function applyOpenCodePlan(plan) {
|
|
|
5335
6304
|
const changedTargets = [];
|
|
5336
6305
|
const backups = [];
|
|
5337
6306
|
const warnings = [];
|
|
5338
|
-
const
|
|
6307
|
+
const updateMainConfig = issued.runtimeContext.updateOpenCodeMainConfig ?? updateOpenCodeMainConfig;
|
|
6308
|
+
const mainConfigResult = updateMainConfig({
|
|
5339
6309
|
ensurePlugin: true,
|
|
5340
|
-
|
|
6310
|
+
pluginVersion: issued.payload.version,
|
|
6311
|
+
disableDefaults: true
|
|
5341
6312
|
});
|
|
5342
6313
|
if (!mainConfigResult.success) {
|
|
5343
6314
|
return rejectPlan3(
|
|
@@ -5350,7 +6321,7 @@ function applyOpenCodePlan(plan) {
|
|
|
5350
6321
|
}
|
|
5351
6322
|
changedTargets.push({
|
|
5352
6323
|
...targetForMainConfig("installed"),
|
|
5353
|
-
observed: `plugin includes ${
|
|
6324
|
+
observed: `plugin includes ${PACKAGE_NAME}@${issued.payload.version}`
|
|
5354
6325
|
});
|
|
5355
6326
|
if (existsSync10(`${mainConfigResult.configPath}.bak`)) {
|
|
5356
6327
|
backups.push({
|
|
@@ -5358,68 +6329,95 @@ function applyOpenCodePlan(plan) {
|
|
|
5358
6329
|
label: "OpenCode config backup"
|
|
5359
6330
|
});
|
|
5360
6331
|
}
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
6332
|
+
const writeLite = issued.runtimeContext.writeLiteConfig ?? writeLiteConfig;
|
|
6333
|
+
const liteResult = writeLite(
|
|
6334
|
+
{
|
|
6335
|
+
agent: "opencode",
|
|
6336
|
+
hasTmux: false,
|
|
6337
|
+
dryRun: false,
|
|
6338
|
+
reset: false
|
|
6339
|
+
},
|
|
6340
|
+
getExistingLiteConfigPath()
|
|
6341
|
+
);
|
|
6342
|
+
if (!liteResult.success) {
|
|
6343
|
+
return rejectPlan3(
|
|
6344
|
+
plan,
|
|
6345
|
+
liteResult.error ?? "Failed to write thoth-agents config."
|
|
5370
6346
|
);
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
6347
|
+
}
|
|
6348
|
+
changedTargets.push({
|
|
6349
|
+
...targetForLiteConfig("installed"),
|
|
6350
|
+
observed: "seven-role roster written"
|
|
6351
|
+
});
|
|
6352
|
+
if (existsSync10(`${liteResult.configPath}.bak`)) {
|
|
6353
|
+
backups.push({
|
|
6354
|
+
path: `${liteResult.configPath}.bak`,
|
|
6355
|
+
label: "thoth-agents config backup"
|
|
5380
6356
|
});
|
|
5381
|
-
if (existsSync10(`${liteResult.configPath}.bak`)) {
|
|
5382
|
-
backups.push({
|
|
5383
|
-
path: `${liteResult.configPath}.bak`,
|
|
5384
|
-
label: "thoth-agents config backup"
|
|
5385
|
-
});
|
|
5386
|
-
}
|
|
5387
6357
|
}
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
6358
|
+
const ownedSkills = applyOwnedSkills(issued.runtimeContext);
|
|
6359
|
+
warnings.push(...ownedSkills.warnings);
|
|
6360
|
+
changedTargets.push({
|
|
6361
|
+
kind: "skill",
|
|
6362
|
+
label: "Thoth-owned OpenCode skills",
|
|
6363
|
+
state: ownedSkills.success ? "installed" : "drift",
|
|
6364
|
+
observed: ownedSkills.success ? "thoth-owned global skills synchronized" : "thoth-owned global skill synchronization failed"
|
|
6365
|
+
});
|
|
6366
|
+
if (!ownedSkills.success) {
|
|
6367
|
+
return {
|
|
6368
|
+
harness: "opencode",
|
|
6369
|
+
action: plan.action,
|
|
6370
|
+
applied: false,
|
|
6371
|
+
summary: "OpenCode configuration was written, but thoth-owned skills failed to synchronize.",
|
|
6372
|
+
changedTargets,
|
|
6373
|
+
backups,
|
|
6374
|
+
warnings,
|
|
6375
|
+
disclaimers: defaultDisclaimers()
|
|
6376
|
+
};
|
|
6377
|
+
}
|
|
6378
|
+
const requiredSkills = applyRequiredSkills(issued.runtimeContext);
|
|
6379
|
+
warnings.push(...requiredSkills.warnings);
|
|
6380
|
+
changedTargets.push({
|
|
6381
|
+
kind: "skill",
|
|
6382
|
+
label: "Required OpenCode skills",
|
|
6383
|
+
state: requiredSkills.success ? "installed" : "drift",
|
|
6384
|
+
observed: requiredSkills.success ? "required external skills installed" : "required external skill installation failed"
|
|
6385
|
+
});
|
|
6386
|
+
if (!requiredSkills.success) {
|
|
6387
|
+
return {
|
|
6388
|
+
harness: "opencode",
|
|
6389
|
+
action: plan.action,
|
|
6390
|
+
applied: false,
|
|
6391
|
+
summary: "OpenCode configuration was written, but required skills failed to install.",
|
|
6392
|
+
changedTargets,
|
|
6393
|
+
backups,
|
|
6394
|
+
warnings,
|
|
6395
|
+
disclaimers: defaultDisclaimers()
|
|
6396
|
+
};
|
|
6397
|
+
}
|
|
6398
|
+
if (issued.payload.action === "install" || issued.payload.action === "update") {
|
|
6399
|
+
const finalize = issued.runtimeContext.finalizeHarnessInstall ?? finalizeHarnessInstall;
|
|
6400
|
+
const completion = finalize({
|
|
6401
|
+
harness: "opencode",
|
|
6402
|
+
version: issued.payload.version,
|
|
6403
|
+
dryRun: false,
|
|
6404
|
+
cwd: issued.runtimeContext.cwd,
|
|
6405
|
+
runThothMemSetup: issued.runtimeContext.runThothMemSetup,
|
|
6406
|
+
ledgerOptions: ledgerOptionsForContext(issued.runtimeContext)
|
|
5396
6407
|
});
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
applied: false,
|
|
5402
|
-
summary: "OpenCode configuration was written, but thoth-owned skills failed to synchronize.",
|
|
5403
|
-
changedTargets,
|
|
5404
|
-
backups,
|
|
5405
|
-
warnings,
|
|
5406
|
-
disclaimers: defaultDisclaimers()
|
|
5407
|
-
};
|
|
5408
|
-
}
|
|
5409
|
-
const requiredSkills = applyRequiredSkills(issued.context);
|
|
5410
|
-
warnings.push(...requiredSkills.warnings);
|
|
5411
|
-
changedTargets.push({
|
|
5412
|
-
kind: "skill",
|
|
5413
|
-
label: "Required OpenCode skills",
|
|
5414
|
-
state: requiredSkills.success ? "installed" : "drift",
|
|
5415
|
-
observed: requiredSkills.success ? "required external skills installed" : "required external skill installation failed"
|
|
6408
|
+
const completionEvidence = getInstallCompletionEvidence(completion, {
|
|
6409
|
+
codePrefix: "opencode",
|
|
6410
|
+
version: issued.payload.version,
|
|
6411
|
+
fallbackError: "OpenCode install finalization failed."
|
|
5416
6412
|
});
|
|
5417
|
-
|
|
6413
|
+
warnings.push(...completionEvidence.warnings);
|
|
6414
|
+
changedTargets.push(...completionEvidence.targets);
|
|
6415
|
+
if (!completion.success) {
|
|
5418
6416
|
return {
|
|
5419
6417
|
harness: "opencode",
|
|
5420
6418
|
action: plan.action,
|
|
5421
6419
|
applied: false,
|
|
5422
|
-
summary: "OpenCode
|
|
6420
|
+
summary: completion.error ?? "OpenCode install finalization failed.",
|
|
5423
6421
|
changedTargets,
|
|
5424
6422
|
backups,
|
|
5425
6423
|
warnings,
|
|
@@ -5431,7 +6429,7 @@ function applyOpenCodePlan(plan) {
|
|
|
5431
6429
|
harness: "opencode",
|
|
5432
6430
|
action: plan.action,
|
|
5433
6431
|
applied: true,
|
|
5434
|
-
summary: plan.action === "install" ? "Applied OpenCode install plan." : plan.action === "sync" ? "Applied OpenCode managed configuration sync." : "Applied OpenCode
|
|
6432
|
+
summary: plan.action === "install" ? "Applied OpenCode install plan." : plan.action === "sync" ? "Applied OpenCode managed configuration sync." : "Applied complete OpenCode update plan.",
|
|
5435
6433
|
changedTargets,
|
|
5436
6434
|
backups,
|
|
5437
6435
|
warnings,
|
|
@@ -5463,13 +6461,13 @@ import { execFileSync } from "child_process";
|
|
|
5463
6461
|
// src/cli/model-catalog/cache.ts
|
|
5464
6462
|
import {
|
|
5465
6463
|
existsSync as existsSync11,
|
|
5466
|
-
mkdirSync as
|
|
5467
|
-
readFileSync as
|
|
5468
|
-
renameSync as
|
|
6464
|
+
mkdirSync as mkdirSync5,
|
|
6465
|
+
readFileSync as readFileSync7,
|
|
6466
|
+
renameSync as renameSync4,
|
|
5469
6467
|
unlinkSync,
|
|
5470
|
-
writeFileSync as
|
|
6468
|
+
writeFileSync as writeFileSync4
|
|
5471
6469
|
} from "fs";
|
|
5472
|
-
import { homedir as
|
|
6470
|
+
import { homedir as homedir5 } from "os";
|
|
5473
6471
|
import { dirname as dirname6, posix, win32 } from "path";
|
|
5474
6472
|
import { z as z2 } from "zod";
|
|
5475
6473
|
var modelCatalogSourceSchema = z2.enum(["remote", "lkg", "manual"]);
|
|
@@ -5490,7 +6488,7 @@ var modelsDevCacheSchema = z2.object({
|
|
|
5490
6488
|
function resolveModelsDevCachePath(options = {}) {
|
|
5491
6489
|
const platform = options.platform ?? process.platform;
|
|
5492
6490
|
const env = options.env ?? process.env;
|
|
5493
|
-
const home = options.homeDir ??
|
|
6491
|
+
const home = options.homeDir ?? homedir5();
|
|
5494
6492
|
if (platform === "win32") {
|
|
5495
6493
|
const path = win32;
|
|
5496
6494
|
const base2 = env.LOCALAPPDATA ?? env.APPDATA ?? path.join(home, "AppData", "Local");
|
|
@@ -5502,7 +6500,7 @@ function resolveModelsDevCachePath(options = {}) {
|
|
|
5502
6500
|
function readModelsDevCache(cachePath) {
|
|
5503
6501
|
if (!existsSync11(cachePath)) return void 0;
|
|
5504
6502
|
try {
|
|
5505
|
-
const input = JSON.parse(
|
|
6503
|
+
const input = JSON.parse(readFileSync7(cachePath, "utf8"));
|
|
5506
6504
|
const parsed = modelsDevCacheSchema.safeParse(input);
|
|
5507
6505
|
return parsed.success ? parsed.data : void 0;
|
|
5508
6506
|
} catch {
|
|
@@ -5514,15 +6512,15 @@ function writeModelsDevCacheAtomic(cachePath, cache) {
|
|
|
5514
6512
|
if (!validated.success) {
|
|
5515
6513
|
throw new Error(`Invalid models.dev cache: ${validated.error.message}`);
|
|
5516
6514
|
}
|
|
5517
|
-
|
|
6515
|
+
mkdirSync5(dirname6(cachePath), { recursive: true });
|
|
5518
6516
|
const temporaryPath = `${cachePath}.${process.pid}.${Date.now()}.tmp`;
|
|
5519
6517
|
try {
|
|
5520
|
-
|
|
6518
|
+
writeFileSync4(
|
|
5521
6519
|
temporaryPath,
|
|
5522
6520
|
`${JSON.stringify(validated.data, null, 2)}
|
|
5523
6521
|
`
|
|
5524
6522
|
);
|
|
5525
|
-
|
|
6523
|
+
renameSync4(temporaryPath, cachePath);
|
|
5526
6524
|
} catch (error) {
|
|
5527
6525
|
if (existsSync11(temporaryPath)) unlinkSync(temporaryPath);
|
|
5528
6526
|
throw error;
|
|
@@ -5837,7 +6835,7 @@ async function getModelOptions(harness) {
|
|
|
5837
6835
|
}
|
|
5838
6836
|
|
|
5839
6837
|
// src/cli/tui/operations.ts
|
|
5840
|
-
import { existsSync as existsSync12, readFileSync as
|
|
6838
|
+
import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
|
|
5841
6839
|
var context = { cwd: process.cwd() };
|
|
5842
6840
|
var codexContext = { cwd: process.cwd() };
|
|
5843
6841
|
var claudeCodeContext = {
|
|
@@ -5879,7 +6877,7 @@ function getCodexModelRoles(source = codexContext) {
|
|
|
5879
6877
|
const item = plan.items.find(
|
|
5880
6878
|
(candidate) => candidate.action === "write-role-toml" && candidate.role === role
|
|
5881
6879
|
);
|
|
5882
|
-
const content = item && existsSync12(item.targetPath) ?
|
|
6880
|
+
const content = item && existsSync12(item.targetPath) ? readFileSync8(item.targetPath, "utf8") : item?.content;
|
|
5883
6881
|
const effort = content ? parseRoleTomlEffort(content) : void 0;
|
|
5884
6882
|
return {
|
|
5885
6883
|
role,
|
|
@@ -6002,8 +7000,11 @@ export {
|
|
|
6002
7000
|
buildClaudeCodeSetupPlan,
|
|
6003
7001
|
applyClaudeCodeSetup,
|
|
6004
7002
|
formatClaudeCodeSetupPlan,
|
|
7003
|
+
buildCodexPluginSetupPlan,
|
|
7004
|
+
applyCodexPluginSetup,
|
|
7005
|
+
formatCodexPluginSetupPlan,
|
|
7006
|
+
finalizeHarnessInstall,
|
|
6005
7007
|
syncOpenCodeOwnedSkills,
|
|
6006
|
-
getNpxCommand,
|
|
6007
7008
|
REQUIRED_SKILLS,
|
|
6008
7009
|
getRequiredSkillInstallCommand,
|
|
6009
7010
|
installRequiredSkill,
|