@kisev/agentomatic 12.0.1-dev.127.g96b4fdeed7cd → 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.
@@ -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>;
@@ -752,8 +752,44 @@ export async function applyAgentProfileChange(request, scope, cwd = process.cwd(
752
752
  });
753
753
  }
754
754
  async function modelCatalog() {
755
- const query = new URLSearchParams({ "location[directory]": process.cwd() });
756
- const { stdout } = await execFileAsync("opencode", ["api", "get", `/api/model?${query}`], {
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 modelCatalog()).find((entry) => `${entry.providerID}/${entry.id}` === selected);
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 (error) {
797
- throw new AgentProfileError("catalog_unavailable", `OpenCode V2 model variants are unavailable: ${error instanceof Error ? error.message : String(error)}`);
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) {
@@ -373,7 +373,14 @@ async function modelSelection(options, name, current) {
373
373
  const model = candidates[value(await selectOption("Model", candidates))];
374
374
  const variants = await availableModelVariants(model);
375
375
  let variant = null;
376
- if (variants.length) {
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) {
377
384
  const labels = variants.map((item) => item === current?.variant ? `${item} (default)` : item);
378
385
  const choice = value(await selectOption("Variant", ["(none)", ...labels], undefined, undefined, current?.variant ? variants.indexOf(current.variant) + 1 : 0));
379
386
  variant = choice === 0 ? null : (variants[choice - 1] ?? null);
@@ -510,18 +517,9 @@ async function deploy(options, repair = false) {
510
517
  ],
511
518
  };
512
519
  }
513
- let changes = [];
514
- if (!repair &&
515
- tty() &&
516
- value(await confirmQuestion("Configure agent models or additional critics now?"))) {
517
- const inventory = await listAgentProfiles(options.scope);
518
- changes = await profileDraft(options, [
519
- ...selection.agents,
520
- ...inventory.profiles
521
- .filter((profile) => profile.name.startsWith("critic-") && profile.ownership !== "user-owned")
522
- .map((profile) => profile.name),
523
- ]);
524
- }
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 = [];
525
523
  const plan = await preview("install", options.scope, undefined, undefined, selection, !options.noDependency, changes);
526
524
  const config = await previewConfigSetup(integration, options.scope, undefined, undefined, false, false);
527
525
  const report = {
@@ -571,6 +569,7 @@ async function deploy(options, repair = false) {
571
569
  if (changes.length)
572
570
  process.stdout.write(`Staged profiles: ${terminalSafe(JSON.stringify(changes))}\n`);
573
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);
574
573
  }
575
574
  if (options.dryRun) {
576
575
  if (options.json)
@@ -609,8 +608,10 @@ async function deploy(options, repair = false) {
609
608
  };
610
609
  if (options.json)
611
610
  json(result);
612
- else
611
+ else {
613
612
  process.stdout.write(renderPlan(applied, { applied: true }) + renderConfigSetup(connected, { applied: true }));
613
+ await criticsEpilogue(options);
614
+ }
614
615
  }
615
616
  catch (error) {
616
617
  if (error instanceof LifecycleError &&
@@ -620,6 +621,16 @@ async function deploy(options, repair = false) {
620
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);
621
622
  }
622
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
+ }
623
634
  function partial(options, error, completed, next, requiresRestart = false) {
624
635
  const code = error instanceof LifecycleError ? error.code : "internal_error";
625
636
  const message = error instanceof Error ? error.message : String(error);
@@ -676,8 +687,10 @@ async function configureIntegration(options) {
676
687
  plan: result,
677
688
  requires_restart: result.requires_restart,
678
689
  });
679
- else
690
+ else {
680
691
  process.stdout.write(renderConfigSetup(result, { applied: true }));
692
+ await criticsEpilogue(options);
693
+ }
681
694
  }
682
695
  catch (error) {
683
696
  partial(options, error, error instanceof LifecycleError && error.code === "npm_dependency_failed"
@@ -722,8 +735,11 @@ async function profileChange(options, request) {
722
735
  });
723
736
  if (options.json)
724
737
  json(result);
725
- else
738
+ else {
726
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
+ }
727
743
  }
728
744
  async function uninstall(options) {
729
745
  requireApply(options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kisev/agentomatic",
3
- "version": "12.0.1-dev.127.g96b4fdeed7cd",
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.127.g96b4fdeed7cd",
53
+ "@kisev/safe-fs": "12.0.1-dev.128.g273b5ff20349",
54
54
  "commander": "15.0.0",
55
55
  "zod": "4.1.8"
56
56
  },