jorgex-stack 1.0.25 → 1.0.26

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.
Files changed (3) hide show
  1. package/README.md +4 -2
  2. package/dist/cli.js +52 -19
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -9,7 +9,7 @@ Portable multi-agent harness: one configuration source — 15 agents, 17 skills,
9
9
  Install and run via npm without cloning the repository:
10
10
 
11
11
  ```
12
- pnpm dlx jorgex-stack install # apply config to your runtimes (interactive pick; idempotent, removes orphans)
12
+ pnpm dlx jorgex-stack install # install runtimes; first OpenCode setup selects connected models
13
13
  pnpm dlx jorgex-stack models # model picker by runtime and tier (strong/standard/cheap)
14
14
  pnpm dlx jorgex-stack sync # alias of install (same idempotent apply)
15
15
  pnpm dlx jorgex-stack doctor # checks that everything is healthy (Engram, drift, hooks, keys)
@@ -24,7 +24,7 @@ For development from a clone, run the same commands through `pnpm cli <command>`
24
24
 
25
25
  Every command supports `--dry-run`, `--yes`, and `--target-dir <dir>` for testing without touching the real config. Writes create automatic backups and verify idempotency; merges into user config are surgical (marked markdown sections, JSON/TOML upserts), so user-owned content is never touched.
26
26
 
27
- Runtime defaults are documented in [docs/references/permissions.md](docs/references/permissions.md) for permissions and [docs/references/models.md](docs/references/models.md) for model selection, GPT-5.6 subagent tiers, and orchestrator inheritance.
27
+ Runtime defaults are documented in [docs/references/permissions.md](docs/references/permissions.md) for permissions and [docs/references/models.md](docs/references/models.md) for provider-aware model selection, Codex tiers, and orchestrator inheritance. OpenCode has no provider defaults.
28
28
 
29
29
  ### Modes: Human and Programmatic
30
30
 
@@ -52,6 +52,8 @@ Flags:
52
52
 
53
53
  This installs into all detected runtimes. To be explicit, add `--agents opencode,claude-code,codex` or a comma-separated subset. Always pass `--mode programmatic`; without `--mode`, `--yes` and non-TTY installs default to `human`.
54
54
 
55
+ OpenCode also requires an existing selection in `~/.jorgex-stack/model-map.json`; run `pnpm dlx jorgex-stack models --agents opencode` interactively once before a headless install.
56
+
55
57
  - `--mode human` cannot be combined with `--subagent-concurrency`.
56
58
  - Without `--mode`, the first run asks interactively; `--yes`, non-TTY, and `--target-dir` default to `human`.
57
59
  - `pnpm dlx jorgex-stack sync` reuses the saved mode; pass `--mode` to change and save the preference.
package/dist/cli.js CHANGED
@@ -178,11 +178,6 @@ function resolveAgentModel(models, agentName, tier) {
178
178
  };
179
179
  }
180
180
  var DEFAULT_MODEL_MAP = {
181
- opencode: {
182
- strong: { model: "openai/gpt-5.6-terra", variant: "xhigh" },
183
- standard: { model: "openai/gpt-5.6-terra", variant: "xhigh" },
184
- cheap: { model: "openai/gpt-5.6-luna", variant: "medium" }
185
- },
186
181
  "claude-code": {
187
182
  strong: { model: "fable" },
188
183
  standard: { model: "sonnet" },
@@ -209,7 +204,7 @@ function loadModelMap() {
209
204
  } catch {
210
205
  return DEFAULT_MODEL_MAP;
211
206
  }
212
- const merged = {};
207
+ const merged = { ...fromDisk };
213
208
  for (const id of Object.keys(DEFAULT_MODEL_MAP)) {
214
209
  merged[id] = { ...DEFAULT_MODEL_MAP[id], ...fromDisk[id] ?? {} };
215
210
  }
@@ -1649,7 +1644,8 @@ async function runInstall(opts) {
1649
1644
  }
1650
1645
  const models = modelMap[id];
1651
1646
  if (!models) {
1652
- p.log.warn(`${adapter.name}: sin model-map para este runtime \u2014 omitido.`);
1647
+ p.log.error(`${adapter.name}: sin modelos seleccionados \u2014 ejecuta 'jorgex-stack models --agents ${id}'.`);
1648
+ exitCode = 1;
1653
1649
  continue;
1654
1650
  }
1655
1651
  const ctx = {
@@ -2867,8 +2863,12 @@ function agentsByTier() {
2867
2863
  }
2868
2864
  return grouped;
2869
2865
  }
2866
+ function isCompleteRuntimeModelMap(models) {
2867
+ return TIERS.every((tier) => models[tier]?.model);
2868
+ }
2870
2869
  async function askModel(det, subject, current) {
2871
2870
  if (det.id === "codex") {
2871
+ if (!current) throw new Error("Codex requiere un model-map base.");
2872
2872
  const effort = await p5.select({
2873
2873
  message: `${det.name} \xB7 ${subject} \u2014 reasoning effort`,
2874
2874
  options: EFFORTS.map((v) => ({ value: v, label: v })),
@@ -2902,16 +2902,18 @@ async function askModel(det, subject, current) {
2902
2902
  }
2903
2903
  const optionsList = det.options;
2904
2904
  const options = optionsList.map((m) => ({ value: m, label: m }));
2905
- if (!optionsList.includes(current.model)) options.unshift({ value: current.model, label: `${current.model} (actual)` });
2905
+ if (current && !optionsList.includes(current.model)) {
2906
+ options.unshift({ value: current.model, label: `${current.model} (actual)` });
2907
+ }
2906
2908
  const choice = await p5.select({
2907
2909
  message: `${det.name} \xB7 ${subject} \u2014 modelo`,
2908
2910
  options,
2909
- initialValue: current.model,
2911
+ initialValue: current?.model,
2910
2912
  maxItems: 12
2911
2913
  });
2912
2914
  if (p5.isCancel(choice)) return CANCEL;
2913
2915
  if (det.id === "claude-code") return { model: choice };
2914
- const keptCurrent = choice === current.model && current.variant ? current.variant : null;
2916
+ const keptCurrent = current && choice === current.model && current.variant ? current.variant : null;
2915
2917
  const variant = await p5.select({
2916
2918
  message: `${det.name} \xB7 ${subject} \u2014 reasoning effort (variant; solo si el modelo lo soporta)`,
2917
2919
  options: [
@@ -2926,12 +2928,16 @@ async function askModel(det, subject, current) {
2926
2928
  }
2927
2929
  async function runModelsPicker(opts) {
2928
2930
  const file = ensureModelMapFile();
2931
+ const map = loadModelMap();
2929
2932
  if (opts.yes || !process.stdout.isTTY) {
2930
- console.log(`Model-map en ${file} (defaults). Ed\xEDtalo o ejecuta 'models' sin --yes para el picker.`);
2933
+ if (opts.runtimes.includes("opencode") && !map.opencode) {
2934
+ console.error("OpenCode requiere selecci\xF3n interactiva desde los proveedores conectados; ejecuta 'models --agents opencode' sin --yes.");
2935
+ return 1;
2936
+ }
2937
+ console.log(`Model-map en ${file}. Ed\xEDtalo o ejecuta 'models' sin --yes para el picker.`);
2931
2938
  return 0;
2932
2939
  }
2933
2940
  p5.intro("jorgex-stack models \u2014 modelos por tier o por subagente");
2934
- const map = loadModelMap();
2935
2941
  const grouped = agentsByTier();
2936
2942
  const tierLine = (tier) => grouped[tier].join(", ");
2937
2943
  const agentCount = TIERS.reduce((n, tier) => n + grouped[tier].length, 0);
@@ -2960,7 +2966,10 @@ async function runModelsPicker(opts) {
2960
2966
  p5.log.warn("OpenCode: no se pudo listar `opencode models` \u2014 se mantiene la selecci\xF3n actual.");
2961
2967
  continue;
2962
2968
  }
2963
- const runtimeMap = { ...map[det.id] };
2969
+ const existingRuntimeMap = map[det.id];
2970
+ const runtimeMap = {
2971
+ ...existingRuntimeMap
2972
+ };
2964
2973
  p5.log.message(
2965
2974
  `${det.name} \u2014 tiers y sus subagentes:
