thoth-agents 0.3.4 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +22 -12
- package/dist/{chunk-W5GCMYA3.js → chunk-HIXSRHEG.js} +295 -66
- package/dist/cli/index.js +37 -4
- package/dist/cli/install.d.ts +2 -0
- package/dist/cli/operations/opencode.d.ts +2 -2
- package/dist/cli/owned-skills.d.ts +20 -0
- package/dist/cli/tui/index.js +9 -8
- package/dist/harness/core/owned-skills.d.ts +2 -0
- package/dist/index.js +5 -4
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/skills/thoth-init/SKILL.md +21 -18
- package/plugin/skills/thoth-init/scripts/init.mjs +152 -68
- package/skills/README.md +5 -3
- package/skills/thoth-init/SKILL.md +21 -18
- package/skills/thoth-init/scripts/init.mjs +152 -68
|
@@ -3777,8 +3777,8 @@ function applyCodexPlan(plan) {
|
|
|
3777
3777
|
|
|
3778
3778
|
// src/cli/operations/opencode.ts
|
|
3779
3779
|
import { createHash as createHash3 } from "crypto";
|
|
3780
|
-
import { existsSync as
|
|
3781
|
-
import { join as
|
|
3780
|
+
import { existsSync as existsSync10 } from "fs";
|
|
3781
|
+
import { join as join5 } from "path";
|
|
3782
3782
|
|
|
3783
3783
|
// src/cli/opencode-effort.ts
|
|
3784
3784
|
var OPENAI_GPT_RUNTIME_VARIANTS = /* @__PURE__ */ new Set([
|
|
@@ -3810,10 +3810,127 @@ function resolveOpenCodeEffort(input) {
|
|
|
3810
3810
|
};
|
|
3811
3811
|
}
|
|
3812
3812
|
|
|
3813
|
+
// src/cli/owned-skills.ts
|
|
3814
|
+
import {
|
|
3815
|
+
cpSync,
|
|
3816
|
+
existsSync as existsSync9,
|
|
3817
|
+
mkdirSync as mkdirSync3,
|
|
3818
|
+
mkdtempSync,
|
|
3819
|
+
renameSync as renameSync2,
|
|
3820
|
+
rmSync,
|
|
3821
|
+
statSync
|
|
3822
|
+
} from "fs";
|
|
3823
|
+
import { homedir as homedir3 } from "os";
|
|
3824
|
+
import { dirname as dirname5, join as join4 } from "path";
|
|
3825
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
3826
|
+
|
|
3827
|
+
// src/harness/core/owned-skills.ts
|
|
3828
|
+
var THOTH_OWNED_SKILL_NAMES = [
|
|
3829
|
+
"thoth-init",
|
|
3830
|
+
"thoth-sdd",
|
|
3831
|
+
"thoth-constitution",
|
|
3832
|
+
"thoth-archive",
|
|
3833
|
+
"plan-reviewer"
|
|
3834
|
+
];
|
|
3835
|
+
|
|
3836
|
+
// src/cli/owned-skills.ts
|
|
3837
|
+
function resolveOwnedSkillPackageRoot(moduleUrl) {
|
|
3838
|
+
const packageRoot = findPackageRoot(dirname5(fileURLToPath4(moduleUrl)));
|
|
3839
|
+
if (!packageRoot) {
|
|
3840
|
+
throw new Error(
|
|
3841
|
+
"Unable to locate the thoth-agents package root for bundled owned skills."
|
|
3842
|
+
);
|
|
3843
|
+
}
|
|
3844
|
+
return packageRoot;
|
|
3845
|
+
}
|
|
3846
|
+
function defaultPackageRoot() {
|
|
3847
|
+
return resolveOwnedSkillPackageRoot(import.meta.url);
|
|
3848
|
+
}
|
|
3849
|
+
function getOpenCodeOwnedSkillEntries(options = {}) {
|
|
3850
|
+
const packageRoot = options.packageRoot ?? defaultPackageRoot();
|
|
3851
|
+
const homeDir = options.homeDir ?? homedir3();
|
|
3852
|
+
return THOTH_OWNED_SKILL_NAMES.map((name) => ({
|
|
3853
|
+
name,
|
|
3854
|
+
sourcePath: join4(packageRoot, "skills", name),
|
|
3855
|
+
destinationPath: join4(homeDir, ".config", "opencode", "skills", name)
|
|
3856
|
+
}));
|
|
3857
|
+
}
|
|
3858
|
+
function validateCanonicalSkill(entry) {
|
|
3859
|
+
if (!existsSync9(entry.sourcePath) || !statSync(entry.sourcePath).isDirectory()) {
|
|
3860
|
+
throw new Error(
|
|
3861
|
+
`Canonical owned skill directory is missing: ${entry.name}`
|
|
3862
|
+
);
|
|
3863
|
+
}
|
|
3864
|
+
const contractPath = join4(entry.sourcePath, "SKILL.md");
|
|
3865
|
+
if (!existsSync9(contractPath) || !statSync(contractPath).isFile()) {
|
|
3866
|
+
throw new Error(`Canonical owned skill contract is missing: ${entry.name}`);
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
function installOwnedSkills(skills) {
|
|
3870
|
+
const firstSkill = skills[0];
|
|
3871
|
+
if (!firstSkill) return;
|
|
3872
|
+
const globalRoot = dirname5(firstSkill.destinationPath);
|
|
3873
|
+
mkdirSync3(globalRoot, { recursive: true });
|
|
3874
|
+
const transactionRoot = mkdtempSync(join4(globalRoot, ".thoth-agents-owned-"));
|
|
3875
|
+
const stagedRoot = join4(transactionRoot, "staged");
|
|
3876
|
+
const backupRoot = join4(transactionRoot, "backup");
|
|
3877
|
+
mkdirSync3(stagedRoot, { recursive: true });
|
|
3878
|
+
mkdirSync3(backupRoot, { recursive: true });
|
|
3879
|
+
try {
|
|
3880
|
+
for (const skill of skills) {
|
|
3881
|
+
cpSync(skill.sourcePath, join4(stagedRoot, skill.name), {
|
|
3882
|
+
recursive: true,
|
|
3883
|
+
force: true
|
|
3884
|
+
});
|
|
3885
|
+
}
|
|
3886
|
+
for (const skill of skills) {
|
|
3887
|
+
const stagedPath = join4(stagedRoot, skill.name);
|
|
3888
|
+
const backupPath = join4(backupRoot, skill.name);
|
|
3889
|
+
const hadExistingDestination = existsSync9(skill.destinationPath);
|
|
3890
|
+
if (hadExistingDestination) {
|
|
3891
|
+
renameSync2(skill.destinationPath, backupPath);
|
|
3892
|
+
}
|
|
3893
|
+
try {
|
|
3894
|
+
renameSync2(stagedPath, skill.destinationPath);
|
|
3895
|
+
} catch (error) {
|
|
3896
|
+
if (existsSync9(skill.destinationPath)) {
|
|
3897
|
+
rmSync(skill.destinationPath, { recursive: true, force: true });
|
|
3898
|
+
}
|
|
3899
|
+
if (hadExistingDestination && existsSync9(backupPath)) {
|
|
3900
|
+
renameSync2(backupPath, skill.destinationPath);
|
|
3901
|
+
}
|
|
3902
|
+
throw error;
|
|
3903
|
+
}
|
|
3904
|
+
if (existsSync9(backupPath)) {
|
|
3905
|
+
rmSync(backupPath, { recursive: true, force: true });
|
|
3906
|
+
}
|
|
3907
|
+
}
|
|
3908
|
+
} finally {
|
|
3909
|
+
rmSync(transactionRoot, { recursive: true, force: true });
|
|
3910
|
+
}
|
|
3911
|
+
}
|
|
3912
|
+
function syncOpenCodeOwnedSkills(options = {}) {
|
|
3913
|
+
const skills = getOpenCodeOwnedSkillEntries(options);
|
|
3914
|
+
try {
|
|
3915
|
+
for (const skill of skills) validateCanonicalSkill(skill);
|
|
3916
|
+
if (options.dryRun) return { success: true, status: "planned", skills };
|
|
3917
|
+
installOwnedSkills(skills);
|
|
3918
|
+
return { success: true, status: "installed", skills };
|
|
3919
|
+
} catch (error) {
|
|
3920
|
+
return {
|
|
3921
|
+
success: false,
|
|
3922
|
+
status: "failed",
|
|
3923
|
+
skills,
|
|
3924
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3925
|
+
};
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3928
|
+
|
|
3813
3929
|
// src/cli/operations/opencode.ts
|
|
3814
3930
|
var PACKAGE_NAME = "thoth-agents";
|
|
3815
3931
|
var EXPECTED_PLUGIN = `${PACKAGE_NAME}@latest`;
|
|
3816
3932
|
var OPENAI_PRESET = "openai";
|
|
3933
|
+
var APPLIED_MODELS_PRESET = "agents";
|
|
3817
3934
|
var ROLE_NAMES = [...ALL_AGENT_NAMES];
|
|
3818
3935
|
var issuedOpenCodePlans = /* @__PURE__ */ new WeakMap();
|
|
3819
3936
|
var NonCanonicalDataError = class extends Error {
|
|
@@ -4044,7 +4161,7 @@ function titleCaseSkillName(value) {
|
|
|
4044
4161
|
}).join("-");
|
|
4045
4162
|
}
|
|
4046
4163
|
function homeDirFromContext(context2) {
|
|
4047
|
-
return context2.env?.HOME ?? context2.env?.USERPROFILE ?? (context2.cwd !== process.cwd() ?
|
|
4164
|
+
return context2.env?.HOME ?? context2.env?.USERPROFILE ?? (context2.cwd !== process.cwd() ? join5(context2.cwd, "home") : void 0);
|
|
4048
4165
|
}
|
|
4049
4166
|
function openCodeSkillTargets(context2) {
|
|
4050
4167
|
const homeDir = homeDirFromContext(context2);
|
|
@@ -4065,8 +4182,24 @@ function openCodeSkillTargets(context2) {
|
|
|
4065
4182
|
observed: installed ? "required global skill installed" : "required global skill missing"
|
|
4066
4183
|
};
|
|
4067
4184
|
});
|
|
4185
|
+
const ownedTargets = getOpenCodeOwnedSkillEntries({
|
|
4186
|
+
homeDir
|
|
4187
|
+
}).map((skill) => {
|
|
4188
|
+
const path = join5(skill.destinationPath, "SKILL.md");
|
|
4189
|
+
const installed = existsSync10(path);
|
|
4190
|
+
return {
|
|
4191
|
+
kind: "skill",
|
|
4192
|
+
path,
|
|
4193
|
+
label: titleCaseSkillName(skill.name),
|
|
4194
|
+
state: installed ? "installed" : "missing",
|
|
4195
|
+
expected: "required global OpenCode skill",
|
|
4196
|
+
observed: installed ? "thoth-owned global skill installed" : "thoth-owned global skill missing"
|
|
4197
|
+
};
|
|
4198
|
+
});
|
|
4068
4199
|
const diagnostics = [];
|
|
4069
|
-
if (requiredTargets.some(
|
|
4200
|
+
if ([...requiredTargets, ...ownedTargets].some(
|
|
4201
|
+
(target) => target.state === "missing"
|
|
4202
|
+
)) {
|
|
4070
4203
|
diagnostics.push({
|
|
4071
4204
|
severity: "important",
|
|
4072
4205
|
message: "Required OpenCode global skills are missing; run install or sync to restore them.",
|
|
@@ -4074,7 +4207,7 @@ function openCodeSkillTargets(context2) {
|
|
|
4074
4207
|
});
|
|
4075
4208
|
}
|
|
4076
4209
|
return {
|
|
4077
|
-
targets: requiredTargets,
|
|
4210
|
+
targets: [...requiredTargets, ...ownedTargets],
|
|
4078
4211
|
diagnostics
|
|
4079
4212
|
};
|
|
4080
4213
|
}
|
|
@@ -4092,28 +4225,23 @@ function hasManagedPluginDrift(config) {
|
|
|
4092
4225
|
}
|
|
4093
4226
|
function liteConfigMarker(config) {
|
|
4094
4227
|
const preset = typeof config?.preset === "string" ? config.preset : void 0;
|
|
4095
|
-
const presets = config?.presets
|
|
4096
|
-
const
|
|
4097
|
-
const legacyAgents = config?.agents
|
|
4228
|
+
const presets = configRecord(config?.presets);
|
|
4229
|
+
const managedPreset = preset === OPENAI_PRESET || preset === APPLIED_MODELS_PRESET ? configRecord(presets[preset]) : {};
|
|
4230
|
+
const legacyAgents = configRecord(config?.agents);
|
|
4098
4231
|
const roles = ROLE_NAMES.filter(
|
|
4099
|
-
(role) => role in
|
|
4232
|
+
(role) => role in managedPreset || role in legacyAgents
|
|
4100
4233
|
);
|
|
4101
4234
|
return `preset: ${preset ?? "none"}; observed roles: ${roles.join(", ")}`;
|
|
4102
4235
|
}
|
|
4103
|
-
function
|
|
4104
|
-
if (!config || config.preset !== OPENAI_PRESET)
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
const
|
|
4108
|
-
|
|
4109
|
-
const roles = openaiPreset;
|
|
4110
|
-
return ROLE_NAMES.every((role) => {
|
|
4111
|
-
const value = roles[role];
|
|
4112
|
-
return value !== null && typeof value === "object" && typeof value.model === "string";
|
|
4113
|
-
});
|
|
4236
|
+
function hasManagedSevenAgentPreset(config) {
|
|
4237
|
+
if (!config || config.preset !== OPENAI_PRESET && config.preset !== APPLIED_MODELS_PRESET) {
|
|
4238
|
+
return false;
|
|
4239
|
+
}
|
|
4240
|
+
const presets = configRecord(config.presets);
|
|
4241
|
+
return hasCompleteAgentRoster(presets[config.preset]);
|
|
4114
4242
|
}
|
|
4115
4243
|
function hasSelectedNamedPreset(config) {
|
|
4116
|
-
if (!config || typeof config.preset !== "string" || config.preset.length === 0 || config.preset === OPENAI_PRESET || config.preset ===
|
|
4244
|
+
if (!config || typeof config.preset !== "string" || config.preset.length === 0 || config.preset === OPENAI_PRESET || config.preset === APPLIED_MODELS_PRESET) {
|
|
4117
4245
|
return false;
|
|
4118
4246
|
}
|
|
4119
4247
|
if (!config.presets || typeof config.presets !== "object" || Array.isArray(config.presets)) {
|
|
@@ -4125,13 +4253,7 @@ function hasSelectedNamedPreset(config) {
|
|
|
4125
4253
|
return selectedPreset !== null && typeof selectedPreset === "object" && !Array.isArray(selectedPreset);
|
|
4126
4254
|
}
|
|
4127
4255
|
function hasLegacySevenAgentRoster(config) {
|
|
4128
|
-
|
|
4129
|
-
if (!config.agents || typeof config.agents !== "object") return false;
|
|
4130
|
-
const agents = config.agents;
|
|
4131
|
-
return ROLE_NAMES.every((role) => {
|
|
4132
|
-
const value = agents[role];
|
|
4133
|
-
return value !== null && typeof value === "object" && typeof value.model === "string";
|
|
4134
|
-
});
|
|
4256
|
+
return config?.preset === APPLIED_MODELS_PRESET && !hasManagedSevenAgentPreset(config) && hasCompleteAgentRoster(config.agents);
|
|
4135
4257
|
}
|
|
4136
4258
|
var ACTIVE_PRESET_SELECTED_CODE = "opencode-active-preset-selected";
|
|
4137
4259
|
function activePresetSelectedDiagnostic() {
|
|
@@ -4184,7 +4306,7 @@ function targetsForBlockingDiagnostic(diagnostic, status) {
|
|
|
4184
4306
|
}
|
|
4185
4307
|
if (code === "opencode-required-skills-missing") {
|
|
4186
4308
|
return status.targets.filter(
|
|
4187
|
-
(target) => target.
|
|
4309
|
+
(target) => target.kind === "skill" && target.state !== "installed"
|
|
4188
4310
|
);
|
|
4189
4311
|
}
|
|
4190
4312
|
return [
|
|
@@ -4269,8 +4391,8 @@ function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
|
4269
4391
|
actions: openCodeActions
|
|
4270
4392
|
};
|
|
4271
4393
|
}
|
|
4272
|
-
const mainExists =
|
|
4273
|
-
const liteExists =
|
|
4394
|
+
const mainExists = existsSync10(mainPath);
|
|
4395
|
+
const liteExists = existsSync10(litePath);
|
|
4274
4396
|
const mainTarget = {
|
|
4275
4397
|
...targetForMainConfig(),
|
|
4276
4398
|
observed: configPluginMarker(main.config)
|
|
@@ -4281,7 +4403,7 @@ function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
|
4281
4403
|
};
|
|
4282
4404
|
if (!mainExists || !hasExpectedPlugin(main.config)) {
|
|
4283
4405
|
if (hasManagedPluginDrift(main.config)) {
|
|
4284
|
-
const currentRoster =
|
|
4406
|
+
const currentRoster = hasManagedSevenAgentPreset(lite.config);
|
|
4285
4407
|
const legacyRoster = hasLegacySevenAgentRoster(lite.config);
|
|
4286
4408
|
const selectedNamedPreset = hasSelectedNamedPreset(lite.config);
|
|
4287
4409
|
const recognizedRoster = currentRoster || legacyRoster || selectedNamedPreset;
|
|
@@ -4347,7 +4469,7 @@ function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
|
4347
4469
|
};
|
|
4348
4470
|
}
|
|
4349
4471
|
if (!mainExists && liteExists) {
|
|
4350
|
-
const currentRoster =
|
|
4472
|
+
const currentRoster = hasManagedSevenAgentPreset(lite.config);
|
|
4351
4473
|
const legacyRoster = hasLegacySevenAgentRoster(lite.config);
|
|
4352
4474
|
const selectedNamedPreset = hasSelectedNamedPreset(lite.config);
|
|
4353
4475
|
if (!currentRoster && !legacyRoster && !selectedNamedPreset) {
|
|
@@ -4490,7 +4612,7 @@ function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
|
4490
4612
|
actions: openCodeActions
|
|
4491
4613
|
};
|
|
4492
4614
|
}
|
|
4493
|
-
if (!
|
|
4615
|
+
if (!hasManagedSevenAgentPreset(lite.config)) {
|
|
4494
4616
|
const selectedNamedPreset = hasSelectedNamedPreset(lite.config);
|
|
4495
4617
|
return {
|
|
4496
4618
|
harness: "opencode",
|
|
@@ -4520,7 +4642,7 @@ function getOpenCodeManagedStatus(context2 = { cwd: process.cwd() }) {
|
|
|
4520
4642
|
harness: "opencode",
|
|
4521
4643
|
displayName: "OpenCode",
|
|
4522
4644
|
state: requiredSkillsMissing ? "drift" : "installed",
|
|
4523
|
-
summary: requiredSkillsMissing ? "OpenCode configuration is installed, but required
|
|
4645
|
+
summary: requiredSkillsMissing ? "OpenCode configuration is installed, but required global skills are missing." : "OpenCode managed thoth-agents configuration is installed.",
|
|
4524
4646
|
targets: [
|
|
4525
4647
|
{ ...mainTarget, state: "installed" },
|
|
4526
4648
|
{ ...liteTarget, state: "installed" },
|
|
@@ -4638,7 +4760,7 @@ function buildOpenCodeUpdatePlan(_context = { cwd: process.cwd() }) {
|
|
|
4638
4760
|
action: "update"
|
|
4639
4761
|
});
|
|
4640
4762
|
}
|
|
4641
|
-
function buildOpenCodeSyncPlan(
|
|
4763
|
+
function buildOpenCodeSyncPlan(context2 = { cwd: process.cwd() }) {
|
|
4642
4764
|
const generatedConfig = generateLiteConfig({
|
|
4643
4765
|
agent: "opencode",
|
|
4644
4766
|
hasTmux: false,
|
|
@@ -4646,6 +4768,10 @@ function buildOpenCodeSyncPlan(_context = { cwd: process.cwd() }) {
|
|
|
4646
4768
|
reset: false
|
|
4647
4769
|
});
|
|
4648
4770
|
const litePath = getExistingLiteConfigPath();
|
|
4771
|
+
const ownedSkills = syncOpenCodeOwnedSkills({
|
|
4772
|
+
dryRun: true,
|
|
4773
|
+
homeDir: homeDirFromContext(context2)
|
|
4774
|
+
});
|
|
4649
4775
|
const { plan, status } = planFromItems(
|
|
4650
4776
|
"opencode-sync-preview",
|
|
4651
4777
|
"sync",
|
|
@@ -4655,7 +4781,7 @@ function buildOpenCodeSyncPlan(_context = { cwd: process.cwd() }) {
|
|
|
4655
4781
|
{
|
|
4656
4782
|
title: "Ensure OpenCode plugin points at thoth-agents@latest",
|
|
4657
4783
|
target: targetForMainConfig(),
|
|
4658
|
-
state: getOpenCodeStatus(
|
|
4784
|
+
state: getOpenCodeStatus(context2).state,
|
|
4659
4785
|
preview: `plugin: ["${EXPECTED_PLUGIN}"]`,
|
|
4660
4786
|
backup: defaultBackup(getExistingConfigPath())
|
|
4661
4787
|
},
|
|
@@ -4671,6 +4797,15 @@ function buildOpenCodeSyncPlan(_context = { cwd: process.cwd() }) {
|
|
|
4671
4797
|
preview: JSON.stringify(generatedConfig, null, 2),
|
|
4672
4798
|
backup: defaultBackup(litePath)
|
|
4673
4799
|
},
|
|
4800
|
+
{
|
|
4801
|
+
title: "Synchronize global thoth-owned OpenCode skills",
|
|
4802
|
+
target: {
|
|
4803
|
+
kind: "skill",
|
|
4804
|
+
label: "Thoth-owned OpenCode skills",
|
|
4805
|
+
expected: ownedSkills.skills.map(({ name }) => name).join(", ")
|
|
4806
|
+
},
|
|
4807
|
+
preview: JSON.stringify(ownedSkills, null, 2)
|
|
4808
|
+
},
|
|
4674
4809
|
{
|
|
4675
4810
|
title: "Install required external skills",
|
|
4676
4811
|
target: {
|
|
@@ -4688,9 +4823,9 @@ function buildOpenCodeSyncPlan(_context = { cwd: process.cwd() }) {
|
|
|
4688
4823
|
)
|
|
4689
4824
|
}
|
|
4690
4825
|
],
|
|
4691
|
-
|
|
4826
|
+
context2
|
|
4692
4827
|
);
|
|
4693
|
-
return issueOpenCodePlan(plan, status,
|
|
4828
|
+
return issueOpenCodePlan(plan, status, context2, {
|
|
4694
4829
|
kind: "fixed",
|
|
4695
4830
|
action: "sync"
|
|
4696
4831
|
});
|
|
@@ -4703,11 +4838,16 @@ function tuiInstallConfig() {
|
|
|
4703
4838
|
reset: false
|
|
4704
4839
|
};
|
|
4705
4840
|
}
|
|
4706
|
-
function buildOpenCodeInstallPlan(
|
|
4841
|
+
function buildOpenCodeInstallPlan(context2 = { cwd: process.cwd() }) {
|
|
4842
|
+
const ownedSkills = syncOpenCodeOwnedSkills({
|
|
4843
|
+
dryRun: true,
|
|
4844
|
+
homeDir: homeDirFromContext(context2)
|
|
4845
|
+
});
|
|
4707
4846
|
const generatedConfig = generateLiteConfig(tuiInstallConfig());
|
|
4708
4847
|
const installPreview = {
|
|
4709
4848
|
noTui: true,
|
|
4710
4849
|
hasTmux: false,
|
|
4850
|
+
ownedSkills: ownedSkills.skills.map((skill) => skill.name),
|
|
4711
4851
|
requiredSkills: REQUIRED_SKILLS.map((skill) => skill.name),
|
|
4712
4852
|
equivalentCommand: "install --agent=opencode --no-tui --tmux=no"
|
|
4713
4853
|
};
|
|
@@ -4716,7 +4856,7 @@ function buildOpenCodeInstallPlan(_context = { cwd: process.cwd() }) {
|
|
|
4716
4856
|
"opencode-install-preview",
|
|
4717
4857
|
"install",
|
|
4718
4858
|
"Preview install",
|
|
4719
|
-
"Preview OpenCode install with the required
|
|
4859
|
+
"Preview OpenCode install with the required global skills.",
|
|
4720
4860
|
[
|
|
4721
4861
|
{
|
|
4722
4862
|
title: "Apply OpenCode TUI install options",
|
|
@@ -4724,15 +4864,15 @@ function buildOpenCodeInstallPlan(_context = { cwd: process.cwd() }) {
|
|
|
4724
4864
|
kind: "config",
|
|
4725
4865
|
path: getExistingConfigPath(),
|
|
4726
4866
|
label: "OpenCode install options",
|
|
4727
|
-
state: getOpenCodeStatus(
|
|
4728
|
-
expected: "--no-tui --tmux=no plus required
|
|
4867
|
+
state: getOpenCodeStatus(context2).state,
|
|
4868
|
+
expected: "--no-tui --tmux=no plus required global skills"
|
|
4729
4869
|
},
|
|
4730
4870
|
preview: JSON.stringify(installPreview, null, 2)
|
|
4731
4871
|
},
|
|
4732
4872
|
{
|
|
4733
4873
|
title: "Ensure OpenCode plugin points at thoth-agents@latest",
|
|
4734
4874
|
target: targetForMainConfig(),
|
|
4735
|
-
state: getOpenCodeStatus(
|
|
4875
|
+
state: getOpenCodeStatus(context2).state,
|
|
4736
4876
|
preview: `plugin: ["${EXPECTED_PLUGIN}"]`,
|
|
4737
4877
|
backup: defaultBackup(getExistingConfigPath())
|
|
4738
4878
|
},
|
|
@@ -4748,6 +4888,15 @@ function buildOpenCodeInstallPlan(_context = { cwd: process.cwd() }) {
|
|
|
4748
4888
|
preview: JSON.stringify(generatedConfig, null, 2),
|
|
4749
4889
|
backup: defaultBackup(litePath)
|
|
4750
4890
|
},
|
|
4891
|
+
{
|
|
4892
|
+
title: "Synchronize global thoth-owned OpenCode skills",
|
|
4893
|
+
target: {
|
|
4894
|
+
kind: "skill",
|
|
4895
|
+
label: "Thoth-owned OpenCode skills",
|
|
4896
|
+
expected: ownedSkills.skills.map(({ name }) => name).join(", ")
|
|
4897
|
+
},
|
|
4898
|
+
preview: JSON.stringify(ownedSkills, null, 2)
|
|
4899
|
+
},
|
|
4751
4900
|
{
|
|
4752
4901
|
title: "Install required external skills",
|
|
4753
4902
|
target: {
|
|
@@ -4765,9 +4914,9 @@ function buildOpenCodeInstallPlan(_context = { cwd: process.cwd() }) {
|
|
|
4765
4914
|
)
|
|
4766
4915
|
}
|
|
4767
4916
|
],
|
|
4768
|
-
|
|
4917
|
+
context2
|
|
4769
4918
|
);
|
|
4770
|
-
return issueOpenCodePlan(plan, status,
|
|
4919
|
+
return issueOpenCodePlan(plan, status, context2, {
|
|
4771
4920
|
kind: "fixed",
|
|
4772
4921
|
action: "install"
|
|
4773
4922
|
});
|
|
@@ -4996,6 +5145,33 @@ function invalidModelPayloadReason(roles) {
|
|
|
4996
5145
|
}
|
|
4997
5146
|
return void 0;
|
|
4998
5147
|
}
|
|
5148
|
+
function configRecord(value) {
|
|
5149
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
5150
|
+
}
|
|
5151
|
+
function hasCompleteAgentRoster(value) {
|
|
5152
|
+
const roles = configRecord(value);
|
|
5153
|
+
return ROLE_NAMES.every(
|
|
5154
|
+
(role) => typeof configRecord(roles[role]).model === "string"
|
|
5155
|
+
);
|
|
5156
|
+
}
|
|
5157
|
+
function effectiveRolePreset(base, generated) {
|
|
5158
|
+
const presets = configRecord(base.presets);
|
|
5159
|
+
const generatedPresets = configRecord(generated.presets);
|
|
5160
|
+
const defaults = configRecord(generatedPresets[OPENAI_PRESET]);
|
|
5161
|
+
const selected = typeof base.preset === "string" ? configRecord(presets[base.preset]) : {};
|
|
5162
|
+
const selectedIsMaterializedAgentsPreset = base.preset === APPLIED_MODELS_PRESET && hasCompleteAgentRoster(selected);
|
|
5163
|
+
const rootAgents = configRecord(base.agents);
|
|
5164
|
+
return Object.fromEntries(
|
|
5165
|
+
ROLE_NAMES.map((role) => [
|
|
5166
|
+
role,
|
|
5167
|
+
{
|
|
5168
|
+
...selectedIsMaterializedAgentsPreset ? {} : configRecord(defaults[role]),
|
|
5169
|
+
...configRecord(selected[role]),
|
|
5170
|
+
...configRecord(rootAgents[role])
|
|
5171
|
+
}
|
|
5172
|
+
])
|
|
5173
|
+
);
|
|
5174
|
+
}
|
|
4999
5175
|
function applyModelPlan(roles) {
|
|
5000
5176
|
const roleModels = new Map(
|
|
5001
5177
|
roles.map((role) => [
|
|
@@ -5006,26 +5182,33 @@ function applyModelPlan(roles) {
|
|
|
5006
5182
|
ensureConfigDir();
|
|
5007
5183
|
const targetPath = getExistingLiteConfigPath();
|
|
5008
5184
|
const parsed = parseConfig(targetPath);
|
|
5009
|
-
const
|
|
5185
|
+
const generated = generateLiteConfig({
|
|
5010
5186
|
agent: "opencode",
|
|
5011
5187
|
hasTmux: false,
|
|
5012
5188
|
dryRun: false,
|
|
5013
5189
|
reset: false
|
|
5014
5190
|
});
|
|
5015
|
-
const
|
|
5191
|
+
const base = parsed.config ?? generated;
|
|
5192
|
+
const agents = { ...configRecord(base.agents) };
|
|
5193
|
+
const appliedPreset = effectiveRolePreset(base, generated);
|
|
5016
5194
|
const statePath = getOpenCodeManagedModelStatePath();
|
|
5017
5195
|
const state = readManagedModelState(statePath, 1);
|
|
5018
5196
|
const configuredEfforts = { ...state.configuredEfforts ?? {} };
|
|
5019
5197
|
const warnings = [];
|
|
5020
5198
|
for (const [role, override] of roleModels) {
|
|
5021
5199
|
const current = {
|
|
5022
|
-
...agents[role]
|
|
5200
|
+
...configRecord(agents[role])
|
|
5201
|
+
};
|
|
5202
|
+
const presetRole = {
|
|
5203
|
+
...configRecord(appliedPreset[role])
|
|
5023
5204
|
};
|
|
5024
5205
|
const currentVariant = typeof current.variant === "string" ? current.variant : void 0;
|
|
5025
5206
|
const trackedVariant = configuredEfforts[role];
|
|
5026
5207
|
const userOwnsVariant = override.variant === null && currentVariant !== void 0 && (trackedVariant === void 0 || currentVariant !== trackedVariant);
|
|
5027
5208
|
current.model = override.model;
|
|
5209
|
+
presetRole.model = override.model;
|
|
5028
5210
|
if (userOwnsVariant) {
|
|
5211
|
+
presetRole.variant = currentVariant;
|
|
5029
5212
|
delete configuredEfforts[role];
|
|
5030
5213
|
warnings.push({
|
|
5031
5214
|
severity: "important",
|
|
@@ -5034,14 +5217,22 @@ function applyModelPlan(roles) {
|
|
|
5034
5217
|
});
|
|
5035
5218
|
} else if (override.variant === null) {
|
|
5036
5219
|
delete current.variant;
|
|
5220
|
+
delete presetRole.variant;
|
|
5037
5221
|
delete configuredEfforts[role];
|
|
5038
5222
|
} else {
|
|
5039
5223
|
current.variant = override.variant;
|
|
5224
|
+
presetRole.variant = override.variant;
|
|
5040
5225
|
configuredEfforts[role] = override.variant;
|
|
5041
5226
|
}
|
|
5042
5227
|
agents[role] = current;
|
|
5228
|
+
appliedPreset[role] = presetRole;
|
|
5043
5229
|
}
|
|
5044
5230
|
base.agents = agents;
|
|
5231
|
+
base.presets = {
|
|
5232
|
+
...configRecord(base.presets),
|
|
5233
|
+
[APPLIED_MODELS_PRESET]: appliedPreset
|
|
5234
|
+
};
|
|
5235
|
+
base.preset = APPLIED_MODELS_PRESET;
|
|
5045
5236
|
writeConfig(targetPath, base);
|
|
5046
5237
|
writeTextWithBackup(
|
|
5047
5238
|
statePath,
|
|
@@ -5062,7 +5253,7 @@ function applyModelPlan(roles) {
|
|
|
5062
5253
|
observed: "agents role overrides updated"
|
|
5063
5254
|
}
|
|
5064
5255
|
],
|
|
5065
|
-
backups:
|
|
5256
|
+
backups: existsSync10(`${targetPath}.bak`) ? [{ path: `${targetPath}.bak`, label: "managed backup" }] : [],
|
|
5066
5257
|
warnings,
|
|
5067
5258
|
disclaimers: defaultDisclaimers()
|
|
5068
5259
|
};
|
|
@@ -5090,6 +5281,22 @@ function applyRequiredSkills(context2) {
|
|
|
5090
5281
|
}
|
|
5091
5282
|
return { success: warnings.length === 0, warnings };
|
|
5092
5283
|
}
|
|
5284
|
+
function applyOwnedSkills(context2) {
|
|
5285
|
+
const result = syncOpenCodeOwnedSkills({
|
|
5286
|
+
homeDir: homeDirFromContext(context2)
|
|
5287
|
+
});
|
|
5288
|
+
if (result.success) return { success: true, warnings: [] };
|
|
5289
|
+
return {
|
|
5290
|
+
success: false,
|
|
5291
|
+
warnings: [
|
|
5292
|
+
{
|
|
5293
|
+
severity: "critical",
|
|
5294
|
+
code: "opencode-owned-skills-sync-failed",
|
|
5295
|
+
message: `Failed to synchronize thoth-owned OpenCode skills: ${result.error ?? "unknown error"}.`
|
|
5296
|
+
}
|
|
5297
|
+
]
|
|
5298
|
+
};
|
|
5299
|
+
}
|
|
5093
5300
|
function applyOpenCodePlan(plan) {
|
|
5094
5301
|
const rejection = validateApplyPlan(plan);
|
|
5095
5302
|
if (rejection) return rejection;
|
|
@@ -5145,7 +5352,7 @@ function applyOpenCodePlan(plan) {
|
|
|
5145
5352
|
...targetForMainConfig("installed"),
|
|
5146
5353
|
observed: `plugin includes ${EXPECTED_PLUGIN}`
|
|
5147
5354
|
});
|
|
5148
|
-
if (
|
|
5355
|
+
if (existsSync10(`${mainConfigResult.configPath}.bak`)) {
|
|
5149
5356
|
backups.push({
|
|
5150
5357
|
path: `${mainConfigResult.configPath}.bak`,
|
|
5151
5358
|
label: "OpenCode config backup"
|
|
@@ -5171,7 +5378,7 @@ function applyOpenCodePlan(plan) {
|
|
|
5171
5378
|
...targetForLiteConfig("installed"),
|
|
5172
5379
|
observed: "seven-role roster written"
|
|
5173
5380
|
});
|
|
5174
|
-
if (
|
|
5381
|
+
if (existsSync10(`${liteResult.configPath}.bak`)) {
|
|
5175
5382
|
backups.push({
|
|
5176
5383
|
path: `${liteResult.configPath}.bak`,
|
|
5177
5384
|
label: "thoth-agents config backup"
|
|
@@ -5179,6 +5386,26 @@ function applyOpenCodePlan(plan) {
|
|
|
5179
5386
|
}
|
|
5180
5387
|
}
|
|
5181
5388
|
if (issued.payload.action === "install" || issued.payload.action === "sync") {
|
|
5389
|
+
const ownedSkills = applyOwnedSkills(issued.context);
|
|
5390
|
+
warnings.push(...ownedSkills.warnings);
|
|
5391
|
+
changedTargets.push({
|
|
5392
|
+
kind: "skill",
|
|
5393
|
+
label: "Thoth-owned OpenCode skills",
|
|
5394
|
+
state: ownedSkills.success ? "installed" : "drift",
|
|
5395
|
+
observed: ownedSkills.success ? "thoth-owned global skills synchronized" : "thoth-owned global skill synchronization failed"
|
|
5396
|
+
});
|
|
5397
|
+
if (!ownedSkills.success) {
|
|
5398
|
+
return {
|
|
5399
|
+
harness: "opencode",
|
|
5400
|
+
action: plan.action,
|
|
5401
|
+
applied: false,
|
|
5402
|
+
summary: "OpenCode configuration was written, but thoth-owned skills failed to synchronize.",
|
|
5403
|
+
changedTargets,
|
|
5404
|
+
backups,
|
|
5405
|
+
warnings,
|
|
5406
|
+
disclaimers: defaultDisclaimers()
|
|
5407
|
+
};
|
|
5408
|
+
}
|
|
5182
5409
|
const requiredSkills = applyRequiredSkills(issued.context);
|
|
5183
5410
|
warnings.push(...requiredSkills.warnings);
|
|
5184
5411
|
changedTargets.push({
|
|
@@ -5235,15 +5462,15 @@ import { execFileSync } from "child_process";
|
|
|
5235
5462
|
|
|
5236
5463
|
// src/cli/model-catalog/cache.ts
|
|
5237
5464
|
import {
|
|
5238
|
-
existsSync as
|
|
5239
|
-
mkdirSync as
|
|
5465
|
+
existsSync as existsSync11,
|
|
5466
|
+
mkdirSync as mkdirSync4,
|
|
5240
5467
|
readFileSync as readFileSync6,
|
|
5241
|
-
renameSync as
|
|
5468
|
+
renameSync as renameSync3,
|
|
5242
5469
|
unlinkSync,
|
|
5243
5470
|
writeFileSync as writeFileSync3
|
|
5244
5471
|
} from "fs";
|
|
5245
|
-
import { homedir as
|
|
5246
|
-
import { dirname as
|
|
5472
|
+
import { homedir as homedir4 } from "os";
|
|
5473
|
+
import { dirname as dirname6, posix, win32 } from "path";
|
|
5247
5474
|
import { z as z2 } from "zod";
|
|
5248
5475
|
var modelCatalogSourceSchema = z2.enum(["remote", "lkg", "manual"]);
|
|
5249
5476
|
var cachedModelOptionSchema = z2.object({
|
|
@@ -5263,7 +5490,7 @@ var modelsDevCacheSchema = z2.object({
|
|
|
5263
5490
|
function resolveModelsDevCachePath(options = {}) {
|
|
5264
5491
|
const platform = options.platform ?? process.platform;
|
|
5265
5492
|
const env = options.env ?? process.env;
|
|
5266
|
-
const home = options.homeDir ??
|
|
5493
|
+
const home = options.homeDir ?? homedir4();
|
|
5267
5494
|
if (platform === "win32") {
|
|
5268
5495
|
const path = win32;
|
|
5269
5496
|
const base2 = env.LOCALAPPDATA ?? env.APPDATA ?? path.join(home, "AppData", "Local");
|
|
@@ -5273,7 +5500,7 @@ function resolveModelsDevCachePath(options = {}) {
|
|
|
5273
5500
|
return posix.join(base, "thoth-agents", "models-dev-v1.json");
|
|
5274
5501
|
}
|
|
5275
5502
|
function readModelsDevCache(cachePath) {
|
|
5276
|
-
if (!
|
|
5503
|
+
if (!existsSync11(cachePath)) return void 0;
|
|
5277
5504
|
try {
|
|
5278
5505
|
const input = JSON.parse(readFileSync6(cachePath, "utf8"));
|
|
5279
5506
|
const parsed = modelsDevCacheSchema.safeParse(input);
|
|
@@ -5287,7 +5514,7 @@ function writeModelsDevCacheAtomic(cachePath, cache) {
|
|
|
5287
5514
|
if (!validated.success) {
|
|
5288
5515
|
throw new Error(`Invalid models.dev cache: ${validated.error.message}`);
|
|
5289
5516
|
}
|
|
5290
|
-
|
|
5517
|
+
mkdirSync4(dirname6(cachePath), { recursive: true });
|
|
5291
5518
|
const temporaryPath = `${cachePath}.${process.pid}.${Date.now()}.tmp`;
|
|
5292
5519
|
try {
|
|
5293
5520
|
writeFileSync3(
|
|
@@ -5295,9 +5522,9 @@ function writeModelsDevCacheAtomic(cachePath, cache) {
|
|
|
5295
5522
|
`${JSON.stringify(validated.data, null, 2)}
|
|
5296
5523
|
`
|
|
5297
5524
|
);
|
|
5298
|
-
|
|
5525
|
+
renameSync3(temporaryPath, cachePath);
|
|
5299
5526
|
} catch (error) {
|
|
5300
|
-
if (
|
|
5527
|
+
if (existsSync11(temporaryPath)) unlinkSync(temporaryPath);
|
|
5301
5528
|
throw error;
|
|
5302
5529
|
}
|
|
5303
5530
|
}
|
|
@@ -5610,7 +5837,7 @@ async function getModelOptions(harness) {
|
|
|
5610
5837
|
}
|
|
5611
5838
|
|
|
5612
5839
|
// src/cli/tui/operations.ts
|
|
5613
|
-
import { existsSync as
|
|
5840
|
+
import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
|
|
5614
5841
|
var context = { cwd: process.cwd() };
|
|
5615
5842
|
var codexContext = { cwd: process.cwd() };
|
|
5616
5843
|
var claudeCodeContext = {
|
|
@@ -5652,7 +5879,7 @@ function getCodexModelRoles(source = codexContext) {
|
|
|
5652
5879
|
const item = plan.items.find(
|
|
5653
5880
|
(candidate) => candidate.action === "write-role-toml" && candidate.role === role
|
|
5654
5881
|
);
|
|
5655
|
-
const content = item &&
|
|
5882
|
+
const content = item && existsSync12(item.targetPath) ? readFileSync7(item.targetPath, "utf8") : item?.content;
|
|
5656
5883
|
const effort = content ? parseRoleTomlEffort(content) : void 0;
|
|
5657
5884
|
return {
|
|
5658
5885
|
role,
|
|
@@ -5688,9 +5915,10 @@ function getOpenCodeModelRoles() {
|
|
|
5688
5915
|
const activePreset = selectedPreset && typeof selectedPreset === "object" && !Array.isArray(selectedPreset) ? selectedPreset : void 0;
|
|
5689
5916
|
return ALL_AGENT_NAMES.map((role) => {
|
|
5690
5917
|
const defaultModel = getDefaultOpenCodeModel(role);
|
|
5691
|
-
const
|
|
5918
|
+
const configuredModel = readRoleField(agents, role, "model") ?? readRoleField(activePreset, role, "model");
|
|
5919
|
+
const model = configuredModel ?? defaultModel;
|
|
5692
5920
|
const configuredVariant = readRoleField(agents, role, "variant") ?? readRoleField(activePreset, role, "variant");
|
|
5693
|
-
const variant = configuredVariant ?? (
|
|
5921
|
+
const variant = configuredVariant ?? (configuredModel === void 0 ? getDefaultOpenCodeVariant(role) : void 0);
|
|
5694
5922
|
return {
|
|
5695
5923
|
role,
|
|
5696
5924
|
model,
|
|
@@ -5774,6 +6002,7 @@ export {
|
|
|
5774
6002
|
buildClaudeCodeSetupPlan,
|
|
5775
6003
|
applyClaudeCodeSetup,
|
|
5776
6004
|
formatClaudeCodeSetupPlan,
|
|
6005
|
+
syncOpenCodeOwnedSkills,
|
|
5777
6006
|
getNpxCommand,
|
|
5778
6007
|
REQUIRED_SKILLS,
|
|
5779
6008
|
getRequiredSkillInstallCommand,
|