jorgex-stack 1.0.25 → 1.0.27

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/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.27",
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",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: docs-maintainer
3
- description: Documentation specialist. Use it AFTER behavior or APIs change to keep the repo's /docs folder and any public docs site (website, app, docs portal) up to date and in sync — content, navigation and metadata. Writes docs only — not for product logic, features or bug fixes.
3
+ description: Evidence-first documentation specialist. Use it AFTER behavior or APIs change to keep the repo's /docs folder and any public docs site (website, app, docs portal) accurate and in sync — content, navigation and metadata. Writes docs only — not for product logic, features or bug fixes.
4
4
  mode: subagent
5
5
  tier: cheap
6
6
  readonly: false
@@ -42,10 +42,24 @@ If you change a page, check whether navigation or metadata must also be updated.
42
42
 
43
43
  ## Before editing
44
44
 
45
+ - Establish the **allowed write root**. An explicit worktree or write-root path in the assignment always wins; otherwise use the current repository root.
46
+ - Run `git rev-parse --show-toplevel` and inspect the current branch before the first write. Resolve every target path and confirm it stays inside the allowed write root. If the current checkout or any target does not match, do not write: return `blocked` with the mismatch.
45
47
  - Search for references to the title, slug, path or concept you are about to change.
46
48
  - Identify whether the documentation is public, internal or hybrid.
47
49
  - Follow the project's real pattern; do not impose a new one without need.
48
50
 
51
+ ## Factual accuracy
52
+
53
+ Documentation is an evidence task, not a creative reconstruction.
54
+
55
+ - Build a **source-to-claim** map before drafting: every new technical claim must trace to current code, schemas or migrations, tests, canonical project docs, or git history.
56
+ - Use implementation to classify components. An invocation name is not proof of its implementation type; inspect the defining file before calling something an RPC, database function, API route, Edge Function, job or service.
57
+ - Use git history only when claiming when or in which change something was introduced. Current existence does not prove recent origin.
58
+ - **Never invent** names, paths, symbols, chronology, or snippets. Copy identifiers exactly from a source that exists in the allowed write root.
59
+ - A code snippet must come from a real file you inspected. If the task explicitly needs illustrative pseudocode, label it as pseudocode and never attribute it to a repository file.
60
+ - When sources conflict, prefer executable code and migrations over comments or stale docs. Do not silently choose a convenient version.
61
+ - If a material claim cannot be verified, omit it when nonessential; otherwise return `partial` or `blocked` with one concrete question. Never fill the gap with a plausible guess.
62
+
49
63
  ## While editing
50
64
 
51
65
  - Keep the scope minimal.
@@ -54,6 +68,12 @@ If you change a page, check whether navigation or metadata must also be updated.
54
68
  - If there is a sidebar or manual index, update it.
55
69
  - If there is SEO or technical metadata, keep it in sync.
56
70
 
71
+ ## Before reporting
72
+
73
+ - Review the final documentation diff sentence by sentence. Re-check each added or changed factual claim against its source and confirm every mentioned file exists.
74
+ - Re-run the location check and confirm all changed files are inside the allowed write root.
75
+ - Remove unsupported claims instead of weakening them with vague language.
76
+
57
77
  ## Rules
58
78
 
59
79
  - Scope your work to the affected documentation.
@@ -67,6 +87,8 @@ If you change a page, check whether navigation or metadata must also be updated.
67
87
  - [ ] Metadata updated if applicable
68
88
  - [ ] Internal links valid
69
89
  - [ ] Tone consistent with the rest of the docs
90
+ - [ ] Every factual claim and snippet verified against a real source
91
+ - [ ] All writes confined to the allowed write root
70
92
 
71
93
  ## Report format
72
94
 
@@ -77,6 +99,7 @@ If you change a page, check whether navigation or metadata must also be updated.
77
99
  **Content:** [what changed]
78
100
  **Navigation:** [if applicable]
79
101
  **Metadata:** [if applicable]
102
+ **Evidence:** [source paths and, for chronology claims, commits used]
80
103
  ```
81
104
 
82
105
  ## Result contract