2966
2975
  strong \u2192 ${tierLine("strong")}
@@ -2978,7 +2987,7 @@ async function runModelsPicker(opts) {
2978
2987
  if (p5.isCancel(mode)) return cancelled();
2979
2988
  if (mode === "tier") {
2980
2989
  for (const tier of TIERS) {
2981
- const asked = await askModel(det, `tier ${tier} (${tierLine(tier)})`, runtimeMap[tier]);
2990
+ const asked = await askModel(det, `tier ${tier} (${tierLine(tier)})`, existingRuntimeMap?.[tier]);
2982
2991
  if (asked === CANCEL) return cancelled();
2983
2992
  runtimeMap[tier] = asked;
2984
2993
  }
@@ -2991,10 +3000,15 @@ async function runModelsPicker(opts) {
2991
3000
  } else {
2992
3001
  const overrides = { ...runtimeMap.overrides ?? {} };
2993
3002
  for (const tier of TIERS) {
2994
- const base = runtimeMap[tier];
3003
+ let base = runtimeMap[tier];
2995
3004
  for (const name of grouped[tier]) {
2996
- const asked = await askModel(det, `${name} (tier ${tier})`, resolveAgentModel(runtimeMap, name, tier));
3005
+ const current = existingRuntimeMap ? resolveAgentModel(existingRuntimeMap, name, tier) : void 0;
3006
+ const asked = await askModel(det, `${name} (tier ${tier})`, current);
2997
3007
  if (asked === CANCEL) return cancelled();
3008
+ if (!base) {
3009
+ base = asked;
3010
+ runtimeMap[tier] = base;
3011
+ }
2998
3012
  const sameAsTier = asked.model === base.model && (asked.variant ?? "") === (base.variant ?? "");
2999
3013
  if (sameAsTier) {
3000
3014
  delete overrides[name];
@@ -3009,6 +3023,9 @@ async function runModelsPicker(opts) {
3009
3023
  if (Object.keys(overrides).length > 0) runtimeMap.overrides = overrides;
3010
3024
  else delete runtimeMap.overrides;
3011
3025
  }
3026
+ if (!isCompleteRuntimeModelMap(runtimeMap)) {
3027
+ throw new Error(`${det.name}: selecci\xF3n de modelos incompleta.`);
3028
+ }
3012
3029
  map[det.id] = runtimeMap;
3013
3030
  }
3014
3031
  writeText(file, JSON.stringify(map, null, 2) + "\n");
@@ -3052,6 +3069,18 @@ function readPackageMetadata() {
3052
3069
  // src/cli.ts
3053
3070
  var VERSION = readPackageVersion();
3054
3071
  var COMMANDS = ["install", "sync", "models", "update", "doctor", "restore", "uninstall"];
3072
+ async function ensureOpenCodeModelsForInstall(command, flags, runtimes) {
3073
+ if (!runtimes.includes("opencode") || loadModelMap().opencode) return true;
3074
+ const canPrompt = command === "install" && !flags.yes && !flags.dryRun && process.stdout.isTTY;
3075
+ if (canPrompt) {
3076
+ const code = await runModelsPicker({ yes: false, runtimes: ["opencode"] });
3077
+ if (code === 0 && loadModelMap().opencode) return true;
3078
+ }
3079
+ console.error(
3080
+ "OpenCode no tiene modelos configurados. Ejecuta 'jorgex-stack models --agents opencode' de forma interactiva antes de install/sync."
3081
+ );
3082
+ return false;
3083
+ }
3055
3084
  function parseFlags(args) {
3056
3085
  const flags = {
3057
3086
  agents: [],
@@ -3204,9 +3233,9 @@ function printHelp() {
3204
3233
  Uso: pnpm dlx jorgex-stack [comando] [opciones]
3205
3234
 
3206
3235
  Comandos:
3207
- install Instala el stack en los runtimes elegidos (default, interactivo)
3208
- sync Re-aplica la config (idempotente; alias de install)
3209
- models Picker de modelos por tier (OpenCode: lista en vivo de 'opencode models')
3236
+ install Instala el stack; OpenCode fresh exige elegir modelos conectados
3237
+ sync Re-aplica la config y el model-map existente (idempotente; sin picker)
3238
+ models Picker por tier o subagente (OpenCode: 'opencode models' en vivo)
3210
3239
  update --check: compara stack/Engram/skills con sus upstreams
3211
3240
  doctor Estado: Engram, drift de config, hooks de Codex, key de context7
3212
3241
  restore --list para ver backups \xB7 'restore <id>' para restaurar
@@ -3266,6 +3295,10 @@ Flags disponibles: jorgex-stack --help`
3266
3295
  process.exitCode = 1;
3267
3296
  return;
3268
3297
  }
3298
+ if (!await ensureOpenCodeModelsForInstall(command, flags, runtimes)) {
3299
+ process.exitCode = 1;
3300
+ return;
3301
+ }
3269
3302
  process.exitCode = await runInstall({ runtimes, targetDir: flags.targetDir, dryRun: flags.dryRun, yes: flags.yes, mode });
3270
3303
  return;
3271
3304
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jorgex-stack",
3
- "version": "1.0.25",
3
+ "version": "1.0.26",
4
4
  "description": "Harness multi-agente portable: instala la config JorgeX (agentes, skills, hooks, Engram, MCPs) en Claude Code, Codex CLI y OpenCode",
5
5
  "type": "module",
6
6
  "license": "MIT",