thoth-agents 0.11.0 → 0.12.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 +5 -3
- 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-5WPP6IY7.js → chunk-7F33WUSD.js} +1 -1
- package/dist/{chunk-RWQPMWEM.js → chunk-JLU3FSBT.js} +17 -2
- package/dist/chunk-MIDGZE34.js +107 -0
- package/dist/{chunk-DNPKVFOB.js → chunk-NY2IPJ7V.js} +78 -87
- package/dist/{chunk-HACUJFKI.js → chunk-QOA2AETX.js} +2 -228
- package/dist/{chunk-I5TXGNSG.js → chunk-SEF6PFM6.js} +504 -211
- package/dist/cli/config-io.d.ts +2 -2
- package/dist/cli/index.js +92 -39
- package/dist/cli/install.d.ts +1 -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 +1 -1
- package/dist/config/schema.d.ts +0 -31
- 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 +822 -1421
- package/dist/opencode/agents.d.ts +3 -0
- package/dist/opencode/command.d.ts +3 -0
- package/dist/opencode/context.d.ts +20 -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/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/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/utils/index.d.ts +0 -1
- package/package.json +14 -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 -26
- 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-7F33WUSD.js";
|
|
20
21
|
import {
|
|
21
22
|
ALL_AGENT_NAMES,
|
|
22
23
|
CLAUDE_CODE_PROMPT_DIALECT,
|
|
@@ -33,22 +34,25 @@ import {
|
|
|
33
34
|
generateLiteConfig,
|
|
34
35
|
getAgentOverride,
|
|
35
36
|
getAgentPackContract,
|
|
37
|
+
getConfigDir,
|
|
36
38
|
getDefaultOpenCodeModel,
|
|
37
39
|
getDefaultOpenCodeVariant,
|
|
38
40
|
getExistingConfigPath,
|
|
39
41
|
getExistingLiteConfigPath,
|
|
40
42
|
getOpenCodeManagedModelStatePath,
|
|
41
43
|
getPrimaryModelId,
|
|
44
|
+
isManagedOpenCodePlugin,
|
|
42
45
|
isPiSpecialistRole,
|
|
43
46
|
isValidPackageVersion,
|
|
44
47
|
parseConfig,
|
|
45
48
|
piSpecialistName,
|
|
49
|
+
pluginEntries,
|
|
46
50
|
renderAgentRoutingDescription,
|
|
47
51
|
resolveExecutingPackageVersion,
|
|
48
52
|
updateOpenCodeMainConfig,
|
|
49
53
|
writeConfig,
|
|
50
54
|
writeLiteConfig
|
|
51
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-NY2IPJ7V.js";
|
|
52
56
|
|
|
53
57
|
// src/cli/model-effort.ts
|
|
54
58
|
function normalizeEffortSelection(value) {
|
|
@@ -4387,47 +4391,35 @@ function recordCompletedInstall(options) {
|
|
|
4387
4391
|
|
|
4388
4392
|
// src/cli/thoth-mem-install.ts
|
|
4389
4393
|
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
|
|
4394
|
+
var THOTH_MEM_PACKAGE_SPEC = "thoth-mem@>=0.7.0";
|
|
4403
4395
|
var THOTH_MEM_SETUP_TIMEOUT_MS = 12e4;
|
|
4404
4396
|
var STATUS_EXIT_CODE = {
|
|
4397
|
+
planned: 0,
|
|
4405
4398
|
complete: 0,
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
requires_user_action: 3
|
|
4399
|
+
unsupported: 1,
|
|
4400
|
+
"requires-user-action": 1
|
|
4409
4401
|
};
|
|
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
4402
|
function isRecord6(value) {
|
|
4421
4403
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4422
4404
|
}
|
|
4423
4405
|
function isStringArray(value) {
|
|
4424
4406
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
4425
4407
|
}
|
|
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
4408
|
function isProviderSetupResult(value, expectedHarness) {
|
|
4430
|
-
|
|
4409
|
+
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") {
|
|
4410
|
+
return false;
|
|
4411
|
+
}
|
|
4412
|
+
if (expectedHarness === "opencode") {
|
|
4413
|
+
return typeof value.plugin === "string" && typeof value.opencodeVersion === "string" && typeof value.configPath === "string" && typeof value.skillPath === "string" && typeof value.providerConfigPath === "string";
|
|
4414
|
+
}
|
|
4415
|
+
if (value.host !== expectedHarness || typeof value.source !== "string" || typeof value.version !== "string" || !isRecord6(value.verification)) {
|
|
4416
|
+
return false;
|
|
4417
|
+
}
|
|
4418
|
+
const verification = value.verification;
|
|
4419
|
+
if (expectedHarness === "pi") {
|
|
4420
|
+
return typeof value.piVersion === "string" && typeof verification.package === "boolean" && typeof verification.source === "boolean" && typeof verification.manifest === "boolean";
|
|
4421
|
+
}
|
|
4422
|
+
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
4423
|
}
|
|
4432
4424
|
function parseProviderSetupResult(stdout, expectedHarness) {
|
|
4433
4425
|
let value;
|
|
@@ -4438,9 +4430,10 @@ function parseProviderSetupResult(stdout, expectedHarness) {
|
|
|
4438
4430
|
}
|
|
4439
4431
|
return isProviderSetupResult(value, expectedHarness) ? value : void 0;
|
|
4440
4432
|
}
|
|
4441
|
-
function
|
|
4433
|
+
function executeThothMemCommand(command, args, options) {
|
|
4442
4434
|
const result = spawnSync3(command, [...args], {
|
|
4443
4435
|
windowsHide: true,
|
|
4436
|
+
windowsVerbatimArguments: process.platform === "win32",
|
|
4444
4437
|
cwd: options.cwd,
|
|
4445
4438
|
encoding: "utf8",
|
|
4446
4439
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -4466,9 +4459,9 @@ function invalidResult(harness, command, args, exitCode, error) {
|
|
|
4466
4459
|
status: "invalid",
|
|
4467
4460
|
changed: false,
|
|
4468
4461
|
harness,
|
|
4469
|
-
|
|
4462
|
+
actions: [],
|
|
4463
|
+
warnings: [],
|
|
4470
4464
|
diagnostics: [],
|
|
4471
|
-
manualActions: [],
|
|
4472
4465
|
receipt: null,
|
|
4473
4466
|
command,
|
|
4474
4467
|
args,
|
|
@@ -4477,24 +4470,32 @@ function invalidResult(harness, command, args, exitCode, error) {
|
|
|
4477
4470
|
};
|
|
4478
4471
|
}
|
|
4479
4472
|
function getThothMemSetupCommand(harness, dryRun, options = {}) {
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4473
|
+
const args = [
|
|
4474
|
+
"-y",
|
|
4475
|
+
THOTH_MEM_PACKAGE_SPEC,
|
|
4476
|
+
"setup",
|
|
4477
|
+
harness,
|
|
4478
|
+
...dryRun ? ["--plan"] : [],
|
|
4479
|
+
"--json"
|
|
4480
|
+
];
|
|
4481
|
+
if ((options.platform ?? process.platform) !== "win32") {
|
|
4482
|
+
return { command: "npx", args };
|
|
4483
|
+
}
|
|
4484
|
+
return {
|
|
4485
|
+
command: options.commandShell ?? process.env.ComSpec ?? "cmd.exe",
|
|
4486
|
+
// Node must not escape this cmd command line: quoted > is a literal,
|
|
4487
|
+
// not redirection, and npx must receive the selector without quote characters.
|
|
4488
|
+
args: [
|
|
4489
|
+
`/d /s /c "npx ${args.map((arg) => arg === THOTH_MEM_PACKAGE_SPEC ? `"${arg}"` : arg).join(" ")}"`
|
|
4490
|
+
]
|
|
4491
|
+
};
|
|
4491
4492
|
}
|
|
4492
4493
|
function runThothMemSetup(options) {
|
|
4493
4494
|
const { command, args } = getThothMemSetupCommand(
|
|
4494
4495
|
options.harness,
|
|
4495
4496
|
options.dryRun ?? false
|
|
4496
4497
|
);
|
|
4497
|
-
const executor = options.commandExecutor ??
|
|
4498
|
+
const executor = options.commandExecutor ?? executeThothMemCommand;
|
|
4498
4499
|
let commandResult;
|
|
4499
4500
|
try {
|
|
4500
4501
|
commandResult = executor(command, args, {
|
|
@@ -4531,34 +4532,37 @@ function runThothMemSetup(options) {
|
|
|
4531
4532
|
command,
|
|
4532
4533
|
args,
|
|
4533
4534
|
commandResult.exitCode,
|
|
4534
|
-
stderr ? `thoth-mem returned invalid JSON evidence: ${stderr.slice(0, 2e3)}` : "thoth-mem returned invalid JSON evidence."
|
|
4535
|
+
stderr ? `thoth-mem setup failed or returned invalid JSON evidence: ${stderr.slice(0, 2e3)}` : "thoth-mem returned invalid JSON evidence."
|
|
4535
4536
|
);
|
|
4536
4537
|
}
|
|
4537
4538
|
const expectedExitCode = STATUS_EXIT_CODE[provider.status];
|
|
4538
|
-
const
|
|
4539
|
+
const expectedStatus = options.dryRun ? "planned" : "complete";
|
|
4540
|
+
const exitConsistent = commandResult.exitCode === expectedExitCode;
|
|
4541
|
+
const modeConsistent = expectedExitCode !== 0 || provider.status === expectedStatus;
|
|
4542
|
+
const evidenceValid = exitConsistent && modeConsistent;
|
|
4539
4543
|
return {
|
|
4540
|
-
success: evidenceValid && provider.status ===
|
|
4544
|
+
success: evidenceValid && provider.status === expectedStatus,
|
|
4541
4545
|
evidenceValid,
|
|
4542
4546
|
status: provider.status,
|
|
4543
4547
|
changed: provider.changed,
|
|
4544
|
-
harness:
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
diagnostics: provider.diagnostics,
|
|
4548
|
-
|
|
4549
|
-
receipt: provider.
|
|
4548
|
+
harness: options.harness,
|
|
4549
|
+
actions: provider.actions,
|
|
4550
|
+
warnings: provider.warnings,
|
|
4551
|
+
diagnostics: provider.diagnostics ?? [],
|
|
4552
|
+
restartRequired: provider.restartRequired,
|
|
4553
|
+
receipt: provider.receiptPath,
|
|
4550
4554
|
command,
|
|
4551
4555
|
args,
|
|
4552
4556
|
exitCode: commandResult.exitCode,
|
|
4553
4557
|
...!evidenceValid ? {
|
|
4554
|
-
error: `thoth-mem exit code ${String(commandResult.exitCode)} contradicts reported status ${provider.status} (expected ${expectedExitCode}).`
|
|
4558
|
+
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
4559
|
} : {}
|
|
4556
4560
|
};
|
|
4557
4561
|
}
|
|
4558
4562
|
|
|
4559
4563
|
// 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;
|
|
4564
|
+
function isConsistentProviderSuccess(result, harness, dryRun) {
|
|
4565
|
+
return result.success && result.evidenceValid && result.status === (dryRun ? "planned" : "complete") && result.exitCode === 0 && result.harness === harness;
|
|
4562
4566
|
}
|
|
4563
4567
|
function recordHarnessInstallCompletion(options) {
|
|
4564
4568
|
const ledgerPath = getInstallLedgerPath(options.ledgerOptions);
|
|
@@ -4603,12 +4607,12 @@ function finalizeHarnessInstall(options) {
|
|
|
4603
4607
|
dryRun: options.dryRun,
|
|
4604
4608
|
cwd: options.cwd
|
|
4605
4609
|
});
|
|
4606
|
-
if (!isConsistentProviderSuccess(provider, options.harness)) {
|
|
4610
|
+
if (!isConsistentProviderSuccess(provider, options.harness, options.dryRun)) {
|
|
4607
4611
|
return {
|
|
4608
4612
|
success: false,
|
|
4609
4613
|
provider,
|
|
4610
4614
|
ledger: { status: "not-attempted", path: ledgerPath },
|
|
4611
|
-
error: provider.error ?? "thoth-mem setup did not return consistent
|
|
4615
|
+
error: provider.error ?? "thoth-mem setup did not return consistent setup evidence."
|
|
4612
4616
|
};
|
|
4613
4617
|
}
|
|
4614
4618
|
const completion = recordHarnessInstallCompletion({
|
|
@@ -4629,6 +4633,19 @@ import { spawnSync as spawnSync4 } from "child_process";
|
|
|
4629
4633
|
import { existsSync as existsSync6 } from "fs";
|
|
4630
4634
|
import { homedir as homedir3 } from "os";
|
|
4631
4635
|
import { join as join6 } from "path";
|
|
4636
|
+
|
|
4637
|
+
// src/cli/npx-command.ts
|
|
4638
|
+
function getNpxCommand(args, options = {}) {
|
|
4639
|
+
if ((options.platform ?? process.platform) !== "win32") {
|
|
4640
|
+
return { command: "npx", args: [...args] };
|
|
4641
|
+
}
|
|
4642
|
+
return {
|
|
4643
|
+
command: options.commandShell ?? process.env.ComSpec ?? "cmd.exe",
|
|
4644
|
+
args: ["/d", "/s", "/c", ["npx", ...args].join(" ")]
|
|
4645
|
+
};
|
|
4646
|
+
}
|
|
4647
|
+
|
|
4648
|
+
// src/cli/skills.ts
|
|
4632
4649
|
var SKILLS_CLI_AGENT = {
|
|
4633
4650
|
opencode: "opencode",
|
|
4634
4651
|
codex: "codex",
|
|
@@ -4773,12 +4790,26 @@ function getInstallCompletionEvidence(completion, options) {
|
|
|
4773
4790
|
code: `${options.codePrefix}-provider-diagnostic`,
|
|
4774
4791
|
message
|
|
4775
4792
|
})),
|
|
4776
|
-
...completion.provider.
|
|
4793
|
+
...completion.provider.warnings.map((message) => ({
|
|
4777
4794
|
severity: "important",
|
|
4778
|
-
code: `${options.codePrefix}-provider-
|
|
4795
|
+
code: `${options.codePrefix}-provider-warning`,
|
|
4779
4796
|
message
|
|
4780
4797
|
}))
|
|
4781
4798
|
];
|
|
4799
|
+
for (const action of completion.provider.actions) {
|
|
4800
|
+
targets.push({
|
|
4801
|
+
kind: "surface",
|
|
4802
|
+
label: "thoth-mem action",
|
|
4803
|
+
observed: action
|
|
4804
|
+
});
|
|
4805
|
+
}
|
|
4806
|
+
if (completion.provider.restartRequired) {
|
|
4807
|
+
warnings.push({
|
|
4808
|
+
severity: "important",
|
|
4809
|
+
code: `${options.codePrefix}-provider-restart`,
|
|
4810
|
+
message: "thoth-mem: restart required"
|
|
4811
|
+
});
|
|
4812
|
+
}
|
|
4782
4813
|
if (completion.provider.receipt) {
|
|
4783
4814
|
targets.push({
|
|
4784
4815
|
kind: "file",
|
|
@@ -6386,19 +6417,18 @@ function resolveOpenCodeEffort(input) {
|
|
|
6386
6417
|
}
|
|
6387
6418
|
|
|
6388
6419
|
// src/cli/owned-skills.ts
|
|
6420
|
+
import { randomBytes } from "crypto";
|
|
6389
6421
|
import {
|
|
6390
|
-
cpSync,
|
|
6391
6422
|
existsSync as existsSync9,
|
|
6392
6423
|
lstatSync as lstatSync5,
|
|
6393
6424
|
mkdirSync as mkdirSync4,
|
|
6394
|
-
mkdtempSync,
|
|
6395
6425
|
readFileSync as readFileSync8,
|
|
6426
|
+
realpathSync as realpathSync2,
|
|
6396
6427
|
renameSync as renameSync3,
|
|
6397
|
-
rmSync as rmSync4,
|
|
6398
6428
|
statSync
|
|
6399
6429
|
} from "fs";
|
|
6400
6430
|
import { homedir as homedir4 } from "os";
|
|
6401
|
-
import { dirname as dirname6, join as join7 } from "path";
|
|
6431
|
+
import { dirname as dirname6, join as join7, resolve as resolve5, sep as sep2 } from "path";
|
|
6402
6432
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6403
6433
|
|
|
6404
6434
|
// src/harness/core/owned-skills.ts
|
|
@@ -6491,7 +6521,7 @@ function getOpenCodeOwnedSkillEntries(options = {}) {
|
|
|
6491
6521
|
return THOTH_OWNED_SKILL_NAMES.map((name) => ({
|
|
6492
6522
|
name,
|
|
6493
6523
|
sourcePath: join7(packageRoot, "skills", name),
|
|
6494
|
-
destinationPath: join7(homeDir, "
|
|
6524
|
+
destinationPath: join7(getConfigDir(homeDir), "skills", name)
|
|
6495
6525
|
}));
|
|
6496
6526
|
}
|
|
6497
6527
|
function getPiOwnedSkillEntries(options) {
|
|
@@ -6513,56 +6543,126 @@ function validateCanonicalSkill(entry) {
|
|
|
6513
6543
|
throw new Error(`Canonical owned skill contract is missing: ${entry.name}`);
|
|
6514
6544
|
}
|
|
6515
6545
|
}
|
|
6516
|
-
function
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6546
|
+
function getLegacyOpenCodeSkillBackupRoot(homeDir = homedir4()) {
|
|
6547
|
+
return resolve5(
|
|
6548
|
+
process.env.XDG_DATA_HOME || join7(homeDir, ".local", "share"),
|
|
6549
|
+
"thoth-agents",
|
|
6550
|
+
".opencode-legacy-skills"
|
|
6551
|
+
);
|
|
6552
|
+
}
|
|
6553
|
+
function inspectLegacyOpenCodeSkills(homeDir = homedir4()) {
|
|
6554
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6555
|
+
const copies = [];
|
|
6556
|
+
for (const [label, root] of [
|
|
6557
|
+
["resolved", getConfigDir(homeDir)],
|
|
6558
|
+
["legacy", join7(homeDir, ".config", "opencode")]
|
|
6559
|
+
]) {
|
|
6560
|
+
for (const directory of ["skills", "skill"]) {
|
|
6561
|
+
for (const name of THOTH_OWNED_SKILL_NAMES) {
|
|
6562
|
+
const sourcePath = join7(root, directory, name);
|
|
6563
|
+
if (!existsSync9(sourcePath)) continue;
|
|
6564
|
+
const real = realpathSync2(sourcePath);
|
|
6565
|
+
if (seen.has(real)) continue;
|
|
6566
|
+
seen.add(real);
|
|
6567
|
+
copies.push({ name, sourcePath, label: `${label}-${directory}` });
|
|
6568
|
+
}
|
|
6532
6569
|
}
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6570
|
+
}
|
|
6571
|
+
return copies;
|
|
6572
|
+
}
|
|
6573
|
+
function planOrMoveLegacySkills(options) {
|
|
6574
|
+
const backupRoot = getLegacyOpenCodeSkillBackupRoot(options.homeDir);
|
|
6575
|
+
const copies = inspectLegacyOpenCodeSkills(options.homeDir);
|
|
6576
|
+
const notices = [];
|
|
6577
|
+
const legacyMoves = [];
|
|
6578
|
+
if (!copies.length) return { backupRoot, notices, legacyMoves };
|
|
6579
|
+
const confirmed = options.moveLegacyOpenCodeSkills && !options.dryRun;
|
|
6580
|
+
let run = join7(backupRoot, "<new-run>");
|
|
6581
|
+
if (confirmed) {
|
|
6582
|
+
try {
|
|
6583
|
+
if (!resolve5(backupRoot).split(sep2).includes(".opencode-legacy-skills"))
|
|
6584
|
+
throw new Error(
|
|
6585
|
+
"Backup path is missing the required hidden .opencode-legacy-skills segment"
|
|
6586
|
+
);
|
|
6587
|
+
mkdirSync4(backupRoot, { recursive: true });
|
|
6588
|
+
let created = false;
|
|
6589
|
+
for (let attempt = 0; attempt < 10; attempt++) {
|
|
6590
|
+
run = join7(
|
|
6591
|
+
backupRoot,
|
|
6592
|
+
`${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${randomBytes(8).toString("hex")}`
|
|
6593
|
+
);
|
|
6594
|
+
try {
|
|
6595
|
+
mkdirSync4(run, { mode: 448 });
|
|
6596
|
+
created = true;
|
|
6597
|
+
break;
|
|
6598
|
+
} catch (error) {
|
|
6599
|
+
if (error.code !== "EEXIST") throw error;
|
|
6600
|
+
}
|
|
6539
6601
|
}
|
|
6602
|
+
if (!created)
|
|
6603
|
+
throw new Error("Unable to create an exclusive backup run directory");
|
|
6604
|
+
} catch (error) {
|
|
6605
|
+
notices.push(`Legacy skill move refused: ${String(error)}`);
|
|
6606
|
+
notices.push(
|
|
6607
|
+
...copies.map(
|
|
6608
|
+
({ sourcePath }) => legacyOpenCodeSkillNotice(sourcePath)
|
|
6609
|
+
)
|
|
6610
|
+
);
|
|
6611
|
+
return { backupRoot, notices, legacyMoves };
|
|
6612
|
+
}
|
|
6613
|
+
}
|
|
6614
|
+
for (const copy of copies) {
|
|
6615
|
+
const destinationPath = join7(run, copy.label, copy.name);
|
|
6616
|
+
const move = {
|
|
6617
|
+
sourcePath: copy.sourcePath,
|
|
6618
|
+
destinationPath,
|
|
6619
|
+
status: "planned"
|
|
6620
|
+
};
|
|
6621
|
+
legacyMoves.push(move);
|
|
6622
|
+
if (!confirmed) {
|
|
6623
|
+
notices.push(legacyOpenCodeSkillNotice(copy.sourcePath));
|
|
6624
|
+
notices.push(
|
|
6625
|
+
`Planned move: ${copy.sourcePath} -> ${destinationPath} (backup root: ${backupRoot})`
|
|
6626
|
+
);
|
|
6627
|
+
continue;
|
|
6628
|
+
}
|
|
6629
|
+
try {
|
|
6630
|
+
mkdirSync4(dirname6(destinationPath), { recursive: true });
|
|
6540
6631
|
try {
|
|
6541
|
-
|
|
6632
|
+
lstatSync5(destinationPath);
|
|
6633
|
+
throw new Error(
|
|
6634
|
+
`Backup destination already exists: ${destinationPath}`
|
|
6635
|
+
);
|
|
6542
6636
|
} 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 });
|
|
6637
|
+
if (error.code !== "ENOENT") throw error;
|
|
6553
6638
|
}
|
|
6639
|
+
renameSync3(copy.sourcePath, destinationPath);
|
|
6640
|
+
move.status = "moved";
|
|
6641
|
+
notices.push(
|
|
6642
|
+
`Moved legacy OpenCode skill: ${copy.sourcePath} -> ${destinationPath} (backup: ${run})`
|
|
6643
|
+
);
|
|
6644
|
+
} catch (error) {
|
|
6645
|
+
move.status = "failed";
|
|
6646
|
+
notices.push(
|
|
6647
|
+
`Legacy skill move failed; original left intact: ${copy.sourcePath}: ${String(error)}. ${legacyOpenCodeSkillNotice(copy.sourcePath)}`
|
|
6648
|
+
);
|
|
6554
6649
|
}
|
|
6555
|
-
} finally {
|
|
6556
|
-
rmSync4(transactionRoot, { recursive: true, force: true });
|
|
6557
6650
|
}
|
|
6651
|
+
return { backupRoot, notices, legacyMoves };
|
|
6652
|
+
}
|
|
6653
|
+
function legacyOpenCodeSkillNotice(path) {
|
|
6654
|
+
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
6655
|
}
|
|
6559
6656
|
function syncOpenCodeOwnedSkills(options = {}) {
|
|
6560
6657
|
const skills = getOpenCodeOwnedSkillEntries(options);
|
|
6561
6658
|
try {
|
|
6562
6659
|
for (const skill of skills) validateCanonicalSkill(skill);
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6660
|
+
return {
|
|
6661
|
+
success: true,
|
|
6662
|
+
status: options.dryRun ? "planned" : "installed",
|
|
6663
|
+
skills,
|
|
6664
|
+
...planOrMoveLegacySkills(options)
|
|
6665
|
+
};
|
|
6566
6666
|
} catch (error) {
|
|
6567
6667
|
return {
|
|
6568
6668
|
success: false,
|
|
@@ -6604,6 +6704,178 @@ function inspectPiPackageSkills(options) {
|
|
|
6604
6704
|
}
|
|
6605
6705
|
}
|
|
6606
6706
|
|
|
6707
|
+
// src/cli/system.ts
|
|
6708
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
6709
|
+
import { statSync as statSync2 } from "fs";
|
|
6710
|
+
var cachedOpenCodePath = null;
|
|
6711
|
+
var OPENCODE_PROBE_TIMEOUT_MS = 1500;
|
|
6712
|
+
function quoteWindowsShellCommand(command) {
|
|
6713
|
+
if (!/[\s&()^<>"|]/.test(command)) {
|
|
6714
|
+
return command;
|
|
6715
|
+
}
|
|
6716
|
+
return `"${command.replace(/"/g, '\\"')}"`;
|
|
6717
|
+
}
|
|
6718
|
+
function getOpenCodeVersionInvocation(opencodePath, platform = process.platform) {
|
|
6719
|
+
const options = {
|
|
6720
|
+
stdout: "pipe",
|
|
6721
|
+
stderr: "pipe"
|
|
6722
|
+
};
|
|
6723
|
+
if (platform === "win32") {
|
|
6724
|
+
return {
|
|
6725
|
+
command: [`${quoteWindowsShellCommand(opencodePath)} --version`],
|
|
6726
|
+
options: { ...options, shell: true }
|
|
6727
|
+
};
|
|
6728
|
+
}
|
|
6729
|
+
return { command: [opencodePath, "--version"], options };
|
|
6730
|
+
}
|
|
6731
|
+
function getOpenCodePaths() {
|
|
6732
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
6733
|
+
return [
|
|
6734
|
+
// PATH (try this first)
|
|
6735
|
+
"opencode",
|
|
6736
|
+
// User local installations (Linux & macOS)
|
|
6737
|
+
`${home}/.local/bin/opencode`,
|
|
6738
|
+
`${home}/.opencode/bin/opencode`,
|
|
6739
|
+
`${home}/bin/opencode`,
|
|
6740
|
+
// System-wide installations
|
|
6741
|
+
"/usr/local/bin/opencode",
|
|
6742
|
+
"/opt/opencode/bin/opencode",
|
|
6743
|
+
"/usr/bin/opencode",
|
|
6744
|
+
"/bin/opencode",
|
|
6745
|
+
// macOS specific
|
|
6746
|
+
"/Applications/OpenCode.app/Contents/MacOS/opencode",
|
|
6747
|
+
`${home}/Applications/OpenCode.app/Contents/MacOS/opencode`,
|
|
6748
|
+
// Homebrew (macOS & Linux)
|
|
6749
|
+
"/opt/homebrew/bin/opencode",
|
|
6750
|
+
"/home/linuxbrew/.linuxbrew/bin/opencode",
|
|
6751
|
+
`${home}/homebrew/bin/opencode`,
|
|
6752
|
+
// macOS user Library
|
|
6753
|
+
`${home}/Library/Application Support/opencode/bin/opencode`,
|
|
6754
|
+
// Snap (Linux)
|
|
6755
|
+
"/snap/bin/opencode",
|
|
6756
|
+
"/var/snap/opencode/current/bin/opencode",
|
|
6757
|
+
// Flatpak (Linux)
|
|
6758
|
+
"/var/lib/flatpak/exports/bin/ai.opencode.OpenCode",
|
|
6759
|
+
`${home}/.local/share/flatpak/exports/bin/ai.opencode.OpenCode`,
|
|
6760
|
+
// Nix (Linux/macOS)
|
|
6761
|
+
"/nix/store/opencode/bin/opencode",
|
|
6762
|
+
`${home}/.nix-profile/bin/opencode`,
|
|
6763
|
+
"/run/current-system/sw/bin/opencode",
|
|
6764
|
+
// Cargo (Rust toolchain)
|
|
6765
|
+
`${home}/.cargo/bin/opencode`,
|
|
6766
|
+
// npm/npx global
|
|
6767
|
+
`${home}/.npm-global/bin/opencode`,
|
|
6768
|
+
"/usr/local/lib/node_modules/opencode/bin/opencode",
|
|
6769
|
+
// Yarn global
|
|
6770
|
+
`${home}/.yarn/bin/opencode`,
|
|
6771
|
+
// PNPM
|
|
6772
|
+
`${home}/.pnpm-global/bin/opencode`
|
|
6773
|
+
];
|
|
6774
|
+
}
|
|
6775
|
+
function resolveOpenCodePath() {
|
|
6776
|
+
if (cachedOpenCodePath) {
|
|
6777
|
+
return cachedOpenCodePath;
|
|
6778
|
+
}
|
|
6779
|
+
const paths = getOpenCodePaths();
|
|
6780
|
+
for (const opencodePath of paths) {
|
|
6781
|
+
if (opencodePath === "opencode") continue;
|
|
6782
|
+
try {
|
|
6783
|
+
const stat = statSync2(opencodePath);
|
|
6784
|
+
if (stat.isFile()) {
|
|
6785
|
+
cachedOpenCodePath = opencodePath;
|
|
6786
|
+
return opencodePath;
|
|
6787
|
+
}
|
|
6788
|
+
} catch {
|
|
6789
|
+
}
|
|
6790
|
+
}
|
|
6791
|
+
return "opencode";
|
|
6792
|
+
}
|
|
6793
|
+
async function isOpenCodeInstalled() {
|
|
6794
|
+
const paths = getOpenCodePaths();
|
|
6795
|
+
for (const opencodePath of paths) {
|
|
6796
|
+
try {
|
|
6797
|
+
const invocation = getOpenCodeVersionInvocation(opencodePath);
|
|
6798
|
+
const proc = spawn(invocation.command, {
|
|
6799
|
+
...invocation.options,
|
|
6800
|
+
windowsHide: true
|
|
6801
|
+
});
|
|
6802
|
+
let timer;
|
|
6803
|
+
const timedOut = /* @__PURE__ */ Symbol("timeout");
|
|
6804
|
+
try {
|
|
6805
|
+
const outcome = await Promise.race([
|
|
6806
|
+
proc.exited.then(() => proc.exitCode),
|
|
6807
|
+
new Promise((resolve11) => {
|
|
6808
|
+
timer = setTimeout(
|
|
6809
|
+
() => resolve11(timedOut),
|
|
6810
|
+
OPENCODE_PROBE_TIMEOUT_MS
|
|
6811
|
+
);
|
|
6812
|
+
})
|
|
6813
|
+
]);
|
|
6814
|
+
if (outcome === timedOut) {
|
|
6815
|
+
proc.kill();
|
|
6816
|
+
continue;
|
|
6817
|
+
}
|
|
6818
|
+
if (outcome === 0) {
|
|
6819
|
+
cachedOpenCodePath = opencodePath;
|
|
6820
|
+
return true;
|
|
6821
|
+
}
|
|
6822
|
+
} finally {
|
|
6823
|
+
if (timer) {
|
|
6824
|
+
clearTimeout(timer);
|
|
6825
|
+
}
|
|
6826
|
+
}
|
|
6827
|
+
} catch {
|
|
6828
|
+
}
|
|
6829
|
+
}
|
|
6830
|
+
return false;
|
|
6831
|
+
}
|
|
6832
|
+
var OPENCODE_VERSION_REQUIREMENT = "OpenCode 2.2.6 or later is required; install or upgrade OpenCode before applying configuration.";
|
|
6833
|
+
function isSupportedOpenCodeVersion(output) {
|
|
6834
|
+
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(
|
|
6835
|
+
output?.trim() ?? ""
|
|
6836
|
+
);
|
|
6837
|
+
if (!match) return false;
|
|
6838
|
+
const [, major, minor, patch] = match;
|
|
6839
|
+
return Number(major) > 2 || Number(major) === 2 && (Number(minor) > 2 || Number(minor) === 2 && Number(patch) >= 6);
|
|
6840
|
+
}
|
|
6841
|
+
function getOpenCodeVersionSync() {
|
|
6842
|
+
try {
|
|
6843
|
+
const invocation = getOpenCodeVersionInvocation(resolveOpenCodePath());
|
|
6844
|
+
const [command, ...args] = invocation.command;
|
|
6845
|
+
if (!command) return null;
|
|
6846
|
+
const result = spawnSync5(command, args, {
|
|
6847
|
+
shell: invocation.options.shell,
|
|
6848
|
+
windowsHide: true,
|
|
6849
|
+
encoding: "utf8",
|
|
6850
|
+
timeout: OPENCODE_PROBE_TIMEOUT_MS
|
|
6851
|
+
});
|
|
6852
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
6853
|
+
} catch {
|
|
6854
|
+
return null;
|
|
6855
|
+
}
|
|
6856
|
+
}
|
|
6857
|
+
async function getOpenCodeVersion() {
|
|
6858
|
+
const opencodePath = resolveOpenCodePath();
|
|
6859
|
+
try {
|
|
6860
|
+
const invocation = getOpenCodeVersionInvocation(opencodePath);
|
|
6861
|
+
const proc = spawn(invocation.command, {
|
|
6862
|
+
...invocation.options,
|
|
6863
|
+
windowsHide: true
|
|
6864
|
+
});
|
|
6865
|
+
const output = await new Response(proc.stdout).text();
|
|
6866
|
+
await proc.exited;
|
|
6867
|
+
if (proc.exitCode === 0) {
|
|
6868
|
+
return output.trim();
|
|
6869
|
+
}
|
|
6870
|
+
} catch {
|
|
6871
|
+
}
|
|
6872
|
+
return null;
|
|
6873
|
+
}
|
|
6874
|
+
function getOpenCodePath() {
|
|
6875
|
+
const path = resolveOpenCodePath();
|
|
6876
|
+
return path === "opencode" ? null : path;
|
|
6877
|
+
}
|
|
6878
|
+
|
|
6607
6879
|
// src/cli/operations/opencode.ts
|
|
6608
6880
|
var PACKAGE_NAME = "thoth-agents";
|
|
6609
6881
|
var EXECUTING_PACKAGE_VERSION = resolveExecutingPackageVersion();
|
|
@@ -6813,7 +7085,7 @@ var opencodeOperationAdapter = {
|
|
|
6813
7085
|
id: "opencode",
|
|
6814
7086
|
displayName: "OpenCode",
|
|
6815
7087
|
available: true,
|
|
6816
|
-
description: "OpenCode plugin and configuration surfaces.",
|
|
7088
|
+
description: "OpenCode 2.2.6 or later: pinned plugin and configuration surfaces.",
|
|
6817
7089
|
actions: openCodeActions
|
|
6818
7090
|
};
|
|
6819
7091
|
function targetForMainConfig(state) {
|
|
@@ -6822,7 +7094,7 @@ function targetForMainConfig(state) {
|
|
|
6822
7094
|
path: getExistingConfigPath(),
|
|
6823
7095
|
label: "OpenCode config",
|
|
6824
7096
|
...state ? { state } : {},
|
|
6825
|
-
expected: EXPECTED_PLUGIN ? `
|
|
7097
|
+
expected: EXPECTED_PLUGIN ? `plugins includes ${EXPECTED_PLUGIN}` : "plugins version unavailable"
|
|
6826
7098
|
};
|
|
6827
7099
|
}
|
|
6828
7100
|
function targetForLiteConfig(state) {
|
|
@@ -6866,18 +7138,21 @@ function openCodeSkillTargets(context2) {
|
|
|
6866
7138
|
observed: installed ? "required global skill installed" : "required global skill missing"
|
|
6867
7139
|
};
|
|
6868
7140
|
});
|
|
7141
|
+
const legacyCopies = inspectLegacyOpenCodeSkills(homeDir);
|
|
6869
7142
|
const ownedTargets = getOpenCodeOwnedSkillEntries({
|
|
6870
7143
|
homeDir
|
|
6871
7144
|
}).map((skill) => {
|
|
6872
|
-
const path = join8(skill.
|
|
7145
|
+
const path = join8(skill.sourcePath, "SKILL.md");
|
|
6873
7146
|
const installed = existsSync10(path);
|
|
6874
7147
|
return {
|
|
6875
7148
|
kind: "skill",
|
|
6876
7149
|
path,
|
|
6877
7150
|
label: titleCaseSkillName(skill.name),
|
|
6878
7151
|
state: installed ? "installed" : "missing",
|
|
6879
|
-
expected: "
|
|
6880
|
-
observed: installed ? "
|
|
7152
|
+
expected: "package-provided OpenCode skill",
|
|
7153
|
+
observed: `${installed ? "package-provided skill available" : "package-provided skill missing"}${legacyCopies.filter((copy) => copy.name === skill.name).map(
|
|
7154
|
+
(copy) => `; Warning: ${copy.sourcePath} shadows packaged skills; planned move to ${getLegacyOpenCodeSkillBackupRoot(homeDir)}/<new-run>/${copy.label}/${copy.name}; confirm with --move-legacy-opencode-skills`
|
|
7155
|
+
).join("")}`
|
|
6881
7156
|
};
|
|
6882
7157
|
});
|
|
6883
7158
|
const diagnostics = [];
|
|
@@ -6896,15 +7171,15 @@ function openCodeSkillTargets(context2) {
|
|
|
6896
7171
|
};
|
|
6897
7172
|
}
|
|
6898
7173
|
function configPluginMarker(config) {
|
|
6899
|
-
|
|
6900
|
-
return plugins.length > 0 ? `plugin: ${JSON.stringify(plugins)}` : "plugin: []";
|
|
7174
|
+
return `plugins: ${JSON.stringify(pluginEntries(config?.plugins))}; legacy plugin: ${JSON.stringify(pluginEntries(config?.plugin))}`;
|
|
6901
7175
|
}
|
|
6902
7176
|
function hasExpectedPlugin(config) {
|
|
6903
|
-
|
|
7177
|
+
const current = pluginEntries(config?.plugins);
|
|
7178
|
+
return EXPECTED_PLUGIN !== null && current.filter(isManagedOpenCodePlugin).length === 1 && current.includes(EXPECTED_PLUGIN) && !pluginEntries(config?.plugin).some(isManagedOpenCodePlugin);
|
|
6904
7179
|
}
|
|
6905
7180
|
function hasManagedPluginDrift(config) {
|
|
6906
|
-
return
|
|
6907
|
-
|
|
7181
|
+
return [...pluginEntries(config?.plugins), ...pluginEntries(config?.plugin)].some(
|
|
7182
|
+
isManagedOpenCodePlugin
|
|
6908
7183
|
) && !hasExpectedPlugin(config);
|
|
6909
7184
|
}
|
|
6910
7185
|
function liteConfigMarker(config) {
|
|
@@ -7347,6 +7622,7 @@ function getOpenCodeStatus(context2 = { cwd: process.cwd() }, evidence = {}) {
|
|
|
7347
7622
|
const managedStatus = getOpenCodeManagedStatus(context2);
|
|
7348
7623
|
return {
|
|
7349
7624
|
...managedStatus,
|
|
7625
|
+
summary: `${managedStatus.summary} Requires OpenCode 2.2.6 or later.`,
|
|
7350
7626
|
targets: [
|
|
7351
7627
|
...managedStatus.targets,
|
|
7352
7628
|
getCliManagedInstallVersionTarget("opencode", context2)
|
|
@@ -7479,7 +7755,6 @@ function buildOpenCodeCompletePlan(action, context2) {
|
|
|
7479
7755
|
const expectedPlugin = `${PACKAGE_NAME}@${version}`;
|
|
7480
7756
|
const generatedConfig = generateLiteConfig({
|
|
7481
7757
|
agent: "opencode",
|
|
7482
|
-
hasTmux: false,
|
|
7483
7758
|
dryRun: true,
|
|
7484
7759
|
reset: false
|
|
7485
7760
|
});
|
|
@@ -7500,16 +7775,10 @@ function buildOpenCodeCompletePlan(action, context2) {
|
|
|
7500
7775
|
title: `Ensure OpenCode plugin points at ${expectedPlugin}`,
|
|
7501
7776
|
target: {
|
|
7502
7777
|
...targetForMainConfig(),
|
|
7503
|
-
expected: `
|
|
7778
|
+
expected: `plugins includes ${expectedPlugin}`
|
|
7504
7779
|
},
|
|
7505
7780
|
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",
|
|
7781
|
+
preview: `plugins: ["${expectedPlugin}"]`,
|
|
7513
7782
|
backup: defaultBackup(getExistingConfigPath())
|
|
7514
7783
|
},
|
|
7515
7784
|
{
|
|
@@ -7519,7 +7788,7 @@ function buildOpenCodeCompletePlan(action, context2) {
|
|
|
7519
7788
|
backup: defaultBackup(getExistingLiteConfigPath())
|
|
7520
7789
|
},
|
|
7521
7790
|
{
|
|
7522
|
-
title: "
|
|
7791
|
+
title: "Inspect package-provided thoth-owned OpenCode skills",
|
|
7523
7792
|
target: {
|
|
7524
7793
|
kind: "skill",
|
|
7525
7794
|
label: "Thoth-owned OpenCode skills",
|
|
@@ -7585,7 +7854,6 @@ function buildOpenCodeSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
|
7585
7854
|
}
|
|
7586
7855
|
const generatedConfig = generateLiteConfig({
|
|
7587
7856
|
agent: "opencode",
|
|
7588
|
-
hasTmux: false,
|
|
7589
7857
|
dryRun: true,
|
|
7590
7858
|
reset: false
|
|
7591
7859
|
});
|
|
@@ -7598,19 +7866,13 @@ function buildOpenCodeSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
|
7598
7866
|
"opencode-sync-preview",
|
|
7599
7867
|
"sync",
|
|
7600
7868
|
"Sync OpenCode managed configuration",
|
|
7601
|
-
"Preview OpenCode
|
|
7869
|
+
"Preview pinned OpenCode plugins and thoth-agents config sync (requires OpenCode 2.2.6 or later).",
|
|
7602
7870
|
[
|
|
7603
7871
|
{
|
|
7604
7872
|
title: `Ensure OpenCode plugin points at ${PACKAGE_NAME}@${packageVersion.version}`,
|
|
7605
7873
|
target: targetForMainConfig(),
|
|
7606
7874
|
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",
|
|
7875
|
+
preview: `plugins: ["${PACKAGE_NAME}@${packageVersion.version}"]`,
|
|
7614
7876
|
backup: defaultBackup(getExistingConfigPath())
|
|
7615
7877
|
},
|
|
7616
7878
|
{
|
|
@@ -7620,7 +7882,7 @@ function buildOpenCodeSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
|
7620
7882
|
backup: defaultBackup(litePath)
|
|
7621
7883
|
},
|
|
7622
7884
|
{
|
|
7623
|
-
title: "
|
|
7885
|
+
title: "Inspect package-provided thoth-owned OpenCode skills",
|
|
7624
7886
|
target: {
|
|
7625
7887
|
kind: "skill",
|
|
7626
7888
|
label: "Thoth-owned OpenCode skills",
|
|
@@ -7917,7 +8179,6 @@ function applyModelPlan(roles) {
|
|
|
7917
8179
|
const parsed = parseConfig(targetPath);
|
|
7918
8180
|
const generated = generateLiteConfig({
|
|
7919
8181
|
agent: "opencode",
|
|
7920
|
-
hasTmux: false,
|
|
7921
8182
|
dryRun: false,
|
|
7922
8183
|
reset: false
|
|
7923
8184
|
});
|
|
@@ -8014,16 +8275,25 @@ function applyRequiredSkills(context2) {
|
|
|
8014
8275
|
function applyOwnedSkills(context2) {
|
|
8015
8276
|
const syncOwned = context2.syncOpenCodeOwnedSkills ?? syncOpenCodeOwnedSkills;
|
|
8016
8277
|
const result = syncOwned({
|
|
8278
|
+
moveLegacyOpenCodeSkills: context2.moveLegacyOpenCodeSkills,
|
|
8017
8279
|
homeDir: homeDirFromContext(context2)
|
|
8018
8280
|
});
|
|
8019
|
-
if (result.success)
|
|
8281
|
+
if (result.success)
|
|
8282
|
+
return {
|
|
8283
|
+
success: true,
|
|
8284
|
+
warnings: (result.notices ?? []).map((message) => ({
|
|
8285
|
+
severity: "minor",
|
|
8286
|
+
code: "opencode-legacy-skill-copy",
|
|
8287
|
+
message
|
|
8288
|
+
}))
|
|
8289
|
+
};
|
|
8020
8290
|
return {
|
|
8021
8291
|
success: false,
|
|
8022
8292
|
warnings: [
|
|
8023
8293
|
{
|
|
8024
8294
|
severity: "critical",
|
|
8025
8295
|
code: "opencode-owned-skills-sync-failed",
|
|
8026
|
-
message: `Failed to
|
|
8296
|
+
message: `Failed to inspect package-provided thoth-owned OpenCode skills: ${result.error ?? "unknown error"}.`
|
|
8027
8297
|
}
|
|
8028
8298
|
]
|
|
8029
8299
|
};
|
|
@@ -8074,6 +8344,16 @@ function applyOpenCodePlan(plan) {
|
|
|
8074
8344
|
issued.context
|
|
8075
8345
|
);
|
|
8076
8346
|
}
|
|
8347
|
+
const hostVersion = (issued.runtimeContext.getOpenCodeVersion ?? getOpenCodeVersionSync)();
|
|
8348
|
+
if (!isSupportedOpenCodeVersion(hostVersion)) {
|
|
8349
|
+
return rejectPlan3(
|
|
8350
|
+
plan,
|
|
8351
|
+
`${OPENCODE_VERSION_REQUIREMENT} Detected: ${hostVersion ?? "unavailable"}.`,
|
|
8352
|
+
"critical",
|
|
8353
|
+
"opencode-host-version-unsupported",
|
|
8354
|
+
issued.context
|
|
8355
|
+
);
|
|
8356
|
+
}
|
|
8077
8357
|
if (issued.payload.kind === "model") {
|
|
8078
8358
|
const invalidPayload = invalidModelPayloadReason(issued.payload.roles);
|
|
8079
8359
|
if (invalidPayload) {
|
|
@@ -8093,8 +8373,7 @@ function applyOpenCodePlan(plan) {
|
|
|
8093
8373
|
const updateMainConfig = issued.runtimeContext.updateOpenCodeMainConfig ?? updateOpenCodeMainConfig;
|
|
8094
8374
|
const mainConfigResult = updateMainConfig({
|
|
8095
8375
|
ensurePlugin: true,
|
|
8096
|
-
pluginVersion: issued.payload.version
|
|
8097
|
-
disableDefaults: true
|
|
8376
|
+
pluginVersion: issued.payload.version
|
|
8098
8377
|
});
|
|
8099
8378
|
if (!mainConfigResult.success) {
|
|
8100
8379
|
return rejectPlan3(
|
|
@@ -8107,7 +8386,7 @@ function applyOpenCodePlan(plan) {
|
|
|
8107
8386
|
}
|
|
8108
8387
|
changedTargets.push({
|
|
8109
8388
|
...targetForMainConfig("installed"),
|
|
8110
|
-
observed: `
|
|
8389
|
+
observed: `plugins includes ${PACKAGE_NAME}@${issued.payload.version}`
|
|
8111
8390
|
});
|
|
8112
8391
|
if (existsSync10(`${mainConfigResult.configPath}.bak`)) {
|
|
8113
8392
|
backups.push({
|
|
@@ -8119,7 +8398,6 @@ function applyOpenCodePlan(plan) {
|
|
|
8119
8398
|
const liteResult = writeLite(
|
|
8120
8399
|
{
|
|
8121
8400
|
agent: "opencode",
|
|
8122
|
-
hasTmux: false,
|
|
8123
8401
|
dryRun: false,
|
|
8124
8402
|
reset: false
|
|
8125
8403
|
},
|
|
@@ -8147,14 +8425,14 @@ function applyOpenCodePlan(plan) {
|
|
|
8147
8425
|
kind: "skill",
|
|
8148
8426
|
label: "Thoth-owned OpenCode skills",
|
|
8149
8427
|
state: ownedSkills.success ? "installed" : "drift",
|
|
8150
|
-
observed: ownedSkills.success ? "
|
|
8428
|
+
observed: ownedSkills.success ? "package-provided skills available" : "package-provided skill inspection failed"
|
|
8151
8429
|
});
|
|
8152
8430
|
if (!ownedSkills.success) {
|
|
8153
8431
|
return {
|
|
8154
8432
|
harness: "opencode",
|
|
8155
8433
|
action: plan.action,
|
|
8156
8434
|
applied: false,
|
|
8157
|
-
summary: "OpenCode configuration was written, but
|
|
8435
|
+
summary: "OpenCode configuration was written, but package-provided skills failed inspection.",
|
|
8158
8436
|
changedTargets,
|
|
8159
8437
|
backups,
|
|
8160
8438
|
warnings,
|
|
@@ -8224,28 +8502,28 @@ function applyOpenCodePlan(plan) {
|
|
|
8224
8502
|
}
|
|
8225
8503
|
|
|
8226
8504
|
// src/cli/operations/pi.ts
|
|
8227
|
-
import { spawnSync as
|
|
8505
|
+
import { spawnSync as spawnSync8 } from "child_process";
|
|
8228
8506
|
import { createHash as createHash7 } from "crypto";
|
|
8229
8507
|
import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
|
|
8230
8508
|
import { basename as basename4, dirname as dirname11, isAbsolute as isAbsolute5, join as join15 } from "path";
|
|
8231
8509
|
|
|
8232
8510
|
// src/cli/pi-install.ts
|
|
8233
|
-
import { spawnSync as
|
|
8511
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
8234
8512
|
import { createHash as createHash6 } from "crypto";
|
|
8235
8513
|
import {
|
|
8236
8514
|
existsSync as existsSync14,
|
|
8237
8515
|
lstatSync as lstatSync6,
|
|
8238
8516
|
readdirSync as readdirSync3,
|
|
8239
8517
|
readFileSync as readFileSync13,
|
|
8240
|
-
realpathSync as
|
|
8241
|
-
statSync as
|
|
8518
|
+
realpathSync as realpathSync4,
|
|
8519
|
+
statSync as statSync4
|
|
8242
8520
|
} from "fs";
|
|
8243
|
-
import { basename as basename3, dirname as dirname10, isAbsolute as isAbsolute4, join as join14, resolve as
|
|
8521
|
+
import { basename as basename3, dirname as dirname10, isAbsolute as isAbsolute4, join as join14, resolve as resolve10, sep as sep4 } from "path";
|
|
8244
8522
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
8245
8523
|
import { lt } from "semver";
|
|
8246
8524
|
|
|
8247
8525
|
// src/cli/pi-external-package.ts
|
|
8248
|
-
import { readFileSync as readFileSync9, realpathSync as
|
|
8526
|
+
import { readFileSync as readFileSync9, realpathSync as realpathSync3 } from "fs";
|
|
8249
8527
|
import { isAbsolute as isAbsolute2, join as join9 } from "path";
|
|
8250
8528
|
import { satisfies, valid } from "semver";
|
|
8251
8529
|
function npmPackageName(source) {
|
|
@@ -8274,7 +8552,7 @@ function piExternalSourceMatches(candidate, expectedSource) {
|
|
|
8274
8552
|
if (candidate.source === expectedSource) return true;
|
|
8275
8553
|
if (!isAbsolute2(expectedSource) || !candidate.installedPath) return false;
|
|
8276
8554
|
try {
|
|
8277
|
-
return
|
|
8555
|
+
return realpathSync3(candidate.installedPath) === realpathSync3(expectedSource);
|
|
8278
8556
|
} catch {
|
|
8279
8557
|
return false;
|
|
8280
8558
|
}
|
|
@@ -8360,7 +8638,7 @@ function inspectPiExternalPackage(packages, spec, requireManagedSource = true) {
|
|
|
8360
8638
|
}
|
|
8361
8639
|
|
|
8362
8640
|
// src/cli/pi-git-source.ts
|
|
8363
|
-
import { resolve as
|
|
8641
|
+
import { resolve as resolve6, sep as sep3 } from "path";
|
|
8364
8642
|
import hostedGitInfo from "hosted-git-info";
|
|
8365
8643
|
function splitRef(url) {
|
|
8366
8644
|
const scpLikeMatch = url.match(/^git@([^:]+):(.+)$/);
|
|
@@ -8502,9 +8780,9 @@ function projectGitPackagePath(source, projectRoot) {
|
|
|
8502
8780
|
return void 0;
|
|
8503
8781
|
const parsed = parseGitUrl(source);
|
|
8504
8782
|
if (!parsed) return void 0;
|
|
8505
|
-
const installRoot =
|
|
8506
|
-
const installedPath =
|
|
8507
|
-
if (installedPath !== installRoot && !installedPath.startsWith(`${installRoot}${
|
|
8783
|
+
const installRoot = resolve6(projectRoot, "git");
|
|
8784
|
+
const installedPath = resolve6(installRoot, parsed.host, parsed.path);
|
|
8785
|
+
if (installedPath !== installRoot && !installedPath.startsWith(`${installRoot}${sep3}`))
|
|
8508
8786
|
return void 0;
|
|
8509
8787
|
return installedPath;
|
|
8510
8788
|
}
|
|
@@ -8517,7 +8795,7 @@ import {
|
|
|
8517
8795
|
readdirSync as readdirSync2,
|
|
8518
8796
|
readFileSync as readFileSync10,
|
|
8519
8797
|
renameSync as renameSync4,
|
|
8520
|
-
statSync as
|
|
8798
|
+
statSync as statSync3,
|
|
8521
8799
|
writeFileSync as writeFileSync3
|
|
8522
8800
|
} from "fs";
|
|
8523
8801
|
import { dirname as dirname7, join as join10 } from "path";
|
|
@@ -8561,7 +8839,7 @@ function migrateLegacyPiResources(options) {
|
|
|
8561
8839
|
if (!existsSync11(source)) continue;
|
|
8562
8840
|
const canonical = join10(options.packageRoot, "skills", name);
|
|
8563
8841
|
const backup = `${source}.thoth-agents-legacy.bak`;
|
|
8564
|
-
if (!
|
|
8842
|
+
if (!statSync3(source).isDirectory() || !identicalTrees(source, canonical) || existsSync11(backup)) {
|
|
8565
8843
|
manualActions.push(
|
|
8566
8844
|
`Legacy Pi skill ${name} is modified or has ambiguous backup ownership; remove it manually after review.`
|
|
8567
8845
|
);
|
|
@@ -8611,16 +8889,16 @@ function migrateLegacyPiResources(options) {
|
|
|
8611
8889
|
}
|
|
8612
8890
|
|
|
8613
8891
|
// src/cli/pi-native-probe.ts
|
|
8614
|
-
import { spawnSync as
|
|
8892
|
+
import { spawnSync as spawnSync6 } from "child_process";
|
|
8615
8893
|
import {
|
|
8616
8894
|
existsSync as existsSync12,
|
|
8617
|
-
mkdtempSync
|
|
8895
|
+
mkdtempSync,
|
|
8618
8896
|
readFileSync as readFileSync11,
|
|
8619
|
-
rmSync as
|
|
8897
|
+
rmSync as rmSync4,
|
|
8620
8898
|
writeFileSync as writeFileSync4
|
|
8621
8899
|
} from "fs";
|
|
8622
8900
|
import { tmpdir } from "os";
|
|
8623
|
-
import { dirname as dirname8, join as join11, resolve as
|
|
8901
|
+
import { dirname as dirname8, join as join11, resolve as resolve7, win32 } from "path";
|
|
8624
8902
|
function resolvePiWindowsCliFromShim(shimPath, shimContents) {
|
|
8625
8903
|
const target = shimContents.match(
|
|
8626
8904
|
/"([^"\r\n]*@earendil-works[\\/]+pi-coding-agent[\\/]+dist[\\/]+bundle[\\/]+cli\.js)"\s+%\*/i
|
|
@@ -8633,7 +8911,7 @@ function resolvePiWindowsCliFromShim(shimPath, shimContents) {
|
|
|
8633
8911
|
return win32.isAbsolute(expanded) ? win32.normalize(expanded) : void 0;
|
|
8634
8912
|
}
|
|
8635
8913
|
function findPiWindowsCli() {
|
|
8636
|
-
const located =
|
|
8914
|
+
const located = spawnSync6("where.exe", ["pi.cmd"], {
|
|
8637
8915
|
windowsHide: true,
|
|
8638
8916
|
encoding: "utf8"
|
|
8639
8917
|
}).stdout?.split(/\r?\n/).find(Boolean);
|
|
@@ -8667,7 +8945,7 @@ function defaultExecutor(command, args, env) {
|
|
|
8667
8945
|
executableArgs = [cli, ...args];
|
|
8668
8946
|
}
|
|
8669
8947
|
}
|
|
8670
|
-
const result =
|
|
8948
|
+
const result = spawnSync6(executable, executableArgs, {
|
|
8671
8949
|
windowsHide: true,
|
|
8672
8950
|
encoding: "utf8",
|
|
8673
8951
|
timeout: 3e4,
|
|
@@ -8685,7 +8963,7 @@ function occurrences(text, marker) {
|
|
|
8685
8963
|
}
|
|
8686
8964
|
function observePiNativeRoot(options) {
|
|
8687
8965
|
const ownsIsolatedRoot = options.piHome === void 0;
|
|
8688
|
-
const isolatedRoot = options.piHome ??
|
|
8966
|
+
const isolatedRoot = options.piHome ?? mkdtempSync(join11(tmpdir(), "thoth-pi-observer-"));
|
|
8689
8967
|
const observerPath = join11(
|
|
8690
8968
|
isolatedRoot,
|
|
8691
8969
|
`observer-${process.pid}-${Date.now()}.mjs`
|
|
@@ -8765,7 +9043,7 @@ export default function observer(pi) {
|
|
|
8765
9043
|
"--offline",
|
|
8766
9044
|
"--no-extensions",
|
|
8767
9045
|
"--extension",
|
|
8768
|
-
|
|
9046
|
+
resolve7(options.extensionPath),
|
|
8769
9047
|
"--extension",
|
|
8770
9048
|
observerPath,
|
|
8771
9049
|
"--provider",
|
|
@@ -8838,8 +9116,8 @@ ${result.stderr}`.split(/\r?\n/).flatMap((line) => {
|
|
|
8838
9116
|
};
|
|
8839
9117
|
} finally {
|
|
8840
9118
|
if (ownsIsolatedRoot)
|
|
8841
|
-
|
|
8842
|
-
else
|
|
9119
|
+
rmSync4(isolatedRoot, { recursive: true, force: true });
|
|
9120
|
+
else rmSync4(observerPath, { force: true });
|
|
8843
9121
|
}
|
|
8844
9122
|
}
|
|
8845
9123
|
|
|
@@ -8849,11 +9127,11 @@ import {
|
|
|
8849
9127
|
mkdirSync as mkdirSync6,
|
|
8850
9128
|
readFileSync as readFileSync12,
|
|
8851
9129
|
renameSync as renameSync5,
|
|
8852
|
-
rmSync as
|
|
9130
|
+
rmSync as rmSync5,
|
|
8853
9131
|
writeFileSync as writeFileSync5
|
|
8854
9132
|
} from "fs";
|
|
8855
9133
|
import { homedir as homedir5 } from "os";
|
|
8856
|
-
import { dirname as dirname9, isAbsolute as isAbsolute3, join as join12, normalize, resolve as
|
|
9134
|
+
import { dirname as dirname9, isAbsolute as isAbsolute3, join as join12, normalize, resolve as resolve8 } from "path";
|
|
8857
9135
|
var keys = [
|
|
8858
9136
|
"schemaVersion",
|
|
8859
9137
|
"owner",
|
|
@@ -8868,8 +9146,8 @@ var keys = [
|
|
|
8868
9146
|
var semver = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
8869
9147
|
var digest = /^[0-9a-f]{64}$/;
|
|
8870
9148
|
function piPackagePathsEqual(left, right) {
|
|
8871
|
-
const normalizedLeft = normalize(
|
|
8872
|
-
const normalizedRight = normalize(
|
|
9149
|
+
const normalizedLeft = normalize(resolve8(left));
|
|
9150
|
+
const normalizedRight = normalize(resolve8(right));
|
|
8873
9151
|
return process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight;
|
|
8874
9152
|
}
|
|
8875
9153
|
function getPiPackageReceiptPath(options = {}) {
|
|
@@ -8926,7 +9204,7 @@ function writePiPackageReceipt(receipt, options = {}) {
|
|
|
8926
9204
|
try {
|
|
8927
9205
|
renameSync5(temporary, path);
|
|
8928
9206
|
} catch (error) {
|
|
8929
|
-
|
|
9207
|
+
rmSync5(temporary, { force: true });
|
|
8930
9208
|
throw error;
|
|
8931
9209
|
}
|
|
8932
9210
|
return { success: true, path };
|
|
@@ -8961,17 +9239,17 @@ function classifyPiPackageOwnership(input) {
|
|
|
8961
9239
|
|
|
8962
9240
|
// src/cli/pi-paths.ts
|
|
8963
9241
|
import { homedir as homedir6 } from "os";
|
|
8964
|
-
import { join as join13, resolve as
|
|
9242
|
+
import { join as join13, resolve as resolve9 } from "path";
|
|
8965
9243
|
function resolvePiPaths(options = {}) {
|
|
8966
9244
|
const env = options.env ?? process.env;
|
|
8967
|
-
const homeDir =
|
|
9245
|
+
const homeDir = resolve9(
|
|
8968
9246
|
options.homeDir ?? env.HOME ?? env.USERPROFILE ?? homedir6()
|
|
8969
9247
|
);
|
|
8970
9248
|
const defaultPiRoot = join13(homeDir, ".pi", "agent");
|
|
8971
9249
|
const configured = env.PI_CODING_AGENT_DIR?.trim();
|
|
8972
|
-
const piRoot = configured ?
|
|
8973
|
-
const cwd =
|
|
8974
|
-
const configRoot = env.XDG_CONFIG_HOME?.trim() ?
|
|
9250
|
+
const piRoot = configured ? resolve9(configured) : defaultPiRoot;
|
|
9251
|
+
const cwd = resolve9(options.cwd ?? process.cwd());
|
|
9252
|
+
const configRoot = env.XDG_CONFIG_HOME?.trim() ? resolve9(env.XDG_CONFIG_HOME) : join13(homeDir, ".config");
|
|
8975
9253
|
return {
|
|
8976
9254
|
homeDir,
|
|
8977
9255
|
piRoot,
|
|
@@ -9299,7 +9577,7 @@ function projectLocalPackagePath(source, projectRoot, homeDir) {
|
|
|
9299
9577
|
else if (path.startsWith("~/") || process.platform === "win32" && path.startsWith("~\\"))
|
|
9300
9578
|
path = join14(homeDir, path.slice(2));
|
|
9301
9579
|
else if (path.startsWith("file://")) path = fileURLToPath5(path);
|
|
9302
|
-
return
|
|
9580
|
+
return resolve10(projectRoot, path);
|
|
9303
9581
|
}
|
|
9304
9582
|
function legacyPiNpmPackagePath(name, settings, execute) {
|
|
9305
9583
|
try {
|
|
@@ -9310,7 +9588,7 @@ function legacyPiNpmPackagePath(name, settings, execute) {
|
|
|
9310
9588
|
if (/[\0\r\n&|<>^%"'`$;!]/.test(command)) return void 0;
|
|
9311
9589
|
const manager = basename3(command).replace(/\.(cmd|exe)$/i, "");
|
|
9312
9590
|
if (!["npm", "pnpm", "bun"].includes(manager)) return void 0;
|
|
9313
|
-
if (/\s/.test(command) && !
|
|
9591
|
+
if (/\s/.test(command) && !statSync4(command, { throwIfNoEntry: false })?.isFile())
|
|
9314
9592
|
return void 0;
|
|
9315
9593
|
const query = (args) => {
|
|
9316
9594
|
const result = execute(command, args);
|
|
@@ -9357,7 +9635,7 @@ function resolvePiPackage(candidate, options, userSettings) {
|
|
|
9357
9635
|
candidate = { ...candidate, resourceOnly };
|
|
9358
9636
|
if (candidate.installedPath) return candidate;
|
|
9359
9637
|
const paths = resolvePiPaths(options);
|
|
9360
|
-
const baseDir = candidate.scope === "user" ? paths.piRoot : join14(
|
|
9638
|
+
const baseDir = candidate.scope === "user" ? paths.piRoot : join14(resolve10(options.cwd ?? process.cwd()), ".pi");
|
|
9361
9639
|
const parsed = parsePiPackageSource(candidate.source, baseDir);
|
|
9362
9640
|
const npmName = parsed.type === "npm" ? parsed.name : void 0;
|
|
9363
9641
|
let path = npmName ? join14(baseDir, "npm", "node_modules", npmName) : parsed.type === "git" ? parsed.path : projectLocalPackagePath(candidate.source, baseDir, paths.homeDir);
|
|
@@ -9365,7 +9643,7 @@ function resolvePiPackage(candidate, options, userSettings) {
|
|
|
9365
9643
|
path = legacyPiNpmPackagePath(
|
|
9366
9644
|
npmName,
|
|
9367
9645
|
userSettings,
|
|
9368
|
-
options.commandExecutor ??
|
|
9646
|
+
options.commandExecutor ?? defaultCommandExecutor3
|
|
9369
9647
|
) ?? path;
|
|
9370
9648
|
return {
|
|
9371
9649
|
...candidate,
|
|
@@ -9381,7 +9659,7 @@ function resolvePiListedPackages(output, options) {
|
|
|
9381
9659
|
const packages = parsePiPackageList(output);
|
|
9382
9660
|
const paths = resolvePiPaths(options);
|
|
9383
9661
|
const settings = readJsonObject(paths.settingsPath);
|
|
9384
|
-
const projectRoot = join14(
|
|
9662
|
+
const projectRoot = join14(resolve10(options.cwd ?? process.cwd()), ".pi");
|
|
9385
9663
|
let projectSettings;
|
|
9386
9664
|
for (const { scope, source, installedPath } of packages) {
|
|
9387
9665
|
const baseDir = scope === "user" ? paths.piRoot : projectRoot;
|
|
@@ -9407,7 +9685,7 @@ function resolvePiListedPackages(output, options) {
|
|
|
9407
9685
|
});
|
|
9408
9686
|
}
|
|
9409
9687
|
function readPiProjectPackages(options) {
|
|
9410
|
-
const projectRoot = join14(
|
|
9688
|
+
const projectRoot = join14(resolve10(options.cwd ?? process.cwd()), ".pi");
|
|
9411
9689
|
return resolvePiConfiguredPackages(
|
|
9412
9690
|
readJsonObject(join14(projectRoot, "settings.json")),
|
|
9413
9691
|
"project",
|
|
@@ -9435,28 +9713,28 @@ function resolvePiPreservationPackages(output, options) {
|
|
|
9435
9713
|
];
|
|
9436
9714
|
}
|
|
9437
9715
|
function scanPiProjectInstalledIncumbents(options, configuredPackages) {
|
|
9438
|
-
const projectRoot = join14(
|
|
9716
|
+
const projectRoot = join14(resolve10(options.cwd ?? process.cwd()), ".pi");
|
|
9439
9717
|
const incumbents = [];
|
|
9440
9718
|
const visited = /* @__PURE__ */ new Set();
|
|
9441
9719
|
const pathKey = (path) => process.platform === "win32" ? path.toLowerCase() : path;
|
|
9442
9720
|
const knownPaths = new Set(
|
|
9443
9721
|
configuredPackages.flatMap(
|
|
9444
|
-
({ installedPath }) => installedPath ? [pathKey(
|
|
9722
|
+
({ installedPath }) => installedPath ? [pathKey(realpathSync4(installedPath))] : []
|
|
9445
9723
|
)
|
|
9446
9724
|
);
|
|
9447
9725
|
for (const root of [join14(projectRoot, "git"), join14(projectRoot, "npm")]) {
|
|
9448
9726
|
if (!existsSync14(root)) continue;
|
|
9449
|
-
const rootKey = pathKey(
|
|
9727
|
+
const rootKey = pathKey(realpathSync4(root));
|
|
9450
9728
|
const pending = [root];
|
|
9451
9729
|
while (pending.length > 0) {
|
|
9452
9730
|
const path = pending.pop();
|
|
9453
|
-
if (!path || !
|
|
9731
|
+
if (!path || !statSync4(path, { throwIfNoEntry: false })?.isDirectory())
|
|
9454
9732
|
continue;
|
|
9455
|
-
const key = pathKey(
|
|
9733
|
+
const key = pathKey(realpathSync4(path));
|
|
9456
9734
|
if (visited.has(key)) continue;
|
|
9457
9735
|
visited.add(key);
|
|
9458
9736
|
const manifestPath = join14(path, "package.json");
|
|
9459
|
-
if (existsSync14(manifestPath) &&
|
|
9737
|
+
if (existsSync14(manifestPath) && statSync4(manifestPath).isFile()) {
|
|
9460
9738
|
const candidate = {
|
|
9461
9739
|
scope: "project",
|
|
9462
9740
|
source: path,
|
|
@@ -9471,7 +9749,7 @@ function scanPiProjectInstalledIncumbents(options, configuredPackages) {
|
|
|
9471
9749
|
identityLimitation: `Installed manifest at ${path} identifies ${identity.packageName}, but no configured settings source maps to this directory.`
|
|
9472
9750
|
});
|
|
9473
9751
|
}
|
|
9474
|
-
if (key !== rootKey && !key.startsWith(`${rootKey}${
|
|
9752
|
+
if (key !== rootKey && !key.startsWith(`${rootKey}${sep4}`)) continue;
|
|
9475
9753
|
for (const entry of readdirSync3(path, { withFileTypes: true }))
|
|
9476
9754
|
if (entry.isDirectory() || entry.isSymbolicLink())
|
|
9477
9755
|
pending.push(join14(path, entry.name));
|
|
@@ -9585,7 +9863,7 @@ function buildPiSetupPlan(options = {}) {
|
|
|
9585
9863
|
if (options.runtimePackageRoot) {
|
|
9586
9864
|
const runtimeRoot = options.runtimePackageRoot;
|
|
9587
9865
|
const spec = getPiExternalPackageSpecs(options)[0];
|
|
9588
|
-
const sourceIsNormalizedAbsolutePath = isAbsolute4(runtimeRoot) &&
|
|
9866
|
+
const sourceIsNormalizedAbsolutePath = isAbsolute4(runtimeRoot) && resolve10(runtimeRoot) === runtimeRoot;
|
|
9589
9867
|
const inspection = sourceIsNormalizedAbsolutePath ? inspectPiExternalPackage(
|
|
9590
9868
|
[{ scope: "user", source: runtimeRoot, installedPath: runtimeRoot }],
|
|
9591
9869
|
spec
|
|
@@ -9643,7 +9921,7 @@ function buildPiSetupPlan(options = {}) {
|
|
|
9643
9921
|
blockers.push(error instanceof Error ? error.message : String(error));
|
|
9644
9922
|
}
|
|
9645
9923
|
const projectSettingsPath = join14(
|
|
9646
|
-
|
|
9924
|
+
resolve10(options.cwd ?? process.cwd()),
|
|
9647
9925
|
".pi",
|
|
9648
9926
|
"settings.json"
|
|
9649
9927
|
);
|
|
@@ -9935,7 +10213,7 @@ function getPiFirstPartyPackages(packages, knownSource) {
|
|
|
9935
10213
|
function matchesInstallSource(candidate, installSource) {
|
|
9936
10214
|
return isAbsolute4(installSource) ? candidate.installedPath !== void 0 && piPackagePathsEqual(candidate.installedPath, installSource) : candidate.source === installSource;
|
|
9937
10215
|
}
|
|
9938
|
-
function
|
|
10216
|
+
function defaultCommandExecutor3(command, args) {
|
|
9939
10217
|
let executable = command;
|
|
9940
10218
|
let executableArgs = [...args];
|
|
9941
10219
|
if (process.platform === "win32" && command === "pi") {
|
|
@@ -9945,7 +10223,7 @@ function defaultCommandExecutor4(command, args) {
|
|
|
9945
10223
|
executableArgs = [cli, ...args];
|
|
9946
10224
|
}
|
|
9947
10225
|
}
|
|
9948
|
-
const result =
|
|
10226
|
+
const result = spawnSync7(executable, executableArgs, {
|
|
9949
10227
|
windowsHide: true,
|
|
9950
10228
|
encoding: "utf8",
|
|
9951
10229
|
timeout: PI_COMMAND_TIMEOUT_MS,
|
|
@@ -9973,7 +10251,7 @@ function applyPiSetup(plan) {
|
|
|
9973
10251
|
};
|
|
9974
10252
|
if (plan.dryRun)
|
|
9975
10253
|
return { success: true, changed: [], diagnostics, installedPackages: [] };
|
|
9976
|
-
const execute = plan.options.commandExecutor ??
|
|
10254
|
+
const execute = plan.options.commandExecutor ?? defaultCommandExecutor3;
|
|
9977
10255
|
const changed = [];
|
|
9978
10256
|
const installedPackages = [];
|
|
9979
10257
|
let receiptCommitted = false;
|
|
@@ -10613,7 +10891,7 @@ function statusFromPlan(plan, context2, evidence = {}) {
|
|
|
10613
10891
|
description: item.kind === "package" ? "Validated installed-package manifest evidence; this does not prove live tool availability." : void 0
|
|
10614
10892
|
}));
|
|
10615
10893
|
const execute = context2.piCommandExecutor ?? ((command, args) => {
|
|
10616
|
-
const result =
|
|
10894
|
+
const result = spawnSync8(command, [...args], {
|
|
10617
10895
|
windowsHide: true,
|
|
10618
10896
|
encoding: "utf8",
|
|
10619
10897
|
timeout: 5e3,
|
|
@@ -11371,11 +11649,19 @@ function applyPiPlan(plan) {
|
|
|
11371
11649
|
homeDir: source.context.homeDir
|
|
11372
11650
|
}
|
|
11373
11651
|
});
|
|
11374
|
-
|
|
11375
|
-
|
|
11652
|
+
const completionEvidence = getInstallCompletionEvidence(completion, {
|
|
11653
|
+
codePrefix: "pi",
|
|
11654
|
+
version: source.version,
|
|
11655
|
+
fallbackError: "Pi provider setup or ledger commit failed."
|
|
11656
|
+
});
|
|
11657
|
+
if (!completion.success) {
|
|
11658
|
+
const rejected = reject(
|
|
11376
11659
|
completion.error ?? "Pi provider setup or ledger commit failed.",
|
|
11377
|
-
[...setupTargets, ...skillTargets]
|
|
11660
|
+
[...setupTargets, ...skillTargets, ...completionEvidence.targets]
|
|
11378
11661
|
);
|
|
11662
|
+
rejected.warnings.push(...completionEvidence.warnings);
|
|
11663
|
+
return rejected;
|
|
11664
|
+
}
|
|
11379
11665
|
return {
|
|
11380
11666
|
harness: "pi",
|
|
11381
11667
|
action: plan.action,
|
|
@@ -11384,12 +11670,7 @@ function applyPiPlan(plan) {
|
|
|
11384
11670
|
changedTargets: [
|
|
11385
11671
|
...setupTargets,
|
|
11386
11672
|
...skillTargets,
|
|
11387
|
-
|
|
11388
|
-
kind: "file",
|
|
11389
|
-
path: completion.ledger.path,
|
|
11390
|
-
label: "CLI-managed install version",
|
|
11391
|
-
state: "installed"
|
|
11392
|
-
}
|
|
11673
|
+
...completionEvidence.targets
|
|
11393
11674
|
],
|
|
11394
11675
|
diagnosticTargets: packageSkills.skills.map((skill) => ({
|
|
11395
11676
|
kind: "skill",
|
|
@@ -11398,7 +11679,7 @@ function applyPiPlan(plan) {
|
|
|
11398
11679
|
state: "installed"
|
|
11399
11680
|
})),
|
|
11400
11681
|
backups: [],
|
|
11401
|
-
warnings:
|
|
11682
|
+
warnings: completionEvidence.warnings,
|
|
11402
11683
|
disclaimers: disclaimers()
|
|
11403
11684
|
};
|
|
11404
11685
|
}
|
|
@@ -11962,6 +12243,7 @@ function buildRestoreModelPlan(harness, options, source = context) {
|
|
|
11962
12243
|
return buildTuiModelPlan(harness, roles, source);
|
|
11963
12244
|
}
|
|
11964
12245
|
var defaultTuiOperations = {
|
|
12246
|
+
legacyOpenCodeSkillCopies: () => inspectLegacyOpenCodeSkills().map((copy) => copy.sourcePath),
|
|
11965
12247
|
status(harness, evidence) {
|
|
11966
12248
|
if (harness === "opencode") return getOpenCodeStatus(context, evidence);
|
|
11967
12249
|
if (harness === "claude") {
|
|
@@ -11983,8 +12265,12 @@ var defaultTuiOperations = {
|
|
|
11983
12265
|
},
|
|
11984
12266
|
plan(harness, action, options) {
|
|
11985
12267
|
if (harness === "opencode") {
|
|
11986
|
-
|
|
11987
|
-
|
|
12268
|
+
const consentContext2 = {
|
|
12269
|
+
...context,
|
|
12270
|
+
moveLegacyOpenCodeSkills: options?.moveLegacyOpenCodeSkills === true
|
|
12271
|
+
};
|
|
12272
|
+
if (action === "install") return buildOpenCodeInstallPlan(consentContext2);
|
|
12273
|
+
if (action === "update") return buildOpenCodeUpdatePlan(consentContext2);
|
|
11988
12274
|
if (action === "sync") return buildOpenCodeSyncPlan(context);
|
|
11989
12275
|
return buildTuiModelPlan(harness, getOpenCodeModelRoles());
|
|
11990
12276
|
}
|
|
@@ -12061,8 +12347,15 @@ export {
|
|
|
12061
12347
|
buildClaudeCodeSetupPlan,
|
|
12062
12348
|
applyClaudeCodeSetup,
|
|
12063
12349
|
formatClaudeCodeSetupPlan,
|
|
12350
|
+
isOpenCodeInstalled,
|
|
12351
|
+
OPENCODE_VERSION_REQUIREMENT,
|
|
12352
|
+
isSupportedOpenCodeVersion,
|
|
12353
|
+
getOpenCodeVersion,
|
|
12354
|
+
getOpenCodePath,
|
|
12064
12355
|
recordHarnessInstallCompletion,
|
|
12065
12356
|
finalizeHarnessInstall,
|
|
12357
|
+
getLegacyOpenCodeSkillBackupRoot,
|
|
12358
|
+
inspectLegacyOpenCodeSkills,
|
|
12066
12359
|
syncOpenCodeOwnedSkills,
|
|
12067
12360
|
inspectPiPackageSkills,
|
|
12068
12361
|
buildPiSetupPlan,
|