thoth-agents 0.2.10 → 0.2.11
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/dist/{chunk-MF5NNCPC.js → chunk-6BNAV534.js} +1 -1
- package/dist/{chunk-YYVE7YHR.js → chunk-ASKXWU6E.js} +824 -182
- package/dist/{chunk-WBRGPRRB.js → chunk-OZE7JVGS.js} +29 -38
- package/dist/cli/config-io.d.ts +5 -0
- package/dist/cli/custom-skills.d.ts +2 -1
- package/dist/cli/index.js +19 -18
- package/dist/cli/operations/types.d.ts +2 -0
- package/dist/cli/tui/index.js +91 -18
- package/dist/index.js +2 -2
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
DEFAULT_THOTH_COMMAND,
|
|
11
11
|
GREP_APP_MCP_URL,
|
|
12
12
|
appendPromptSections,
|
|
13
|
+
checkCustomSkillsNeedUpdate,
|
|
13
14
|
claudeCodeSubagentType,
|
|
14
15
|
composeAgentPrompt,
|
|
15
16
|
createModelFamilySection,
|
|
@@ -17,9 +18,7 @@ import {
|
|
|
17
18
|
createReadOnlySpecialistPromptSections,
|
|
18
19
|
createStepBudgetSection,
|
|
19
20
|
createWriteCapableSpecialistPromptSections,
|
|
20
|
-
disableDefaultAgents,
|
|
21
21
|
ensureConfigDir,
|
|
22
|
-
ensureOpenCodeConfigDir,
|
|
23
22
|
exa,
|
|
24
23
|
findPackageRoot,
|
|
25
24
|
generateLiteConfig,
|
|
@@ -35,9 +34,10 @@ import {
|
|
|
35
34
|
parseConfig,
|
|
36
35
|
renderPromptSection,
|
|
37
36
|
renderRolePrompt,
|
|
37
|
+
updateOpenCodeMainConfig,
|
|
38
38
|
writeConfig,
|
|
39
39
|
writeLiteConfig
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-OZE7JVGS.js";
|
|
41
41
|
|
|
42
42
|
// src/cli/codex-paths.ts
|
|
43
43
|
import { homedir } from "os";
|
|
@@ -4180,6 +4180,7 @@ function applyCodexPlan(plan) {
|
|
|
4180
4180
|
}
|
|
4181
4181
|
|
|
4182
4182
|
// src/cli/operations/opencode.ts
|
|
4183
|
+
import { createHash as createHash3 } from "crypto";
|
|
4183
4184
|
import { existsSync as existsSync10 } from "fs";
|
|
4184
4185
|
import { join as join10 } from "path";
|
|
4185
4186
|
|
|
@@ -4292,6 +4293,143 @@ var PACKAGE_NAME = "thoth-agents";
|
|
|
4292
4293
|
var EXPECTED_PLUGIN = `${PACKAGE_NAME}@latest`;
|
|
4293
4294
|
var OPENAI_PRESET = "openai";
|
|
4294
4295
|
var ROLE_NAMES = [...ALL_AGENT_NAMES];
|
|
4296
|
+
var issuedOpenCodePlans = /* @__PURE__ */ new WeakMap();
|
|
4297
|
+
var NonCanonicalDataError = class extends Error {
|
|
4298
|
+
};
|
|
4299
|
+
function rejectSerializationHook(value, prototype) {
|
|
4300
|
+
if (Object.getOwnPropertyDescriptor(value, "toJSON")) {
|
|
4301
|
+
throw new NonCanonicalDataError("Own toJSON hooks are not canonical data.");
|
|
4302
|
+
}
|
|
4303
|
+
let current = prototype;
|
|
4304
|
+
while (current) {
|
|
4305
|
+
if (Object.getOwnPropertyDescriptor(current, "toJSON")) {
|
|
4306
|
+
throw new NonCanonicalDataError(
|
|
4307
|
+
"Inherited toJSON hooks are not canonical data."
|
|
4308
|
+
);
|
|
4309
|
+
}
|
|
4310
|
+
current = Object.getPrototypeOf(current);
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
function canonicalData(value, active) {
|
|
4314
|
+
if (value === null) return "null";
|
|
4315
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
4316
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
4317
|
+
if (typeof value === "number") {
|
|
4318
|
+
if (!Number.isFinite(value)) {
|
|
4319
|
+
throw new NonCanonicalDataError("Non-finite numbers are not canonical.");
|
|
4320
|
+
}
|
|
4321
|
+
return JSON.stringify(value);
|
|
4322
|
+
}
|
|
4323
|
+
if (typeof value !== "object") {
|
|
4324
|
+
throw new NonCanonicalDataError("Only JSON-like data is canonical.");
|
|
4325
|
+
}
|
|
4326
|
+
if (active.has(value)) {
|
|
4327
|
+
throw new NonCanonicalDataError("Cycles are not canonical.");
|
|
4328
|
+
}
|
|
4329
|
+
const prototype = Object.getPrototypeOf(value);
|
|
4330
|
+
const array = Array.isArray(value);
|
|
4331
|
+
if (array && prototype !== Array.prototype || !array && prototype !== Object.prototype && prototype !== null) {
|
|
4332
|
+
throw new NonCanonicalDataError("Non-plain prototypes are not canonical.");
|
|
4333
|
+
}
|
|
4334
|
+
rejectSerializationHook(value, prototype);
|
|
4335
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
4336
|
+
if (ownKeys.some((key) => typeof key === "symbol")) {
|
|
4337
|
+
throw new NonCanonicalDataError("Symbol keys are not canonical.");
|
|
4338
|
+
}
|
|
4339
|
+
const keys = ownKeys;
|
|
4340
|
+
active.add(value);
|
|
4341
|
+
try {
|
|
4342
|
+
if (array) {
|
|
4343
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
4344
|
+
if (!lengthDescriptor || !("value" in lengthDescriptor)) {
|
|
4345
|
+
throw new NonCanonicalDataError("Array length must be data.");
|
|
4346
|
+
}
|
|
4347
|
+
const length = lengthDescriptor.value;
|
|
4348
|
+
if (!Number.isSafeInteger(length) || length < 0) {
|
|
4349
|
+
throw new NonCanonicalDataError("Array length is invalid.");
|
|
4350
|
+
}
|
|
4351
|
+
const indexKeys = keys.filter((key) => key !== "length");
|
|
4352
|
+
if (indexKeys.length !== length || indexKeys.some(
|
|
4353
|
+
(key) => !/^(0|[1-9]\d*)$/.test(key) || Number(key) >= length || String(Number(key)) !== key
|
|
4354
|
+
)) {
|
|
4355
|
+
throw new NonCanonicalDataError(
|
|
4356
|
+
"Sparse arrays and extra array properties are not canonical."
|
|
4357
|
+
);
|
|
4358
|
+
}
|
|
4359
|
+
const parts2 = [];
|
|
4360
|
+
for (let index = 0; index < length; index += 1) {
|
|
4361
|
+
const descriptor = Object.getOwnPropertyDescriptor(
|
|
4362
|
+
value,
|
|
4363
|
+
String(index)
|
|
4364
|
+
);
|
|
4365
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
4366
|
+
throw new NonCanonicalDataError("Array accessors are not canonical.");
|
|
4367
|
+
}
|
|
4368
|
+
parts2.push(canonicalData(descriptor.value, active));
|
|
4369
|
+
}
|
|
4370
|
+
return `[${parts2.join(",")}]`;
|
|
4371
|
+
}
|
|
4372
|
+
const parts = [];
|
|
4373
|
+
for (const key of keys.sort()) {
|
|
4374
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
4375
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
4376
|
+
throw new NonCanonicalDataError("Accessors are not canonical.");
|
|
4377
|
+
}
|
|
4378
|
+
parts.push(
|
|
4379
|
+
`${JSON.stringify(key)}:${canonicalData(descriptor.value, active)}`
|
|
4380
|
+
);
|
|
4381
|
+
}
|
|
4382
|
+
return `{${parts.join(",")}}`;
|
|
4383
|
+
} finally {
|
|
4384
|
+
active.delete(value);
|
|
4385
|
+
}
|
|
4386
|
+
}
|
|
4387
|
+
function canonicalDigest(value) {
|
|
4388
|
+
try {
|
|
4389
|
+
const serialized = canonicalData(value, /* @__PURE__ */ new Set());
|
|
4390
|
+
return {
|
|
4391
|
+
ok: true,
|
|
4392
|
+
digest: createHash3("sha256").update(serialized).digest("hex")
|
|
4393
|
+
};
|
|
4394
|
+
} catch {
|
|
4395
|
+
return { ok: false };
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4398
|
+
function liveStatusDigest(status) {
|
|
4399
|
+
return canonicalDigest({
|
|
4400
|
+
state: status.state,
|
|
4401
|
+
diagnostics: status.diagnostics,
|
|
4402
|
+
targets: status.targets
|
|
4403
|
+
});
|
|
4404
|
+
}
|
|
4405
|
+
function immutableContext(context2) {
|
|
4406
|
+
return Object.freeze({
|
|
4407
|
+
cwd: context2.cwd,
|
|
4408
|
+
...context2.env ? { env: Object.freeze({ ...context2.env }) } : {}
|
|
4409
|
+
});
|
|
4410
|
+
}
|
|
4411
|
+
function issueOpenCodePlan(plan, status, context2, payload) {
|
|
4412
|
+
const planDigest = canonicalDigest(plan);
|
|
4413
|
+
const statusDigest = liveStatusDigest(status);
|
|
4414
|
+
if (!planDigest.ok || !planDigest.digest || !statusDigest.ok || !statusDigest.digest) {
|
|
4415
|
+
return plan;
|
|
4416
|
+
}
|
|
4417
|
+
const immutablePayload = payload.kind === "fixed" ? Object.freeze({ kind: "fixed", action: payload.action }) : Object.freeze({
|
|
4418
|
+
kind: "model",
|
|
4419
|
+
roles: Object.freeze(
|
|
4420
|
+
payload.roles.map((role) => Object.freeze({ ...role }))
|
|
4421
|
+
)
|
|
4422
|
+
});
|
|
4423
|
+
issuedOpenCodePlans.set(plan, {
|
|
4424
|
+
planDigest: planDigest.digest,
|
|
4425
|
+
liveStatusDigest: statusDigest.digest,
|
|
4426
|
+
harness: "opencode",
|
|
4427
|
+
action: payload.kind === "model" ? "model-config" : payload.action,
|
|
4428
|
+
context: immutableContext(context2),
|
|
4429
|
+
payload: immutablePayload
|
|
4430
|
+
});
|
|
4431
|
+
return plan;
|
|
4432
|
+
}
|
|
4295
4433
|
var openCodeActions = [
|
|
4296
4434
|
{
|
|
4297
4435
|
id: "opencode-status",
|
|
@@ -4360,7 +4498,7 @@ function targetForMainConfig(state) {
|
|
|
4360
4498
|
kind: "config",
|
|
4361
4499
|
path: getExistingConfigPath(),
|
|
4362
4500
|
label: "OpenCode config",
|
|
4363
|
-
state,
|
|
4501
|
+
...state ? { state } : {},
|
|
4364
4502
|
expected: `plugin includes ${EXPECTED_PLUGIN}`
|
|
4365
4503
|
};
|
|
4366
4504
|
}
|
|
@@ -4369,7 +4507,7 @@ function targetForLiteConfig(state) {
|
|
|
4369
4507
|
kind: "config",
|
|
4370
4508
|
path: getExistingLiteConfigPath(),
|
|
4371
4509
|
label: "thoth-agents config",
|
|
4372
|
-
state,
|
|
4510
|
+
...state ? { state } : {},
|
|
4373
4511
|
expected: `seven-agent ${OPENAI_PRESET} roster`
|
|
4374
4512
|
};
|
|
4375
4513
|
}
|
|
@@ -4384,7 +4522,7 @@ function titleCaseSkillName(value) {
|
|
|
4384
4522
|
}).join("-");
|
|
4385
4523
|
}
|
|
4386
4524
|
function homeDirFromContext(context2) {
|
|
4387
|
-
return context2.env?.HOME ?? context2.env?.USERPROFILE;
|
|
4525
|
+
return context2.env?.HOME ?? context2.env?.USERPROFILE ?? (context2.cwd !== process.cwd() ? join10(context2.cwd, "home") : void 0);
|
|
4388
4526
|
}
|
|
4389
4527
|
function openCodeSkillTargets(context2) {
|
|
4390
4528
|
const homeDir = homeDirFromContext(context2);
|
|
@@ -4402,16 +4540,38 @@ function openCodeSkillTargets(context2) {
|
|
|
4402
4540
|
};
|
|
4403
4541
|
}
|
|
4404
4542
|
);
|
|
4543
|
+
let staleSkillNames = /* @__PURE__ */ new Set();
|
|
4544
|
+
let removedSkillNames = [];
|
|
4545
|
+
let skillHealthError;
|
|
4546
|
+
try {
|
|
4547
|
+
const updateCheck = checkCustomSkillsNeedUpdate();
|
|
4548
|
+
staleSkillNames = new Set(
|
|
4549
|
+
updateCheck.skillsNeedingUpdate.map(({ skill }) => skill.name)
|
|
4550
|
+
);
|
|
4551
|
+
removedSkillNames = updateCheck.removedSkills;
|
|
4552
|
+
} catch (error) {
|
|
4553
|
+
skillHealthError = String(error);
|
|
4554
|
+
}
|
|
4405
4555
|
const bundledTargets = CUSTOM_SKILLS.map((skill) => {
|
|
4406
4556
|
const path4 = join10(getCustomSkillsDir(), skill.name, "SKILL.md");
|
|
4407
4557
|
const installed = existsSync10(path4);
|
|
4558
|
+
const stale = installed && staleSkillNames.has(skill.name);
|
|
4559
|
+
let state = "installed";
|
|
4560
|
+
let observed = "bundled OpenCode skill installed and current";
|
|
4561
|
+
if (!installed) {
|
|
4562
|
+
state = "missing";
|
|
4563
|
+
observed = "bundled OpenCode skill missing";
|
|
4564
|
+
} else if (stale) {
|
|
4565
|
+
state = "drift";
|
|
4566
|
+
observed = "bundled OpenCode skill is outdated";
|
|
4567
|
+
}
|
|
4408
4568
|
return {
|
|
4409
4569
|
kind: "skill",
|
|
4410
4570
|
path: path4,
|
|
4411
4571
|
label: titleCaseSkillName(skill.name),
|
|
4412
|
-
state
|
|
4572
|
+
state,
|
|
4413
4573
|
expected: "bundled thoth-agents OpenCode skill",
|
|
4414
|
-
observed
|
|
4574
|
+
observed
|
|
4415
4575
|
};
|
|
4416
4576
|
});
|
|
4417
4577
|
const diagnostics = [];
|
|
@@ -4429,6 +4589,20 @@ function openCodeSkillTargets(context2) {
|
|
|
4429
4589
|
code: "opencode-bundled-skills-missing"
|
|
4430
4590
|
});
|
|
4431
4591
|
}
|
|
4592
|
+
if (bundledTargets.some((target) => target.state === "drift") || removedSkillNames.length > 0) {
|
|
4593
|
+
diagnostics.push({
|
|
4594
|
+
severity: "important",
|
|
4595
|
+
message: "Bundled thoth-agents OpenCode skills are outdated; run OpenCode install or sync to refresh managed skills.",
|
|
4596
|
+
code: "opencode-bundled-skills-outdated"
|
|
4597
|
+
});
|
|
4598
|
+
}
|
|
4599
|
+
if (skillHealthError) {
|
|
4600
|
+
diagnostics.push({
|
|
4601
|
+
severity: "critical",
|
|
4602
|
+
message: `Bundled skill health could not be classified: ${skillHealthError}`,
|
|
4603
|
+
code: "opencode-bundled-skills-health-unknown"
|
|
4604
|
+
});
|
|
4605
|
+
}
|
|
4432
4606
|
return {
|
|
4433
4607
|
targets: [...recommendedTargets, ...bundledTargets],
|
|
4434
4608
|
diagnostics
|
|
@@ -4450,8 +4624,11 @@ function liteConfigMarker(config) {
|
|
|
4450
4624
|
const preset = typeof config?.preset === "string" ? config.preset : void 0;
|
|
4451
4625
|
const presets = config?.presets && typeof config.presets === "object" ? config.presets : {};
|
|
4452
4626
|
const openaiPreset = presets[OPENAI_PRESET] && typeof presets[OPENAI_PRESET] === "object" ? presets[OPENAI_PRESET] : {};
|
|
4453
|
-
const
|
|
4454
|
-
|
|
4627
|
+
const legacyAgents = config?.agents && typeof config.agents === "object" ? config.agents : {};
|
|
4628
|
+
const roles = ROLE_NAMES.filter(
|
|
4629
|
+
(role) => role in openaiPreset || role in legacyAgents
|
|
4630
|
+
);
|
|
4631
|
+
return `preset: ${preset ?? "none"}; observed roles: ${roles.join(", ")}`;
|
|
4455
4632
|
}
|
|
4456
4633
|
function hasSevenAgentPreset(config) {
|
|
4457
4634
|
if (!config || config.preset !== OPENAI_PRESET) return false;
|
|
@@ -4465,8 +4642,122 @@ function hasSevenAgentPreset(config) {
|
|
|
4465
4642
|
return value !== null && typeof value === "object" && typeof value.model === "string";
|
|
4466
4643
|
});
|
|
4467
4644
|
}
|
|
4468
|
-
function
|
|
4469
|
-
|
|
4645
|
+
function hasSelectedNamedPreset(config) {
|
|
4646
|
+
if (!config || typeof config.preset !== "string" || config.preset.length === 0 || config.preset === OPENAI_PRESET || config.preset === "agents") {
|
|
4647
|
+
return false;
|
|
4648
|
+
}
|
|
4649
|
+
if (!config.presets || typeof config.presets !== "object" || Array.isArray(config.presets)) {
|
|
4650
|
+
return false;
|
|
4651
|
+
}
|
|
4652
|
+
const presets = config.presets;
|
|
4653
|
+
if (!Object.hasOwn(presets, config.preset)) return false;
|
|
4654
|
+
const selectedPreset = presets[config.preset];
|
|
4655
|
+
return selectedPreset !== null && typeof selectedPreset === "object" && !Array.isArray(selectedPreset);
|
|
4656
|
+
}
|
|
4657
|
+
function hasLegacySevenAgentRoster(config) {
|
|
4658
|
+
if (!config || config.preset !== "agents") return false;
|
|
4659
|
+
if (!config.agents || typeof config.agents !== "object") return false;
|
|
4660
|
+
const agents = config.agents;
|
|
4661
|
+
return ROLE_NAMES.every((role) => {
|
|
4662
|
+
const value = agents[role];
|
|
4663
|
+
return value !== null && typeof value === "object" && typeof value.model === "string";
|
|
4664
|
+
});
|
|
4665
|
+
}
|
|
4666
|
+
var ACTIVE_PRESET_SELECTED_CODE = "opencode-active-preset-selected";
|
|
4667
|
+
function activePresetSelectedDiagnostic() {
|
|
4668
|
+
return {
|
|
4669
|
+
severity: "important",
|
|
4670
|
+
message: "A valid named preset is active; model configuration preserves it while sync or install may repair the managed roster.",
|
|
4671
|
+
code: ACTIVE_PRESET_SELECTED_CODE
|
|
4672
|
+
};
|
|
4673
|
+
}
|
|
4674
|
+
var REPAIRABLE_DIAGNOSTIC_CODES = /* @__PURE__ */ new Set([
|
|
4675
|
+
"opencode-plugin-drift",
|
|
4676
|
+
"opencode-main-config-missing",
|
|
4677
|
+
"opencode-lite-config-missing",
|
|
4678
|
+
"opencode-roster-drift",
|
|
4679
|
+
ACTIVE_PRESET_SELECTED_CODE,
|
|
4680
|
+
"opencode-bundled-skills-missing",
|
|
4681
|
+
"opencode-bundled-skills-outdated"
|
|
4682
|
+
]);
|
|
4683
|
+
var MODEL_PRECONFIG_SKILL_CODES = /* @__PURE__ */ new Set([
|
|
4684
|
+
"opencode-bundled-skills-missing",
|
|
4685
|
+
"opencode-bundled-skills-outdated"
|
|
4686
|
+
]);
|
|
4687
|
+
function uniqueTargets(targets) {
|
|
4688
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4689
|
+
return targets.filter((target) => {
|
|
4690
|
+
const key = JSON.stringify([
|
|
4691
|
+
target.kind,
|
|
4692
|
+
target.path,
|
|
4693
|
+
target.label,
|
|
4694
|
+
target.state,
|
|
4695
|
+
target.expected,
|
|
4696
|
+
target.observed
|
|
4697
|
+
]);
|
|
4698
|
+
if (seen.has(key)) return false;
|
|
4699
|
+
seen.add(key);
|
|
4700
|
+
return true;
|
|
4701
|
+
});
|
|
4702
|
+
}
|
|
4703
|
+
function targetsForBlockingDiagnostic(diagnostic, status) {
|
|
4704
|
+
const main = status.targets.find(
|
|
4705
|
+
(target) => target.label === "OpenCode config"
|
|
4706
|
+
);
|
|
4707
|
+
const lite = status.targets.find(
|
|
4708
|
+
(target) => target.label === "thoth-agents config"
|
|
4709
|
+
);
|
|
4710
|
+
const code = diagnostic.code;
|
|
4711
|
+
if (code === "opencode-plugin-drift" || code === "opencode-main-config-missing" || code === "opencode-config-parse-error" || code === "opencode-unmanaged-config") {
|
|
4712
|
+
return main ? [main] : [];
|
|
4713
|
+
}
|
|
4714
|
+
if (code === "opencode-roster-drift" || code === ACTIVE_PRESET_SELECTED_CODE || code === "opencode-lite-config-missing" || code === "thoth-config-parse-error" || code === "opencode-roster-unrecognized") {
|
|
4715
|
+
return lite ? [lite] : [];
|
|
4716
|
+
}
|
|
4717
|
+
if (code === "opencode-bundled-skills-missing" || code === "opencode-bundled-skills-outdated" || code === "opencode-bundled-skills-health-unknown") {
|
|
4718
|
+
return status.targets.filter(
|
|
4719
|
+
(target) => target.expected === "bundled thoth-agents OpenCode skill" && (code === "opencode-bundled-skills-health-unknown" || target.state !== "installed")
|
|
4720
|
+
);
|
|
4721
|
+
}
|
|
4722
|
+
return [
|
|
4723
|
+
{
|
|
4724
|
+
kind: "unknown",
|
|
4725
|
+
label: "Unknown OpenCode blocker",
|
|
4726
|
+
state: "unknown",
|
|
4727
|
+
observed: code ? `${code}: ${diagnostic.message}` : diagnostic.message
|
|
4728
|
+
}
|
|
4729
|
+
];
|
|
4730
|
+
}
|
|
4731
|
+
function blockingDetails(action, status, includeRepairable = false) {
|
|
4732
|
+
const main = status.targets.find(
|
|
4733
|
+
(target) => target.label === "OpenCode config"
|
|
4734
|
+
);
|
|
4735
|
+
const lite = status.targets.find(
|
|
4736
|
+
(target) => target.label === "thoth-agents config"
|
|
4737
|
+
);
|
|
4738
|
+
const allowModelPreconfiguration = action === "model-config" && main?.state === "missing" && lite?.state === "missing";
|
|
4739
|
+
const diagnostics = status.diagnostics.filter((diagnostic) => {
|
|
4740
|
+
if (diagnostic.severity === "critical") return true;
|
|
4741
|
+
if (diagnostic.severity === "minor") return false;
|
|
4742
|
+
if (!includeRepairable && action === "model-config" && diagnostic.code === ACTIVE_PRESET_SELECTED_CODE) {
|
|
4743
|
+
return false;
|
|
4744
|
+
}
|
|
4745
|
+
if (!includeRepairable && allowModelPreconfiguration && diagnostic.code !== void 0 && MODEL_PRECONFIG_SKILL_CODES.has(diagnostic.code)) {
|
|
4746
|
+
return false;
|
|
4747
|
+
}
|
|
4748
|
+
return includeRepairable || !((action === "sync" || action === "install") && diagnostic.code !== void 0 && REPAIRABLE_DIAGNOSTIC_CODES.has(diagnostic.code));
|
|
4749
|
+
});
|
|
4750
|
+
return {
|
|
4751
|
+
diagnostics,
|
|
4752
|
+
targets: uniqueTargets(
|
|
4753
|
+
diagnostics.flatMap(
|
|
4754
|
+
(diagnostic) => targetsForBlockingDiagnostic(diagnostic, status)
|
|
4755
|
+
)
|
|
4756
|
+
)
|
|
4757
|
+
};
|
|
4758
|
+
}
|
|
4759
|
+
function canApplyToManagedHealth(action, status) {
|
|
4760
|
+
return blockingDetails(action, status).diagnostics.length === 0;
|
|
4470
4761
|
}
|
|
4471
4762
|
function getOpenCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
4472
4763
|
const mainPath = getExistingConfigPath();
|
|
@@ -4522,6 +4813,18 @@ function getOpenCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
|
4522
4813
|
};
|
|
4523
4814
|
if (!mainExists || !hasExpectedPlugin(main.config)) {
|
|
4524
4815
|
if (hasManagedPluginDrift(main.config)) {
|
|
4816
|
+
const currentRoster = hasSevenAgentPreset(lite.config);
|
|
4817
|
+
const legacyRoster = hasLegacySevenAgentRoster(lite.config);
|
|
4818
|
+
const selectedNamedPreset = hasSelectedNamedPreset(lite.config);
|
|
4819
|
+
const recognizedRoster = currentRoster || legacyRoster || selectedNamedPreset;
|
|
4820
|
+
let liteState = "unknown";
|
|
4821
|
+
if (!liteExists) {
|
|
4822
|
+
liteState = "missing";
|
|
4823
|
+
} else if (currentRoster) {
|
|
4824
|
+
liteState = "installed";
|
|
4825
|
+
} else if (legacyRoster || selectedNamedPreset) {
|
|
4826
|
+
liteState = "drift";
|
|
4827
|
+
}
|
|
4525
4828
|
return {
|
|
4526
4829
|
harness: "opencode",
|
|
4527
4830
|
displayName: "OpenCode",
|
|
@@ -4529,7 +4832,10 @@ function getOpenCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
|
4529
4832
|
summary: "OpenCode config has a managed thoth-agents plugin entry that is not latest.",
|
|
4530
4833
|
targets: [
|
|
4531
4834
|
{ ...mainTarget, state: "drift" },
|
|
4532
|
-
{
|
|
4835
|
+
{
|
|
4836
|
+
...liteTarget,
|
|
4837
|
+
state: liteState
|
|
4838
|
+
},
|
|
4533
4839
|
...skillStatus.targets
|
|
4534
4840
|
],
|
|
4535
4841
|
diagnostics: [
|
|
@@ -4538,6 +4844,132 @@ function getOpenCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
|
4538
4844
|
message: "Managed plugin drift requires an explicit repair before applying sync plans.",
|
|
4539
4845
|
code: "opencode-plugin-drift"
|
|
4540
4846
|
},
|
|
4847
|
+
...legacyRoster ? [
|
|
4848
|
+
{
|
|
4849
|
+
severity: "important",
|
|
4850
|
+
message: "Roster drift requires repair before applying generated plans.",
|
|
4851
|
+
code: "opencode-roster-drift"
|
|
4852
|
+
}
|
|
4853
|
+
] : [],
|
|
4854
|
+
...selectedNamedPreset && !currentRoster ? [activePresetSelectedDiagnostic()] : [],
|
|
4855
|
+
...!liteExists ? [
|
|
4856
|
+
{
|
|
4857
|
+
severity: "important",
|
|
4858
|
+
message: "The managed thoth-agents config is missing and must be restored by install or sync.",
|
|
4859
|
+
code: "opencode-lite-config-missing"
|
|
4860
|
+
}
|
|
4861
|
+
] : [],
|
|
4862
|
+
...!liteExists ? [
|
|
4863
|
+
{
|
|
4864
|
+
severity: "important",
|
|
4865
|
+
message: "The managed thoth-agents config is missing and must be restored by install or sync.",
|
|
4866
|
+
code: "opencode-lite-config-missing"
|
|
4867
|
+
}
|
|
4868
|
+
] : [],
|
|
4869
|
+
...liteExists && !recognizedRoster ? [
|
|
4870
|
+
{
|
|
4871
|
+
severity: "critical",
|
|
4872
|
+
message: "Existing thoth-agents config has an unrecognized roster shape.",
|
|
4873
|
+
code: "opencode-roster-unrecognized"
|
|
4874
|
+
}
|
|
4875
|
+
] : [],
|
|
4876
|
+
...skillStatus.diagnostics
|
|
4877
|
+
],
|
|
4878
|
+
actions: openCodeActions
|
|
4879
|
+
};
|
|
4880
|
+
}
|
|
4881
|
+
if (!mainExists && liteExists) {
|
|
4882
|
+
const currentRoster = hasSevenAgentPreset(lite.config);
|
|
4883
|
+
const legacyRoster = hasLegacySevenAgentRoster(lite.config);
|
|
4884
|
+
const selectedNamedPreset = hasSelectedNamedPreset(lite.config);
|
|
4885
|
+
if (!currentRoster && !legacyRoster && !selectedNamedPreset) {
|
|
4886
|
+
return {
|
|
4887
|
+
harness: "opencode",
|
|
4888
|
+
displayName: "OpenCode",
|
|
4889
|
+
state: "unknown",
|
|
4890
|
+
summary: "Existing thoth-agents config has an unrecognized roster shape.",
|
|
4891
|
+
targets: [
|
|
4892
|
+
{ ...mainTarget, state: "missing" },
|
|
4893
|
+
{ ...liteTarget, state: "unknown" },
|
|
4894
|
+
...skillStatus.targets
|
|
4895
|
+
],
|
|
4896
|
+
diagnostics: [
|
|
4897
|
+
{
|
|
4898
|
+
severity: "critical",
|
|
4899
|
+
message: "Existing thoth-agents config is not a recognized current or legacy managed roster.",
|
|
4900
|
+
code: "opencode-roster-unrecognized"
|
|
4901
|
+
},
|
|
4902
|
+
...skillStatus.diagnostics
|
|
4903
|
+
],
|
|
4904
|
+
actions: openCodeActions
|
|
4905
|
+
};
|
|
4906
|
+
}
|
|
4907
|
+
if (selectedNamedPreset && !currentRoster) {
|
|
4908
|
+
return {
|
|
4909
|
+
harness: "opencode",
|
|
4910
|
+
displayName: "OpenCode",
|
|
4911
|
+
state: "drift",
|
|
4912
|
+
summary: "OpenCode plugin is missing and a valid named preset is active.",
|
|
4913
|
+
targets: [
|
|
4914
|
+
{ ...mainTarget, state: "missing" },
|
|
4915
|
+
{ ...liteTarget, state: "drift" },
|
|
4916
|
+
...skillStatus.targets
|
|
4917
|
+
],
|
|
4918
|
+
diagnostics: [
|
|
4919
|
+
{
|
|
4920
|
+
severity: "important",
|
|
4921
|
+
message: "The managed OpenCode main config is missing and must be restored by install or sync.",
|
|
4922
|
+
code: "opencode-main-config-missing"
|
|
4923
|
+
},
|
|
4924
|
+
activePresetSelectedDiagnostic(),
|
|
4925
|
+
...skillStatus.diagnostics
|
|
4926
|
+
],
|
|
4927
|
+
actions: openCodeActions
|
|
4928
|
+
};
|
|
4929
|
+
}
|
|
4930
|
+
if (legacyRoster) {
|
|
4931
|
+
return {
|
|
4932
|
+
harness: "opencode",
|
|
4933
|
+
displayName: "OpenCode",
|
|
4934
|
+
state: "drift",
|
|
4935
|
+
summary: "OpenCode plugin is missing and the managed roster uses the recognized legacy layout.",
|
|
4936
|
+
targets: [
|
|
4937
|
+
{ ...mainTarget, state: "missing" },
|
|
4938
|
+
{ ...liteTarget, state: "drift" },
|
|
4939
|
+
...skillStatus.targets
|
|
4940
|
+
],
|
|
4941
|
+
diagnostics: [
|
|
4942
|
+
{
|
|
4943
|
+
severity: "important",
|
|
4944
|
+
message: "The managed OpenCode main config is missing and must be restored by install or sync.",
|
|
4945
|
+
code: "opencode-main-config-missing"
|
|
4946
|
+
},
|
|
4947
|
+
{
|
|
4948
|
+
severity: "important",
|
|
4949
|
+
message: "Roster drift requires repair before applying generated plans.",
|
|
4950
|
+
code: "opencode-roster-drift"
|
|
4951
|
+
},
|
|
4952
|
+
...skillStatus.diagnostics
|
|
4953
|
+
],
|
|
4954
|
+
actions: openCodeActions
|
|
4955
|
+
};
|
|
4956
|
+
}
|
|
4957
|
+
return {
|
|
4958
|
+
harness: "opencode",
|
|
4959
|
+
displayName: "OpenCode",
|
|
4960
|
+
state: "missing",
|
|
4961
|
+
summary: "The managed roster is present, but the OpenCode main config is missing.",
|
|
4962
|
+
targets: [
|
|
4963
|
+
{ ...mainTarget, state: "missing" },
|
|
4964
|
+
{ ...liteTarget, state: "installed" },
|
|
4965
|
+
...skillStatus.targets
|
|
4966
|
+
],
|
|
4967
|
+
diagnostics: [
|
|
4968
|
+
{
|
|
4969
|
+
severity: "important",
|
|
4970
|
+
message: "The managed OpenCode main config is missing and must be restored by install or sync.",
|
|
4971
|
+
code: "opencode-main-config-missing"
|
|
4972
|
+
},
|
|
4541
4973
|
...skillStatus.diagnostics
|
|
4542
4974
|
],
|
|
4543
4975
|
actions: openCodeActions
|
|
@@ -4553,7 +4985,17 @@ function getOpenCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
|
4553
4985
|
{ ...liteTarget, state: liteExists ? "installed" : "missing" },
|
|
4554
4986
|
...skillStatus.targets
|
|
4555
4987
|
],
|
|
4556
|
-
diagnostics: [
|
|
4988
|
+
diagnostics: [
|
|
4989
|
+
...diagnostics,
|
|
4990
|
+
...mainExists ? [
|
|
4991
|
+
{
|
|
4992
|
+
severity: "critical",
|
|
4993
|
+
message: "Existing OpenCode config is not positively attributed to thoth-agents management.",
|
|
4994
|
+
code: "opencode-unmanaged-config"
|
|
4995
|
+
}
|
|
4996
|
+
] : [],
|
|
4997
|
+
...skillStatus.diagnostics
|
|
4998
|
+
],
|
|
4557
4999
|
actions: openCodeActions
|
|
4558
5000
|
};
|
|
4559
5001
|
}
|
|
@@ -4568,23 +5010,32 @@ function getOpenCodeStatus(context2 = { cwd: process.cwd() }) {
|
|
|
4568
5010
|
{ ...liteTarget, state: "missing" },
|
|
4569
5011
|
...skillStatus.targets
|
|
4570
5012
|
],
|
|
4571
|
-
diagnostics: [
|
|
5013
|
+
diagnostics: [
|
|
5014
|
+
...diagnostics,
|
|
5015
|
+
{
|
|
5016
|
+
severity: "important",
|
|
5017
|
+
message: "The managed thoth-agents config is missing and must be restored by install or sync.",
|
|
5018
|
+
code: "opencode-lite-config-missing"
|
|
5019
|
+
},
|
|
5020
|
+
...skillStatus.diagnostics
|
|
5021
|
+
],
|
|
4572
5022
|
actions: openCodeActions
|
|
4573
5023
|
};
|
|
4574
5024
|
}
|
|
4575
5025
|
if (!hasSevenAgentPreset(lite.config)) {
|
|
5026
|
+
const selectedNamedPreset = hasSelectedNamedPreset(lite.config);
|
|
4576
5027
|
return {
|
|
4577
5028
|
harness: "opencode",
|
|
4578
5029
|
displayName: "OpenCode",
|
|
4579
5030
|
state: "drift",
|
|
4580
|
-
summary: "thoth-agents config does not match the expected seven-agent roster.",
|
|
5031
|
+
summary: selectedNamedPreset ? "A valid named OpenCode preset is active outside the managed seven-agent roster." : "thoth-agents config does not match the expected seven-agent roster.",
|
|
4581
5032
|
targets: [
|
|
4582
5033
|
{ ...mainTarget, state: "installed" },
|
|
4583
5034
|
{ ...liteTarget, state: "drift" },
|
|
4584
5035
|
...skillStatus.targets
|
|
4585
5036
|
],
|
|
4586
5037
|
diagnostics: [
|
|
4587
|
-
{
|
|
5038
|
+
selectedNamedPreset ? activePresetSelectedDiagnostic() : {
|
|
4588
5039
|
severity: "important",
|
|
4589
5040
|
message: "Roster drift requires repair before applying generated plans.",
|
|
4590
5041
|
code: "opencode-roster-drift"
|
|
@@ -4615,6 +5066,37 @@ function defaultBackup(path4) {
|
|
|
4615
5066
|
destinations: [{ path: `${path4}.bak`, label: "managed backup" }]
|
|
4616
5067
|
};
|
|
4617
5068
|
}
|
|
5069
|
+
function backupFromItems(items) {
|
|
5070
|
+
const backups = items.flatMap((item) => item.backup ? [item.backup] : []);
|
|
5071
|
+
const primary = backups.find((backup) => backup.required) ?? backups[0];
|
|
5072
|
+
const destinations = [];
|
|
5073
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
5074
|
+
for (const backup of backups) {
|
|
5075
|
+
for (const destination of backup.destinations ?? []) {
|
|
5076
|
+
if (seenPaths.has(destination.path)) continue;
|
|
5077
|
+
seenPaths.add(destination.path);
|
|
5078
|
+
destinations.push(destination);
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5081
|
+
return {
|
|
5082
|
+
required: backups.some((backup) => backup.required),
|
|
5083
|
+
strategy: primary?.strategy ?? "none",
|
|
5084
|
+
...destinations.length > 0 ? { destinations } : {},
|
|
5085
|
+
...primary?.description ? { description: primary.description } : {}
|
|
5086
|
+
};
|
|
5087
|
+
}
|
|
5088
|
+
function bundledSkillsRefreshItem() {
|
|
5089
|
+
const names = CUSTOM_SKILLS.map(({ name }) => name);
|
|
5090
|
+
return {
|
|
5091
|
+
title: "Refresh bundled thoth-agents OpenCode skills",
|
|
5092
|
+
target: {
|
|
5093
|
+
kind: "skill",
|
|
5094
|
+
label: "Bundled thoth-agents OpenCode skills",
|
|
5095
|
+
expected: `${names.length} managed bundled skills`
|
|
5096
|
+
},
|
|
5097
|
+
preview: JSON.stringify({ count: names.length, names }, null, 2)
|
|
5098
|
+
};
|
|
5099
|
+
}
|
|
4618
5100
|
function defaultDisclaimers() {
|
|
4619
5101
|
return [
|
|
4620
5102
|
{
|
|
@@ -4623,9 +5105,10 @@ function defaultDisclaimers() {
|
|
|
4623
5105
|
}
|
|
4624
5106
|
];
|
|
4625
5107
|
}
|
|
4626
|
-
function planFromItems(id, action, title, summary, items) {
|
|
4627
|
-
const status = getOpenCodeStatus();
|
|
4628
|
-
const
|
|
5108
|
+
function planFromItems(id, action, title, summary, items, context2) {
|
|
5109
|
+
const status = getOpenCodeStatus(context2);
|
|
5110
|
+
const blockers = blockingDetails(action, status);
|
|
5111
|
+
const safe = blockers.diagnostics.length === 0;
|
|
4629
5112
|
const warnings = safe ? status.diagnostics : [
|
|
4630
5113
|
...status.diagnostics,
|
|
4631
5114
|
{
|
|
@@ -4635,37 +5118,41 @@ function planFromItems(id, action, title, summary, items) {
|
|
|
4635
5118
|
}
|
|
4636
5119
|
];
|
|
4637
5120
|
return {
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
5121
|
+
status,
|
|
5122
|
+
plan: {
|
|
5123
|
+
id,
|
|
5124
|
+
harness: "opencode",
|
|
5125
|
+
action,
|
|
5126
|
+
title,
|
|
5127
|
+
summary,
|
|
5128
|
+
dryRun: true,
|
|
5129
|
+
canApply: safe,
|
|
5130
|
+
targets: status.targets,
|
|
5131
|
+
blockerTargets: blockers.targets,
|
|
5132
|
+
surfaces: [
|
|
5133
|
+
{
|
|
5134
|
+
id: "opencode-config",
|
|
5135
|
+
label: "OpenCode user config",
|
|
5136
|
+
path: getExistingConfigPath(),
|
|
5137
|
+
state: status.targets[0]?.state
|
|
5138
|
+
},
|
|
5139
|
+
{
|
|
5140
|
+
id: "thoth-agents-config",
|
|
5141
|
+
label: "thoth-agents plugin config",
|
|
5142
|
+
path: getExistingLiteConfigPath(),
|
|
5143
|
+
state: status.targets[1]?.state
|
|
5144
|
+
}
|
|
5145
|
+
],
|
|
5146
|
+
backup: backupFromItems(items),
|
|
5147
|
+
items,
|
|
5148
|
+
warnings,
|
|
5149
|
+
disclaimers: defaultDisclaimers()
|
|
5150
|
+
}
|
|
4664
5151
|
};
|
|
4665
5152
|
}
|
|
4666
5153
|
function buildOpenCodeUpdatePlan(_context = { cwd: process.cwd() }) {
|
|
4667
5154
|
const path4 = getExistingConfigPath();
|
|
4668
|
-
|
|
5155
|
+
const { plan, status } = planFromItems(
|
|
4669
5156
|
"opencode-update-preview",
|
|
4670
5157
|
"update",
|
|
4671
5158
|
"Update OpenCode managed plugin entry",
|
|
@@ -4674,12 +5161,17 @@ function buildOpenCodeUpdatePlan(_context = { cwd: process.cwd() }) {
|
|
|
4674
5161
|
{
|
|
4675
5162
|
title: "Ensure OpenCode plugin points at thoth-agents@latest",
|
|
4676
5163
|
target: targetForMainConfig(),
|
|
4677
|
-
state: getOpenCodeStatus().state,
|
|
5164
|
+
state: getOpenCodeStatus(_context).state,
|
|
4678
5165
|
preview: `plugin: ["${EXPECTED_PLUGIN}"]`,
|
|
4679
5166
|
backup: defaultBackup(path4)
|
|
4680
5167
|
}
|
|
4681
|
-
]
|
|
5168
|
+
],
|
|
5169
|
+
_context
|
|
4682
5170
|
);
|
|
5171
|
+
return issueOpenCodePlan(plan, status, _context, {
|
|
5172
|
+
kind: "fixed",
|
|
5173
|
+
action: "update"
|
|
5174
|
+
});
|
|
4683
5175
|
}
|
|
4684
5176
|
function buildOpenCodeSyncPlan(_context = { cwd: process.cwd() }) {
|
|
4685
5177
|
const generatedConfig = generateLiteConfig({
|
|
@@ -4691,7 +5183,7 @@ function buildOpenCodeSyncPlan(_context = { cwd: process.cwd() }) {
|
|
|
4691
5183
|
reset: false
|
|
4692
5184
|
});
|
|
4693
5185
|
const litePath = getExistingLiteConfigPath();
|
|
4694
|
-
|
|
5186
|
+
const { plan, status } = planFromItems(
|
|
4695
5187
|
"opencode-sync-preview",
|
|
4696
5188
|
"sync",
|
|
4697
5189
|
"Sync OpenCode managed configuration",
|
|
@@ -4700,7 +5192,7 @@ function buildOpenCodeSyncPlan(_context = { cwd: process.cwd() }) {
|
|
|
4700
5192
|
{
|
|
4701
5193
|
title: "Ensure OpenCode plugin points at thoth-agents@latest",
|
|
4702
5194
|
target: targetForMainConfig(),
|
|
4703
|
-
state: getOpenCodeStatus().state,
|
|
5195
|
+
state: getOpenCodeStatus(_context).state,
|
|
4704
5196
|
preview: `plugin: ["${EXPECTED_PLUGIN}"]`,
|
|
4705
5197
|
backup: defaultBackup(getExistingConfigPath())
|
|
4706
5198
|
},
|
|
@@ -4715,9 +5207,15 @@ function buildOpenCodeSyncPlan(_context = { cwd: process.cwd() }) {
|
|
|
4715
5207
|
target: targetForLiteConfig(),
|
|
4716
5208
|
preview: JSON.stringify(generatedConfig, null, 2),
|
|
4717
5209
|
backup: defaultBackup(litePath)
|
|
4718
|
-
}
|
|
4719
|
-
|
|
5210
|
+
},
|
|
5211
|
+
bundledSkillsRefreshItem()
|
|
5212
|
+
],
|
|
5213
|
+
_context
|
|
4720
5214
|
);
|
|
5215
|
+
return issueOpenCodePlan(plan, status, _context, {
|
|
5216
|
+
kind: "fixed",
|
|
5217
|
+
action: "sync"
|
|
5218
|
+
});
|
|
4721
5219
|
}
|
|
4722
5220
|
function tuiInstallConfig() {
|
|
4723
5221
|
return {
|
|
@@ -4739,7 +5237,7 @@ function buildOpenCodeInstallPlan(_context = { cwd: process.cwd() }) {
|
|
|
4739
5237
|
equivalentCommand: "install --agent=opencode --no-tui --tmux=no --skills=yes"
|
|
4740
5238
|
};
|
|
4741
5239
|
const litePath = getExistingLiteConfigPath();
|
|
4742
|
-
|
|
5240
|
+
const { plan, status } = planFromItems(
|
|
4743
5241
|
"opencode-install-preview",
|
|
4744
5242
|
"install",
|
|
4745
5243
|
"Preview install",
|
|
@@ -4751,7 +5249,7 @@ function buildOpenCodeInstallPlan(_context = { cwd: process.cwd() }) {
|
|
|
4751
5249
|
kind: "config",
|
|
4752
5250
|
path: getExistingConfigPath(),
|
|
4753
5251
|
label: "OpenCode install options",
|
|
4754
|
-
state: getOpenCodeStatus().state,
|
|
5252
|
+
state: getOpenCodeStatus(_context).state,
|
|
4755
5253
|
expected: "--no-tui --tmux=no --skills=yes"
|
|
4756
5254
|
},
|
|
4757
5255
|
preview: JSON.stringify(installPreview, null, 2)
|
|
@@ -4759,7 +5257,7 @@ function buildOpenCodeInstallPlan(_context = { cwd: process.cwd() }) {
|
|
|
4759
5257
|
{
|
|
4760
5258
|
title: "Ensure OpenCode plugin points at thoth-agents@latest",
|
|
4761
5259
|
target: targetForMainConfig(),
|
|
4762
|
-
state: getOpenCodeStatus().state,
|
|
5260
|
+
state: getOpenCodeStatus(_context).state,
|
|
4763
5261
|
preview: `plugin: ["${EXPECTED_PLUGIN}"]`,
|
|
4764
5262
|
backup: defaultBackup(getExistingConfigPath())
|
|
4765
5263
|
},
|
|
@@ -4775,6 +5273,7 @@ function buildOpenCodeInstallPlan(_context = { cwd: process.cwd() }) {
|
|
|
4775
5273
|
preview: JSON.stringify(generatedConfig, null, 2),
|
|
4776
5274
|
backup: defaultBackup(litePath)
|
|
4777
5275
|
},
|
|
5276
|
+
bundledSkillsRefreshItem(),
|
|
4778
5277
|
{
|
|
4779
5278
|
title: "Install recommended external skills",
|
|
4780
5279
|
target: {
|
|
@@ -4784,8 +5283,13 @@ function buildOpenCodeInstallPlan(_context = { cwd: process.cwd() }) {
|
|
|
4784
5283
|
},
|
|
4785
5284
|
preview: JSON.stringify({ installSkills: true }, null, 2)
|
|
4786
5285
|
}
|
|
4787
|
-
]
|
|
5286
|
+
],
|
|
5287
|
+
_context
|
|
4788
5288
|
);
|
|
5289
|
+
return issueOpenCodePlan(plan, status, _context, {
|
|
5290
|
+
kind: "fixed",
|
|
5291
|
+
action: "install"
|
|
5292
|
+
});
|
|
4789
5293
|
}
|
|
4790
5294
|
function normalizeRoleModel(input) {
|
|
4791
5295
|
if (input.provider && !input.model.includes("/")) {
|
|
@@ -4794,16 +5298,24 @@ function normalizeRoleModel(input) {
|
|
|
4794
5298
|
return input.model;
|
|
4795
5299
|
}
|
|
4796
5300
|
function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
4797
|
-
const
|
|
5301
|
+
const normalizedRoles = input.roles.map((role) => {
|
|
4798
5302
|
const model = normalizeRoleModel(role);
|
|
4799
5303
|
const effort = resolveOpenCodeEffort(role);
|
|
5304
|
+
return Object.freeze({
|
|
5305
|
+
role: role.role,
|
|
5306
|
+
model,
|
|
5307
|
+
variant: effort.ok ? effort.variant ?? null : null,
|
|
5308
|
+
effort
|
|
5309
|
+
});
|
|
5310
|
+
});
|
|
5311
|
+
const items = normalizedRoles.map(({ role, model, variant, effort }) => {
|
|
4800
5312
|
return {
|
|
4801
|
-
title: `Set ${role
|
|
5313
|
+
title: `Set ${role} OpenCode model override`,
|
|
4802
5314
|
target: targetForLiteConfig(),
|
|
4803
5315
|
preview: JSON.stringify({
|
|
4804
|
-
[role
|
|
5316
|
+
[role]: {
|
|
4805
5317
|
model,
|
|
4806
|
-
variant
|
|
5318
|
+
variant
|
|
4807
5319
|
}
|
|
4808
5320
|
}),
|
|
4809
5321
|
backup: defaultBackup(getExistingLiteConfigPath()),
|
|
@@ -4818,12 +5330,13 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
4818
5330
|
} : {}
|
|
4819
5331
|
};
|
|
4820
5332
|
});
|
|
4821
|
-
const plan = planFromItems(
|
|
5333
|
+
const { plan, status } = planFromItems(
|
|
4822
5334
|
"opencode-model-config-preview",
|
|
4823
5335
|
"model-config",
|
|
4824
5336
|
"Configure OpenCode role model overrides",
|
|
4825
5337
|
"Preview thoth-agents role model overrides in the plugin config agents map.",
|
|
4826
|
-
items
|
|
5338
|
+
items,
|
|
5339
|
+
_context
|
|
4827
5340
|
);
|
|
4828
5341
|
const effortWarnings = items.flatMap((item) => item.warnings ?? []);
|
|
4829
5342
|
if (effortWarnings.length > 0) {
|
|
@@ -4831,7 +5344,7 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
4831
5344
|
plan.warnings.push(...effortWarnings);
|
|
4832
5345
|
}
|
|
4833
5346
|
if (input.harness !== "opencode") {
|
|
4834
|
-
|
|
5347
|
+
const rejectedPlan = {
|
|
4835
5348
|
...plan,
|
|
4836
5349
|
canApply: false,
|
|
4837
5350
|
warnings: [
|
|
@@ -4843,12 +5356,13 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
4843
5356
|
}
|
|
4844
5357
|
]
|
|
4845
5358
|
};
|
|
5359
|
+
return rejectedPlan;
|
|
4846
5360
|
}
|
|
4847
5361
|
const unsupportedRoles = input.roles.filter(
|
|
4848
5362
|
(role) => !ROLE_NAMES.includes(role.role)
|
|
4849
5363
|
);
|
|
4850
5364
|
if (input.roles.length === 0 || unsupportedRoles.length > 0) {
|
|
4851
|
-
|
|
5365
|
+
const rejectedPlan = {
|
|
4852
5366
|
...plan,
|
|
4853
5367
|
canApply: false,
|
|
4854
5368
|
warnings: [
|
|
@@ -4860,116 +5374,154 @@ function buildOpenCodeModelPlan(input, _context = { cwd: process.cwd() }) {
|
|
|
4860
5374
|
}
|
|
4861
5375
|
]
|
|
4862
5376
|
};
|
|
5377
|
+
return rejectedPlan;
|
|
4863
5378
|
}
|
|
4864
|
-
|
|
5379
|
+
if (effortWarnings.length > 0) {
|
|
5380
|
+
return plan;
|
|
5381
|
+
}
|
|
5382
|
+
const payloadRoles = Object.freeze(
|
|
5383
|
+
normalizedRoles.map(
|
|
5384
|
+
({ role, model, variant }) => Object.freeze({ role, model, variant })
|
|
5385
|
+
)
|
|
5386
|
+
);
|
|
5387
|
+
if (!plan.canApply || invalidModelPayloadReason(payloadRoles)) {
|
|
5388
|
+
return plan;
|
|
5389
|
+
}
|
|
5390
|
+
return issueOpenCodePlan(plan, status, _context, {
|
|
5391
|
+
kind: "model",
|
|
5392
|
+
roles: payloadRoles
|
|
5393
|
+
});
|
|
4865
5394
|
}
|
|
4866
|
-
function rejectPlan3(plan, message, severity = "critical") {
|
|
5395
|
+
function rejectPlan3(plan, message, severity = "critical", code = "opencode-plan-rejected", context2 = { cwd: process.cwd() }) {
|
|
5396
|
+
const issued = issuedOpenCodePlans.get(plan);
|
|
5397
|
+
const actionDescriptor = Object.getOwnPropertyDescriptor(plan, "action");
|
|
5398
|
+
const describedAction = actionDescriptor && "value" in actionDescriptor && typeof actionDescriptor.value === "string" ? actionDescriptor.value : void 0;
|
|
5399
|
+
const action = issued?.action ?? (describedAction === "install" || describedAction === "update" || describedAction === "sync" || describedAction === "model-config" ? describedAction : "repair");
|
|
5400
|
+
const liveStatus = getOpenCodeStatus(context2);
|
|
5401
|
+
const blockers = blockingDetails(
|
|
5402
|
+
action,
|
|
5403
|
+
liveStatus,
|
|
5404
|
+
code === "opencode-plan-live-state-changed"
|
|
5405
|
+
);
|
|
4867
5406
|
return {
|
|
4868
|
-
harness:
|
|
4869
|
-
action
|
|
5407
|
+
harness: issued?.harness ?? "opencode",
|
|
5408
|
+
action,
|
|
4870
5409
|
applied: false,
|
|
4871
5410
|
summary: message,
|
|
4872
5411
|
changedTargets: [],
|
|
5412
|
+
diagnosticTargets: blockers.targets,
|
|
4873
5413
|
backups: [],
|
|
4874
|
-
warnings: [{ severity, message }],
|
|
5414
|
+
warnings: [...liveStatus.diagnostics, { severity, code, message }],
|
|
4875
5415
|
disclaimers: defaultDisclaimers()
|
|
4876
5416
|
};
|
|
4877
5417
|
}
|
|
4878
|
-
function ensureLatestPluginEntry() {
|
|
4879
|
-
const configPath = getExistingConfigPath();
|
|
4880
|
-
try {
|
|
4881
|
-
ensureOpenCodeConfigDir();
|
|
4882
|
-
const { config: parsedConfig, error } = parseConfig(configPath);
|
|
4883
|
-
if (error) {
|
|
4884
|
-
return {
|
|
4885
|
-
success: false,
|
|
4886
|
-
configPath,
|
|
4887
|
-
error: `Failed to parse config: ${error}`
|
|
4888
|
-
};
|
|
4889
|
-
}
|
|
4890
|
-
const config = parsedConfig ?? {};
|
|
4891
|
-
const plugins = Array.isArray(config.plugin) ? config.plugin : [];
|
|
4892
|
-
config.plugin = [
|
|
4893
|
-
...plugins.filter(
|
|
4894
|
-
(plugin) => plugin !== PACKAGE_NAME && !plugin.startsWith(`${PACKAGE_NAME}@`)
|
|
4895
|
-
),
|
|
4896
|
-
EXPECTED_PLUGIN
|
|
4897
|
-
];
|
|
4898
|
-
writeConfig(configPath, config);
|
|
4899
|
-
return { success: true, configPath };
|
|
4900
|
-
} catch (err) {
|
|
4901
|
-
return {
|
|
4902
|
-
success: false,
|
|
4903
|
-
configPath,
|
|
4904
|
-
error: `Failed to update OpenCode plugin config: ${err}`
|
|
4905
|
-
};
|
|
4906
|
-
}
|
|
4907
|
-
}
|
|
4908
5418
|
function validateApplyPlan(plan) {
|
|
5419
|
+
const issued = issuedOpenCodePlans.get(plan);
|
|
5420
|
+
if (!issued) {
|
|
5421
|
+
return rejectPlan3(
|
|
5422
|
+
plan,
|
|
5423
|
+
"OpenCode plan was not issued by this process.",
|
|
5424
|
+
"critical",
|
|
5425
|
+
"opencode-plan-unissued"
|
|
5426
|
+
);
|
|
5427
|
+
}
|
|
5428
|
+
const planDigest = canonicalDigest(plan);
|
|
5429
|
+
if (!planDigest.ok || !planDigest.digest) {
|
|
5430
|
+
return rejectPlan3(
|
|
5431
|
+
plan,
|
|
5432
|
+
"OpenCode plan contains noncanonical data and cannot be applied.",
|
|
5433
|
+
"critical",
|
|
5434
|
+
"opencode-plan-noncanonical",
|
|
5435
|
+
issued.context
|
|
5436
|
+
);
|
|
5437
|
+
}
|
|
5438
|
+
if (planDigest.digest !== issued.planDigest) {
|
|
5439
|
+
return rejectPlan3(
|
|
5440
|
+
plan,
|
|
5441
|
+
"OpenCode plan changed after it was issued.",
|
|
5442
|
+
"critical",
|
|
5443
|
+
"opencode-plan-mutated",
|
|
5444
|
+
issued.context
|
|
5445
|
+
);
|
|
5446
|
+
}
|
|
5447
|
+
const liveStatus = getOpenCodeStatus(issued.context);
|
|
5448
|
+
const statusDigest = liveStatusDigest(liveStatus);
|
|
5449
|
+
if (!statusDigest.ok || !statusDigest.digest || statusDigest.digest !== issued.liveStatusDigest) {
|
|
5450
|
+
return rejectPlan3(
|
|
5451
|
+
plan,
|
|
5452
|
+
"OpenCode managed state changed after plan preview.",
|
|
5453
|
+
"critical",
|
|
5454
|
+
"opencode-plan-live-state-changed",
|
|
5455
|
+
issued.context
|
|
5456
|
+
);
|
|
5457
|
+
}
|
|
4909
5458
|
if (plan.harness !== "opencode") {
|
|
4910
|
-
return rejectPlan3(
|
|
5459
|
+
return rejectPlan3(
|
|
5460
|
+
plan,
|
|
5461
|
+
"Only OpenCode operation plans can be applied.",
|
|
5462
|
+
"critical",
|
|
5463
|
+
"opencode-plan-harness-mismatch",
|
|
5464
|
+
issued.context
|
|
5465
|
+
);
|
|
4911
5466
|
}
|
|
4912
5467
|
if (!plan.canApply) {
|
|
4913
5468
|
return rejectPlan3(
|
|
4914
5469
|
plan,
|
|
4915
|
-
"OpenCode plan cannot be applied because canApply is false."
|
|
5470
|
+
"OpenCode plan cannot be applied because canApply is false.",
|
|
5471
|
+
"critical",
|
|
5472
|
+
"opencode-plan-blocked",
|
|
5473
|
+
issued.context
|
|
4916
5474
|
);
|
|
4917
5475
|
}
|
|
4918
5476
|
if (!["install", "update", "sync", "model-config"].includes(plan.action)) {
|
|
4919
5477
|
return rejectPlan3(
|
|
4920
5478
|
plan,
|
|
4921
|
-
`Unsupported OpenCode apply action: ${plan.action}
|
|
5479
|
+
`Unsupported OpenCode apply action: ${plan.action}.`,
|
|
5480
|
+
"critical",
|
|
5481
|
+
"opencode-plan-action-unsupported",
|
|
5482
|
+
issued.context
|
|
4922
5483
|
);
|
|
4923
5484
|
}
|
|
4924
5485
|
if (plan.items.length === 0) {
|
|
4925
|
-
return rejectPlan3(plan, "OpenCode plan has no items to apply.");
|
|
4926
|
-
}
|
|
4927
|
-
if (plan.items.some(
|
|
4928
|
-
(item) => !item.title || !item.target.path && item.target.kind !== "skill" || item.state === "drift" || item.state === "unknown"
|
|
4929
|
-
)) {
|
|
4930
5486
|
return rejectPlan3(
|
|
4931
5487
|
plan,
|
|
4932
|
-
"OpenCode plan
|
|
5488
|
+
"OpenCode plan has no items to apply.",
|
|
5489
|
+
"critical",
|
|
5490
|
+
"opencode-plan-empty",
|
|
5491
|
+
issued.context
|
|
4933
5492
|
);
|
|
4934
5493
|
}
|
|
4935
5494
|
return null;
|
|
4936
5495
|
}
|
|
4937
|
-
function
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
);
|
|
5496
|
+
function invalidModelPayloadReason(roles) {
|
|
5497
|
+
if (!Array.isArray(roles) || roles.length === 0) {
|
|
5498
|
+
return "Model payload must contain at least one role override.";
|
|
5499
|
+
}
|
|
5500
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5501
|
+
for (const role of roles) {
|
|
5502
|
+
if (!role || typeof role !== "object" || typeof role.role !== "string" || !ROLE_NAMES.includes(role.role)) {
|
|
5503
|
+
return "Model payload contains an unsupported role.";
|
|
4946
5504
|
}
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
parsed2 = item.preview ? JSON.parse(item.preview) : {};
|
|
4950
|
-
} catch {
|
|
4951
|
-
return rejectPlan3(
|
|
4952
|
-
plan,
|
|
4953
|
-
"OpenCode model plan contains malformed preview JSON."
|
|
4954
|
-
);
|
|
5505
|
+
if (seen.has(role.role)) {
|
|
5506
|
+
return `Model payload contains duplicate role ${role.role}.`;
|
|
4955
5507
|
}
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
"OpenCode model plan contains an invalid role or model."
|
|
4963
|
-
);
|
|
5508
|
+
seen.add(role.role);
|
|
5509
|
+
if (typeof role.model !== "string" || role.model.trim().length === 0) {
|
|
5510
|
+
return `Model payload for ${role.role} has an empty model.`;
|
|
5511
|
+
}
|
|
5512
|
+
if (role.variant !== null && typeof role.variant !== "string") {
|
|
5513
|
+
return `Model payload for ${role.role} has an invalid variant.`;
|
|
4964
5514
|
}
|
|
4965
|
-
roleModels.set(role, { model, variant });
|
|
4966
|
-
}
|
|
4967
|
-
if (roleModels.size === 0) {
|
|
4968
|
-
return rejectPlan3(
|
|
4969
|
-
plan,
|
|
4970
|
-
"OpenCode model plan does not contain any role overrides."
|
|
4971
|
-
);
|
|
4972
5515
|
}
|
|
5516
|
+
return void 0;
|
|
5517
|
+
}
|
|
5518
|
+
function applyModelPlan(roles) {
|
|
5519
|
+
const roleModels = new Map(
|
|
5520
|
+
roles.map((role) => [
|
|
5521
|
+
role.role,
|
|
5522
|
+
{ model: role.model, variant: role.variant }
|
|
5523
|
+
])
|
|
5524
|
+
);
|
|
4973
5525
|
ensureConfigDir();
|
|
4974
5526
|
const targetPath = getExistingLiteConfigPath();
|
|
4975
5527
|
const parsed = parseConfig(targetPath);
|
|
@@ -5036,68 +5588,148 @@ function applyModelPlan(plan) {
|
|
|
5036
5588
|
disclaimers: defaultDisclaimers()
|
|
5037
5589
|
};
|
|
5038
5590
|
}
|
|
5039
|
-
function
|
|
5591
|
+
function applyRecommendedSkills() {
|
|
5592
|
+
const warnings = [];
|
|
5040
5593
|
for (const skill of RECOMMENDED_SKILLS) {
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5594
|
+
try {
|
|
5595
|
+
const result = installRecommendedSkill(skill);
|
|
5596
|
+
if (result.status !== "failed") continue;
|
|
5597
|
+
warnings.push({
|
|
5598
|
+
severity: "important",
|
|
5599
|
+
code: "opencode-recommended-skill-failed",
|
|
5600
|
+
message: `Failed to install optional recommended OpenCode skill: ${skill.name}.`
|
|
5601
|
+
});
|
|
5602
|
+
} catch (error) {
|
|
5603
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
5604
|
+
warnings.push({
|
|
5605
|
+
severity: "important",
|
|
5606
|
+
code: "opencode-recommended-skill-failed",
|
|
5607
|
+
message: `Failed to install optional recommended OpenCode skill ${skill.name}: ${reason}.`
|
|
5608
|
+
});
|
|
5047
5609
|
}
|
|
5048
5610
|
}
|
|
5049
|
-
|
|
5611
|
+
return warnings;
|
|
5612
|
+
}
|
|
5613
|
+
function bundledSkillsFailure(plan, changedTargets, backups, warnings, detail) {
|
|
5614
|
+
const message = `Failed to install bundled thoth-agents OpenCode skills: ${detail}.`;
|
|
5615
|
+
return {
|
|
5616
|
+
harness: plan.harness,
|
|
5617
|
+
action: plan.action,
|
|
5618
|
+
applied: false,
|
|
5619
|
+
summary: message,
|
|
5620
|
+
changedTargets,
|
|
5621
|
+
backups,
|
|
5622
|
+
warnings: [
|
|
5623
|
+
...warnings,
|
|
5624
|
+
{
|
|
5625
|
+
severity: "critical",
|
|
5626
|
+
code: "opencode-bundled-skills-failed",
|
|
5627
|
+
message
|
|
5628
|
+
}
|
|
5629
|
+
],
|
|
5630
|
+
disclaimers: defaultDisclaimers()
|
|
5631
|
+
};
|
|
5632
|
+
}
|
|
5633
|
+
function applyBundledSkills(plan, changedTargets, backups, warnings) {
|
|
5634
|
+
let bundled;
|
|
5635
|
+
try {
|
|
5636
|
+
bundled = installCustomSkills();
|
|
5637
|
+
} catch (error) {
|
|
5638
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
5639
|
+
return bundledSkillsFailure(
|
|
5640
|
+
plan,
|
|
5641
|
+
changedTargets,
|
|
5642
|
+
backups,
|
|
5643
|
+
warnings,
|
|
5644
|
+
detail
|
|
5645
|
+
);
|
|
5646
|
+
}
|
|
5050
5647
|
if (!bundled.success) {
|
|
5051
|
-
|
|
5648
|
+
const failedNames = bundled.failedSkills.map(({ skill }) => skill.name);
|
|
5649
|
+
const detail = failedNames.length > 0 ? failedNames.join(", ") : "unknown bundled skills";
|
|
5650
|
+
return bundledSkillsFailure(
|
|
5052
5651
|
plan,
|
|
5053
|
-
|
|
5652
|
+
changedTargets,
|
|
5653
|
+
backups,
|
|
5654
|
+
warnings,
|
|
5655
|
+
detail
|
|
5054
5656
|
);
|
|
5055
5657
|
}
|
|
5658
|
+
changedTargets.push({
|
|
5659
|
+
kind: "skill",
|
|
5660
|
+
label: "Bundled thoth-agents OpenCode skills",
|
|
5661
|
+
state: "installed",
|
|
5662
|
+
observed: "managed bundled skills refreshed"
|
|
5663
|
+
});
|
|
5056
5664
|
return null;
|
|
5057
5665
|
}
|
|
5058
5666
|
function applyOpenCodePlan(plan) {
|
|
5059
5667
|
const rejection = validateApplyPlan(plan);
|
|
5060
5668
|
if (rejection) return rejection;
|
|
5061
|
-
const
|
|
5062
|
-
if (!
|
|
5669
|
+
const issued = issuedOpenCodePlans.get(plan);
|
|
5670
|
+
if (!issued) {
|
|
5063
5671
|
return rejectPlan3(
|
|
5064
5672
|
plan,
|
|
5065
|
-
|
|
5673
|
+
"OpenCode plan provenance was lost before apply.",
|
|
5674
|
+
"critical",
|
|
5675
|
+
"opencode-plan-unissued"
|
|
5066
5676
|
);
|
|
5067
5677
|
}
|
|
5068
|
-
|
|
5678
|
+
const status = getOpenCodeStatus(issued.context);
|
|
5679
|
+
if (!canApplyToManagedHealth(plan.action, status)) {
|
|
5680
|
+
return rejectPlan3(
|
|
5681
|
+
plan,
|
|
5682
|
+
`OpenCode state is ${status.state}; refusing to apply plan without a safe status.`,
|
|
5683
|
+
"critical",
|
|
5684
|
+
"opencode-plan-live-state-blocked",
|
|
5685
|
+
issued.context
|
|
5686
|
+
);
|
|
5687
|
+
}
|
|
5688
|
+
if (issued.payload.kind === "model") {
|
|
5689
|
+
const invalidPayload = invalidModelPayloadReason(issued.payload.roles);
|
|
5690
|
+
if (invalidPayload) {
|
|
5691
|
+
return rejectPlan3(
|
|
5692
|
+
plan,
|
|
5693
|
+
invalidPayload,
|
|
5694
|
+
"critical",
|
|
5695
|
+
"opencode-model-payload-invalid",
|
|
5696
|
+
issued.context
|
|
5697
|
+
);
|
|
5698
|
+
}
|
|
5699
|
+
return applyModelPlan(issued.payload.roles);
|
|
5700
|
+
}
|
|
5069
5701
|
const changedTargets = [];
|
|
5070
5702
|
const backups = [];
|
|
5071
|
-
const
|
|
5072
|
-
|
|
5703
|
+
const warnings = [];
|
|
5704
|
+
const mainConfigResult = updateOpenCodeMainConfig({
|
|
5705
|
+
ensurePlugin: true,
|
|
5706
|
+
disableDefaults: issued.payload.action === "sync" || issued.payload.action === "install"
|
|
5707
|
+
});
|
|
5708
|
+
if (!mainConfigResult.success) {
|
|
5073
5709
|
return rejectPlan3(
|
|
5074
5710
|
plan,
|
|
5075
|
-
|
|
5711
|
+
mainConfigResult.error ?? "Failed to update OpenCode main config.",
|
|
5712
|
+
"critical",
|
|
5713
|
+
"opencode-main-config-update-failed",
|
|
5714
|
+
issued.context
|
|
5076
5715
|
);
|
|
5077
5716
|
}
|
|
5078
5717
|
changedTargets.push({
|
|
5079
5718
|
...targetForMainConfig("installed"),
|
|
5080
5719
|
observed: `plugin includes ${EXPECTED_PLUGIN}`
|
|
5081
5720
|
});
|
|
5082
|
-
if (existsSync10(`${
|
|
5721
|
+
if (existsSync10(`${mainConfigResult.configPath}.bak`)) {
|
|
5083
5722
|
backups.push({
|
|
5084
|
-
path: `${
|
|
5723
|
+
path: `${mainConfigResult.configPath}.bak`,
|
|
5085
5724
|
label: "OpenCode config backup"
|
|
5086
5725
|
});
|
|
5087
5726
|
}
|
|
5088
|
-
if (
|
|
5089
|
-
const defaultAgentResult = disableDefaultAgents();
|
|
5090
|
-
if (!defaultAgentResult.success) {
|
|
5091
|
-
return rejectPlan3(
|
|
5092
|
-
plan,
|
|
5093
|
-
defaultAgentResult.error ?? "Failed to disable OpenCode default agents."
|
|
5094
|
-
);
|
|
5095
|
-
}
|
|
5727
|
+
if (issued.payload.action === "sync" || issued.payload.action === "install") {
|
|
5096
5728
|
const liteResult = writeLiteConfig(
|
|
5097
5729
|
{
|
|
5098
5730
|
agent: "opencode",
|
|
5099
5731
|
hasTmux: false,
|
|
5100
|
-
installSkills:
|
|
5732
|
+
installSkills: issued.payload.action === "install",
|
|
5101
5733
|
installCustomSkills: true,
|
|
5102
5734
|
dryRun: false,
|
|
5103
5735
|
reset: false
|
|
@@ -5120,15 +5752,23 @@ function applyOpenCodePlan(plan) {
|
|
|
5120
5752
|
label: "thoth-agents config backup"
|
|
5121
5753
|
});
|
|
5122
5754
|
}
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5755
|
+
const skillsRejection = applyBundledSkills(
|
|
5756
|
+
plan,
|
|
5757
|
+
changedTargets,
|
|
5758
|
+
backups,
|
|
5759
|
+
warnings
|
|
5760
|
+
);
|
|
5126
5761
|
if (skillsRejection) return skillsRejection;
|
|
5762
|
+
}
|
|
5763
|
+
if (issued.payload.action === "install") {
|
|
5764
|
+
warnings.push(...applyRecommendedSkills());
|
|
5127
5765
|
changedTargets.push({
|
|
5128
5766
|
kind: "skill",
|
|
5129
|
-
label: "Recommended
|
|
5130
|
-
state:
|
|
5131
|
-
|
|
5767
|
+
label: "Recommended OpenCode skills",
|
|
5768
|
+
state: warnings.some(
|
|
5769
|
+
(warning3) => warning3.code === "opencode-recommended-skill-failed"
|
|
5770
|
+
) ? "drift" : "installed",
|
|
5771
|
+
observed: "optional skills=yes processed"
|
|
5132
5772
|
});
|
|
5133
5773
|
}
|
|
5134
5774
|
return {
|
|
@@ -5138,7 +5778,7 @@ function applyOpenCodePlan(plan) {
|
|
|
5138
5778
|
summary: plan.action === "install" ? "Applied OpenCode install plan." : plan.action === "sync" ? "Applied OpenCode managed configuration sync." : "Applied OpenCode plugin update.",
|
|
5139
5779
|
changedTargets,
|
|
5140
5780
|
backups,
|
|
5141
|
-
warnings
|
|
5781
|
+
warnings,
|
|
5142
5782
|
disclaimers: defaultDisclaimers()
|
|
5143
5783
|
};
|
|
5144
5784
|
}
|
|
@@ -5476,12 +6116,12 @@ function getOpenCodeModelsInvocation(platform = process.platform) {
|
|
|
5476
6116
|
};
|
|
5477
6117
|
if (platform === "win32") {
|
|
5478
6118
|
return {
|
|
5479
|
-
command: "opencode models",
|
|
6119
|
+
command: "opencode models --pure",
|
|
5480
6120
|
args: [],
|
|
5481
6121
|
options: { ...options, shell: true }
|
|
5482
6122
|
};
|
|
5483
6123
|
}
|
|
5484
|
-
return { command: "opencode", args: ["models"], options };
|
|
6124
|
+
return { command: "opencode", args: ["models", "--pure"], options };
|
|
5485
6125
|
}
|
|
5486
6126
|
function getNativeOpenCodeOptions() {
|
|
5487
6127
|
try {
|
|
@@ -5636,13 +6276,15 @@ function readRoleField(config, role, field) {
|
|
|
5636
6276
|
function getOpenCodeModelRoles() {
|
|
5637
6277
|
const parsed = parseConfig(getExistingLiteConfigPath());
|
|
5638
6278
|
const agents = parsed.config?.agents && typeof parsed.config.agents === "object" ? parsed.config.agents : void 0;
|
|
5639
|
-
const presets = parsed.config?.presets && typeof parsed.config.presets === "object" ? parsed.config.presets : {};
|
|
5640
|
-
const
|
|
6279
|
+
const presets = parsed.config?.presets && typeof parsed.config.presets === "object" && !Array.isArray(parsed.config.presets) ? parsed.config.presets : {};
|
|
6280
|
+
const presetName = typeof parsed.config?.preset === "string" ? parsed.config.preset : void 0;
|
|
6281
|
+
const selectedPreset = presetName !== void 0 ? presets[presetName] : void 0;
|
|
6282
|
+
const activePreset = selectedPreset && typeof selectedPreset === "object" && !Array.isArray(selectedPreset) ? selectedPreset : void 0;
|
|
5641
6283
|
return ALL_AGENT_NAMES.map((role) => {
|
|
5642
|
-
const variant = readRoleField(agents, role, "variant") ?? readRoleField(
|
|
6284
|
+
const variant = readRoleField(agents, role, "variant") ?? readRoleField(activePreset, role, "variant");
|
|
5643
6285
|
return {
|
|
5644
6286
|
role,
|
|
5645
|
-
model: readRoleField(agents, role, "model") ?? readRoleField(
|
|
6287
|
+
model: readRoleField(agents, role, "model") ?? readRoleField(activePreset, role, "model") ?? DEFAULT_MODELS[role] ?? "openai/gpt-5.4",
|
|
5646
6288
|
effort: variant ? { kind: "effort", value: variant } : { kind: "inherit" }
|
|
5647
6289
|
};
|
|
5648
6290
|
});
|