@magnusekdahl/parallix 1.0.5 → 1.1.0
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 +13 -17
- package/config/agents.json +3 -3
- package/config/workflow.config.schema.json +1 -1
- package/docs/agents.md +8 -8
- package/docs/authority-reference.md +2 -2
- package/docs/doc-standards.md +89 -0
- package/docs/forgejo-setup.md +28 -4
- package/docs/migration/task-classification.md +1 -1
- package/docs/operator-setup.md +1 -1
- package/docs/readme-rewrite-benchmark.md +1 -1
- package/docs/use-cases.md +2 -2
- package/examples/README.md +1 -1
- package/index.js +7 -0
- package/lib/agents/agents.js +14 -10
- package/lib/agents/limit-hit.js +1 -1
- package/lib/agents/opencode-telemetry.js +28 -18
- package/lib/agents/opencode.js +18 -4
- package/lib/commands/active.js +3 -2
- package/lib/commands/draft.js +34 -23
- package/lib/commands/integrate.js +28 -12
- package/lib/commands/mission-start.js +2 -2
- package/lib/commands/rebase.js +14 -6
- package/lib/commands/stats-backfill.js +1 -1
- package/lib/commands/stats.js +11 -9
- package/lib/core/fmt.js +7 -3
- package/lib/core/persistent-data-migration.js +9 -1
- package/lib/review/review-loop.js +8 -5
- package/lib/review/review-prompts.js +1 -1
- package/lib/tools/forgejo.js +53 -5
- package/package.json +1 -1
- package/prompts/review.md +8 -62
- package/tools/setup-forgejo-docker.sh +5 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Parallix is a local-first Git workflow CLI for running AI coding agents in isolated, reviewable missions instead of letting one long-lived agent session mutate your main checkout.**
|
|
4
4
|
|
|
5
|
-
It is for engineers who already use Git and terminal-first coding agents such as Claude Code, Codex, OpenCode/
|
|
5
|
+
It is for engineers who already use Git and terminal-first coding agents such as Claude Code, Codex, OpenCode/custom, and Vibe/Mistral, and want branch isolation, resumable checkpoints, agent-family failover, and a forced review step without building that harness by hand.
|
|
6
6
|
|
|
7
7
|
It wraps your existing AI coding workflow without replacing it: each mission gets its own branch and worktree, long runs checkpoint to markdown, review is a separate phase, and integration still goes through your repo's own verification command. A human still chooses the mission, launches each phase, reads the output, and decides what lands.
|
|
8
8
|
|
|
@@ -12,11 +12,10 @@ It wraps your existing AI coding workflow without replacing it: each mission get
|
|
|
12
12
|
npm install -g @magnusekdahl/parallix
|
|
13
13
|
px draft "create a hello world program"
|
|
14
14
|
px active
|
|
15
|
-
px review
|
|
16
15
|
px integrate
|
|
17
16
|
```
|
|
18
17
|
|
|
19
|
-
That path shows the whole value: isolate the work on its own branch and worktree, let an agent execute it with checkpoints,
|
|
18
|
+
That path shows the whole value: isolate the work on its own branch and worktree, let an agent execute it with checkpoints, autorun a separate review phase, and only then integrate it back when you are satisfied with the result.
|
|
20
19
|
|
|
21
20
|
## Why Parallix?
|
|
22
21
|
|
|
@@ -33,14 +32,14 @@ Parallix is a mission-based development workflow that addresses each of these di
|
|
|
33
32
|
|
|
34
33
|
Each capability below is tied to a use case in [`docs/use-cases.md`](docs/use-cases.md), with the confidence level (Confirmed / Partial) carried through honestly.
|
|
35
34
|
|
|
36
|
-
- **Run several AI coding agents on one repo without clobbering each other
|
|
35
|
+
- **Run several AI coding agents on one repo without clobbering each other**. Every mission gets its own `mission/<slug>` branch and its own sibling git worktree (`../<repo>-<slug>`) automatically, so N agents make progress independently and each lands by squash-merge.
|
|
37
36
|
- **Fail over automatically when an agent hits its usage limit** *(UC-2 — Confirmed).* Per-family limit messages are pattern-detected; the agent family is written to a timed blocklist and the run retries with the next eligible, unblocked family. Only when all are exhausted does it fail loudly. Agent usage limits stop a single session; they don't have to stop the mission.
|
|
38
|
-
- **Resume a long mission deterministically
|
|
37
|
+
- **Resume a long mission deterministically**. Every checkpoint runs the gate, commits a checkpoint document with a literal `Next action:` line, and pushes it — so a later session or a different agent resumes from a written instruction, not a guess.
|
|
39
38
|
- **Force a second, preferentially-different coding agent review before merge** *(UC-4 — Partial).* Review is a separate step whose reviewer selection excludes the implementer to prefer a different agent family, and a self-approval is code-blocked at the provider. It falls back to the same family when no other agent is runnable, so this forces a second review *attempt* — it does not guarantee a different reviewer.
|
|
40
|
-
- **Publish work to a Forgejo reviewer surface without making Forgejo your branch authority
|
|
41
|
-
- **Use a repo-local Graphify knowledge graph for smaller codebase context pulls
|
|
42
|
-
- **Keep your existing verification gate instead of agent self-reporting
|
|
43
|
-
- **See which agent family actually pays off across every repo one runtime drives** *(UC-6 — Partial).* A single operator-owned `stats.csv` accumulates per-agent usage telemetry across repositories. Token-cost comparison is complete today only for the families with structured telemetry (codex, claude, opencode/
|
|
39
|
+
- **Publish work to a Forgejo reviewer surface without making Forgejo your branch authority**. When the review provider is enabled, Parallix syncs the local baseline to a dedicated `review` remote and opens or updates the PR there; if Forgejo is disabled, the branch/worktree flow still runs locally.
|
|
40
|
+
- **Use a repo-local Graphify knowledge graph for smaller codebase context pulls**. In repositories where the operator has already installed the Graphify skill, the workflow keeps `graphify-out/` isolated per worktree and refreshes it during review/integration, while the installed agent guidance steers codebase questions toward `graphify query` / `path` / `explain` before full reports or raw grep. That reduces token-usage.
|
|
41
|
+
- **Keep your existing verification gate instead of agent self-reporting**. The gate is a configured shell command with a no-op default: declare your existing `make` / `npm` / script command in `workflow.config.json` and it runs verbatim; declare nothing and verification is a documented no-op pass, not an invented gate.
|
|
42
|
+
- **See which agent family actually pays off across every repo one runtime drives** *(UC-6 — Partial).* A single operator-owned `stats.csv` accumulates per-agent usage telemetry across repositories. Token-cost comparison is complete today only for the families with structured telemetry (codex, claude, opencode/local AI/custom); vibe/mistral record honest zeros by design.
|
|
44
43
|
|
|
45
44
|
## The core workflow
|
|
46
45
|
|
|
@@ -62,14 +61,12 @@ Install from the public npm registry and run a complete mission:
|
|
|
62
61
|
|
|
63
62
|
```sh
|
|
64
63
|
npm install -g @magnusekdahl/parallix
|
|
65
|
-
px --version
|
|
66
64
|
px draft "hello world"
|
|
67
65
|
px active
|
|
68
|
-
px review
|
|
69
66
|
px integrate
|
|
70
67
|
```
|
|
71
68
|
|
|
72
|
-
`px draft` creates the mission branch, sibling worktree, mission file, and task record. Then `cd` into the mission worktree and run `px active
|
|
69
|
+
`px draft` creates the mission branch, sibling worktree, mission file, and task record. Then `cd` into the mission worktree and run `px active` and `px integrate` there with no slug; the CLI infers the mission from the current branch/worktree.
|
|
73
70
|
|
|
74
71
|
Other draft entry points are available when you need them:
|
|
75
72
|
|
|
@@ -145,7 +142,6 @@ px active task-042
|
|
|
145
142
|
# A second, preferentially-different agent reviews <main>..HEAD.
|
|
146
143
|
# If Forgejo review is enabled, the PR is published to the dedicated
|
|
147
144
|
# review surface; a self-approval by the implementing agent is blocked.
|
|
148
|
-
px review task-042
|
|
149
145
|
|
|
150
146
|
# Land it: runs configured integration gates, squash-merges to
|
|
151
147
|
# the primary branch, updates board state, removes the branch
|
|
@@ -165,7 +161,7 @@ The full evidence-backed inventory is in [`docs/use-cases.md`](docs/use-cases.md
|
|
|
165
161
|
|
|
166
162
|
## What Parallix is not
|
|
167
163
|
|
|
168
|
-
- **Not a model and not an AI coding agent.** It does not generate code itself. It coordinates the agents and models you already use (Claude Code, Codex, OpenCode/
|
|
164
|
+
- **Not a model and not an AI coding agent.** It does not generate code itself. It coordinates the agents and models you already use (Claude Code, Codex, OpenCode/custom, and Vibe/Mistral).
|
|
169
165
|
- **Not an IDE or an editor plugin.** It is a CLI workflow harness around Git and your existing toolchain — there is no UI, no autocomplete, no inline suggestions.
|
|
170
166
|
- **Not a magic autonomous engineer.** This is a human-in-the-loop workflow. Nothing merges itself, and the safe operating model is that a human decides what to queue, when to run `px active`, how to respond to review findings, and whether `px integrate` should happen at all.
|
|
171
167
|
- **Not a guaranteed throughput multiplier.** The observed gain varies with context. In the data we have, it ranges from roughly **+57%** on strict user-value output to about **+1,280%** on total completed-mission throughput in a later productized setup. Those are both real observations, but they are different mission-output measures and should be labeled that way.
|
|
@@ -177,8 +173,8 @@ The full evidence-backed inventory is in [`docs/use-cases.md`](docs/use-cases.md
|
|
|
177
173
|
- **Distribution:** Published to the public npm registry as `@magnusekdahl/parallix`. Local tarball install (`npm pack`) is also supported. No Homebrew, no Docker image, no standalone binary, and no CI/release automation today.
|
|
178
174
|
- **Review surface:** Forgejo is supported as the hosted PR viewer/publication surface, but the workflow remains local-first and can run without Forgejo when that provider is disabled.
|
|
179
175
|
- **Versioning:** `CHANGELOG.md` is the versioning authority; PATCH bumps are the release discipline.
|
|
180
|
-
- **Telemetry:** structured token/usage telemetry exists for the codex and claude families; the local-
|
|
181
|
-
- **Graphify:** the knowledge-graph path is supported for codex, claude, and
|
|
176
|
+
- **Telemetry:** structured token/usage telemetry exists for the codex and claude families; the local-custom and mistral paths record honest zeros by design rather than fabricated numbers.
|
|
177
|
+
- **Graphify:** the knowledge-graph path is supported for codex, claude, and custom/opencode after one-time operator setup. It is optional, not a workflow prerequisite. The credible claim today is better-scoped context retrieval, not a proven token-savings benchmark.
|
|
182
178
|
- **Review coverage** is best-effort, not guaranteed — see UC-4's caveats in [`docs/use-cases.md`](docs/use-cases.md).
|
|
183
179
|
|
|
184
180
|
This is a tool for a local-first developer workflow on one machine, driven by an operator who reads the caveats.
|
|
@@ -188,7 +184,7 @@ This is a tool for a local-first developer workflow on one machine, driven by an
|
|
|
188
184
|
- [`docs/use-cases.md`](docs/use-cases.md) — evidence-backed use-case inventory with confidence levels and red-team analysis (primary source of truth for what Parallix actually does today).
|
|
189
185
|
- [`docs/authority-reference.md`](docs/authority-reference.md) — the internal operator reference: workflow modes, the authority model, agent selection, the layered validation model, checkpoint model, state mapping, command aliases, stats, persistent operator data, and the full public-distribution story.
|
|
190
186
|
- [`docs/forgejo-setup.md`](docs/forgejo-setup.md) — how the Forgejo review surface, tokens, and `review` remote are bootstrapped.
|
|
191
|
-
- [`docs/operator-setup.md`](docs/operator-setup.md) — one-time Graphify skill installation for codex, claude, and
|
|
187
|
+
- [`docs/operator-setup.md`](docs/operator-setup.md) — one-time Graphify skill installation for codex, claude, and custom/opencode.
|
|
192
188
|
- [`docs/readme-rewrite-benchmark.md`](docs/readme-rewrite-benchmark.md) — how comparable developer-tool READMEs are structured, and the decisions behind this one.
|
|
193
189
|
- [`AGENTS.md`](AGENTS.md) — hard rules, restricted actions, and verification entrypoints.
|
|
194
190
|
- `docs/adr/` — architecture decision records, including ADR 0044 (distribution model).
|
package/config/agents.json
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
"_weights_comment": "Weights are relative integers (not percentages). Agent is selected by weighted random draw from eligible-and-supported set. Omit weights to use equal probability.",
|
|
4
4
|
"steps": {
|
|
5
5
|
"draft": {
|
|
6
|
-
"eligible": ["codex", "
|
|
6
|
+
"eligible": ["codex", "custom", "mistral"],
|
|
7
7
|
"selection": "random"
|
|
8
8
|
},
|
|
9
9
|
"active": {
|
|
10
|
-
"eligible": ["codex", "claude", "
|
|
10
|
+
"eligible": ["codex", "claude", "custom", "mistral"],
|
|
11
11
|
"selection": "random"
|
|
12
12
|
},
|
|
13
13
|
"conflict-resolution": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"selection": "random"
|
|
16
16
|
},
|
|
17
17
|
"review": {
|
|
18
|
-
"eligible": ["codex", "claude", "
|
|
18
|
+
"eligible": ["codex", "claude", "custom", "mistral"],
|
|
19
19
|
"selection": "random"
|
|
20
20
|
}
|
|
21
21
|
},
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"properties": {
|
|
88
88
|
"models": {
|
|
89
89
|
"type": "object",
|
|
90
|
-
"description": "Optional per-agent-family LLM model override, keyed by agent family name (e.g. codex, claude, gemini, glm, mistral,
|
|
90
|
+
"description": "Optional per-agent-family LLM model override, keyed by agent family name (e.g. codex, claude, gemini, glm, mistral, custom). Each value is the model identifier passed to that agent's CLI. A family that is not listed sends no model parameter, so the agent uses its own default. There is no 'default' key.",
|
|
91
91
|
"additionalProperties": { "type": "string" }
|
|
92
92
|
}
|
|
93
93
|
}
|
package/docs/agents.md
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
| codex | `codex` |
|
|
8
8
|
| claude | `claude` |
|
|
9
9
|
| mistral | `vibe` |
|
|
10
|
-
|
|
|
10
|
+
| custom | `opencode` |
|
|
11
11
|
|
|
12
12
|
All four listed launchers are supported on this workstation. Step eligibility for all workflow steps (`draft`, `active`, `conflict-resolution`, `review`) is controlled by `parallix/config/agents.json`. If a launcher is missing from `PATH`, the harness fails loudly with the exact blocker before launching.
|
|
13
13
|
|
|
14
|
-
## Tool Calling Workaround (
|
|
14
|
+
## Tool Calling Workaround (custom/opencode)
|
|
15
15
|
|
|
16
|
-
Opencode (
|
|
16
|
+
Opencode (custom agent family) may encounter issues with concurrent tool calls or tool call timeouts during long-running workflow sessions. When working with opencode:
|
|
17
17
|
|
|
18
18
|
- **Prefer sequential tool calls** over parallel calls for dependent operations — if tool B needs output from tool A, call them separately.
|
|
19
19
|
- **Use `workdir` parameter instead of `cd` chains** — avoid `cd <dir> && command` patterns; always use `workdir` for directory changes.
|
|
@@ -27,7 +27,7 @@ Opencode (qwen agent family) may encounter issues with concurrent tool calls or
|
|
|
27
27
|
| codex | `codex exec --sandbox danger-full-access --cd <worktree> <prompt>` with a worktree-local `HOME` under `.workflow/codex-home`; resume uses `codex exec resume <session-id-or---last> <prompt>`; the launcher also seeds `.workflow/codex-home/.codex/config.toml` with the repo-standard trusted posture and copies `.codex/auth.json` so headless review commands can start and keep localhost Forgejo access |
|
|
28
28
|
| claude | `claude --dangerously-skip-permissions --output-format stream-json --verbose --include-partial-messages -p <prompt>` (cwd=worktree) — uses `--output-format stream-json --verbose --include-partial-messages` to stream real-time JSONL events (tool calls, assistant text chunks) to the operator's terminal via the spawn-tee mechanism. `--include-partial-messages` is required: without it, the assistant event contains the full response at once and no intermediate progress is emitted. Session-id extraction parses the `result` event from stream-json output, falling back to the `claude --resume <id>` regex on plain text. |
|
|
29
29
|
| mistral | `vibe --prompt <prompt> --trust --output text` (cwd=worktree) — **Note: NOT resume-capable in current Vibe version**; session management uses internal state in `~/.vibe/logs/session/` but does not emit a parseable resume hint to stdout/stderr. |
|
|
30
|
-
|
|
|
30
|
+
| custom | `opencode run --pure --dangerously-skip-permissions <prompt>` (cwd=worktree); resume uses `-s <session>` when a session id is known or `--continue` when only the family marker is known |
|
|
31
31
|
|
|
32
32
|
## Launch output watchdog
|
|
33
33
|
|
|
@@ -74,10 +74,10 @@ Eligibility is controlled by `parallix/config/agents.json`. The default config c
|
|
|
74
74
|
```json
|
|
75
75
|
{
|
|
76
76
|
"steps": {
|
|
77
|
-
"draft": { "eligible": ["codex", "
|
|
78
|
-
"active": { "eligible": ["codex", "claude", "
|
|
77
|
+
"draft": { "eligible": ["codex", "custom", "mistral"], "selection": "random" },
|
|
78
|
+
"active": { "eligible": ["codex", "claude", "custom", "mistral"], "selection": "random" },
|
|
79
79
|
"conflict-resolution": { "eligible": ["claude", "codex", "mistral"], "selection": "random" },
|
|
80
|
-
"review": { "eligible": ["codex", "claude", "
|
|
80
|
+
"review": { "eligible": ["codex", "claude", "custom", "mistral"], "selection": "random" }
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
```
|
|
@@ -141,7 +141,7 @@ The workflow detects limit-hit messages in agent stdout/stderr and automatically
|
|
|
141
141
|
|
|
142
142
|
### Detection
|
|
143
143
|
|
|
144
|
-
`parallix/lib/limit-hit.js` ships a regex catalog per agent family (`claude`, `codex`, `
|
|
144
|
+
`parallix/lib/limit-hit.js` ships a regex catalog per agent family (`claude`, `codex`, `custom`, `mistral`) covering the common shapes:
|
|
145
145
|
- explicit phrases like `Claude usage limit reached`, `weekly limit`, `Quota exceeded`, `RESOURCE_EXHAUSTED`
|
|
146
146
|
- HTTP signals (`429 Too Many Requests`, `rate_limit_exceeded`, `Retry-After: ...`)
|
|
147
147
|
|
|
@@ -45,7 +45,7 @@ Mission flow: `backlog → draft → ready → active → review → approved
|
|
|
45
45
|
|
|
46
46
|
## 3. Agent Selection
|
|
47
47
|
|
|
48
|
-
Four families: `codex`, `claude`, `mistral`, `
|
|
48
|
+
Four families: `codex`, `claude`, `mistral`, `custom`. Step eligibility is in `config/agents.json`. Launchers are resolved from `PATH` by bare executable name.
|
|
49
49
|
|
|
50
50
|
`WORKFLOW_AGENT=<name>` overrides random selection only when that agent is eligible and unblocked. The effective blocklist is operator-owned at `<PARALLIX_HOME>/agents.local.json`; on first use parallix non-destructively migrates the legacy runtime-config, repo-root, and main-worktree files in that precedence order. Per-agent values: `true` (permanent block), `{ "until": "YYYY-MM-DD HH" }` (timed), `{ "blocked": false }` (unblock).
|
|
51
51
|
|
|
@@ -256,7 +256,7 @@ Behavior:
|
|
|
256
256
|
Telemetry capture contract (task-1285):
|
|
257
257
|
- Stage rows are keyed by `(mission, stage)`; `draft.js`, `active.js`, and the review loop each record their phase via `recordStageStats`/`recordActiveStats`/`recordReviewStats`.
|
|
258
258
|
- Structured sources: Codex (`codex-telemetry.js`, rollout JSONL) and Claude (`claude-telemetry.js`, stdout SSE) populate real token/usage fields.
|
|
259
|
-
- `opencode` (local
|
|
259
|
+
- `opencode` (local custom) exposes no structured usage source, so `opencode-telemetry.js` records honest zeros with provider/model falling back to the agent family — never fabricated numbers.
|
|
260
260
|
- `vibe`/`mistral` telemetry is **blocked** in this environment; `mistral-telemetry.js` records honest zeros and the verification is tracked as follow-up task-1288.
|
|
261
261
|
|
|
262
262
|
---
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Documentation Standard
|
|
2
|
+
|
|
3
|
+
Rules for writing and editing README.md and subdirectory READMEs in this repository. Derived from `docs/readme-rewrite-benchmark.md` (task-1336) and applied to the current product state.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Headline / Tagline
|
|
8
|
+
|
|
9
|
+
- The first line after the H1 must be a **one-line capability statement** that tells a skeptical engineer what the tool does.
|
|
10
|
+
- Zero internal jargon. Do not use terms like `authority stack`, `state-map`, `adapter internals`, `worktree pattern`, or any abstraction that only makes sense to someone who has read the code.
|
|
11
|
+
- Name the audience or the execution context if it matters (e.g., "local-first", "CLI", "for Git operators").
|
|
12
|
+
- **Bad:** "Parallix is an authority-driven mission orchestration engine."
|
|
13
|
+
- **Good:** "Parallix is a local-first Git workflow CLI for running AI coding agents in isolated, reviewable missions."
|
|
14
|
+
|
|
15
|
+
## 2. Opening Paragraph
|
|
16
|
+
|
|
17
|
+
- After the headline, the first paragraph should be **problem-first or contrast-first**: name the pain or define against the adjacent category before describing the mechanism.
|
|
18
|
+
- Keep it to 2-4 sentences. Do not lead with features, screenshots, or install commands.
|
|
19
|
+
- If the tool has a potentially surprising default behavior, state it early (like Aider stating Git auto-commit behavior, like Cline stating the permission model).
|
|
20
|
+
|
|
21
|
+
## 3. Quickstart Depth
|
|
22
|
+
|
|
23
|
+
- Provide a **shallow path to value**: 2-3 commands to a first working result.
|
|
24
|
+
- Do not bury the quickstart behind enterprise walkthroughs, multi-provider setups, or configuration steps.
|
|
25
|
+
- The quickstart section must be visually separated (fenced code block) and immediately scannable.
|
|
26
|
+
|
|
27
|
+
## 4. Caveats and Honesty
|
|
28
|
+
|
|
29
|
+
- Caveats are **trust signals** when stated early and specifically. Alpha status, local-first constraints, limited telemetry, and best-effort guarantees belong in their own visible section ("Current status" or "Limitations").
|
|
30
|
+
- Stating limitations upfront builds more credibility than hiding them in fine print.
|
|
31
|
+
- Every quantitative claim must travel with its caveats and source references.
|
|
32
|
+
|
|
33
|
+
## 5. Superlative Prohibition
|
|
34
|
+
|
|
35
|
+
- No superlatives ("leading", "best", "fastest", "2x faster") without **adoption data or measured benchmarks** to back them.
|
|
36
|
+
- If a claim cannot be falsified with a specific number and source, do not use it.
|
|
37
|
+
- This is a hard rule for alpha-stage projects with limited measured data.
|
|
38
|
+
|
|
39
|
+
## 6. "What It Is Not" Section
|
|
40
|
+
|
|
41
|
+
- Every README must include a section titled **"What \<tool\> is not"** (or equivalent).
|
|
42
|
+
- Pre-empt the top 3 objections a skeptical user would have (e.g., "not a model", "not an IDE", "not a magic autonomous engineer").
|
|
43
|
+
- This section should appear after use cases and before current status.
|
|
44
|
+
|
|
45
|
+
## 7. Structural Ordering
|
|
46
|
+
|
|
47
|
+
The canonical order for this repository's README.md is:
|
|
48
|
+
|
|
49
|
+
1. H1 title + one-line capability headline (bold)
|
|
50
|
+
2. Audience + positioning paragraph
|
|
51
|
+
3. **The first concrete thing you can do** (brief intro to quickstart)
|
|
52
|
+
4. Quick start (fenced code block, 2-3 commands)
|
|
53
|
+
5. Why \<tool\>? (problem-first)
|
|
54
|
+
6. What it does (capabilities with evidence refs)
|
|
55
|
+
7. The core workflow (diagram or description)
|
|
56
|
+
8. Example (realistic human-in-the-loop pass)
|
|
57
|
+
9. Use cases (summary with link to full inventory)
|
|
58
|
+
10. What \<tool\> is not
|
|
59
|
+
11. Current status (alpha caveats, distribution, review surface, versioning, telemetry)
|
|
60
|
+
12. Documentation (links to supporting docs)
|
|
61
|
+
13. Development (test command)
|
|
62
|
+
14. License
|
|
63
|
+
|
|
64
|
+
Subdirectory READMEs follow a simplified variant: H1 with capability statement, brief description, and relevant sections.
|
|
65
|
+
|
|
66
|
+
## 8. Tone and Voice
|
|
67
|
+
|
|
68
|
+
- **Plain, anti-hype, engineer-to-engineer.** Write like an engineer wrote it for engineers, not a marketing team.
|
|
69
|
+
- Precise and sober. Casual is fine but do not drift into jokes or bluntness that undermines credibility with a skeptical engineering manager.
|
|
70
|
+
- Use active voice. Prefer concrete verbs over abstract nouns.
|
|
71
|
+
- When describing capabilities, tie them to evidence: use-case IDs, file paths, test names, or config references.
|
|
72
|
+
|
|
73
|
+
## 9. Link Hygiene
|
|
74
|
+
|
|
75
|
+
- All relative links in README.md must resolve to existing files.
|
|
76
|
+
- Before editing any README, verify: `docs/use-cases.md`, `docs/authority-reference.md`, `docs/forgejo-setup.md`, `docs/operator-setup.md`, `docs/readme-rewrite-benchmark.md`, `AGENTS.md`, `CHANGELOG.md`, `LICENSE`, `docs/adr/`.
|
|
77
|
+
- Broken links are defects, not acceptable trade-offs.
|
|
78
|
+
|
|
79
|
+
## 10. Subdirectory READMEs
|
|
80
|
+
|
|
81
|
+
- `lib/README.md`, `examples/README.md`, and any future subdirectory READMEs must begin with a one-line capability statement (no internal jargon).
|
|
82
|
+
- Use consistent heading hierarchy: H1 for the directory name, H2 for sections, H3 for subsections.
|
|
83
|
+
- Keep them concise — they are navigation aids, not deep documentation.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Enforcement Hook
|
|
88
|
+
|
|
89
|
+
These rules are enforced by the hook in `AGENTS.md` under the "Documentation" section. Agents editing any `.md` file in the repo root or `docs/` directory MUST consult this standard before committing changes.
|
package/docs/forgejo-setup.md
CHANGED
|
@@ -10,18 +10,42 @@ Standalone workflow installs need three review-surface pieces before `active`, `
|
|
|
10
10
|
|
|
11
11
|
1. Export the workflow into the repo.
|
|
12
12
|
2. If you need a local Forgejo instance, run `parallix/tools/setup-forgejo-docker.sh` and start it with Docker Compose.
|
|
13
|
-
3.
|
|
14
|
-
4.
|
|
13
|
+
3. Create the Forgejo accounts the workflow will use (see [Create the agent accounts](#create-the-agent-accounts-fresh-instance)). On a fresh instance these do not exist yet, and `px setup` only mints *tokens* for accounts that already exist — it does not create the accounts themselves.
|
|
14
|
+
4. Run `px setup`.
|
|
15
|
+
5. Choose whether to keep the standard Backlog.md-style layout:
|
|
15
16
|
- task storage in `backlog/`
|
|
16
17
|
- missions in `missions/`
|
|
17
18
|
- `mission/*` branches on `main`
|
|
18
19
|
- worktrees in `../<repo>-<slug>`
|
|
19
20
|
- verification via `npm test`
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
6. If you keep Forgejo bootstrap enabled, enter the Forgejo password for the login that can create the review repo.
|
|
22
|
+
7. Enter passwords for the agent users you want available on this machine, or leave them blank to skip token creation for that user.
|
|
22
23
|
|
|
23
24
|
`setup` writes `workflow.config.json`, writes token files into `.forgejo-local/tokens/`, grants the listed agent users write access to the configured review repo, creates or updates the git `review` remote, and runs `verify-env` so the install is validated before you start missions.
|
|
24
25
|
|
|
26
|
+
## Create the agent accounts (fresh instance)
|
|
27
|
+
|
|
28
|
+
`px setup` / `px setup-review` create **tokens** and grant repo access, but they do **not** create Forgejo user accounts — both the basic-auth token path and the owner-token bootstrap path call `POST /users/<user>/tokens` and `PUT /repos/<repo>/collaborators/<user>`, which require the account to already exist. On a fresh Forgejo you must create the accounts first, or token creation fails with HTTP 404 / 401 for users that don't exist.
|
|
29
|
+
|
|
30
|
+
Create one account per identity the workflow uses:
|
|
31
|
+
|
|
32
|
+
- the **owner** that holds the review repo (default `human`),
|
|
33
|
+
- one account per agent family that runs `active`/`review` steps. The canonical list comes from `suggestedForgejoUsers()` — currently `codex`, `claude`, `custom`, `mistral`. (`custom` is the opencode-backed local-model family; it is a first-class identity just like the hosted agents.)
|
|
34
|
+
|
|
35
|
+
For the bundled Docker instance, create them with the Forgejo admin CLI inside the container (replace `<container>` with your Forgejo container name, e.g. `workflow-forgejo`):
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
for u in human codex claude custom mistral; do
|
|
39
|
+
docker exec -u 1000 <container> forgejo admin user create \
|
|
40
|
+
--username "$u" --email "$u@localhost" \
|
|
41
|
+
--password "CHANGE-ME-$u" --must-change-password=false
|
|
42
|
+
done
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then run `px setup` and enter each account's password so the token files are minted into `.forgejo-local/tokens/`.
|
|
46
|
+
|
|
47
|
+
> **Adding or renaming an agent family later** (e.g. the `qwen` → `custom` rename): the new family name is a new Forgejo identity. Create its account with `forgejo admin user create`, grant it write on the review repo, then re-run `px setup-review` to mint its token. Without this, `integrate`/`review` fail with `no token file found for <family>` even though every other agent works.
|
|
48
|
+
|
|
25
49
|
## Notes
|
|
26
50
|
|
|
27
51
|
- The configured review repo is created only if it does not already exist.
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
| `TASK-1275` | move | workflow/parallix subject from title and mission context |
|
|
16
16
|
| `TASK-1277` | stay | model-capacity tuning note for GPT family selection; not the parallix workflow tool itself |
|
|
17
17
|
| `TASK-1281` | move | workflow/parallix subject from title and mission context |
|
|
18
|
-
| `TASK-1287` | stay |
|
|
18
|
+
| `TASK-1287` | stay | custom-9B benchmark note; a model-evaluation task, not a parallix workflow change |
|
|
19
19
|
| `TASK-1288` | move | workflow/parallix subject from title and mission context |
|
|
20
20
|
| `TASK-1290` | move | workflow/parallix subject from title and mission context |
|
|
21
21
|
| `TASK-1294` | move | workflow/parallix subject from title and mission context |
|
package/docs/operator-setup.md
CHANGED
|
@@ -25,7 +25,7 @@ Run the installer once per agent family. It copies a platform-specific skill (an
|
|
|
25
25
|
|--------|---------|-----------------|
|
|
26
26
|
| claude | `graphify install --platform claude` | `~/.claude/skills/graphify/` + `CLAUDE.md` directive |
|
|
27
27
|
| codex | `graphify install --platform codex` | `~/.agents/skills/graphify/` |
|
|
28
|
-
|
|
|
28
|
+
| custom/opencode | `graphify install --platform opencode` | `~/.config/opencode/skills/graphify/` |
|
|
29
29
|
|
|
30
30
|
Each command produces a `SKILL.md` file in the target directory. After running all three, verify:
|
|
31
31
|
|
|
@@ -73,7 +73,7 @@ line-by-line transcription. The decisions column is the actionable output.
|
|
|
73
73
|
caveats are light.
|
|
74
74
|
- **Borrow:** Naming the audience inside the positioning ("for operators comfortable with
|
|
75
75
|
Git and CLI"); stating provider-agnosticism (Parallix's multi-family support is a
|
|
76
|
-
genuine parallel — codex/claude/mistral/
|
|
76
|
+
genuine parallel — codex/claude/mistral/custom).
|
|
77
77
|
- **Avoid:** The screenshot-first above-the-fold — Parallix's mission is explicitly *out
|
|
78
78
|
of scope* for screenshots/demos (task-1336 Out of Scope), and a CLI workflow harness is
|
|
79
79
|
better shown by a command-flow block than a TUI image. Lead with text and a fenced
|
package/docs/use-cases.md
CHANGED
|
@@ -24,7 +24,7 @@ Each use case carries the four required parts: **(P) persona/buyer**, **(B) befo
|
|
|
24
24
|
|
|
25
25
|
### UC-2 — Don't lose a run when one AI provider hits its usage cap
|
|
26
26
|
|
|
27
|
-
- **(P)** Anyone driving agents on metered/rate-limited LLM subscriptions (Claude, Codex/GPT, Mistral, local
|
|
27
|
+
- **(P)** Anyone driving agents on metered/rate-limited LLM subscriptions (Claude, Codex/GPT, Mistral, local custom).
|
|
28
28
|
- **(B)** *Before:* the agent prints "usage limit reached", the run dies, and you babysit it — manually restarting later or hand-switching to a different model. *After:* the limit message is pattern-detected, that agent family is written to a timed blocklist, and the run retries with the next eligible, unblocked family; only when all are exhausted does it fail loudly.
|
|
29
29
|
- **(E)** Per-family limit regexes: `lib/agents/limit-hit.js:8-36`; selection honoring eligibility + blocklist + env override: `lib/agents/agents.js:382` (`selectAgent`), `:340-348` (`isAgentBlocked` for permanent/timed/`blocked:false`). Tested: `test/agents-limit-hit.test.js` — `startAgent persists a block via updateAgentBlock when limit-hit detector fires`, `startAgent throws when every eligible agent hits the limit`, `startAgent does not loop forever when WORKFLOW_AGENT is pinned and that agent hits limit`.
|
|
30
30
|
- **(C)** **Confirmed** — detection, timed-block persistence, and next-agent retry are each covered by named passing tests.
|
|
@@ -55,7 +55,7 @@ Each use case carries the four required parts: **(P) persona/buyer**, **(B) befo
|
|
|
55
55
|
- **(P)** Operator/buyer deciding which paid agent subscriptions to keep or cut.
|
|
56
56
|
- **(B)** *Before:* no durable, cross-repo record of how each agent performs, so the keep/cut decision is a hunch. *After:* a single parallix-owned `stats.csv` accumulates per-agent telemetry (`classification, implementer, pr_fix_rounds`, plus an extended 21-column schema) across every repository one runtime drives, keyed so the same mission in different repos stays distinct.
|
|
57
57
|
- **(E)** `lib/commands/stats.js:14` (legacy 5-col schema), `:21-30` (extended schema). Tested: `test/stats.test.js` — `upsertStatsRow writes the workflow stats schema and updates existing missions idempotently`, `task-1314: upsertStatsRow keys on (repo, mission, stage) so same mission in different repos stays distinct`. The kind of agent-comparison this enables is demonstrated in `../visualBoard/docs/missions/2026/task-1023/RETROSPECTIVE_P5.md:198-243` (per-family PRs, reviews/PR, durations).
|
|
58
|
-
- **(C)** **Partial.** Schema and CSV upsert are tested, but the value is bounded: the richest per-agent comparison in the evidence came from Forgejo PR data, not `stats.csv`, and two of four families record honest zeros for token usage (`opencode`/local
|
|
58
|
+
- **(C)** **Partial.** Schema and CSV upsert are tested, but the value is bounded: the richest per-agent comparison in the evidence came from Forgejo PR data, not `stats.csv`, and two of four families record honest zeros for token usage (`opencode`/local custom and `mistral`/vibe telemetry are zeroed by design, per `README.md:230-231` describing `opencode-telemetry.js`/`mistral-telemetry.js`). So cross-agent *cost/value* comparison is complete only for `codex` and `claude` today.
|
|
59
59
|
|
|
60
60
|
---
|
|
61
61
|
|
package/examples/README.md
CHANGED
package/index.js
CHANGED
|
@@ -223,6 +223,13 @@ ${fmt.bold('Core Commands:')}
|
|
|
223
223
|
rebase [<slug>] [--push] Rebase mission branch onto the primary integration branch (main) with auto-resolution of mission-specific conflicts.
|
|
224
224
|
diff [<slug>] Launch the primary local diff tool for branch-vs-main review.
|
|
225
225
|
stats [<csv_file>|--csv-file <path>] [--today YYYY-MM-DD|--from YYYY-MM-DD --to YYYY-MM-DD] [--output <file>] Print parallix weekly or range tables from <PARALLIX_HOME>/stats.csv; legacy retrospective CSVs remain supported.
|
|
226
|
+
config Print the effective configuration (built-in defaults merged with workflow.config.json). Read-only.
|
|
227
|
+
aliases Print the derived command-alias table (state-map virtual states → canonical commands).
|
|
228
|
+
|
|
229
|
+
${fmt.bold('Utility Commands:')}
|
|
230
|
+
version, --version, -v Print the package version, px path, package root, and Node version.
|
|
231
|
+
shell-init [bash|zsh] Print the shell integration snippet that cds your terminal into the next mission worktree on transitions.
|
|
232
|
+
review-event <slug> --type <type> --actor <actor> --content <text> [--timestamp <stamp>] [--skip-git] Append a review-thread event for a mission.
|
|
226
233
|
|
|
227
234
|
${fmt.bold('Notes:')}
|
|
228
235
|
- <slug> is optional if it can be inferred from the current branch, directory name, or git worktree.
|
package/lib/agents/agents.js
CHANGED
|
@@ -5,7 +5,7 @@ const fmt = require('../core/fmt');
|
|
|
5
5
|
const { startCodexDraftAgent, resolveCodexCommand } = require('./codex');
|
|
6
6
|
const { startClaudeAgent, resolveClaudeCommand } = require('./claude');
|
|
7
7
|
const { startMistralAgent, resolveMistralCommand } = require('./mistral');
|
|
8
|
-
const { startOpencodeAgent, resolveOpencodeCommand } = require('./opencode');
|
|
8
|
+
const { startOpencodeAgent, resolveOpencodeCommand, isSpuriousOpencodeExit } = require('./opencode');
|
|
9
9
|
const { detectLimitHit, formatBlockUntil, DEFAULT_FALLBACK_HOURS } = require('./limit-hit');
|
|
10
10
|
const sessions = require('../tools/sessions');
|
|
11
11
|
const storage = require('../core/storage');
|
|
@@ -17,9 +17,9 @@ const { migrateAgentBlocklists } = require('../core/persistent-data-migration');
|
|
|
17
17
|
// "codex resume <id>", "opencode -s ses_<id>",
|
|
18
18
|
// "claude --resume <id>"). The resume flag is only used when the caller
|
|
19
19
|
// passes slug+role+worktree and the session marker matches the chosen agent.
|
|
20
|
-
//
|
|
20
|
+
// custom (opencode) always uses --continue; claude uses --continue; codex uses
|
|
21
21
|
// `exec resume --last`.
|
|
22
|
-
const RESUME_CAPABLE = new Set(['claude', 'codex', '
|
|
22
|
+
const RESUME_CAPABLE = new Set(['claude', 'codex', 'custom']);
|
|
23
23
|
|
|
24
24
|
const CONFIG_PATH = path.join(__dirname, '..', '..', 'config', 'agents.json');
|
|
25
25
|
|
|
@@ -30,21 +30,21 @@ const LAUNCHERS = {
|
|
|
30
30
|
codex: startCodexDraftAgent,
|
|
31
31
|
claude: startClaudeAgent,
|
|
32
32
|
mistral: startMistralAgent,
|
|
33
|
-
|
|
33
|
+
custom: startOpencodeAgent
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
const RESOLVERS = {
|
|
37
37
|
codex: resolveCodexCommand,
|
|
38
38
|
claude: resolveClaudeCommand,
|
|
39
39
|
mistral: resolveMistralCommand,
|
|
40
|
-
|
|
40
|
+
custom: resolveOpencodeCommand
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
const HEALTH_PROBE_ARGS = Object.freeze({
|
|
44
44
|
codex: ['--help'],
|
|
45
45
|
claude: ['--help'],
|
|
46
46
|
mistral: ['--help'],
|
|
47
|
-
|
|
47
|
+
custom: ['--help']
|
|
48
48
|
});
|
|
49
49
|
const LAUNCHER_HEALTH_TIMEOUT_MS = 3000;
|
|
50
50
|
const DEFAULT_NO_OUTPUT_INITIAL_DELAY_MS = 60_000;
|
|
@@ -800,9 +800,13 @@ async function startAgent(step, opts = {}) {
|
|
|
800
800
|
// Only treat `status !== null && status !== 0` or `signal` (with no spawn
|
|
801
801
|
// error) as a launch failure; `status: null` without signal is ambiguous
|
|
802
802
|
// (spawn-tee close event can emit null code) and should not trigger a retry.
|
|
803
|
+
// Spurious opencode v2.0.0 JSON-mode exits (exit 1 after a valid
|
|
804
|
+
// "reason":"stop" completion) are excluded — the agent completed, the
|
|
805
|
+
// non-zero code is a post-run cleanup race.
|
|
803
806
|
const launchFailed = result &&
|
|
804
807
|
((result.status !== null && result.status !== 0) || (result.signal && !result.error)) &&
|
|
805
|
-
!limitHit
|
|
808
|
+
!limitHit &&
|
|
809
|
+
!isSpuriousOpencodeExit(result);
|
|
806
810
|
if (launchFailed) {
|
|
807
811
|
const exitInfo = result.signal
|
|
808
812
|
? `signal ${result.signal}`
|
|
@@ -820,10 +824,10 @@ async function startAgent(step, opts = {}) {
|
|
|
820
824
|
});
|
|
821
825
|
tried.add(chosen);
|
|
822
826
|
launched.add(chosen);
|
|
823
|
-
// Block non-
|
|
827
|
+
// Block non-custom agents on non-limit failures so selectAgent excludes them
|
|
824
828
|
// on the next retry iteration, and the review-loop fallback path can activate.
|
|
825
|
-
//
|
|
826
|
-
if (chosen !== '
|
|
829
|
+
// custom (opencode/local AI) is excluded — exit 1 is a temporary local error.
|
|
830
|
+
if (chosen !== 'custom') {
|
|
827
831
|
const blockUntil = formatBlockUntil(new Date(Date.now() + DEFAULT_FALLBACK_HOURS * 60 * 60 * 1000));
|
|
828
832
|
try {
|
|
829
833
|
const blockResult = updateAgentBlockFn(chosen, blockUntil);
|
package/lib/agents/limit-hit.js
CHANGED
|
@@ -4,20 +4,20 @@
|
|
|
4
4
|
* Opencode Telemetry Parser
|
|
5
5
|
*
|
|
6
6
|
* Parses `opencode export` JSON output to extract real token-usage data for
|
|
7
|
-
*
|
|
7
|
+
* custom/opencode sessions. The exported JSON may contain token usage in various shapes
|
|
8
8
|
* depending on the opencode version, so this parser is resilient to:
|
|
9
9
|
* - Missing token fields (substitutes 0)
|
|
10
10
|
* - Nested vs flat structures
|
|
11
11
|
* - Different field name conventions
|
|
12
12
|
*
|
|
13
|
-
* Provider and model fields are set to "opencode" and "
|
|
13
|
+
* Provider and model fields are set to "opencode" and "custom" respectively,
|
|
14
14
|
* matching the convention used by other telemetry modules.
|
|
15
15
|
*
|
|
16
16
|
* See task-1285 for the full telemetry credibility design.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
const PROVIDER = 'opencode';
|
|
20
|
-
const MODEL = '
|
|
20
|
+
const MODEL = 'custom';
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Safely extract a finite number from a value, defaulting to 0.
|
|
@@ -255,10 +255,18 @@ function countToolCalls(parsed) {
|
|
|
255
255
|
* Returns null when the content yields no usable token signal (empty string,
|
|
256
256
|
* non-JSON, or JSON without any token-usage fields).
|
|
257
257
|
*
|
|
258
|
+
* When the JSON contains token data but no model field, falls back to
|
|
259
|
+
* `fallbackModel` — the model the launcher was configured with for this run
|
|
260
|
+
* (from workflow.config.json) — so telemetry records the actual model id
|
|
261
|
+
* instead of the generic family label. When no model is configured, falls
|
|
262
|
+
* back to the generic family label `MODEL`.
|
|
263
|
+
*
|
|
258
264
|
* @param {string} jsonString - Raw JSON string from `opencode export`
|
|
265
|
+
* @param {string=} [fallbackModel] - Configured model id for this run, used
|
|
266
|
+
* when the export JSON omits the model field
|
|
259
267
|
* @returns {object|null} Normalized telemetry object or null
|
|
260
268
|
*/
|
|
261
|
-
function extractOpencodeTelemetryFromExport(jsonString) {
|
|
269
|
+
function extractOpencodeTelemetryFromExport(jsonString, fallbackModel) {
|
|
262
270
|
if (!jsonString || typeof jsonString !== 'string' || !jsonString.trim()) {
|
|
263
271
|
return null;
|
|
264
272
|
}
|
|
@@ -292,17 +300,17 @@ function extractOpencodeTelemetryFromExport(jsonString) {
|
|
|
292
300
|
// total_tokens is set but individual fields are zero — treat total_tokens as the real signal
|
|
293
301
|
// and derive input/output from it conservatively
|
|
294
302
|
const totalTokens = num(tokenUsage.total_tokens);
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
303
|
+
return {
|
|
304
|
+
sessionId: extractSessionId(parsed),
|
|
305
|
+
provider: PROVIDER,
|
|
306
|
+
model: extractModelName(parsed) || fallbackModel || MODEL,
|
|
307
|
+
inputTokens: 0,
|
|
308
|
+
outputTokens: 0,
|
|
309
|
+
cachedTokens: 0,
|
|
310
|
+
totalTokens,
|
|
311
|
+
toolCalls: countToolCalls(parsed),
|
|
312
|
+
usagePercent: null,
|
|
313
|
+
};
|
|
306
314
|
}
|
|
307
315
|
|
|
308
316
|
const totalTokens = num(tokenUsage.total_tokens) || (inputTokens + outputTokens);
|
|
@@ -310,7 +318,7 @@ function extractOpencodeTelemetryFromExport(jsonString) {
|
|
|
310
318
|
return {
|
|
311
319
|
sessionId: extractSessionId(parsed),
|
|
312
320
|
provider: PROVIDER,
|
|
313
|
-
model: extractModelName(parsed) || MODEL,
|
|
321
|
+
model: extractModelName(parsed) || fallbackModel || MODEL,
|
|
314
322
|
inputTokens,
|
|
315
323
|
outputTokens,
|
|
316
324
|
cachedTokens,
|
|
@@ -342,10 +350,12 @@ function extractOpencodeTelemetry(result) {
|
|
|
342
350
|
* Return the provider/model pair for opencode tasks.
|
|
343
351
|
* Used as fallback when telemetry is null.
|
|
344
352
|
*
|
|
353
|
+
* @param {string=} [defaultModel] - Optional configured model id; falls back
|
|
354
|
+
* to the generic family label when absent.
|
|
345
355
|
* @returns {{provider: string, model: string}}
|
|
346
356
|
*/
|
|
347
|
-
function getOpencodeProviderModel() {
|
|
348
|
-
return { provider: PROVIDER, model: MODEL };
|
|
357
|
+
function getOpencodeProviderModel(defaultModel) {
|
|
358
|
+
return { provider: PROVIDER, model: defaultModel || MODEL };
|
|
349
359
|
}
|
|
350
360
|
|
|
351
361
|
module.exports = {
|