thoth-agents 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -5
- package/dist/agents/agent-config.d.ts +13 -0
- package/dist/agents/index.d.ts +1 -1
- package/dist/agents/orchestrator.d.ts +1 -1
- package/dist/{chunk-I5TXGNSG.js → chunk-5MYXVRZZ.js} +634 -366
- package/dist/chunk-6O4GKDS7.js +207 -0
- package/dist/{chunk-RWQPMWEM.js → chunk-JLU3FSBT.js} +17 -2
- package/dist/chunk-MIDGZE34.js +107 -0
- package/dist/{chunk-HACUJFKI.js → chunk-NOLDFT7O.js} +4 -230
- package/dist/{chunk-5WPP6IY7.js → chunk-VVYAQTYE.js} +2 -2
- package/dist/{chunk-DNPKVFOB.js → chunk-YCWGTDFE.js} +604 -315
- package/dist/cli/config-io.d.ts +2 -2
- package/dist/cli/index.js +94 -41
- package/dist/cli/install.d.ts +1 -0
- package/dist/cli/opencode-profiles.d.ts +10 -0
- package/dist/cli/opencode-skill-consent.d.ts +5 -0
- package/dist/cli/operations/opencode.d.ts +2 -1
- package/dist/cli/operations/types.d.ts +1 -0
- package/dist/cli/owned-skills.d.ts +16 -0
- package/dist/cli/paths.d.ts +1 -1
- package/dist/cli/providers.d.ts +1 -1
- package/dist/cli/system.d.ts +3 -1
- package/dist/cli/thoth-mem-install.d.ts +10 -10
- package/dist/cli/tui/index.js +53 -5
- package/dist/cli/tui/operations.d.ts +2 -0
- package/dist/cli/types.d.ts +2 -4
- package/dist/config/loader.d.ts +2 -5
- package/dist/config/schema.d.ts +0 -53
- package/dist/harness/adapters/opencode.d.ts +1 -1
- package/dist/hooks/delegate-task-retry/hook.d.ts +8 -8
- package/dist/hooks/foreground-fallback/index.d.ts +14 -69
- package/dist/hooks/index.d.ts +0 -2
- package/dist/hooks/json-error-recovery/hook.d.ts +2 -15
- package/dist/hooks/oracle-gate-verdict/hook.d.ts +4 -11
- package/dist/index.d.ts +7 -4
- package/dist/index.js +1603 -1408
- package/dist/opencode/agents.d.ts +3 -0
- package/dist/opencode/command.d.ts +3 -0
- package/dist/opencode/context.d.ts +36 -0
- package/dist/opencode/delegation-hooks.d.ts +3 -0
- package/dist/opencode/mcp.d.ts +3 -0
- package/dist/opencode/permission-rules.d.ts +9 -0
- package/dist/opencode/permissions.d.ts +3 -0
- package/dist/opencode/profile-rpc-handlers.d.ts +3 -0
- package/dist/opencode/profile-rpc.d.ts +688 -0
- package/dist/opencode/profile-runtime.d.ts +3 -0
- package/dist/opencode/profile-sessions.d.ts +18 -0
- package/dist/opencode/profiles/editor.d.ts +44 -0
- package/dist/opencode/profiles/index.d.ts +18 -0
- package/dist/opencode/profiles/resolver.d.ts +12 -0
- package/dist/opencode/profiles/store.d.ts +63 -0
- package/dist/opencode/profiles/types.d.ts +26 -0
- package/dist/opencode/session-hooks.d.ts +3 -0
- package/dist/opencode/session-state.d.ts +39 -0
- package/dist/opencode/skills.d.ts +3 -0
- package/dist/opencode/tool-overrides/evaluator.d.ts +13 -0
- package/dist/opencode/tool-overrides/index.d.ts +5 -0
- package/dist/opencode/tool-overrides/store.d.ts +38 -0
- package/dist/opencode/tool-overrides/types.d.ts +32 -0
- package/dist/opencode/tool-overrides/validation.d.ts +5 -0
- package/dist/opencode/tool-overrides/wildcard.d.ts +3 -0
- package/dist/opencode/tool-rpc-handlers.d.ts +3 -0
- package/dist/opencode/tool-rpc.d.ts +428 -0
- package/dist/opencode/tools.d.ts +3 -0
- package/dist/pi.js +3 -3
- package/dist/tools/ast-grep/cli.d.ts +1 -0
- package/dist/tools/ast-grep/index.d.ts +108 -2
- package/dist/tools/ast-grep/tools.d.ts +107 -3
- package/dist/tools/define-tool.d.ts +23 -0
- package/dist/tools/lsp/tools.d.ts +97 -5
- package/dist/tui/fixtures.d.ts +2 -0
- package/dist/tui/flow.d.ts +16 -0
- package/dist/tui/index.d.ts +7 -0
- package/dist/tui/index.js +1384 -0
- package/dist/tui/model.d.ts +84 -0
- package/dist/tui/tools/fixtures.d.ts +3 -0
- package/dist/tui/tools/flow.d.ts +11 -0
- package/dist/tui/tools/model.d.ts +97 -0
- package/dist/utils/index.d.ts +0 -1
- package/package.json +18 -3
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/skills/thoth-sdd/SKILL.md +8 -2
- package/plugin/skills/thoth-sdd/scripts/validate-result.mjs +28 -2
- package/skills/thoth-sdd/SKILL.md +8 -2
- package/skills/thoth-sdd/scripts/validate-result.mjs +28 -2
- package/thoth-agents.schema.json +0 -96
- package/dist/chunk-62L3ORXN.js +0 -30
- package/dist/delegation/project-id.d.ts +0 -10
- package/dist/hooks/auto-update-checker/checker.d.ts +0 -23
- package/dist/hooks/auto-update-checker/constants.d.ts +0 -11
- package/dist/hooks/auto-update-checker/index.d.ts +0 -17
- package/dist/hooks/auto-update-checker/types.d.ts +0 -22
- package/dist/utils/tmux-session-manager.d.ts +0 -63
- package/dist/utils/tmux.d.ts +0 -32
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CONTEXT7_MCP_URL,
|
|
3
3
|
GREP_APP_MCP_URL,
|
|
4
|
-
exa
|
|
5
|
-
|
|
4
|
+
exa,
|
|
5
|
+
spawn
|
|
6
|
+
} from "./chunk-MIDGZE34.js";
|
|
6
7
|
import {
|
|
7
8
|
PI_EFFORT_VALUES,
|
|
8
9
|
PI_MANAGED_OWNER,
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
16
17
|
savePiModelConfig,
|
|
17
18
|
syncPiSpecialists,
|
|
18
19
|
writePiManagedText
|
|
19
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-VVYAQTYE.js";
|
|
20
21
|
import {
|
|
21
22
|
ALL_AGENT_NAMES,
|
|
22
23
|
CLAUDE_CODE_PROMPT_DIALECT,
|
|
@@ -25,30 +26,38 @@ import {
|
|
|
25
26
|
CONFIRMED_OPENAI_SUBAGENT_PRESET,
|
|
26
27
|
DEFAULT_MODELS,
|
|
27
28
|
LEGACY_OPENAI_PRESET,
|
|
29
|
+
MODEL_MAPPINGS,
|
|
28
30
|
PI_SPECIALIST_ROLES,
|
|
29
31
|
SUBAGENT_NAMES,
|
|
32
|
+
activeOpenCodeProfile,
|
|
30
33
|
claudeCodeSubagentType,
|
|
34
|
+
effectiveOpenCodeProfile,
|
|
31
35
|
ensureConfigDir,
|
|
32
36
|
findPackageRoot,
|
|
33
37
|
generateLiteConfig,
|
|
34
38
|
getAgentOverride,
|
|
35
39
|
getAgentPackContract,
|
|
40
|
+
getConfigDir,
|
|
36
41
|
getDefaultOpenCodeModel,
|
|
37
42
|
getDefaultOpenCodeVariant,
|
|
38
43
|
getExistingConfigPath,
|
|
39
44
|
getExistingLiteConfigPath,
|
|
40
45
|
getOpenCodeManagedModelStatePath,
|
|
41
46
|
getPrimaryModelId,
|
|
47
|
+
isManagedOpenCodePlugin,
|
|
42
48
|
isPiSpecialistRole,
|
|
43
49
|
isValidPackageVersion,
|
|
50
|
+
openCodeProfileStore,
|
|
44
51
|
parseConfig,
|
|
45
52
|
piSpecialistName,
|
|
53
|
+
pluginEntries,
|
|
46
54
|
renderAgentRoutingDescription,
|
|
47
55
|
resolveExecutingPackageVersion,
|
|
48
56
|
updateOpenCodeMainConfig,
|
|
57
|
+
withoutLegacyPresets,
|
|
49
58
|
writeConfig,
|
|
50
59
|
writeLiteConfig
|
|
51
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-YCWGTDFE.js";
|
|
52
61
|
|
|
53
62
|
// src/cli/model-effort.ts
|
|
54
63
|
function normalizeEffortSelection(value) {
|
|
@@ -4387,47 +4396,35 @@ function recordCompletedInstall(options) {
|
|
|
4387
4396
|
|
|
4388
4397
|
// src/cli/thoth-mem-install.ts
|
|
4389
4398
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
4390
|
-
|
|
4391
|
-
// src/cli/npx-command.ts
|
|
4392
|
-
function getNpxCommand(args, options = {}) {
|
|
4393
|
-
if ((options.platform ?? process.platform) !== "win32") {
|
|
4394
|
-
return { command: "npx", args: [...args] };
|
|
4395
|
-
}
|
|
4396
|
-
return {
|
|
4397
|
-
command: options.commandShell ?? process.env.ComSpec ?? "cmd.exe",
|
|
4398
|
-
args: ["/d", "/s", "/c", ["npx", ...args].join(" ")]
|
|
4399
|
-
};
|
|
4400
|
-
}
|
|
4401
|
-
|
|
4402
|
-
// src/cli/thoth-mem-install.ts
|
|
4399
|
+
var THOTH_MEM_PACKAGE_SPEC = "thoth-mem@>=0.7.0";
|
|
4403
4400
|
var THOTH_MEM_SETUP_TIMEOUT_MS = 12e4;
|
|
4404
4401
|
var STATUS_EXIT_CODE = {
|
|
4402
|
+
planned: 0,
|
|
4405
4403
|
complete: 0,
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
requires_user_action: 3
|
|
4404
|
+
unsupported: 1,
|
|
4405
|
+
"requires-user-action": 1
|
|
4409
4406
|
};
|
|
4410
|
-
var SETUP_STATUSES = new Set(
|
|
4411
|
-
Object.keys(STATUS_EXIT_CODE)
|
|
4412
|
-
);
|
|
4413
|
-
var STEP_OUTCOMES = /* @__PURE__ */ new Set([
|
|
4414
|
-
"planned",
|
|
4415
|
-
"skipped",
|
|
4416
|
-
"confirmed",
|
|
4417
|
-
"failed",
|
|
4418
|
-
"unavailable"
|
|
4419
|
-
]);
|
|
4420
4407
|
function isRecord6(value) {
|
|
4421
4408
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4422
4409
|
}
|
|
4423
4410
|
function isStringArray(value) {
|
|
4424
4411
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
4425
4412
|
}
|
|
4426
|
-
function isSetupStep(value) {
|
|
4427
|
-
return isRecord6(value) && typeof value.name === "string" && value.name.length > 0 && typeof value.outcome === "string" && STEP_OUTCOMES.has(value.outcome);
|
|
4428
|
-
}
|
|
4429
4413
|
function isProviderSetupResult(value, expectedHarness) {
|
|
4430
|
-
|
|
4414
|
+
if (!isRecord6(value) || typeof value.status !== "string" || !Object.hasOwn(STATUS_EXIT_CODE, value.status) || typeof value.changed !== "boolean" || typeof value.recovered !== "boolean" || value.host !== void 0 && value.host !== expectedHarness || !isStringArray(value.actions) || !isStringArray(value.warnings) || !(value.receiptPath === null || typeof value.receiptPath === "string") || value.diagnostics !== void 0 && !isStringArray(value.diagnostics) || value.restartRequired !== void 0 && typeof value.restartRequired !== "boolean") {
|
|
4415
|
+
return false;
|
|
4416
|
+
}
|
|
4417
|
+
if (expectedHarness === "opencode") {
|
|
4418
|
+
return typeof value.plugin === "string" && typeof value.opencodeVersion === "string" && typeof value.configPath === "string" && typeof value.skillPath === "string" && typeof value.providerConfigPath === "string";
|
|
4419
|
+
}
|
|
4420
|
+
if (value.host !== expectedHarness || typeof value.source !== "string" || typeof value.version !== "string" || !isRecord6(value.verification)) {
|
|
4421
|
+
return false;
|
|
4422
|
+
}
|
|
4423
|
+
const verification = value.verification;
|
|
4424
|
+
if (expectedHarness === "pi") {
|
|
4425
|
+
return typeof value.piVersion === "string" && typeof verification.package === "boolean" && typeof verification.source === "boolean" && typeof verification.manifest === "boolean";
|
|
4426
|
+
}
|
|
4427
|
+
return (value.strategy === "plugin_manager" || value.strategy === "unsupported") && isStringArray(value.diagnostics) && typeof value.restartRequired === "boolean" && typeof verification.marketplace === "boolean" && typeof verification.plugin === "boolean" && typeof verification.enabled === "boolean" && (verification.modelUse === void 0 || verification.modelUse === false);
|
|
4431
4428
|
}
|
|
4432
4429
|
function parseProviderSetupResult(stdout, expectedHarness) {
|
|
4433
4430
|
let value;
|
|
@@ -4438,9 +4435,10 @@ function parseProviderSetupResult(stdout, expectedHarness) {
|
|
|
4438
4435
|
}
|
|
4439
4436
|
return isProviderSetupResult(value, expectedHarness) ? value : void 0;
|
|
4440
4437
|
}
|
|
4441
|
-
function
|
|
4438
|
+
function executeThothMemCommand(command, args, options) {
|
|
4442
4439
|
const result = spawnSync3(command, [...args], {
|
|
4443
4440
|
windowsHide: true,
|
|
4441
|
+
windowsVerbatimArguments: process.platform === "win32",
|
|
4444
4442
|
cwd: options.cwd,
|
|
4445
4443
|
encoding: "utf8",
|
|
4446
4444
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -4466,9 +4464,9 @@ function invalidResult(harness, command, args, exitCode, error) {
|
|
|
4466
4464
|
status: "invalid",
|
|
4467
4465
|
changed: false,
|
|
4468
4466
|
harness,
|
|
4469
|
-
|
|
4467
|
+
actions: [],
|
|
4468
|
+
warnings: [],
|
|
4470
4469
|
diagnostics: [],
|
|
4471
|
-
manualActions: [],
|
|
4472
4470
|
receipt: null,
|
|
4473
4471
|
command,
|
|
4474
4472
|
args,
|
|
@@ -4477,24 +4475,32 @@ function invalidResult(harness, command, args, exitCode, error) {
|
|
|
4477
4475
|
};
|
|
4478
4476
|
}
|
|
4479
4477
|
function getThothMemSetupCommand(harness, dryRun, options = {}) {
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4478
|
+
const args = [
|
|
4479
|
+
"-y",
|
|
4480
|
+
THOTH_MEM_PACKAGE_SPEC,
|
|
4481
|
+
"setup",
|
|
4482
|
+
harness,
|
|
4483
|
+
...dryRun ? ["--plan"] : [],
|
|
4484
|
+
"--json"
|
|
4485
|
+
];
|
|
4486
|
+
if ((options.platform ?? process.platform) !== "win32") {
|
|
4487
|
+
return { command: "npx", args };
|
|
4488
|
+
}
|
|
4489
|
+
return {
|
|
4490
|
+
command: options.commandShell ?? process.env.ComSpec ?? "cmd.exe",
|
|
4491
|
+
// Node must not escape this cmd command line: quoted > is a literal,
|
|
4492
|
+
// not redirection, and npx must receive the selector without quote characters.
|
|
4493
|
+
args: [
|
|
4494
|
+
`/d /s /c "npx ${args.map((arg) => arg === THOTH_MEM_PACKAGE_SPEC ? `"${arg}"` : arg).join(" ")}"`
|
|
4495
|
+
]
|
|
4496
|
+
};
|
|
4491
4497
|
}
|
|
4492
4498
|
function runThothMemSetup(options) {
|
|
4493
4499
|
const { command, args } = getThothMemSetupCommand(
|
|
4494
4500
|
options.harness,
|
|
4495
4501
|
options.dryRun ?? false
|
|
4496
4502
|
);
|
|
4497
|
-
const executor = options.commandExecutor ??
|
|
4503
|
+
const executor = options.commandExecutor ?? executeThothMemCommand;
|
|
4498
4504
|
let commandResult;
|
|
4499
4505
|
try {
|
|
4500
4506
|
commandResult = executor(command, args, {
|
|
@@ -4531,34 +4537,37 @@ function runThothMemSetup(options) {
|
|
|
4531
4537
|
command,
|
|
4532
4538
|
args,
|
|
4533
4539
|
commandResult.exitCode,
|
|
4534
|
-
stderr ? `thoth-mem returned invalid JSON evidence: ${stderr.slice(0, 2e3)}` : "thoth-mem returned invalid JSON evidence."
|
|
4540
|
+
stderr ? `thoth-mem setup failed or returned invalid JSON evidence: ${stderr.slice(0, 2e3)}` : "thoth-mem returned invalid JSON evidence."
|
|
4535
4541
|
);
|
|
4536
4542
|
}
|
|
4537
4543
|
const expectedExitCode = STATUS_EXIT_CODE[provider.status];
|
|
4538
|
-
const
|
|
4544
|
+
const expectedStatus = options.dryRun ? "planned" : "complete";
|
|
4545
|
+
const exitConsistent = commandResult.exitCode === expectedExitCode;
|
|
4546
|
+
const modeConsistent = expectedExitCode !== 0 || provider.status === expectedStatus;
|
|
4547
|
+
const evidenceValid = exitConsistent && modeConsistent;
|
|
4539
4548
|
return {
|
|
4540
|
-
success: evidenceValid && provider.status ===
|
|
4549
|
+
success: evidenceValid && provider.status === expectedStatus,
|
|
4541
4550
|
evidenceValid,
|
|
4542
4551
|
status: provider.status,
|
|
4543
4552
|
changed: provider.changed,
|
|
4544
|
-
harness:
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
diagnostics: provider.diagnostics,
|
|
4548
|
-
|
|
4549
|
-
receipt: provider.
|
|
4553
|
+
harness: options.harness,
|
|
4554
|
+
actions: provider.actions,
|
|
4555
|
+
warnings: provider.warnings,
|
|
4556
|
+
diagnostics: provider.diagnostics ?? [],
|
|
4557
|
+
restartRequired: provider.restartRequired,
|
|
4558
|
+
receipt: provider.receiptPath,
|
|
4550
4559
|
command,
|
|
4551
4560
|
args,
|
|
4552
4561
|
exitCode: commandResult.exitCode,
|
|
4553
4562
|
...!evidenceValid ? {
|
|
4554
|
-
error: `thoth-mem exit code ${String(commandResult.exitCode)} contradicts reported status ${provider.status} (expected ${expectedExitCode}).`
|
|
4563
|
+
error: !exitConsistent ? `thoth-mem exit code ${String(commandResult.exitCode)} contradicts reported status ${provider.status} (expected ${expectedExitCode}).` : `thoth-mem reported ${provider.status}; expected ${expectedStatus} for this setup mode.`
|
|
4555
4564
|
} : {}
|
|
4556
4565
|
};
|
|
4557
4566
|
}
|
|
4558
4567
|
|
|
4559
4568
|
// src/cli/install-completion.ts
|
|
4560
|
-
function isConsistentProviderSuccess(result, harness) {
|
|
4561
|
-
return result.success && result.evidenceValid && result.status === "complete" && result.exitCode === 0 && result.harness === harness;
|
|
4569
|
+
function isConsistentProviderSuccess(result, harness, dryRun) {
|
|
4570
|
+
return result.success && result.evidenceValid && result.status === (dryRun ? "planned" : "complete") && result.exitCode === 0 && result.harness === harness;
|
|
4562
4571
|
}
|
|
4563
4572
|
function recordHarnessInstallCompletion(options) {
|
|
4564
4573
|
const ledgerPath = getInstallLedgerPath(options.ledgerOptions);
|
|
@@ -4603,12 +4612,12 @@ function finalizeHarnessInstall(options) {
|
|
|
4603
4612
|
dryRun: options.dryRun,
|
|
4604
4613
|
cwd: options.cwd
|
|
4605
4614
|
});
|
|
4606
|
-
if (!isConsistentProviderSuccess(provider, options.harness)) {
|
|
4615
|
+
if (!isConsistentProviderSuccess(provider, options.harness, options.dryRun)) {
|
|
4607
4616
|
return {
|
|
4608
4617
|
success: false,
|
|
4609
4618
|
provider,
|
|
4610
4619
|
ledger: { status: "not-attempted", path: ledgerPath },
|
|
4611
|
-
error: provider.error ?? "thoth-mem setup did not return consistent
|
|
4620
|
+
error: provider.error ?? "thoth-mem setup did not return consistent setup evidence."
|
|
4612
4621
|
};
|
|
4613
4622
|
}
|
|
4614
4623
|
const completion = recordHarnessInstallCompletion({
|
|
@@ -4629,6 +4638,19 @@ import { spawnSync as spawnSync4 } from "child_process";
|
|
|
4629
4638
|
import { existsSync as existsSync6 } from "fs";
|
|
4630
4639
|
import { homedir as homedir3 } from "os";
|
|
4631
4640
|
import { join as join6 } from "path";
|
|
4641
|
+
|
|
4642
|
+
// src/cli/npx-command.ts
|
|
4643
|
+
function getNpxCommand(args, options = {}) {
|
|
4644
|
+
if ((options.platform ?? process.platform) !== "win32") {
|
|
4645
|
+
return { command: "npx", args: [...args] };
|
|
4646
|
+
}
|
|
4647
|
+
return {
|
|
4648
|
+
command: options.commandShell ?? process.env.ComSpec ?? "cmd.exe",
|
|
4649
|
+
args: ["/d", "/s", "/c", ["npx", ...args].join(" ")]
|
|
4650
|
+
};
|
|
4651
|
+
}
|
|
4652
|
+
|
|
4653
|
+
// src/cli/skills.ts
|
|
4632
4654
|
var SKILLS_CLI_AGENT = {
|
|
4633
4655
|
opencode: "opencode",
|
|
4634
4656
|
codex: "codex",
|
|
@@ -4773,12 +4795,26 @@ function getInstallCompletionEvidence(completion, options) {
|
|
|
4773
4795
|
code: `${options.codePrefix}-provider-diagnostic`,
|
|
4774
4796
|
message
|
|
4775
4797
|
})),
|
|
4776
|
-
...completion.provider.
|
|
4798
|
+
...completion.provider.warnings.map((message) => ({
|
|
4777
4799
|
severity: "important",
|
|
4778
|
-
code: `${options.codePrefix}-provider-
|
|
4800
|
+
code: `${options.codePrefix}-provider-warning`,
|
|
4779
4801
|
message
|
|
4780
4802
|
}))
|
|
4781
4803
|
];
|
|
4804
|
+
for (const action of completion.provider.actions) {
|
|
4805
|
+
targets.push({
|
|
4806
|
+
kind: "surface",
|
|
4807
|
+
label: "thoth-mem action",
|
|
4808
|
+
observed: action
|
|
4809
|
+
});
|
|
4810
|
+
}
|
|
4811
|
+
if (completion.provider.restartRequired) {
|
|
4812
|
+
warnings.push({
|
|
4813
|
+
severity: "important",
|
|
4814
|
+
code: `${options.codePrefix}-provider-restart`,
|
|
4815
|
+
message: "thoth-mem: restart required"
|
|
4816
|
+
});
|
|
4817
|
+
}
|
|
4782
4818
|
if (completion.provider.receipt) {
|
|
4783
4819
|
targets.push({
|
|
4784
4820
|
kind: "file",
|
|
@@ -6386,19 +6422,18 @@ function resolveOpenCodeEffort(input) {
|
|
|
6386
6422
|
}
|
|
6387
6423
|
|
|
6388
6424
|
// src/cli/owned-skills.ts
|
|
6425
|
+
import { randomBytes } from "crypto";
|
|
6389
6426
|
import {
|
|
6390
|
-
cpSync,
|
|
6391
6427
|
existsSync as existsSync9,
|
|
6392
6428
|
lstatSync as lstatSync5,
|
|
6393
6429
|
mkdirSync as mkdirSync4,
|
|
6394
|
-
mkdtempSync,
|
|
6395
6430
|
readFileSync as readFileSync8,
|
|
6431
|
+
realpathSync as realpathSync2,
|
|
6396
6432
|
renameSync as renameSync3,
|
|
6397
|
-
rmSync as rmSync4,
|
|
6398
6433
|
statSync
|
|
6399
6434
|
} from "fs";
|
|
6400
6435
|
import { homedir as homedir4 } from "os";
|
|
6401
|
-
import { dirname as dirname6, join as join7 } from "path";
|
|
6436
|
+
import { dirname as dirname6, join as join7, resolve as resolve5, sep as sep2 } from "path";
|
|
6402
6437
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6403
6438
|
|
|
6404
6439
|
// src/harness/core/owned-skills.ts
|
|
@@ -6491,7 +6526,7 @@ function getOpenCodeOwnedSkillEntries(options = {}) {
|
|
|
6491
6526
|
return THOTH_OWNED_SKILL_NAMES.map((name) => ({
|
|
6492
6527
|
name,
|
|
6493
6528
|
sourcePath: join7(packageRoot, "skills", name),
|
|
6494
|
-
destinationPath: join7(homeDir, "
|
|
6529
|
+
destinationPath: join7(getConfigDir(homeDir), "skills", name)
|
|
6495
6530
|
}));
|
|
6496
6531
|
}
|
|
6497
6532
|
function getPiOwnedSkillEntries(options) {
|
|
@@ -6513,56 +6548,126 @@ function validateCanonicalSkill(entry) {
|
|
|
6513
6548
|
throw new Error(`Canonical owned skill contract is missing: ${entry.name}`);
|
|
6514
6549
|
}
|
|
6515
6550
|
}
|
|
6516
|
-
function
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6551
|
+
function getLegacyOpenCodeSkillBackupRoot(homeDir = homedir4()) {
|
|
6552
|
+
return resolve5(
|
|
6553
|
+
process.env.XDG_DATA_HOME || join7(homeDir, ".local", "share"),
|
|
6554
|
+
"thoth-agents",
|
|
6555
|
+
".opencode-legacy-skills"
|
|
6556
|
+
);
|
|
6557
|
+
}
|
|
6558
|
+
function inspectLegacyOpenCodeSkills(homeDir = homedir4()) {
|
|
6559
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6560
|
+
const copies = [];
|
|
6561
|
+
for (const [label, root] of [
|
|
6562
|
+
["resolved", getConfigDir(homeDir)],
|
|
6563
|
+
["legacy", join7(homeDir, ".config", "opencode")]
|
|
6564
|
+
]) {
|
|
6565
|
+
for (const directory of ["skills", "skill"]) {
|
|
6566
|
+
for (const name of THOTH_OWNED_SKILL_NAMES) {
|
|
6567
|
+
const sourcePath = join7(root, directory, name);
|
|
6568
|
+
if (!existsSync9(sourcePath)) continue;
|
|
6569
|
+
const real = realpathSync2(sourcePath);
|
|
6570
|
+
if (seen.has(real)) continue;
|
|
6571
|
+
seen.add(real);
|
|
6572
|
+
copies.push({ name, sourcePath, label: `${label}-${directory}` });
|
|
6573
|
+
}
|
|
6532
6574
|
}
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6575
|
+
}
|
|
6576
|
+
return copies;
|
|
6577
|
+
}
|
|
6578
|
+
function planOrMoveLegacySkills(options) {
|
|
6579
|
+
const backupRoot = getLegacyOpenCodeSkillBackupRoot(options.homeDir);
|
|
6580
|
+
const copies = inspectLegacyOpenCodeSkills(options.homeDir);
|
|
6581
|
+
const notices = [];
|
|
6582
|
+
const legacyMoves = [];
|
|
6583
|
+
if (!copies.length) return { backupRoot, notices, legacyMoves };
|
|
6584
|
+
const confirmed = options.moveLegacyOpenCodeSkills && !options.dryRun;
|
|
6585
|
+
let run = join7(backupRoot, "<new-run>");
|
|
6586
|
+
if (confirmed) {
|
|
6587
|
+
try {
|
|
6588
|
+
if (!resolve5(backupRoot).split(sep2).includes(".opencode-legacy-skills"))
|
|
6589
|
+
throw new Error(
|
|
6590
|
+
"Backup path is missing the required hidden .opencode-legacy-skills segment"
|
|
6591
|
+
);
|
|
6592
|
+
mkdirSync4(backupRoot, { recursive: true });
|
|
6593
|
+
let created = false;
|
|
6594
|
+
for (let attempt = 0; attempt < 10; attempt++) {
|
|
6595
|
+
run = join7(
|
|
6596
|
+
backupRoot,
|
|
6597
|
+
`${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${randomBytes(8).toString("hex")}`
|
|
6598
|
+
);
|
|
6599
|
+
try {
|
|
6600
|
+
mkdirSync4(run, { mode: 448 });
|
|
6601
|
+
created = true;
|
|
6602
|
+
break;
|
|
6603
|
+
} catch (error) {
|
|
6604
|
+
if (error.code !== "EEXIST") throw error;
|
|
6605
|
+
}
|
|
6539
6606
|
}
|
|
6607
|
+
if (!created)
|
|
6608
|
+
throw new Error("Unable to create an exclusive backup run directory");
|
|
6609
|
+
} catch (error) {
|
|
6610
|
+
notices.push(`Legacy skill move refused: ${String(error)}`);
|
|
6611
|
+
notices.push(
|
|
6612
|
+
...copies.map(
|
|
6613
|
+
({ sourcePath }) => legacyOpenCodeSkillNotice(sourcePath)
|
|
6614
|
+
)
|
|
6615
|
+
);
|
|
6616
|
+
return { backupRoot, notices, legacyMoves };
|
|
6617
|
+
}
|
|
6618
|
+
}
|
|
6619
|
+
for (const copy of copies) {
|
|
6620
|
+
const destinationPath = join7(run, copy.label, copy.name);
|
|
6621
|
+
const move = {
|
|
6622
|
+
sourcePath: copy.sourcePath,
|
|
6623
|
+
destinationPath,
|
|
6624
|
+
status: "planned"
|
|
6625
|
+
};
|
|
6626
|
+
legacyMoves.push(move);
|
|
6627
|
+
if (!confirmed) {
|
|
6628
|
+
notices.push(legacyOpenCodeSkillNotice(copy.sourcePath));
|
|
6629
|
+
notices.push(
|
|
6630
|
+
`Planned move: ${copy.sourcePath} -> ${destinationPath} (backup root: ${backupRoot})`
|
|
6631
|
+
);
|
|
6632
|
+
continue;
|
|
6633
|
+
}
|
|
6634
|
+
try {
|
|
6635
|
+
mkdirSync4(dirname6(destinationPath), { recursive: true });
|
|
6540
6636
|
try {
|
|
6541
|
-
|
|
6637
|
+
lstatSync5(destinationPath);
|
|
6638
|
+
throw new Error(
|
|
6639
|
+
`Backup destination already exists: ${destinationPath}`
|
|
6640
|
+
);
|
|
6542
6641
|
} catch (error) {
|
|
6543
|
-
if (
|
|
6544
|
-
rmSync4(skill.destinationPath, { recursive: true, force: true });
|
|
6545
|
-
}
|
|
6546
|
-
if (hadExistingDestination && existsSync9(backupPath)) {
|
|
6547
|
-
renameSync3(backupPath, skill.destinationPath);
|
|
6548
|
-
}
|
|
6549
|
-
throw error;
|
|
6550
|
-
}
|
|
6551
|
-
if (existsSync9(backupPath)) {
|
|
6552
|
-
rmSync4(backupPath, { recursive: true, force: true });
|
|
6642
|
+
if (error.code !== "ENOENT") throw error;
|
|
6553
6643
|
}
|
|
6644
|
+
renameSync3(copy.sourcePath, destinationPath);
|
|
6645
|
+
move.status = "moved";
|
|
6646
|
+
notices.push(
|
|
6647
|
+
`Moved legacy OpenCode skill: ${copy.sourcePath} -> ${destinationPath} (backup: ${run})`
|
|
6648
|
+
);
|
|
6649
|
+
} catch (error) {
|
|
6650
|
+
move.status = "failed";
|
|
6651
|
+
notices.push(
|
|
6652
|
+
`Legacy skill move failed; original left intact: ${copy.sourcePath}: ${String(error)}. ${legacyOpenCodeSkillNotice(copy.sourcePath)}`
|
|
6653
|
+
);
|
|
6554
6654
|
}
|
|
6555
|
-
} finally {
|
|
6556
|
-
rmSync4(transactionRoot, { recursive: true, force: true });
|
|
6557
6655
|
}
|
|
6656
|
+
return { backupRoot, notices, legacyMoves };
|
|
6657
|
+
}
|
|
6658
|
+
function legacyOpenCodeSkillNotice(path) {
|
|
6659
|
+
return `Warning: legacy OpenCode skill copies shadow the packaged skills: ${path}. Confirm the backup move interactively or pass --move-legacy-opencode-skills to install/update.`;
|
|
6558
6660
|
}
|
|
6559
6661
|
function syncOpenCodeOwnedSkills(options = {}) {
|
|
6560
6662
|
const skills = getOpenCodeOwnedSkillEntries(options);
|
|
6561
6663
|
try {
|
|
6562
6664
|
for (const skill of skills) validateCanonicalSkill(skill);
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6665
|
+
return {
|
|
6666
|
+
success: true,
|
|
6667
|
+
status: options.dryRun ? "planned" : "installed",
|
|
6668
|
+
skills,
|
|
6669
|
+
...planOrMoveLegacySkills(options)
|
|
6670
|
+
};
|
|
6566
6671
|
} catch (error) {
|
|
6567
6672
|
return {
|
|
6568
6673
|
success: false,
|
|
@@ -6604,12 +6709,184 @@ function inspectPiPackageSkills(options) {
|
|
|
6604
6709
|
}
|
|
6605
6710
|
}
|
|
6606
6711
|
|
|
6712
|
+
// src/cli/system.ts
|
|
6713
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
6714
|
+
import { statSync as statSync2 } from "fs";
|
|
6715
|
+
var cachedOpenCodePath = null;
|
|
6716
|
+
var OPENCODE_PROBE_TIMEOUT_MS = 1500;
|
|
6717
|
+
function quoteWindowsShellCommand(command) {
|
|
6718
|
+
if (!/[\s&()^<>"|]/.test(command)) {
|
|
6719
|
+
return command;
|
|
6720
|
+
}
|
|
6721
|
+
return `"${command.replace(/"/g, '\\"')}"`;
|
|
6722
|
+
}
|
|
6723
|
+
function getOpenCodeVersionInvocation(opencodePath, platform = process.platform) {
|
|
6724
|
+
const options = {
|
|
6725
|
+
stdout: "pipe",
|
|
6726
|
+
stderr: "pipe"
|
|
6727
|
+
};
|
|
6728
|
+
if (platform === "win32") {
|
|
6729
|
+
return {
|
|
6730
|
+
command: [`${quoteWindowsShellCommand(opencodePath)} --version`],
|
|
6731
|
+
options: { ...options, shell: true }
|
|
6732
|
+
};
|
|
6733
|
+
}
|
|
6734
|
+
return { command: [opencodePath, "--version"], options };
|
|
6735
|
+
}
|
|
6736
|
+
function getOpenCodePaths() {
|
|
6737
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
6738
|
+
return [
|
|
6739
|
+
// PATH (try this first)
|
|
6740
|
+
"opencode",
|
|
6741
|
+
// User local installations (Linux & macOS)
|
|
6742
|
+
`${home}/.local/bin/opencode`,
|
|
6743
|
+
`${home}/.opencode/bin/opencode`,
|
|
6744
|
+
`${home}/bin/opencode`,
|
|
6745
|
+
// System-wide installations
|
|
6746
|
+
"/usr/local/bin/opencode",
|
|
6747
|
+
"/opt/opencode/bin/opencode",
|
|
6748
|
+
"/usr/bin/opencode",
|
|
6749
|
+
"/bin/opencode",
|
|
6750
|
+
// macOS specific
|
|
6751
|
+
"/Applications/OpenCode.app/Contents/MacOS/opencode",
|
|
6752
|
+
`${home}/Applications/OpenCode.app/Contents/MacOS/opencode`,
|
|
6753
|
+
// Homebrew (macOS & Linux)
|
|
6754
|
+
"/opt/homebrew/bin/opencode",
|
|
6755
|
+
"/home/linuxbrew/.linuxbrew/bin/opencode",
|
|
6756
|
+
`${home}/homebrew/bin/opencode`,
|
|
6757
|
+
// macOS user Library
|
|
6758
|
+
`${home}/Library/Application Support/opencode/bin/opencode`,
|
|
6759
|
+
// Snap (Linux)
|
|
6760
|
+
"/snap/bin/opencode",
|
|
6761
|
+
"/var/snap/opencode/current/bin/opencode",
|
|
6762
|
+
// Flatpak (Linux)
|
|
6763
|
+
"/var/lib/flatpak/exports/bin/ai.opencode.OpenCode",
|
|
6764
|
+
`${home}/.local/share/flatpak/exports/bin/ai.opencode.OpenCode`,
|
|
6765
|
+
// Nix (Linux/macOS)
|
|
6766
|
+
"/nix/store/opencode/bin/opencode",
|
|
6767
|
+
`${home}/.nix-profile/bin/opencode`,
|
|
6768
|
+
"/run/current-system/sw/bin/opencode",
|
|
6769
|
+
// Cargo (Rust toolchain)
|
|
6770
|
+
`${home}/.cargo/bin/opencode`,
|
|
6771
|
+
// npm/npx global
|
|
6772
|
+
`${home}/.npm-global/bin/opencode`,
|
|
6773
|
+
"/usr/local/lib/node_modules/opencode/bin/opencode",
|
|
6774
|
+
// Yarn global
|
|
6775
|
+
`${home}/.yarn/bin/opencode`,
|
|
6776
|
+
// PNPM
|
|
6777
|
+
`${home}/.pnpm-global/bin/opencode`
|
|
6778
|
+
];
|
|
6779
|
+
}
|
|
6780
|
+
function resolveOpenCodePath() {
|
|
6781
|
+
if (cachedOpenCodePath) {
|
|
6782
|
+
return cachedOpenCodePath;
|
|
6783
|
+
}
|
|
6784
|
+
const paths = getOpenCodePaths();
|
|
6785
|
+
for (const opencodePath of paths) {
|
|
6786
|
+
if (opencodePath === "opencode") continue;
|
|
6787
|
+
try {
|
|
6788
|
+
const stat = statSync2(opencodePath);
|
|
6789
|
+
if (stat.isFile()) {
|
|
6790
|
+
cachedOpenCodePath = opencodePath;
|
|
6791
|
+
return opencodePath;
|
|
6792
|
+
}
|
|
6793
|
+
} catch {
|
|
6794
|
+
}
|
|
6795
|
+
}
|
|
6796
|
+
return "opencode";
|
|
6797
|
+
}
|
|
6798
|
+
async function isOpenCodeInstalled() {
|
|
6799
|
+
const paths = getOpenCodePaths();
|
|
6800
|
+
for (const opencodePath of paths) {
|
|
6801
|
+
try {
|
|
6802
|
+
const invocation = getOpenCodeVersionInvocation(opencodePath);
|
|
6803
|
+
const proc = spawn(invocation.command, {
|
|
6804
|
+
...invocation.options,
|
|
6805
|
+
windowsHide: true
|
|
6806
|
+
});
|
|
6807
|
+
let timer;
|
|
6808
|
+
const timedOut = /* @__PURE__ */ Symbol("timeout");
|
|
6809
|
+
try {
|
|
6810
|
+
const outcome = await Promise.race([
|
|
6811
|
+
proc.exited.then(() => proc.exitCode),
|
|
6812
|
+
new Promise((resolve11) => {
|
|
6813
|
+
timer = setTimeout(
|
|
6814
|
+
() => resolve11(timedOut),
|
|
6815
|
+
OPENCODE_PROBE_TIMEOUT_MS
|
|
6816
|
+
);
|
|
6817
|
+
})
|
|
6818
|
+
]);
|
|
6819
|
+
if (outcome === timedOut) {
|
|
6820
|
+
proc.kill();
|
|
6821
|
+
continue;
|
|
6822
|
+
}
|
|
6823
|
+
if (outcome === 0) {
|
|
6824
|
+
cachedOpenCodePath = opencodePath;
|
|
6825
|
+
return true;
|
|
6826
|
+
}
|
|
6827
|
+
} finally {
|
|
6828
|
+
if (timer) {
|
|
6829
|
+
clearTimeout(timer);
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
6832
|
+
} catch {
|
|
6833
|
+
}
|
|
6834
|
+
}
|
|
6835
|
+
return false;
|
|
6836
|
+
}
|
|
6837
|
+
var OPENCODE_VERSION_REQUIREMENT = "OpenCode 2.2.6 or later is required; install or upgrade OpenCode before applying configuration.";
|
|
6838
|
+
function isSupportedOpenCodeVersion(output) {
|
|
6839
|
+
const match = /^(?:opencode\s+)?v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/iu.exec(
|
|
6840
|
+
output?.trim() ?? ""
|
|
6841
|
+
);
|
|
6842
|
+
if (!match) return false;
|
|
6843
|
+
const [, major, minor, patch] = match;
|
|
6844
|
+
return Number(major) > 2 || Number(major) === 2 && (Number(minor) > 2 || Number(minor) === 2 && Number(patch) >= 6);
|
|
6845
|
+
}
|
|
6846
|
+
function getOpenCodeVersionSync() {
|
|
6847
|
+
try {
|
|
6848
|
+
const invocation = getOpenCodeVersionInvocation(resolveOpenCodePath());
|
|
6849
|
+
const [command, ...args] = invocation.command;
|
|
6850
|
+
if (!command) return null;
|
|
6851
|
+
const result = spawnSync5(command, args, {
|
|
6852
|
+
shell: invocation.options.shell,
|
|
6853
|
+
windowsHide: true,
|
|
6854
|
+
encoding: "utf8",
|
|
6855
|
+
timeout: OPENCODE_PROBE_TIMEOUT_MS
|
|
6856
|
+
});
|
|
6857
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
6858
|
+
} catch {
|
|
6859
|
+
return null;
|
|
6860
|
+
}
|
|
6861
|
+
}
|
|
6862
|
+
async function getOpenCodeVersion() {
|
|
6863
|
+
const opencodePath = resolveOpenCodePath();
|
|
6864
|
+
try {
|
|
6865
|
+
const invocation = getOpenCodeVersionInvocation(opencodePath);
|
|
6866
|
+
const proc = spawn(invocation.command, {
|
|
6867
|
+
...invocation.options,
|
|
6868
|
+
windowsHide: true
|
|
6869
|
+
});
|
|
6870
|
+
const output = await new Response(proc.stdout).text();
|
|
6871
|
+
await proc.exited;
|
|
6872
|
+
if (proc.exitCode === 0) {
|
|
6873
|
+
return output.trim();
|
|
6874
|
+
}
|
|
6875
|
+
} catch {
|
|
6876
|
+
}
|
|
6877
|
+
return null;
|
|
6878
|
+
}
|
|
6879
|
+
function getOpenCodePath() {
|
|
6880
|
+
const path = resolveOpenCodePath();
|
|
6881
|
+
return path === "opencode" ? null : path;
|
|
6882
|
+
}
|
|
6883
|
+
|
|
6607
6884
|
// src/cli/operations/opencode.ts
|
|
6608
6885
|
var PACKAGE_NAME = "thoth-agents";
|
|
6609
6886
|
var EXECUTING_PACKAGE_VERSION = resolveExecutingPackageVersion();
|
|
6610
6887
|
var EXPECTED_PLUGIN = EXECUTING_PACKAGE_VERSION.ok ? `${PACKAGE_NAME}@${EXECUTING_PACKAGE_VERSION.version}` : null;
|
|
6611
|
-
var
|
|
6612
|
-
var
|
|
6888
|
+
var OPENAI_PROFILE = "openai";
|
|
6889
|
+
var APPLIED_MODELS_PROFILE = "agents";
|
|
6613
6890
|
var ROLE_NAMES = [...ALL_AGENT_NAMES];
|
|
6614
6891
|
var issuedOpenCodePlans = /* @__PURE__ */ new WeakMap();
|
|
6615
6892
|
var NonCanonicalDataError = class extends Error {
|
|
@@ -6813,7 +7090,7 @@ var opencodeOperationAdapter = {
|
|
|
6813
7090
|
id: "opencode",
|
|
6814
7091
|
displayName: "OpenCode",
|
|
6815
7092
|
available: true,
|
|
6816
|
-
description: "OpenCode plugin and configuration surfaces.",
|
|
7093
|
+
description: "OpenCode 2.2.6 or later: pinned plugin and configuration surfaces.",
|
|
6817
7094
|
actions: openCodeActions
|
|
6818
7095
|
};
|
|
6819
7096
|
function targetForMainConfig(state) {
|
|
@@ -6822,7 +7099,7 @@ function targetForMainConfig(state) {
|
|
|
6822
7099
|
path: getExistingConfigPath(),
|
|
6823
7100
|
label: "OpenCode config",
|
|
6824
7101
|
...state ? { state } : {},
|
|
6825
|
-
expected: EXPECTED_PLUGIN ? `
|
|
7102
|
+
expected: EXPECTED_PLUGIN ? `plugins includes ${EXPECTED_PLUGIN}` : "plugins version unavailable"
|
|
6826
7103
|
};
|
|
6827
7104
|
}
|
|
6828
7105
|
function targetForLiteConfig(state) {
|
|
@@ -6831,7 +7108,26 @@ function targetForLiteConfig(state) {
|
|
|
6831
7108
|
path: getExistingLiteConfigPath(),
|
|
6832
7109
|
label: "thoth-agents config",
|
|
6833
7110
|
...state ? { state } : {},
|
|
6834
|
-
expected: `six-role ${
|
|
7111
|
+
expected: `six-role ${OPENAI_PROFILE} roster`
|
|
7112
|
+
};
|
|
7113
|
+
}
|
|
7114
|
+
function targetForProfiles() {
|
|
7115
|
+
return {
|
|
7116
|
+
kind: "config",
|
|
7117
|
+
path: openCodeProfileStore().paths.global,
|
|
7118
|
+
label: "OpenCode model profiles",
|
|
7119
|
+
expected: "complete active model profile"
|
|
7120
|
+
};
|
|
7121
|
+
}
|
|
7122
|
+
function shippedProfilePlanItem() {
|
|
7123
|
+
return {
|
|
7124
|
+
title: "Sync built-in OpenAI model profile (preserve customized roles and valid active selection)",
|
|
7125
|
+
target: targetForProfiles(),
|
|
7126
|
+
preview: JSON.stringify(
|
|
7127
|
+
{ version: 1, profiles: { openai: { agents: MODEL_MAPPINGS.openai } } },
|
|
7128
|
+
null,
|
|
7129
|
+
2
|
|
7130
|
+
)
|
|
6835
7131
|
};
|
|
6836
7132
|
}
|
|
6837
7133
|
function titleCaseSkillName(value) {
|
|
@@ -6866,18 +7162,21 @@ function openCodeSkillTargets(context2) {
|
|
|
6866
7162
|
observed: installed ? "required global skill installed" : "required global skill missing"
|
|
6867
7163
|
};
|
|
6868
7164
|
});
|
|
7165
|
+
const legacyCopies = inspectLegacyOpenCodeSkills(homeDir);
|
|
6869
7166
|
const ownedTargets = getOpenCodeOwnedSkillEntries({
|
|
6870
7167
|
homeDir
|
|
6871
7168
|
}).map((skill) => {
|
|
6872
|
-
const path = join8(skill.
|
|
7169
|
+
const path = join8(skill.sourcePath, "SKILL.md");
|
|
6873
7170
|
const installed = existsSync10(path);
|
|
6874
7171
|
return {
|
|
6875
7172
|
kind: "skill",
|
|
6876
7173
|
path,
|
|
6877
7174
|
label: titleCaseSkillName(skill.name),
|
|
6878
7175
|
state: installed ? "installed" : "missing",
|
|
6879
|
-
expected: "
|
|
6880
|
-
observed: installed ? "
|
|
7176
|
+
expected: "package-provided OpenCode skill",
|
|
7177
|
+
observed: `${installed ? "package-provided skill available" : "package-provided skill missing"}${legacyCopies.filter((copy) => copy.name === skill.name).map(
|
|
7178
|
+
(copy) => `; Warning: ${copy.sourcePath} shadows packaged skills; planned move to ${getLegacyOpenCodeSkillBackupRoot(homeDir)}/<new-run>/${copy.label}/${copy.name}; confirm with --move-legacy-opencode-skills`
|
|
7179
|
+
).join("")}`
|
|
6881
7180
|
};
|
|
6882
7181
|
});
|
|
6883
7182
|
const diagnostics = [];
|
|
@@ -6896,55 +7195,39 @@ function openCodeSkillTargets(context2) {
|
|
|
6896
7195
|
};
|
|
6897
7196
|
}
|
|
6898
7197
|
function configPluginMarker(config) {
|
|
6899
|
-
|
|
6900
|
-
return plugins.length > 0 ? `plugin: ${JSON.stringify(plugins)}` : "plugin: []";
|
|
7198
|
+
return `plugins: ${JSON.stringify(pluginEntries(config?.plugins))}; legacy plugin: ${JSON.stringify(pluginEntries(config?.plugin))}`;
|
|
6901
7199
|
}
|
|
6902
7200
|
function hasExpectedPlugin(config) {
|
|
6903
|
-
|
|
7201
|
+
const current = pluginEntries(config?.plugins);
|
|
7202
|
+
return EXPECTED_PLUGIN !== null && current.filter(isManagedOpenCodePlugin).length === 1 && current.includes(EXPECTED_PLUGIN) && !pluginEntries(config?.plugin).some(isManagedOpenCodePlugin);
|
|
6904
7203
|
}
|
|
6905
7204
|
function hasManagedPluginDrift(config) {
|
|
6906
|
-
return
|
|
6907
|
-
|
|
7205
|
+
return [...pluginEntries(config?.plugins), ...pluginEntries(config?.plugin)].some(
|
|
7206
|
+
isManagedOpenCodePlugin
|
|
6908
7207
|
) && !hasExpectedPlugin(config);
|
|
6909
7208
|
}
|
|
6910
|
-
function liteConfigMarker(
|
|
6911
|
-
const
|
|
6912
|
-
const
|
|
6913
|
-
|
|
6914
|
-
const legacyAgents = configRecord(config?.agents);
|
|
6915
|
-
const roles = ROLE_NAMES.filter(
|
|
6916
|
-
(role) => role in managedPreset || role in legacyAgents
|
|
6917
|
-
);
|
|
6918
|
-
return `preset: ${preset ?? "none"}; observed roles: ${roles.join(", ")}`;
|
|
7209
|
+
function liteConfigMarker(_config) {
|
|
7210
|
+
const { name, profile } = activeOpenCodeProfile();
|
|
7211
|
+
const roles = ROLE_NAMES.filter((role) => role in (profile?.agents ?? {}));
|
|
7212
|
+
return `profile: ${name ?? "none"}; observed roles: ${roles.join(", ")}`;
|
|
6919
7213
|
}
|
|
6920
|
-
function
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
}
|
|
6924
|
-
const presets = configRecord(config.presets);
|
|
6925
|
-
return hasCompleteAgentRoster(presets[config.preset]);
|
|
7214
|
+
function hasManagedProfile(_config) {
|
|
7215
|
+
const { name, profile } = activeOpenCodeProfile();
|
|
7216
|
+
return (name === OPENAI_PROFILE || name === APPLIED_MODELS_PROFILE) && hasCompleteAgentRoster(profile?.agents);
|
|
6926
7217
|
}
|
|
6927
|
-
function
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
}
|
|
6931
|
-
if (!config.presets || typeof config.presets !== "object" || Array.isArray(config.presets)) {
|
|
6932
|
-
return false;
|
|
6933
|
-
}
|
|
6934
|
-
const presets = config.presets;
|
|
6935
|
-
if (!Object.hasOwn(presets, config.preset)) return false;
|
|
6936
|
-
const selectedPreset = presets[config.preset];
|
|
6937
|
-
return selectedPreset !== null && typeof selectedPreset === "object" && !Array.isArray(selectedPreset);
|
|
7218
|
+
function hasSelectedNamedProfile(_config) {
|
|
7219
|
+
const { name, profile } = activeOpenCodeProfile();
|
|
7220
|
+
return !!profile && name !== OPENAI_PROFILE && name !== APPLIED_MODELS_PROFILE;
|
|
6938
7221
|
}
|
|
6939
7222
|
function hasLegacySevenAgentRoster(config) {
|
|
6940
|
-
return
|
|
7223
|
+
return !hasManagedProfile(config) && hasCompleteAgentRoster(config?.agents);
|
|
6941
7224
|
}
|
|
6942
|
-
var
|
|
6943
|
-
function
|
|
7225
|
+
var ACTIVE_PROFILE_SELECTED_CODE = "opencode-active-profile-selected";
|
|
7226
|
+
function activeProfileSelectedDiagnostic() {
|
|
6944
7227
|
return {
|
|
6945
7228
|
severity: "important",
|
|
6946
|
-
message: "A valid named
|
|
6947
|
-
code:
|
|
7229
|
+
message: "A valid named profile is active; model configuration preserves it while sync or install may repair the managed roster.",
|
|
7230
|
+
code: ACTIVE_PROFILE_SELECTED_CODE
|
|
6948
7231
|
};
|
|
6949
7232
|
}
|
|
6950
7233
|
var REPAIRABLE_DIAGNOSTIC_CODES = /* @__PURE__ */ new Set([
|
|
@@ -6952,7 +7235,7 @@ var REPAIRABLE_DIAGNOSTIC_CODES = /* @__PURE__ */ new Set([
|
|
|
6952
7235
|
"opencode-main-config-missing",
|
|
6953
7236
|
"opencode-lite-config-missing",
|
|
6954
7237
|
"opencode-roster-drift",
|
|
6955
|
-
|
|
7238
|
+
ACTIVE_PROFILE_SELECTED_CODE,
|
|
6956
7239
|
"opencode-required-skills-missing"
|
|
6957
7240
|
]);
|
|
6958
7241
|
var MODEL_PRECONFIG_SKILL_CODES = /* @__PURE__ */ new Set([
|
|
@@ -6985,7 +7268,7 @@ function targetsForBlockingDiagnostic(diagnostic, status) {
|
|
|
6985
7268
|
if (code === "opencode-plugin-drift" || code === "opencode-main-config-missing" || code === "opencode-config-parse-error" || code === "opencode-unmanaged-config") {
|
|
6986
7269
|
return main ? [main] : [];
|
|
6987
7270
|
}
|
|
6988
|
-
if (code === "opencode-roster-drift" || code ===
|
|
7271
|
+
if (code === "opencode-roster-drift" || code === ACTIVE_PROFILE_SELECTED_CODE || code === "opencode-lite-config-missing" || code === "thoth-config-parse-error" || code === "opencode-roster-unrecognized") {
|
|
6989
7272
|
return lite ? [lite] : [];
|
|
6990
7273
|
}
|
|
6991
7274
|
if (code === "opencode-required-skills-missing") {
|
|
@@ -7013,7 +7296,7 @@ function blockingDetails(action, status, includeRepairable = false) {
|
|
|
7013
7296
|
const diagnostics = status.diagnostics.filter((diagnostic) => {
|
|
7014
7297
|
if (diagnostic.severity === "critical") return true;
|
|
7015
7298
|
if (diagnostic.severity === "minor") return false;
|
|
7016
|
-
if (!includeRepairable && action === "model-config" && diagnostic.code ===
|
|
7299
|
+
if (!includeRepairable && action === "model-config" && diagnostic.code === ACTIVE_PROFILE_SELECTED_CODE) {
|
|
7017
7300
|
return false;
|
|
7018
7301
|
}
|
|
7019
7302
|
if (!includeRepairable && allowModelPreconfiguration && diagnostic.code !== void 0 && MODEL_PRECONFIG_SKILL_CODES.has(diagnostic.code)) {
|
|
@@ -7094,16 +7377,16 @@ function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
|
7094
7377
|
};
|
|
7095
7378
|
if (!mainExists || !hasExpectedPlugin(main.config)) {
|
|
7096
7379
|
if (hasManagedPluginDrift(main.config)) {
|
|
7097
|
-
const currentRoster =
|
|
7380
|
+
const currentRoster = hasManagedProfile(lite.config);
|
|
7098
7381
|
const legacyRoster = hasLegacySevenAgentRoster(lite.config);
|
|
7099
|
-
const
|
|
7100
|
-
const recognizedRoster = currentRoster || legacyRoster ||
|
|
7382
|
+
const selectedNamedProfile = hasSelectedNamedProfile(lite.config);
|
|
7383
|
+
const recognizedRoster = currentRoster || legacyRoster || selectedNamedProfile;
|
|
7101
7384
|
let liteState = "unknown";
|
|
7102
7385
|
if (!liteExists) {
|
|
7103
7386
|
liteState = "missing";
|
|
7104
7387
|
} else if (currentRoster) {
|
|
7105
7388
|
liteState = "installed";
|
|
7106
|
-
} else if (legacyRoster ||
|
|
7389
|
+
} else if (legacyRoster || selectedNamedProfile) {
|
|
7107
7390
|
liteState = "drift";
|
|
7108
7391
|
}
|
|
7109
7392
|
return {
|
|
@@ -7132,7 +7415,7 @@ function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
|
7132
7415
|
code: "opencode-roster-drift"
|
|
7133
7416
|
}
|
|
7134
7417
|
] : [],
|
|
7135
|
-
...
|
|
7418
|
+
...selectedNamedProfile && !currentRoster ? [activeProfileSelectedDiagnostic()] : [],
|
|
7136
7419
|
...!liteExists ? [
|
|
7137
7420
|
{
|
|
7138
7421
|
severity: "important",
|
|
@@ -7160,10 +7443,10 @@ function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
|
7160
7443
|
};
|
|
7161
7444
|
}
|
|
7162
7445
|
if (!mainExists && liteExists) {
|
|
7163
|
-
const currentRoster =
|
|
7446
|
+
const currentRoster = hasManagedProfile(lite.config);
|
|
7164
7447
|
const legacyRoster = hasLegacySevenAgentRoster(lite.config);
|
|
7165
|
-
const
|
|
7166
|
-
if (!currentRoster && !legacyRoster && !
|
|
7448
|
+
const selectedNamedProfile = hasSelectedNamedProfile(lite.config);
|
|
7449
|
+
if (!currentRoster && !legacyRoster && !selectedNamedProfile) {
|
|
7167
7450
|
return {
|
|
7168
7451
|
harness: "opencode",
|
|
7169
7452
|
displayName: "OpenCode",
|
|
@@ -7185,12 +7468,12 @@ function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
|
7185
7468
|
actions: openCodeActions
|
|
7186
7469
|
};
|
|
7187
7470
|
}
|
|
7188
|
-
if (
|
|
7471
|
+
if (selectedNamedProfile && !currentRoster) {
|
|
7189
7472
|
return {
|
|
7190
7473
|
harness: "opencode",
|
|
7191
7474
|
displayName: "OpenCode",
|
|
7192
7475
|
state: "drift",
|
|
7193
|
-
summary: "OpenCode plugin is missing and a valid named
|
|
7476
|
+
summary: "OpenCode plugin is missing and a valid named profile is active.",
|
|
7194
7477
|
targets: [
|
|
7195
7478
|
{ ...mainTarget, state: "missing" },
|
|
7196
7479
|
{ ...liteTarget, state: "drift" },
|
|
@@ -7202,7 +7485,7 @@ function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
|
7202
7485
|
message: "The managed OpenCode main config is missing and must be restored by install or sync.",
|
|
7203
7486
|
code: "opencode-main-config-missing"
|
|
7204
7487
|
},
|
|
7205
|
-
|
|
7488
|
+
activeProfileSelectedDiagnostic(),
|
|
7206
7489
|
...skillStatus.diagnostics
|
|
7207
7490
|
],
|
|
7208
7491
|
actions: openCodeActions
|
|
@@ -7303,20 +7586,20 @@ function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
|
7303
7586
|
actions: openCodeActions
|
|
7304
7587
|
};
|
|
7305
7588
|
}
|
|
7306
|
-
if (!
|
|
7307
|
-
const
|
|
7589
|
+
if (!hasManagedProfile(lite.config)) {
|
|
7590
|
+
const selectedNamedProfile = hasSelectedNamedProfile(lite.config);
|
|
7308
7591
|
return {
|
|
7309
7592
|
harness: "opencode",
|
|
7310
7593
|
displayName: "OpenCode",
|
|
7311
7594
|
state: "drift",
|
|
7312
|
-
summary:
|
|
7595
|
+
summary: selectedNamedProfile ? "A valid named OpenCode profile is active outside the managed six-role roster." : "thoth-agents config does not match the expected six-role roster.",
|
|
7313
7596
|
targets: [
|
|
7314
7597
|
{ ...mainTarget, state: "installed" },
|
|
7315
7598
|
{ ...liteTarget, state: "drift" },
|
|
7316
7599
|
...skillStatus.targets
|
|
7317
7600
|
],
|
|
7318
7601
|
diagnostics: [
|
|
7319
|
-
|
|
7602
|
+
selectedNamedProfile ? activeProfileSelectedDiagnostic() : {
|
|
7320
7603
|
severity: "important",
|
|
7321
7604
|
message: "Roster drift requires repair before applying generated plans.",
|
|
7322
7605
|
code: "opencode-roster-drift"
|
|
@@ -7347,8 +7630,18 @@ function getOpenCodeStatus(context2 = { cwd: process.cwd() }, evidence = {}) {
|
|
|
7347
7630
|
const managedStatus = getOpenCodeManagedStatus(context2);
|
|
7348
7631
|
return {
|
|
7349
7632
|
...managedStatus,
|
|
7633
|
+
summary: `${managedStatus.summary} Requires OpenCode 2.2.6 or later.`,
|
|
7350
7634
|
targets: [
|
|
7351
7635
|
...managedStatus.targets,
|
|
7636
|
+
{
|
|
7637
|
+
...targetForProfiles(),
|
|
7638
|
+
observed: JSON.stringify({
|
|
7639
|
+
active: activeOpenCodeProfile().name ?? "default",
|
|
7640
|
+
agents: effectiveOpenCodeProfile(
|
|
7641
|
+
parseConfig(getExistingLiteConfigPath()).config
|
|
7642
|
+
).agents
|
|
7643
|
+
})
|
|
7644
|
+
},
|
|
7352
7645
|
getCliManagedInstallVersionTarget("opencode", context2)
|
|
7353
7646
|
],
|
|
7354
7647
|
providerCapability: classifyProviderCapabilityEvidence(evidence)
|
|
@@ -7479,7 +7772,6 @@ function buildOpenCodeCompletePlan(action, context2) {
|
|
|
7479
7772
|
const expectedPlugin = `${PACKAGE_NAME}@${version}`;
|
|
7480
7773
|
const generatedConfig = generateLiteConfig({
|
|
7481
7774
|
agent: "opencode",
|
|
7482
|
-
hasTmux: false,
|
|
7483
7775
|
dryRun: true,
|
|
7484
7776
|
reset: false
|
|
7485
7777
|
});
|
|
@@ -7500,16 +7792,10 @@ function buildOpenCodeCompletePlan(action, context2) {
|
|
|
7500
7792
|
title: `Ensure OpenCode plugin points at ${expectedPlugin}`,
|
|
7501
7793
|
target: {
|
|
7502
7794
|
...targetForMainConfig(),
|
|
7503
|
-
expected: `
|
|
7795
|
+
expected: `plugins includes ${expectedPlugin}`
|
|
7504
7796
|
},
|
|
7505
7797
|
state: getOpenCodeStatus(context2).state,
|
|
7506
|
-
preview: `
|
|
7507
|
-
backup: defaultBackup(getExistingConfigPath())
|
|
7508
|
-
},
|
|
7509
|
-
{
|
|
7510
|
-
title: "Disable OpenCode default agents",
|
|
7511
|
-
target: targetForMainConfig(),
|
|
7512
|
-
preview: "agent.explore.disable = true; agent.general.disable = true",
|
|
7798
|
+
preview: `plugins: ["${expectedPlugin}"]`,
|
|
7513
7799
|
backup: defaultBackup(getExistingConfigPath())
|
|
7514
7800
|
},
|
|
7515
7801
|
{
|
|
@@ -7518,8 +7804,9 @@ function buildOpenCodeCompletePlan(action, context2) {
|
|
|
7518
7804
|
preview: JSON.stringify(generatedConfig, null, 2),
|
|
7519
7805
|
backup: defaultBackup(getExistingLiteConfigPath())
|
|
7520
7806
|
},
|
|
7807
|
+
shippedProfilePlanItem(),
|
|
7521
7808
|
{
|
|
7522
|
-
title: "
|
|
7809
|
+
title: "Inspect package-provided thoth-owned OpenCode skills",
|
|
7523
7810
|
target: {
|
|
7524
7811
|
kind: "skill",
|
|
7525
7812
|
label: "Thoth-owned OpenCode skills",
|
|
@@ -7585,7 +7872,6 @@ function buildOpenCodeSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
|
7585
7872
|
}
|
|
7586
7873
|
const generatedConfig = generateLiteConfig({
|
|
7587
7874
|
agent: "opencode",
|
|
7588
|
-
hasTmux: false,
|
|
7589
7875
|
dryRun: true,
|
|
7590
7876
|
reset: false
|
|
7591
7877
|
});
|
|
@@ -7598,19 +7884,13 @@ function buildOpenCodeSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
|
7598
7884
|
"opencode-sync-preview",
|
|
7599
7885
|
"sync",
|
|
7600
7886
|
"Sync OpenCode managed configuration",
|
|
7601
|
-
"Preview OpenCode
|
|
7887
|
+
"Preview pinned OpenCode plugins and thoth-agents config sync (requires OpenCode 2.2.6 or later).",
|
|
7602
7888
|
[
|
|
7603
7889
|
{
|
|
7604
7890
|
title: `Ensure OpenCode plugin points at ${PACKAGE_NAME}@${packageVersion.version}`,
|
|
7605
7891
|
target: targetForMainConfig(),
|
|
7606
7892
|
state: getOpenCodeStatus(context2).state,
|
|
7607
|
-
preview: `
|
|
7608
|
-
backup: defaultBackup(getExistingConfigPath())
|
|
7609
|
-
},
|
|
7610
|
-
{
|
|
7611
|
-
title: "Disable OpenCode default agents",
|
|
7612
|
-
target: targetForMainConfig(),
|
|
7613
|
-
preview: "agent.explore.disable = true; agent.general.disable = true",
|
|
7893
|
+
preview: `plugins: ["${PACKAGE_NAME}@${packageVersion.version}"]`,
|
|
7614
7894
|
backup: defaultBackup(getExistingConfigPath())
|
|
7615
7895
|
},
|
|
7616
7896
|
{
|
|
@@ -7619,8 +7899,9 @@ function buildOpenCodeSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
|
7619
7899
|
preview: JSON.stringify(generatedConfig, null, 2),
|
|
7620
7900
|
backup: defaultBackup(litePath)
|
|
7621
7901
|
},
|
|
7902
|
+
shippedProfilePlanItem(),
|
|
7622
7903
|
{
|
|
7623
|
-
title: "
|
|
7904
|
+
title: "Inspect package-provided thoth-owned OpenCode skills",
|
|
7624
7905
|
target: {
|
|
7625
7906
|
kind: "skill",
|
|
7626
7907
|
label: "Thoth-owned OpenCode skills",
|
|
@@ -7676,7 +7957,7 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
7676
7957
|
const items = normalizedRoles.map(({ role, model, variant, effort }) => {
|
|
7677
7958
|
return {
|
|
7678
7959
|
title: `Set ${role} OpenCode model override`,
|
|
7679
|
-
target:
|
|
7960
|
+
target: targetForProfiles(),
|
|
7680
7961
|
preview: JSON.stringify({
|
|
7681
7962
|
[role]: {
|
|
7682
7963
|
model,
|
|
@@ -7699,8 +7980,21 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
7699
7980
|
"opencode-model-config-preview",
|
|
7700
7981
|
"model-config",
|
|
7701
7982
|
"Configure OpenCode role model overrides",
|
|
7702
|
-
"Preview
|
|
7703
|
-
|
|
7983
|
+
"Preview complete agents profile materialization and global activation.",
|
|
7984
|
+
[
|
|
7985
|
+
...items,
|
|
7986
|
+
{
|
|
7987
|
+
title: "Materialize and globally activate the complete agents profile",
|
|
7988
|
+
target: targetForProfiles(),
|
|
7989
|
+
preview: "All six effective roles; active: agents"
|
|
7990
|
+
},
|
|
7991
|
+
{
|
|
7992
|
+
title: "Remove unsupported preset keys, preserving root configuration",
|
|
7993
|
+
target: targetForLiteConfig(),
|
|
7994
|
+
preview: "Retain unrelated config keys; remove preset/presets",
|
|
7995
|
+
backup: defaultBackup(getExistingLiteConfigPath())
|
|
7996
|
+
}
|
|
7997
|
+
],
|
|
7704
7998
|
_context
|
|
7705
7999
|
);
|
|
7706
8000
|
const effortWarnings = items.flatMap((item) => item.warnings ?? []);
|
|
@@ -7725,7 +8019,7 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
7725
8019
|
const unsupportedRoles = input.roles.filter(
|
|
7726
8020
|
(role) => !ROLE_NAMES.includes(role.role)
|
|
7727
8021
|
);
|
|
7728
|
-
if (
|
|
8022
|
+
if (unsupportedRoles.length > 0) {
|
|
7729
8023
|
return {
|
|
7730
8024
|
...plan,
|
|
7731
8025
|
canApply: false,
|
|
@@ -7733,7 +8027,7 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
7733
8027
|
...plan.warnings,
|
|
7734
8028
|
{
|
|
7735
8029
|
severity: "critical",
|
|
7736
|
-
message:
|
|
8030
|
+
message: `Unsupported OpenCode model role(s): ${unsupportedRoles.map((role) => role.role).join(", ")}.`,
|
|
7737
8031
|
code: "opencode-model-roster-incomplete"
|
|
7738
8032
|
}
|
|
7739
8033
|
]
|
|
@@ -7857,8 +8151,8 @@ function validateApplyPlan(plan) {
|
|
|
7857
8151
|
return null;
|
|
7858
8152
|
}
|
|
7859
8153
|
function invalidModelPayloadReason(roles) {
|
|
7860
|
-
if (!Array.isArray(roles)
|
|
7861
|
-
return "Model payload must
|
|
8154
|
+
if (!Array.isArray(roles)) {
|
|
8155
|
+
return "Model payload must be a role override array.";
|
|
7862
8156
|
}
|
|
7863
8157
|
const seen = /* @__PURE__ */ new Set();
|
|
7864
8158
|
for (const role of roles) {
|
|
@@ -7887,89 +8181,33 @@ function hasCompleteAgentRoster(value) {
|
|
|
7887
8181
|
(role) => typeof configRecord(roles[role]).model === "string"
|
|
7888
8182
|
);
|
|
7889
8183
|
}
|
|
7890
|
-
function effectiveRolePreset(base, generated) {
|
|
7891
|
-
const presets = configRecord(base.presets);
|
|
7892
|
-
const generatedPresets = configRecord(generated.presets);
|
|
7893
|
-
const defaults = configRecord(generatedPresets[OPENAI_PRESET]);
|
|
7894
|
-
const selected = typeof base.preset === "string" ? configRecord(presets[base.preset]) : {};
|
|
7895
|
-
const selectedIsMaterializedAgentsPreset = base.preset === APPLIED_MODELS_PRESET && hasCompleteAgentRoster(selected);
|
|
7896
|
-
const rootAgents = configRecord(base.agents);
|
|
7897
|
-
return Object.fromEntries(
|
|
7898
|
-
ROLE_NAMES.map((role) => [
|
|
7899
|
-
role,
|
|
7900
|
-
{
|
|
7901
|
-
...selectedIsMaterializedAgentsPreset ? {} : configRecord(defaults[role]),
|
|
7902
|
-
...configRecord(selected[role]),
|
|
7903
|
-
...configRecord(rootAgents[role])
|
|
7904
|
-
}
|
|
7905
|
-
])
|
|
7906
|
-
);
|
|
7907
|
-
}
|
|
7908
8184
|
function applyModelPlan(roles) {
|
|
7909
|
-
const roleModels = new Map(
|
|
7910
|
-
roles.map((role) => [
|
|
7911
|
-
role.role,
|
|
7912
|
-
{ model: role.model, variant: role.variant }
|
|
7913
|
-
])
|
|
7914
|
-
);
|
|
7915
8185
|
ensureConfigDir();
|
|
7916
8186
|
const targetPath = getExistingLiteConfigPath();
|
|
7917
8187
|
const parsed = parseConfig(targetPath);
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
dryRun: false,
|
|
7922
|
-
reset: false
|
|
7923
|
-
});
|
|
7924
|
-
const base = parsed.config ?? generated;
|
|
7925
|
-
const agents = { ...configRecord(base.agents) };
|
|
7926
|
-
const appliedPreset = effectiveRolePreset(base, generated);
|
|
8188
|
+
if (parsed.error) throw new Error(parsed.error);
|
|
8189
|
+
const base = parsed.config ?? generateLiteConfig({ agent: "opencode", reset: false });
|
|
8190
|
+
const profile = effectiveOpenCodeProfile(base);
|
|
7927
8191
|
const statePath = getOpenCodeManagedModelStatePath();
|
|
7928
8192
|
const state = readManagedModelState(statePath, 1);
|
|
7929
8193
|
const configuredEfforts = { ...state.configuredEfforts ?? {} };
|
|
7930
8194
|
const warnings = [];
|
|
7931
|
-
for (const
|
|
7932
|
-
const
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
...configRecord(appliedPreset[role])
|
|
8195
|
+
for (const override of roles) {
|
|
8196
|
+
const role = override.role;
|
|
8197
|
+
profile.agents[role] = {
|
|
8198
|
+
model: override.model,
|
|
8199
|
+
...override.variant !== null ? { variant: override.variant } : {}
|
|
7937
8200
|
};
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
const userOwnsVariant = override.variant === null && currentVariant !== void 0 && (trackedVariant === void 0 || currentVariant !== trackedVariant);
|
|
7941
|
-
current.model = override.model;
|
|
7942
|
-
presetRole.model = override.model;
|
|
7943
|
-
if (userOwnsVariant) {
|
|
7944
|
-
presetRole.variant = currentVariant;
|
|
7945
|
-
delete configuredEfforts[role];
|
|
7946
|
-
warnings.push({
|
|
7947
|
-
severity: "important",
|
|
7948
|
-
code: "opencode-effort-user-owned",
|
|
7949
|
-
message: `Preserved user-owned OpenCode variant ${currentVariant} for ${role}.`
|
|
7950
|
-
});
|
|
7951
|
-
} else if (override.variant === null) {
|
|
7952
|
-
delete current.variant;
|
|
7953
|
-
delete presetRole.variant;
|
|
7954
|
-
delete configuredEfforts[role];
|
|
7955
|
-
} else {
|
|
7956
|
-
current.variant = override.variant;
|
|
7957
|
-
presetRole.variant = override.variant;
|
|
7958
|
-
configuredEfforts[role] = override.variant;
|
|
7959
|
-
}
|
|
7960
|
-
agents[role] = current;
|
|
7961
|
-
appliedPreset[role] = presetRole;
|
|
8201
|
+
if (override.variant === null) delete configuredEfforts[role];
|
|
8202
|
+
else configuredEfforts[role] = override.variant;
|
|
7962
8203
|
}
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
};
|
|
7968
|
-
base.preset = APPLIED_MODELS_PRESET;
|
|
7969
|
-
writeConfig(targetPath, base);
|
|
8204
|
+
const store = openCodeProfileStore();
|
|
8205
|
+
store.save("global", APPLIED_MODELS_PROFILE, profile);
|
|
8206
|
+
store.setGlobalActive(APPLIED_MODELS_PROFILE);
|
|
8207
|
+
writeConfig(targetPath, withoutLegacyPresets(base));
|
|
7970
8208
|
writeManagedModelState(statePath, 1, {
|
|
7971
8209
|
models: state.models,
|
|
7972
|
-
configuredEfforts: Object.keys(configuredEfforts).length
|
|
8210
|
+
configuredEfforts: Object.keys(configuredEfforts).length ? configuredEfforts : void 0
|
|
7973
8211
|
});
|
|
7974
8212
|
return {
|
|
7975
8213
|
harness: "opencode",
|
|
@@ -7979,7 +8217,11 @@ function applyModelPlan(roles) {
|
|
|
7979
8217
|
changedTargets: [
|
|
7980
8218
|
{
|
|
7981
8219
|
...targetForLiteConfig("installed"),
|
|
7982
|
-
observed: "
|
|
8220
|
+
observed: "root configuration preserved; unsupported preset keys removed"
|
|
8221
|
+
},
|
|
8222
|
+
{
|
|
8223
|
+
...targetForProfiles(),
|
|
8224
|
+
observed: "complete agents profile activated"
|
|
7983
8225
|
}
|
|
7984
8226
|
],
|
|
7985
8227
|
backups: existsSync10(`${targetPath}.bak`) ? [{ path: `${targetPath}.bak`, label: "managed backup" }] : [],
|
|
@@ -8014,16 +8256,25 @@ function applyRequiredSkills(context2) {
|
|
|
8014
8256
|
function applyOwnedSkills(context2) {
|
|
8015
8257
|
const syncOwned = context2.syncOpenCodeOwnedSkills ?? syncOpenCodeOwnedSkills;
|
|
8016
8258
|
const result = syncOwned({
|
|
8259
|
+
moveLegacyOpenCodeSkills: context2.moveLegacyOpenCodeSkills,
|
|
8017
8260
|
homeDir: homeDirFromContext(context2)
|
|
8018
8261
|
});
|
|
8019
|
-
if (result.success)
|
|
8262
|
+
if (result.success)
|
|
8263
|
+
return {
|
|
8264
|
+
success: true,
|
|
8265
|
+
warnings: (result.notices ?? []).map((message) => ({
|
|
8266
|
+
severity: "minor",
|
|
8267
|
+
code: "opencode-legacy-skill-copy",
|
|
8268
|
+
message
|
|
8269
|
+
}))
|
|
8270
|
+
};
|
|
8020
8271
|
return {
|
|
8021
8272
|
success: false,
|
|
8022
8273
|
warnings: [
|
|
8023
8274
|
{
|
|
8024
8275
|
severity: "critical",
|
|
8025
8276
|
code: "opencode-owned-skills-sync-failed",
|
|
8026
|
-
message: `Failed to
|
|
8277
|
+
message: `Failed to inspect package-provided thoth-owned OpenCode skills: ${result.error ?? "unknown error"}.`
|
|
8027
8278
|
}
|
|
8028
8279
|
]
|
|
8029
8280
|
};
|
|
@@ -8074,6 +8325,16 @@ function applyOpenCodePlan(plan) {
|
|
|
8074
8325
|
issued.context
|
|
8075
8326
|
);
|
|
8076
8327
|
}
|
|
8328
|
+
const hostVersion = (issued.runtimeContext.getOpenCodeVersion ?? getOpenCodeVersionSync)();
|
|
8329
|
+
if (!isSupportedOpenCodeVersion(hostVersion)) {
|
|
8330
|
+
return rejectPlan3(
|
|
8331
|
+
plan,
|
|
8332
|
+
`${OPENCODE_VERSION_REQUIREMENT} Detected: ${hostVersion ?? "unavailable"}.`,
|
|
8333
|
+
"critical",
|
|
8334
|
+
"opencode-host-version-unsupported",
|
|
8335
|
+
issued.context
|
|
8336
|
+
);
|
|
8337
|
+
}
|
|
8077
8338
|
if (issued.payload.kind === "model") {
|
|
8078
8339
|
const invalidPayload = invalidModelPayloadReason(issued.payload.roles);
|
|
8079
8340
|
if (invalidPayload) {
|
|
@@ -8085,7 +8346,17 @@ function applyOpenCodePlan(plan) {
|
|
|
8085
8346
|
issued.context
|
|
8086
8347
|
);
|
|
8087
8348
|
}
|
|
8088
|
-
|
|
8349
|
+
try {
|
|
8350
|
+
return applyModelPlan(issued.payload.roles);
|
|
8351
|
+
} catch (error) {
|
|
8352
|
+
return rejectPlan3(
|
|
8353
|
+
plan,
|
|
8354
|
+
`Failed to apply OpenCode model profile: ${String(error)}`,
|
|
8355
|
+
"critical",
|
|
8356
|
+
"opencode-profile-write-failed",
|
|
8357
|
+
issued.context
|
|
8358
|
+
);
|
|
8359
|
+
}
|
|
8089
8360
|
}
|
|
8090
8361
|
const changedTargets = [];
|
|
8091
8362
|
const backups = [];
|
|
@@ -8093,8 +8364,7 @@ function applyOpenCodePlan(plan) {
|
|
|
8093
8364
|
const updateMainConfig = issued.runtimeContext.updateOpenCodeMainConfig ?? updateOpenCodeMainConfig;
|
|
8094
8365
|
const mainConfigResult = updateMainConfig({
|
|
8095
8366
|
ensurePlugin: true,
|
|
8096
|
-
pluginVersion: issued.payload.version
|
|
8097
|
-
disableDefaults: true
|
|
8367
|
+
pluginVersion: issued.payload.version
|
|
8098
8368
|
});
|
|
8099
8369
|
if (!mainConfigResult.success) {
|
|
8100
8370
|
return rejectPlan3(
|
|
@@ -8107,7 +8377,7 @@ function applyOpenCodePlan(plan) {
|
|
|
8107
8377
|
}
|
|
8108
8378
|
changedTargets.push({
|
|
8109
8379
|
...targetForMainConfig("installed"),
|
|
8110
|
-
observed: `
|
|
8380
|
+
observed: `plugins includes ${PACKAGE_NAME}@${issued.payload.version}`
|
|
8111
8381
|
});
|
|
8112
8382
|
if (existsSync10(`${mainConfigResult.configPath}.bak`)) {
|
|
8113
8383
|
backups.push({
|
|
@@ -8119,7 +8389,6 @@ function applyOpenCodePlan(plan) {
|
|
|
8119
8389
|
const liteResult = writeLite(
|
|
8120
8390
|
{
|
|
8121
8391
|
agent: "opencode",
|
|
8122
|
-
hasTmux: false,
|
|
8123
8392
|
dryRun: false,
|
|
8124
8393
|
reset: false
|
|
8125
8394
|
},
|
|
@@ -8135,6 +8404,10 @@ function applyOpenCodePlan(plan) {
|
|
|
8135
8404
|
...targetForLiteConfig("installed"),
|
|
8136
8405
|
observed: "six-role roster written"
|
|
8137
8406
|
});
|
|
8407
|
+
changedTargets.push({
|
|
8408
|
+
...targetForProfiles(),
|
|
8409
|
+
observed: "built-in openai profile synchronized"
|
|
8410
|
+
});
|
|
8138
8411
|
if (existsSync10(`${liteResult.configPath}.bak`)) {
|
|
8139
8412
|
backups.push({
|
|
8140
8413
|
path: `${liteResult.configPath}.bak`,
|
|
@@ -8147,14 +8420,14 @@ function applyOpenCodePlan(plan) {
|
|
|
8147
8420
|
kind: "skill",
|
|
8148
8421
|
label: "Thoth-owned OpenCode skills",
|
|
8149
8422
|
state: ownedSkills.success ? "installed" : "drift",
|
|
8150
|
-
observed: ownedSkills.success ? "
|
|
8423
|
+
observed: ownedSkills.success ? "package-provided skills available" : "package-provided skill inspection failed"
|
|
8151
8424
|
});
|
|
8152
8425
|
if (!ownedSkills.success) {
|
|
8153
8426
|
return {
|
|
8154
8427
|
harness: "opencode",
|
|
8155
8428
|
action: plan.action,
|
|
8156
8429
|
applied: false,
|
|
8157
|
-
summary: "OpenCode configuration was written, but
|
|
8430
|
+
summary: "OpenCode configuration was written, but package-provided skills failed inspection.",
|
|
8158
8431
|
changedTargets,
|
|
8159
8432
|
backups,
|
|
8160
8433
|
warnings,
|
|
@@ -8224,28 +8497,28 @@ function applyOpenCodePlan(plan) {
|
|
|
8224
8497
|
}
|
|
8225
8498
|
|
|
8226
8499
|
// src/cli/operations/pi.ts
|
|
8227
|
-
import { spawnSync as
|
|
8500
|
+
import { spawnSync as spawnSync8 } from "child_process";
|
|
8228
8501
|
import { createHash as createHash7 } from "crypto";
|
|
8229
8502
|
import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
|
|
8230
8503
|
import { basename as basename4, dirname as dirname11, isAbsolute as isAbsolute5, join as join15 } from "path";
|
|
8231
8504
|
|
|
8232
8505
|
// src/cli/pi-install.ts
|
|
8233
|
-
import { spawnSync as
|
|
8506
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
8234
8507
|
import { createHash as createHash6 } from "crypto";
|
|
8235
8508
|
import {
|
|
8236
8509
|
existsSync as existsSync14,
|
|
8237
8510
|
lstatSync as lstatSync6,
|
|
8238
8511
|
readdirSync as readdirSync3,
|
|
8239
8512
|
readFileSync as readFileSync13,
|
|
8240
|
-
realpathSync as
|
|
8241
|
-
statSync as
|
|
8513
|
+
realpathSync as realpathSync4,
|
|
8514
|
+
statSync as statSync4
|
|
8242
8515
|
} from "fs";
|
|
8243
|
-
import { basename as basename3, dirname as dirname10, isAbsolute as isAbsolute4, join as join14, resolve as
|
|
8516
|
+
import { basename as basename3, dirname as dirname10, isAbsolute as isAbsolute4, join as join14, resolve as resolve10, sep as sep4 } from "path";
|
|
8244
8517
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
8245
8518
|
import { lt } from "semver";
|
|
8246
8519
|
|
|
8247
8520
|
// src/cli/pi-external-package.ts
|
|
8248
|
-
import { readFileSync as readFileSync9, realpathSync as
|
|
8521
|
+
import { readFileSync as readFileSync9, realpathSync as realpathSync3 } from "fs";
|
|
8249
8522
|
import { isAbsolute as isAbsolute2, join as join9 } from "path";
|
|
8250
8523
|
import { satisfies, valid } from "semver";
|
|
8251
8524
|
function npmPackageName(source) {
|
|
@@ -8274,7 +8547,7 @@ function piExternalSourceMatches(candidate, expectedSource) {
|
|
|
8274
8547
|
if (candidate.source === expectedSource) return true;
|
|
8275
8548
|
if (!isAbsolute2(expectedSource) || !candidate.installedPath) return false;
|
|
8276
8549
|
try {
|
|
8277
|
-
return
|
|
8550
|
+
return realpathSync3(candidate.installedPath) === realpathSync3(expectedSource);
|
|
8278
8551
|
} catch {
|
|
8279
8552
|
return false;
|
|
8280
8553
|
}
|
|
@@ -8360,7 +8633,7 @@ function inspectPiExternalPackage(packages, spec, requireManagedSource = true) {
|
|
|
8360
8633
|
}
|
|
8361
8634
|
|
|
8362
8635
|
// src/cli/pi-git-source.ts
|
|
8363
|
-
import { resolve as
|
|
8636
|
+
import { resolve as resolve6, sep as sep3 } from "path";
|
|
8364
8637
|
import hostedGitInfo from "hosted-git-info";
|
|
8365
8638
|
function splitRef(url) {
|
|
8366
8639
|
const scpLikeMatch = url.match(/^git@([^:]+):(.+)$/);
|
|
@@ -8502,9 +8775,9 @@ function projectGitPackagePath(source, projectRoot) {
|
|
|
8502
8775
|
return void 0;
|
|
8503
8776
|
const parsed = parseGitUrl(source);
|
|
8504
8777
|
if (!parsed) return void 0;
|
|
8505
|
-
const installRoot =
|
|
8506
|
-
const installedPath =
|
|
8507
|
-
if (installedPath !== installRoot && !installedPath.startsWith(`${installRoot}${
|
|
8778
|
+
const installRoot = resolve6(projectRoot, "git");
|
|
8779
|
+
const installedPath = resolve6(installRoot, parsed.host, parsed.path);
|
|
8780
|
+
if (installedPath !== installRoot && !installedPath.startsWith(`${installRoot}${sep3}`))
|
|
8508
8781
|
return void 0;
|
|
8509
8782
|
return installedPath;
|
|
8510
8783
|
}
|
|
@@ -8517,7 +8790,7 @@ import {
|
|
|
8517
8790
|
readdirSync as readdirSync2,
|
|
8518
8791
|
readFileSync as readFileSync10,
|
|
8519
8792
|
renameSync as renameSync4,
|
|
8520
|
-
statSync as
|
|
8793
|
+
statSync as statSync3,
|
|
8521
8794
|
writeFileSync as writeFileSync3
|
|
8522
8795
|
} from "fs";
|
|
8523
8796
|
import { dirname as dirname7, join as join10 } from "path";
|
|
@@ -8561,7 +8834,7 @@ function migrateLegacyPiResources(options) {
|
|
|
8561
8834
|
if (!existsSync11(source)) continue;
|
|
8562
8835
|
const canonical = join10(options.packageRoot, "skills", name);
|
|
8563
8836
|
const backup = `${source}.thoth-agents-legacy.bak`;
|
|
8564
|
-
if (!
|
|
8837
|
+
if (!statSync3(source).isDirectory() || !identicalTrees(source, canonical) || existsSync11(backup)) {
|
|
8565
8838
|
manualActions.push(
|
|
8566
8839
|
`Legacy Pi skill ${name} is modified or has ambiguous backup ownership; remove it manually after review.`
|
|
8567
8840
|
);
|
|
@@ -8611,16 +8884,16 @@ function migrateLegacyPiResources(options) {
|
|
|
8611
8884
|
}
|
|
8612
8885
|
|
|
8613
8886
|
// src/cli/pi-native-probe.ts
|
|
8614
|
-
import { spawnSync as
|
|
8887
|
+
import { spawnSync as spawnSync6 } from "child_process";
|
|
8615
8888
|
import {
|
|
8616
8889
|
existsSync as existsSync12,
|
|
8617
|
-
mkdtempSync
|
|
8890
|
+
mkdtempSync,
|
|
8618
8891
|
readFileSync as readFileSync11,
|
|
8619
|
-
rmSync as
|
|
8892
|
+
rmSync as rmSync4,
|
|
8620
8893
|
writeFileSync as writeFileSync4
|
|
8621
8894
|
} from "fs";
|
|
8622
8895
|
import { tmpdir } from "os";
|
|
8623
|
-
import { dirname as dirname8, join as join11, resolve as
|
|
8896
|
+
import { dirname as dirname8, join as join11, resolve as resolve7, win32 } from "path";
|
|
8624
8897
|
function resolvePiWindowsCliFromShim(shimPath, shimContents) {
|
|
8625
8898
|
const target = shimContents.match(
|
|
8626
8899
|
/"([^"\r\n]*@earendil-works[\\/]+pi-coding-agent[\\/]+dist[\\/]+bundle[\\/]+cli\.js)"\s+%\*/i
|
|
@@ -8633,7 +8906,7 @@ function resolvePiWindowsCliFromShim(shimPath, shimContents) {
|
|
|
8633
8906
|
return win32.isAbsolute(expanded) ? win32.normalize(expanded) : void 0;
|
|
8634
8907
|
}
|
|
8635
8908
|
function findPiWindowsCli() {
|
|
8636
|
-
const located =
|
|
8909
|
+
const located = spawnSync6("where.exe", ["pi.cmd"], {
|
|
8637
8910
|
windowsHide: true,
|
|
8638
8911
|
encoding: "utf8"
|
|
8639
8912
|
}).stdout?.split(/\r?\n/).find(Boolean);
|
|
@@ -8667,7 +8940,7 @@ function defaultExecutor(command, args, env) {
|
|
|
8667
8940
|
executableArgs = [cli, ...args];
|
|
8668
8941
|
}
|
|
8669
8942
|
}
|
|
8670
|
-
const result =
|
|
8943
|
+
const result = spawnSync6(executable, executableArgs, {
|
|
8671
8944
|
windowsHide: true,
|
|
8672
8945
|
encoding: "utf8",
|
|
8673
8946
|
timeout: 3e4,
|
|
@@ -8685,7 +8958,7 @@ function occurrences(text, marker) {
|
|
|
8685
8958
|
}
|
|
8686
8959
|
function observePiNativeRoot(options) {
|
|
8687
8960
|
const ownsIsolatedRoot = options.piHome === void 0;
|
|
8688
|
-
const isolatedRoot = options.piHome ??
|
|
8961
|
+
const isolatedRoot = options.piHome ?? mkdtempSync(join11(tmpdir(), "thoth-pi-observer-"));
|
|
8689
8962
|
const observerPath = join11(
|
|
8690
8963
|
isolatedRoot,
|
|
8691
8964
|
`observer-${process.pid}-${Date.now()}.mjs`
|
|
@@ -8765,7 +9038,7 @@ export default function observer(pi) {
|
|
|
8765
9038
|
"--offline",
|
|
8766
9039
|
"--no-extensions",
|
|
8767
9040
|
"--extension",
|
|
8768
|
-
|
|
9041
|
+
resolve7(options.extensionPath),
|
|
8769
9042
|
"--extension",
|
|
8770
9043
|
observerPath,
|
|
8771
9044
|
"--provider",
|
|
@@ -8838,8 +9111,8 @@ ${result.stderr}`.split(/\r?\n/).flatMap((line) => {
|
|
|
8838
9111
|
};
|
|
8839
9112
|
} finally {
|
|
8840
9113
|
if (ownsIsolatedRoot)
|
|
8841
|
-
|
|
8842
|
-
else
|
|
9114
|
+
rmSync4(isolatedRoot, { recursive: true, force: true });
|
|
9115
|
+
else rmSync4(observerPath, { force: true });
|
|
8843
9116
|
}
|
|
8844
9117
|
}
|
|
8845
9118
|
|
|
@@ -8849,11 +9122,11 @@ import {
|
|
|
8849
9122
|
mkdirSync as mkdirSync6,
|
|
8850
9123
|
readFileSync as readFileSync12,
|
|
8851
9124
|
renameSync as renameSync5,
|
|
8852
|
-
rmSync as
|
|
9125
|
+
rmSync as rmSync5,
|
|
8853
9126
|
writeFileSync as writeFileSync5
|
|
8854
9127
|
} from "fs";
|
|
8855
9128
|
import { homedir as homedir5 } from "os";
|
|
8856
|
-
import { dirname as dirname9, isAbsolute as isAbsolute3, join as join12, normalize, resolve as
|
|
9129
|
+
import { dirname as dirname9, isAbsolute as isAbsolute3, join as join12, normalize, resolve as resolve8 } from "path";
|
|
8857
9130
|
var keys = [
|
|
8858
9131
|
"schemaVersion",
|
|
8859
9132
|
"owner",
|
|
@@ -8868,8 +9141,8 @@ var keys = [
|
|
|
8868
9141
|
var semver = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
8869
9142
|
var digest = /^[0-9a-f]{64}$/;
|
|
8870
9143
|
function piPackagePathsEqual(left, right) {
|
|
8871
|
-
const normalizedLeft = normalize(
|
|
8872
|
-
const normalizedRight = normalize(
|
|
9144
|
+
const normalizedLeft = normalize(resolve8(left));
|
|
9145
|
+
const normalizedRight = normalize(resolve8(right));
|
|
8873
9146
|
return process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight;
|
|
8874
9147
|
}
|
|
8875
9148
|
function getPiPackageReceiptPath(options = {}) {
|
|
@@ -8926,7 +9199,7 @@ function writePiPackageReceipt(receipt, options = {}) {
|
|
|
8926
9199
|
try {
|
|
8927
9200
|
renameSync5(temporary, path);
|
|
8928
9201
|
} catch (error) {
|
|
8929
|
-
|
|
9202
|
+
rmSync5(temporary, { force: true });
|
|
8930
9203
|
throw error;
|
|
8931
9204
|
}
|
|
8932
9205
|
return { success: true, path };
|
|
@@ -8961,17 +9234,17 @@ function classifyPiPackageOwnership(input) {
|
|
|
8961
9234
|
|
|
8962
9235
|
// src/cli/pi-paths.ts
|
|
8963
9236
|
import { homedir as homedir6 } from "os";
|
|
8964
|
-
import { join as join13, resolve as
|
|
9237
|
+
import { join as join13, resolve as resolve9 } from "path";
|
|
8965
9238
|
function resolvePiPaths(options = {}) {
|
|
8966
9239
|
const env = options.env ?? process.env;
|
|
8967
|
-
const homeDir =
|
|
9240
|
+
const homeDir = resolve9(
|
|
8968
9241
|
options.homeDir ?? env.HOME ?? env.USERPROFILE ?? homedir6()
|
|
8969
9242
|
);
|
|
8970
9243
|
const defaultPiRoot = join13(homeDir, ".pi", "agent");
|
|
8971
9244
|
const configured = env.PI_CODING_AGENT_DIR?.trim();
|
|
8972
|
-
const piRoot = configured ?
|
|
8973
|
-
const cwd =
|
|
8974
|
-
const configRoot = env.XDG_CONFIG_HOME?.trim() ?
|
|
9245
|
+
const piRoot = configured ? resolve9(configured) : defaultPiRoot;
|
|
9246
|
+
const cwd = resolve9(options.cwd ?? process.cwd());
|
|
9247
|
+
const configRoot = env.XDG_CONFIG_HOME?.trim() ? resolve9(env.XDG_CONFIG_HOME) : join13(homeDir, ".config");
|
|
8975
9248
|
return {
|
|
8976
9249
|
homeDir,
|
|
8977
9250
|
piRoot,
|
|
@@ -9299,7 +9572,7 @@ function projectLocalPackagePath(source, projectRoot, homeDir) {
|
|
|
9299
9572
|
else if (path.startsWith("~/") || process.platform === "win32" && path.startsWith("~\\"))
|
|
9300
9573
|
path = join14(homeDir, path.slice(2));
|
|
9301
9574
|
else if (path.startsWith("file://")) path = fileURLToPath5(path);
|
|
9302
|
-
return
|
|
9575
|
+
return resolve10(projectRoot, path);
|
|
9303
9576
|
}
|
|
9304
9577
|
function legacyPiNpmPackagePath(name, settings, execute) {
|
|
9305
9578
|
try {
|
|
@@ -9310,7 +9583,7 @@ function legacyPiNpmPackagePath(name, settings, execute) {
|
|
|
9310
9583
|
if (/[\0\r\n&|<>^%"'`$;!]/.test(command)) return void 0;
|
|
9311
9584
|
const manager = basename3(command).replace(/\.(cmd|exe)$/i, "");
|
|
9312
9585
|
if (!["npm", "pnpm", "bun"].includes(manager)) return void 0;
|
|
9313
|
-
if (/\s/.test(command) && !
|
|
9586
|
+
if (/\s/.test(command) && !statSync4(command, { throwIfNoEntry: false })?.isFile())
|
|
9314
9587
|
return void 0;
|
|
9315
9588
|
const query = (args) => {
|
|
9316
9589
|
const result = execute(command, args);
|
|
@@ -9357,7 +9630,7 @@ function resolvePiPackage(candidate, options, userSettings) {
|
|
|
9357
9630
|
candidate = { ...candidate, resourceOnly };
|
|
9358
9631
|
if (candidate.installedPath) return candidate;
|
|
9359
9632
|
const paths = resolvePiPaths(options);
|
|
9360
|
-
const baseDir = candidate.scope === "user" ? paths.piRoot : join14(
|
|
9633
|
+
const baseDir = candidate.scope === "user" ? paths.piRoot : join14(resolve10(options.cwd ?? process.cwd()), ".pi");
|
|
9361
9634
|
const parsed = parsePiPackageSource(candidate.source, baseDir);
|
|
9362
9635
|
const npmName = parsed.type === "npm" ? parsed.name : void 0;
|
|
9363
9636
|
let path = npmName ? join14(baseDir, "npm", "node_modules", npmName) : parsed.type === "git" ? parsed.path : projectLocalPackagePath(candidate.source, baseDir, paths.homeDir);
|
|
@@ -9365,7 +9638,7 @@ function resolvePiPackage(candidate, options, userSettings) {
|
|
|
9365
9638
|
path = legacyPiNpmPackagePath(
|
|
9366
9639
|
npmName,
|
|
9367
9640
|
userSettings,
|
|
9368
|
-
options.commandExecutor ??
|
|
9641
|
+
options.commandExecutor ?? defaultCommandExecutor3
|
|
9369
9642
|
) ?? path;
|
|
9370
9643
|
return {
|
|
9371
9644
|
...candidate,
|
|
@@ -9381,7 +9654,7 @@ function resolvePiListedPackages(output, options) {
|
|
|
9381
9654
|
const packages = parsePiPackageList(output);
|
|
9382
9655
|
const paths = resolvePiPaths(options);
|
|
9383
9656
|
const settings = readJsonObject(paths.settingsPath);
|
|
9384
|
-
const projectRoot = join14(
|
|
9657
|
+
const projectRoot = join14(resolve10(options.cwd ?? process.cwd()), ".pi");
|
|
9385
9658
|
let projectSettings;
|
|
9386
9659
|
for (const { scope, source, installedPath } of packages) {
|
|
9387
9660
|
const baseDir = scope === "user" ? paths.piRoot : projectRoot;
|
|
@@ -9407,7 +9680,7 @@ function resolvePiListedPackages(output, options) {
|
|
|
9407
9680
|
});
|
|
9408
9681
|
}
|
|
9409
9682
|
function readPiProjectPackages(options) {
|
|
9410
|
-
const projectRoot = join14(
|
|
9683
|
+
const projectRoot = join14(resolve10(options.cwd ?? process.cwd()), ".pi");
|
|
9411
9684
|
return resolvePiConfiguredPackages(
|
|
9412
9685
|
readJsonObject(join14(projectRoot, "settings.json")),
|
|
9413
9686
|
"project",
|
|
@@ -9435,28 +9708,28 @@ function resolvePiPreservationPackages(output, options) {
|
|
|
9435
9708
|
];
|
|
9436
9709
|
}
|
|
9437
9710
|
function scanPiProjectInstalledIncumbents(options, configuredPackages) {
|
|
9438
|
-
const projectRoot = join14(
|
|
9711
|
+
const projectRoot = join14(resolve10(options.cwd ?? process.cwd()), ".pi");
|
|
9439
9712
|
const incumbents = [];
|
|
9440
9713
|
const visited = /* @__PURE__ */ new Set();
|
|
9441
9714
|
const pathKey = (path) => process.platform === "win32" ? path.toLowerCase() : path;
|
|
9442
9715
|
const knownPaths = new Set(
|
|
9443
9716
|
configuredPackages.flatMap(
|
|
9444
|
-
({ installedPath }) => installedPath ? [pathKey(
|
|
9717
|
+
({ installedPath }) => installedPath ? [pathKey(realpathSync4(installedPath))] : []
|
|
9445
9718
|
)
|
|
9446
9719
|
);
|
|
9447
9720
|
for (const root of [join14(projectRoot, "git"), join14(projectRoot, "npm")]) {
|
|
9448
9721
|
if (!existsSync14(root)) continue;
|
|
9449
|
-
const rootKey = pathKey(
|
|
9722
|
+
const rootKey = pathKey(realpathSync4(root));
|
|
9450
9723
|
const pending = [root];
|
|
9451
9724
|
while (pending.length > 0) {
|
|
9452
9725
|
const path = pending.pop();
|
|
9453
|
-
if (!path || !
|
|
9726
|
+
if (!path || !statSync4(path, { throwIfNoEntry: false })?.isDirectory())
|
|
9454
9727
|
continue;
|
|
9455
|
-
const key = pathKey(
|
|
9728
|
+
const key = pathKey(realpathSync4(path));
|
|
9456
9729
|
if (visited.has(key)) continue;
|
|
9457
9730
|
visited.add(key);
|
|
9458
9731
|
const manifestPath = join14(path, "package.json");
|
|
9459
|
-
if (existsSync14(manifestPath) &&
|
|
9732
|
+
if (existsSync14(manifestPath) && statSync4(manifestPath).isFile()) {
|
|
9460
9733
|
const candidate = {
|
|
9461
9734
|
scope: "project",
|
|
9462
9735
|
source: path,
|
|
@@ -9471,7 +9744,7 @@ function scanPiProjectInstalledIncumbents(options, configuredPackages) {
|
|
|
9471
9744
|
identityLimitation: `Installed manifest at ${path} identifies ${identity.packageName}, but no configured settings source maps to this directory.`
|
|
9472
9745
|
});
|
|
9473
9746
|
}
|
|
9474
|
-
if (key !== rootKey && !key.startsWith(`${rootKey}${
|
|
9747
|
+
if (key !== rootKey && !key.startsWith(`${rootKey}${sep4}`)) continue;
|
|
9475
9748
|
for (const entry of readdirSync3(path, { withFileTypes: true }))
|
|
9476
9749
|
if (entry.isDirectory() || entry.isSymbolicLink())
|
|
9477
9750
|
pending.push(join14(path, entry.name));
|
|
@@ -9585,7 +9858,7 @@ function buildPiSetupPlan(options = {}) {
|
|
|
9585
9858
|
if (options.runtimePackageRoot) {
|
|
9586
9859
|
const runtimeRoot = options.runtimePackageRoot;
|
|
9587
9860
|
const spec = getPiExternalPackageSpecs(options)[0];
|
|
9588
|
-
const sourceIsNormalizedAbsolutePath = isAbsolute4(runtimeRoot) &&
|
|
9861
|
+
const sourceIsNormalizedAbsolutePath = isAbsolute4(runtimeRoot) && resolve10(runtimeRoot) === runtimeRoot;
|
|
9589
9862
|
const inspection = sourceIsNormalizedAbsolutePath ? inspectPiExternalPackage(
|
|
9590
9863
|
[{ scope: "user", source: runtimeRoot, installedPath: runtimeRoot }],
|
|
9591
9864
|
spec
|
|
@@ -9643,7 +9916,7 @@ function buildPiSetupPlan(options = {}) {
|
|
|
9643
9916
|
blockers.push(error instanceof Error ? error.message : String(error));
|
|
9644
9917
|
}
|
|
9645
9918
|
const projectSettingsPath = join14(
|
|
9646
|
-
|
|
9919
|
+
resolve10(options.cwd ?? process.cwd()),
|
|
9647
9920
|
".pi",
|
|
9648
9921
|
"settings.json"
|
|
9649
9922
|
);
|
|
@@ -9935,7 +10208,7 @@ function getPiFirstPartyPackages(packages, knownSource) {
|
|
|
9935
10208
|
function matchesInstallSource(candidate, installSource) {
|
|
9936
10209
|
return isAbsolute4(installSource) ? candidate.installedPath !== void 0 && piPackagePathsEqual(candidate.installedPath, installSource) : candidate.source === installSource;
|
|
9937
10210
|
}
|
|
9938
|
-
function
|
|
10211
|
+
function defaultCommandExecutor3(command, args) {
|
|
9939
10212
|
let executable = command;
|
|
9940
10213
|
let executableArgs = [...args];
|
|
9941
10214
|
if (process.platform === "win32" && command === "pi") {
|
|
@@ -9945,7 +10218,7 @@ function defaultCommandExecutor4(command, args) {
|
|
|
9945
10218
|
executableArgs = [cli, ...args];
|
|
9946
10219
|
}
|
|
9947
10220
|
}
|
|
9948
|
-
const result =
|
|
10221
|
+
const result = spawnSync7(executable, executableArgs, {
|
|
9949
10222
|
windowsHide: true,
|
|
9950
10223
|
encoding: "utf8",
|
|
9951
10224
|
timeout: PI_COMMAND_TIMEOUT_MS,
|
|
@@ -9973,7 +10246,7 @@ function applyPiSetup(plan) {
|
|
|
9973
10246
|
};
|
|
9974
10247
|
if (plan.dryRun)
|
|
9975
10248
|
return { success: true, changed: [], diagnostics, installedPackages: [] };
|
|
9976
|
-
const execute = plan.options.commandExecutor ??
|
|
10249
|
+
const execute = plan.options.commandExecutor ?? defaultCommandExecutor3;
|
|
9977
10250
|
const changed = [];
|
|
9978
10251
|
const installedPackages = [];
|
|
9979
10252
|
let receiptCommitted = false;
|
|
@@ -10613,7 +10886,7 @@ function statusFromPlan(plan, context2, evidence = {}) {
|
|
|
10613
10886
|
description: item.kind === "package" ? "Validated installed-package manifest evidence; this does not prove live tool availability." : void 0
|
|
10614
10887
|
}));
|
|
10615
10888
|
const execute = context2.piCommandExecutor ?? ((command, args) => {
|
|
10616
|
-
const result =
|
|
10889
|
+
const result = spawnSync8(command, [...args], {
|
|
10617
10890
|
windowsHide: true,
|
|
10618
10891
|
encoding: "utf8",
|
|
10619
10892
|
timeout: 5e3,
|
|
@@ -11371,11 +11644,19 @@ function applyPiPlan(plan) {
|
|
|
11371
11644
|
homeDir: source.context.homeDir
|
|
11372
11645
|
}
|
|
11373
11646
|
});
|
|
11374
|
-
|
|
11375
|
-
|
|
11647
|
+
const completionEvidence = getInstallCompletionEvidence(completion, {
|
|
11648
|
+
codePrefix: "pi",
|
|
11649
|
+
version: source.version,
|
|
11650
|
+
fallbackError: "Pi provider setup or ledger commit failed."
|
|
11651
|
+
});
|
|
11652
|
+
if (!completion.success) {
|
|
11653
|
+
const rejected = reject(
|
|
11376
11654
|
completion.error ?? "Pi provider setup or ledger commit failed.",
|
|
11377
|
-
[...setupTargets, ...skillTargets]
|
|
11655
|
+
[...setupTargets, ...skillTargets, ...completionEvidence.targets]
|
|
11378
11656
|
);
|
|
11657
|
+
rejected.warnings.push(...completionEvidence.warnings);
|
|
11658
|
+
return rejected;
|
|
11659
|
+
}
|
|
11379
11660
|
return {
|
|
11380
11661
|
harness: "pi",
|
|
11381
11662
|
action: plan.action,
|
|
@@ -11384,12 +11665,7 @@ function applyPiPlan(plan) {
|
|
|
11384
11665
|
changedTargets: [
|
|
11385
11666
|
...setupTargets,
|
|
11386
11667
|
...skillTargets,
|
|
11387
|
-
|
|
11388
|
-
kind: "file",
|
|
11389
|
-
path: completion.ledger.path,
|
|
11390
|
-
label: "CLI-managed install version",
|
|
11391
|
-
state: "installed"
|
|
11392
|
-
}
|
|
11668
|
+
...completionEvidence.targets
|
|
11393
11669
|
],
|
|
11394
11670
|
diagnosticTargets: packageSkills.skills.map((skill) => ({
|
|
11395
11671
|
kind: "skill",
|
|
@@ -11398,7 +11674,7 @@ function applyPiPlan(plan) {
|
|
|
11398
11674
|
state: "installed"
|
|
11399
11675
|
})),
|
|
11400
11676
|
backups: [],
|
|
11401
|
-
warnings:
|
|
11677
|
+
warnings: completionEvidence.warnings,
|
|
11402
11678
|
disclaimers: disclaimers()
|
|
11403
11679
|
};
|
|
11404
11680
|
}
|
|
@@ -11902,35 +12178,15 @@ function getClaudeCodeModelRoles(_source = claudeCodeContext) {
|
|
|
11902
12178
|
function getPiModelRoles() {
|
|
11903
12179
|
return defaultPiModelRoles(context);
|
|
11904
12180
|
}
|
|
11905
|
-
function readRoleField(config, role, field) {
|
|
11906
|
-
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
11907
|
-
return void 0;
|
|
11908
|
-
}
|
|
11909
|
-
const record = config;
|
|
11910
|
-
const value = record[role];
|
|
11911
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
11912
|
-
return void 0;
|
|
11913
|
-
}
|
|
11914
|
-
const fieldValue = value[field];
|
|
11915
|
-
return typeof fieldValue === "string" && fieldValue.length > 0 ? fieldValue : void 0;
|
|
11916
|
-
}
|
|
11917
12181
|
function getOpenCodeModelRoles() {
|
|
11918
12182
|
const parsed = parseConfig(getExistingLiteConfigPath());
|
|
11919
|
-
const
|
|
11920
|
-
const presets = parsed.config?.presets && typeof parsed.config.presets === "object" && !Array.isArray(parsed.config.presets) ? parsed.config.presets : {};
|
|
11921
|
-
const presetName = typeof parsed.config?.preset === "string" ? parsed.config.preset : void 0;
|
|
11922
|
-
const selectedPreset = presetName !== void 0 ? presets[presetName] : void 0;
|
|
11923
|
-
const activePreset = selectedPreset && typeof selectedPreset === "object" && !Array.isArray(selectedPreset) ? selectedPreset : void 0;
|
|
12183
|
+
const profile = effectiveOpenCodeProfile(parsed.config);
|
|
11924
12184
|
return ALL_AGENT_NAMES.map((role) => {
|
|
11925
|
-
const
|
|
11926
|
-
const configuredModel = readRoleField(agents, role, "model") ?? readRoleField(activePreset, role, "model");
|
|
11927
|
-
const model = configuredModel ?? defaultModel;
|
|
11928
|
-
const configuredVariant = readRoleField(agents, role, "variant") ?? readRoleField(activePreset, role, "variant");
|
|
11929
|
-
const variant = configuredVariant ?? (configuredModel === void 0 ? getDefaultOpenCodeVariant(role) : void 0);
|
|
12185
|
+
const value = profile.agents[role];
|
|
11930
12186
|
return {
|
|
11931
12187
|
role,
|
|
11932
|
-
model,
|
|
11933
|
-
effort: variant ? { kind: "effort", value: variant } : { kind: "inherit" }
|
|
12188
|
+
model: value?.model ?? getDefaultOpenCodeModel(role),
|
|
12189
|
+
effort: value?.variant ? { kind: "effort", value: value.variant } : { kind: "inherit" }
|
|
11934
12190
|
};
|
|
11935
12191
|
});
|
|
11936
12192
|
}
|
|
@@ -11962,6 +12218,7 @@ function buildRestoreModelPlan(harness, options, source = context) {
|
|
|
11962
12218
|
return buildTuiModelPlan(harness, roles, source);
|
|
11963
12219
|
}
|
|
11964
12220
|
var defaultTuiOperations = {
|
|
12221
|
+
legacyOpenCodeSkillCopies: () => inspectLegacyOpenCodeSkills().map((copy) => copy.sourcePath),
|
|
11965
12222
|
status(harness, evidence) {
|
|
11966
12223
|
if (harness === "opencode") return getOpenCodeStatus(context, evidence);
|
|
11967
12224
|
if (harness === "claude") {
|
|
@@ -11983,8 +12240,12 @@ var defaultTuiOperations = {
|
|
|
11983
12240
|
},
|
|
11984
12241
|
plan(harness, action, options) {
|
|
11985
12242
|
if (harness === "opencode") {
|
|
11986
|
-
|
|
11987
|
-
|
|
12243
|
+
const consentContext2 = {
|
|
12244
|
+
...context,
|
|
12245
|
+
moveLegacyOpenCodeSkills: options?.moveLegacyOpenCodeSkills === true
|
|
12246
|
+
};
|
|
12247
|
+
if (action === "install") return buildOpenCodeInstallPlan(consentContext2);
|
|
12248
|
+
if (action === "update") return buildOpenCodeUpdatePlan(consentContext2);
|
|
11988
12249
|
if (action === "sync") return buildOpenCodeSyncPlan(context);
|
|
11989
12250
|
return buildTuiModelPlan(harness, getOpenCodeModelRoles());
|
|
11990
12251
|
}
|
|
@@ -12061,8 +12322,15 @@ export {
|
|
|
12061
12322
|
buildClaudeCodeSetupPlan,
|
|
12062
12323
|
applyClaudeCodeSetup,
|
|
12063
12324
|
formatClaudeCodeSetupPlan,
|
|
12325
|
+
isOpenCodeInstalled,
|
|
12326
|
+
OPENCODE_VERSION_REQUIREMENT,
|
|
12327
|
+
isSupportedOpenCodeVersion,
|
|
12328
|
+
getOpenCodeVersion,
|
|
12329
|
+
getOpenCodePath,
|
|
12064
12330
|
recordHarnessInstallCompletion,
|
|
12065
12331
|
finalizeHarnessInstall,
|
|
12332
|
+
getLegacyOpenCodeSkillBackupRoot,
|
|
12333
|
+
inspectLegacyOpenCodeSkills,
|
|
12066
12334
|
syncOpenCodeOwnedSkills,
|
|
12067
12335
|
inspectPiPackageSkills,
|
|
12068
12336
|
buildPiSetupPlan,
|