@kisev/agentomatic 12.0.1-dev.126.g020f34744898 → 12.0.1-dev.128.g273b5ff20349
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/agent-profiles.d.ts +2 -1
- package/dist/agent-profiles.js +42 -5
- package/dist/command-cli.js +81 -70
- package/package.json +2 -2
package/dist/agent-profiles.d.ts
CHANGED
|
@@ -117,8 +117,9 @@ export declare function previewAgentProfileChange(request: AgentProfileRequest,
|
|
|
117
117
|
export declare function applyAgentProfileChange(request: AgentProfileRequest, scope: Scope, cwd?: string, home?: string, options?: TransactionOptions & {
|
|
118
118
|
expectedDigest?: string;
|
|
119
119
|
}): Promise<AgentProfileResult>;
|
|
120
|
+
export declare function parseModelsOutput(stdout: string): string[];
|
|
120
121
|
export declare function availableModels(): Promise<string[]>;
|
|
121
|
-
export declare function availableModelVariants(model: string): Promise<string[]>;
|
|
122
|
+
export declare function availableModelVariants(model: string): Promise<string[] | null>;
|
|
122
123
|
export declare function inspectFileMode(path: string): Promise<number | undefined>;
|
|
123
124
|
export declare function readLegacyManifest(root: string): Promise<LegacyAgentOwnership | undefined>;
|
|
124
125
|
export declare function applyBuiltAgentPlan(built: BuiltPlan, stateRoot: string, options?: TransactionOptions): Promise<void>;
|
package/dist/agent-profiles.js
CHANGED
|
@@ -752,8 +752,44 @@ export async function applyAgentProfileChange(request, scope, cwd = process.cwd(
|
|
|
752
752
|
});
|
|
753
753
|
}
|
|
754
754
|
async function modelCatalog() {
|
|
755
|
-
|
|
756
|
-
|
|
755
|
+
// The CLI catalog is the primary source: it works in a clean terminal, needs
|
|
756
|
+
// no background service, and never depends on the per-directory location
|
|
757
|
+
// filter that returns an empty list for some checkouts. If the background
|
|
758
|
+
// service is down, retry standalone before giving up with a remedy.
|
|
759
|
+
const models = await cliModels().catch((error) => cliModels(["--standalone"]).catch(() => {
|
|
760
|
+
throw new AgentProfileError("catalog_unavailable", `The opencode models CLI is unavailable (${error instanceof Error ? error.message : String(error)}); start opencode in a clean terminal or run 'opencode models' yourself to verify, then retry - or pass an explicit provider/model`);
|
|
761
|
+
}));
|
|
762
|
+
return models.map((model) => {
|
|
763
|
+
const [providerID, ...rest] = model.split("/");
|
|
764
|
+
return { providerID, id: rest.join("/") };
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
// Line-by-line `provider/model` output of `opencode models`; anything else in
|
|
768
|
+
// the stream is ignored. Exported for the parser test.
|
|
769
|
+
export function parseModelsOutput(stdout) {
|
|
770
|
+
return [
|
|
771
|
+
...new Set(stdout
|
|
772
|
+
.split("\n")
|
|
773
|
+
.map((line) => line.trim())
|
|
774
|
+
.filter((line) => MODEL_PATTERN.test(line))),
|
|
775
|
+
].sort();
|
|
776
|
+
}
|
|
777
|
+
async function cliModels(extra = []) {
|
|
778
|
+
const { stdout } = await execFileAsync("opencode", ["models", ...extra], {
|
|
779
|
+
timeout: 10_000,
|
|
780
|
+
encoding: "utf8",
|
|
781
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
782
|
+
});
|
|
783
|
+
const models = parseModelsOutput(stdout);
|
|
784
|
+
if (!models.length)
|
|
785
|
+
throw new Error("opencode models printed no provider/model lines");
|
|
786
|
+
return models;
|
|
787
|
+
}
|
|
788
|
+
// The variants (effort) metadata still comes from the API - without the
|
|
789
|
+
// location filter: some directories answer an empty list to a located query,
|
|
790
|
+
// and that must never zero the cascade.
|
|
791
|
+
async function modelApiEntries() {
|
|
792
|
+
const { stdout } = await execFileAsync("opencode", ["api", "get", "/api/model"], {
|
|
757
793
|
timeout: 10_000,
|
|
758
794
|
encoding: "utf8",
|
|
759
795
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -783,7 +819,7 @@ export async function availableModels() {
|
|
|
783
819
|
export async function availableModelVariants(model) {
|
|
784
820
|
const selected = validateModel(model);
|
|
785
821
|
try {
|
|
786
|
-
const metadata = (await
|
|
822
|
+
const metadata = (await modelApiEntries()).find((entry) => `${entry.providerID}/${entry.id}` === selected);
|
|
787
823
|
if (!metadata)
|
|
788
824
|
throw new Error("selected model is absent");
|
|
789
825
|
if (metadata.variants === undefined)
|
|
@@ -793,8 +829,9 @@ export async function availableModelVariants(model) {
|
|
|
793
829
|
throw new Error("invalid variants metadata");
|
|
794
830
|
return metadata.variants.map((variant) => variant.id);
|
|
795
831
|
}
|
|
796
|
-
catch
|
|
797
|
-
|
|
832
|
+
catch {
|
|
833
|
+
// Unknown is honest: the cascade says so and allows a manual variant.
|
|
834
|
+
return null;
|
|
798
835
|
}
|
|
799
836
|
}
|
|
800
837
|
export async function inspectFileMode(path) {
|
package/dist/command-cli.js
CHANGED
|
@@ -51,20 +51,13 @@ const commands = {
|
|
|
51
51
|
description: "Remove owned components; disconnect the plugin by default; retain models and npm dependency unless selected.",
|
|
52
52
|
options: ["disconnect", "removeDependency"],
|
|
53
53
|
},
|
|
54
|
-
agent: {
|
|
55
|
-
|
|
56
|
-
description: "Show installed and saved profiles, models, ownership, collisions, and drift.",
|
|
54
|
+
agent: {
|
|
55
|
+
description: "Read-only agent overview: profiles, models, ownership, collisions, and drift (mutations: configure agent).",
|
|
57
56
|
options: [],
|
|
58
57
|
},
|
|
59
|
-
"agent
|
|
60
|
-
description: "
|
|
61
|
-
options: ["provider", "model", "variant"],
|
|
62
|
-
name: true,
|
|
63
|
-
},
|
|
64
|
-
"agent remove": {
|
|
65
|
-
description: "Remove an additional critic; fixed roles cannot be removed here.",
|
|
58
|
+
"agent list": {
|
|
59
|
+
description: "Show installed and saved profiles, models, ownership, collisions, and drift.",
|
|
66
60
|
options: [],
|
|
67
|
-
name: true,
|
|
68
61
|
},
|
|
69
62
|
maintenance: {
|
|
70
63
|
description: "Clean up retired files, repair selected components, or recover an interrupted transaction.",
|
|
@@ -89,6 +82,15 @@ const commands = {
|
|
|
89
82
|
};
|
|
90
83
|
const mutationOptions = new Set(["dryRun", "yes"]);
|
|
91
84
|
const observations = new Set(["status", "doctor", "agent list", "catalog"]);
|
|
85
|
+
// Mutations were unified into configure agent; removed commands answer with a
|
|
86
|
+
// one-line pointer to it.
|
|
87
|
+
const removedCommands = {
|
|
88
|
+
"agent add-critic": "Removed command; agentomatic configure agent is the single mutation point for agent models and critics",
|
|
89
|
+
"agent remove": "Removed command; agentomatic configure agent is the single mutation point for agent models and critics",
|
|
90
|
+
};
|
|
91
|
+
function failRemoved(topic) {
|
|
92
|
+
return fail("invalid_input", removedCommands[topic]);
|
|
93
|
+
}
|
|
92
94
|
function fail(code, message) {
|
|
93
95
|
throw new InstallerError(code, message);
|
|
94
96
|
}
|
|
@@ -125,6 +127,8 @@ async function promptCriticName() {
|
|
|
125
127
|
}
|
|
126
128
|
}
|
|
127
129
|
function help(topic = "") {
|
|
130
|
+
if (topic && removedCommands[topic])
|
|
131
|
+
failRemoved(topic);
|
|
128
132
|
if (topic && !commands[topic])
|
|
129
133
|
fail("invalid_input", `Unknown command: ${topic}`);
|
|
130
134
|
const visible = topic
|
|
@@ -177,7 +181,7 @@ function help(topic = "") {
|
|
|
177
181
|
"Use status to inspect the installation, doctor to diagnose problems, and maintenance for explicit recovery.",
|
|
178
182
|
"",
|
|
179
183
|
"Examples:",
|
|
180
|
-
` ${shellCommand([...(topic ? topic.split(" ") : ["install"]), ...(commands[topic]?.name ? [
|
|
184
|
+
` ${shellCommand([...(topic ? topic.split(" ") : ["install"]), ...(commands[topic]?.name ? ["critic"] : []), ...(observations.has(topic) || ["agent", "maintenance"].includes(topic) ? [] : ["--dry-run"])])}`,
|
|
181
185
|
"Documentation: https://github.com/kisev/skills/blob/main/docs/how-to/opencode-integration.md",
|
|
182
186
|
"",
|
|
183
187
|
].join("\n");
|
|
@@ -369,7 +373,14 @@ async function modelSelection(options, name, current) {
|
|
|
369
373
|
const model = candidates[value(await selectOption("Model", candidates))];
|
|
370
374
|
const variants = await availableModelVariants(model);
|
|
371
375
|
let variant = null;
|
|
372
|
-
if (variants
|
|
376
|
+
if (variants === null) {
|
|
377
|
+
// The variants metadata is unavailable: say so honestly and accept a
|
|
378
|
+
// manual variant instead of degrading the whole model selection.
|
|
379
|
+
process.stderr.write("Model variants are unknown (variants metadata unavailable); enter one manually or leave it empty.\n");
|
|
380
|
+
const answer = await promptText(current?.variant ? "Variant (empty keeps current)" : "Variant (optional)");
|
|
381
|
+
variant = (answer ? validateVariant(answer) : current?.variant) ?? null;
|
|
382
|
+
}
|
|
383
|
+
else if (variants.length) {
|
|
373
384
|
const labels = variants.map((item) => item === current?.variant ? `${item} (default)` : item);
|
|
374
385
|
const choice = value(await selectOption("Variant", ["(none)", ...labels], undefined, undefined, current?.variant ? variants.indexOf(current.variant) + 1 : 0));
|
|
375
386
|
variant = choice === 0 ? null : (variants[choice - 1] ?? null);
|
|
@@ -506,18 +517,9 @@ async function deploy(options, repair = false) {
|
|
|
506
517
|
],
|
|
507
518
|
};
|
|
508
519
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
value(await confirmQuestion("Configure agent models or additional critics now?"))) {
|
|
513
|
-
const inventory = await listAgentProfiles(options.scope);
|
|
514
|
-
changes = await profileDraft(options, [
|
|
515
|
-
...selection.agents,
|
|
516
|
-
...inventory.profiles
|
|
517
|
-
.filter((profile) => profile.name.startsWith("critic-") && profile.ownership !== "user-owned")
|
|
518
|
-
.map((profile) => profile.name),
|
|
519
|
-
]);
|
|
520
|
-
}
|
|
520
|
+
// Install never mutates the critic panel: the summary ends with the panel
|
|
521
|
+
// table and the pointer to configure agent, the single mutation point.
|
|
522
|
+
const changes = [];
|
|
521
523
|
const plan = await preview("install", options.scope, undefined, undefined, selection, !options.noDependency, changes);
|
|
522
524
|
const config = await previewConfigSetup(integration, options.scope, undefined, undefined, false, false);
|
|
523
525
|
const report = {
|
|
@@ -567,6 +569,7 @@ async function deploy(options, repair = false) {
|
|
|
567
569
|
if (changes.length)
|
|
568
570
|
process.stdout.write(`Staged profiles: ${terminalSafe(JSON.stringify(changes))}\n`);
|
|
569
571
|
process.stdout.write("Components, npm, and application configuration are separate stages; a later failure does not roll back completed stages.\n");
|
|
572
|
+
await criticsEpilogue(options);
|
|
570
573
|
}
|
|
571
574
|
if (options.dryRun) {
|
|
572
575
|
if (options.json)
|
|
@@ -605,8 +608,10 @@ async function deploy(options, repair = false) {
|
|
|
605
608
|
};
|
|
606
609
|
if (options.json)
|
|
607
610
|
json(result);
|
|
608
|
-
else
|
|
611
|
+
else {
|
|
609
612
|
process.stdout.write(renderPlan(applied, { applied: true }) + renderConfigSetup(connected, { applied: true }));
|
|
613
|
+
await criticsEpilogue(options);
|
|
614
|
+
}
|
|
610
615
|
}
|
|
611
616
|
catch (error) {
|
|
612
617
|
if (error instanceof LifecycleError &&
|
|
@@ -616,6 +621,16 @@ async function deploy(options, repair = false) {
|
|
|
616
621
|
partial(options, error, completed, `Run status, then ${repair ? "maintenance repair" : "install"} with the same selected components; use configure integration for configuration-only continuation.`, completed.includes("owned-components-and-profiles") && plan.requires_restart);
|
|
617
622
|
}
|
|
618
623
|
}
|
|
624
|
+
// The critic panel epilogue: the table plus the canonical view and mutation
|
|
625
|
+
// pointers, printed wherever the panel matters (install, staged applies,
|
|
626
|
+
// configure integration).
|
|
627
|
+
async function criticsEpilogue(options) {
|
|
628
|
+
if (options.json)
|
|
629
|
+
return;
|
|
630
|
+
const inventory = await listAgentProfiles(options.scope);
|
|
631
|
+
process.stdout.write(renderCriticsTable(inventory));
|
|
632
|
+
process.stdout.write("Inspect any time: agentomatic agent list | Manage critics: agentomatic configure agent\n");
|
|
633
|
+
}
|
|
619
634
|
function partial(options, error, completed, next, requiresRestart = false) {
|
|
620
635
|
const code = error instanceof LifecycleError ? error.code : "internal_error";
|
|
621
636
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -672,8 +687,10 @@ async function configureIntegration(options) {
|
|
|
672
687
|
plan: result,
|
|
673
688
|
requires_restart: result.requires_restart,
|
|
674
689
|
});
|
|
675
|
-
else
|
|
690
|
+
else {
|
|
676
691
|
process.stdout.write(renderConfigSetup(result, { applied: true }));
|
|
692
|
+
await criticsEpilogue(options);
|
|
693
|
+
}
|
|
677
694
|
}
|
|
678
695
|
catch (error) {
|
|
679
696
|
partial(options, error, error instanceof LifecycleError && error.code === "npm_dependency_failed"
|
|
@@ -689,20 +706,18 @@ async function profileChange(options, request) {
|
|
|
689
706
|
if (!options.json) {
|
|
690
707
|
const args = request.changes
|
|
691
708
|
? ["configure", "critics"]
|
|
692
|
-
:
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
request.
|
|
700
|
-
|
|
701
|
-
? ["--variant"
|
|
702
|
-
:
|
|
703
|
-
|
|
704
|
-
: []),
|
|
705
|
-
];
|
|
709
|
+
: [
|
|
710
|
+
"configure",
|
|
711
|
+
"agent",
|
|
712
|
+
request.name,
|
|
713
|
+
"--model",
|
|
714
|
+
request.model,
|
|
715
|
+
...(request.variant
|
|
716
|
+
? ["--variant", request.variant]
|
|
717
|
+
: request.action === "model-set"
|
|
718
|
+
? ["--clear-variant"]
|
|
719
|
+
: []),
|
|
720
|
+
];
|
|
706
721
|
process.stdout.write(renderPlan(plan, {
|
|
707
722
|
applied: false,
|
|
708
723
|
applyHint: applyHint([...args, ...scopeArguments(options.scope)]),
|
|
@@ -720,8 +735,11 @@ async function profileChange(options, request) {
|
|
|
720
735
|
});
|
|
721
736
|
if (options.json)
|
|
722
737
|
json(result);
|
|
723
|
-
else
|
|
738
|
+
else {
|
|
724
739
|
process.stdout.write(renderPlan(result.plan, { applied: true }));
|
|
740
|
+
// After a panel mutation the fresh table closes the loop.
|
|
741
|
+
await criticsEpilogue(options);
|
|
742
|
+
}
|
|
725
743
|
}
|
|
726
744
|
async function uninstall(options) {
|
|
727
745
|
requireApply(options);
|
|
@@ -830,12 +848,16 @@ async function run(args) {
|
|
|
830
848
|
return;
|
|
831
849
|
}
|
|
832
850
|
if (args.includes("--help") || args.includes("-h")) {
|
|
851
|
+
if (removedCommands[topic])
|
|
852
|
+
failRemoved(topic);
|
|
833
853
|
if (!commands[topic])
|
|
834
854
|
fail("invalid_input", `Unknown command: ${topic}`);
|
|
835
855
|
parse(topic, args.filter((arg) => !["--help", "-h"].includes(arg)));
|
|
836
856
|
process.stdout.write(help(topic));
|
|
837
857
|
return;
|
|
838
858
|
}
|
|
859
|
+
if (removedCommands[topic])
|
|
860
|
+
failRemoved(topic);
|
|
839
861
|
if (!commands[topic]) {
|
|
840
862
|
const match = [...new Set(Object.keys(commands).map((key) => key.split(" ", 1)[0]))].find((root) => root.startsWith(topic) || topic.startsWith(root));
|
|
841
863
|
fail("invalid_input", `Unknown command: ${topic}.${match ? ` Did you mean '${match}'?` : ""} Use install, configure, status, doctor, uninstall, agent, maintenance, or catalog.`);
|
|
@@ -904,12 +926,11 @@ async function run(args) {
|
|
|
904
926
|
process.exitCode = doctorExitCode(report);
|
|
905
927
|
return;
|
|
906
928
|
}
|
|
907
|
-
if (topic === "configure critics" ||
|
|
908
|
-
topic === "configure agent" ||
|
|
909
|
-
topic === "agent add-critic") {
|
|
929
|
+
if (topic === "configure critics" || topic === "configure agent") {
|
|
910
930
|
const inventory = await listAgentProfiles(options.scope);
|
|
911
|
-
// configure critics is an alias; a nameless configure agent opens the
|
|
912
|
-
|
|
931
|
+
// configure critics is an alias; a nameless configure agent opens the staged
|
|
932
|
+
// editor - the single mutation point for models, additions, and removals.
|
|
933
|
+
if (!options.name) {
|
|
913
934
|
requireApply(options);
|
|
914
935
|
if (!options.json)
|
|
915
936
|
process.stdout.write(renderCriticsTable(inventory));
|
|
@@ -926,30 +947,20 @@ async function run(args) {
|
|
|
926
947
|
await profileChange(options, { action: "model-set", changes });
|
|
927
948
|
return;
|
|
928
949
|
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
else {
|
|
934
|
-
const names = inventory.profiles
|
|
935
|
-
.filter((profile) => profile.ownership !== "user-owned")
|
|
936
|
-
.map((profile) => profile.name);
|
|
937
|
-
name = names[value(await selectOption("Agent", names))];
|
|
938
|
-
}
|
|
950
|
+
const name = options.name;
|
|
951
|
+
const existing = inventory.profiles.find((profile) => profile.name === name);
|
|
952
|
+
if (!existing && !name.startsWith("critic-")) {
|
|
953
|
+
fail("invalid_input", `Unknown agent profile: ${name}`);
|
|
939
954
|
}
|
|
940
|
-
if (
|
|
941
|
-
name
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
...request,
|
|
945
|
-
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
if (topic === "agent remove") {
|
|
950
|
-
if (!options.name)
|
|
951
|
-
fail("invalid_input", "agent remove requires an additional critic name");
|
|
952
|
-
await profileChange(options, { action: "critic-remove", name: options.name });
|
|
955
|
+
if (!existing) {
|
|
956
|
+
// configure agent on a fresh critic-<name> adds the critic: one command
|
|
957
|
+
// covers model-set for existing profiles and critic-add for new critics.
|
|
958
|
+
const request = await modelSelection(options, name, undefined);
|
|
959
|
+
await profileChange(options, { ...request, action: "critic-add" });
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
const request = await modelSelection(options, name, existing);
|
|
963
|
+
await profileChange(options, { ...request, action: "model-set" });
|
|
953
964
|
return;
|
|
954
965
|
}
|
|
955
966
|
if (topic === "maintenance recover") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kisev/agentomatic",
|
|
3
|
-
"version": "12.0.1-dev.
|
|
3
|
+
"version": "12.0.1-dev.128.g273b5ff20349",
|
|
4
4
|
"description": "OpenCode integration and opt-in installer for portable Agent Skills.",
|
|
5
5
|
"homepage": "https://github.com/kisev/skills#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@clack/prompts": "^1.8.1",
|
|
53
|
-
"@kisev/safe-fs": "12.0.1-dev.
|
|
53
|
+
"@kisev/safe-fs": "12.0.1-dev.128.g273b5ff20349",
|
|
54
54
|
"commander": "15.0.0",
|
|
55
55
|
"zod": "4.1.8"
|
|
56
56
|
},
|