spectoflow 0.20.1 → 0.21.1

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
@@ -16,17 +16,49 @@
16
16
  <a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT license"></a>
17
17
  </p>
18
18
 
19
+ <p align="center">
20
+ <img src="https://raw.githubusercontent.com/georgesmomo/spectoflow/main/docs/screenshot-board.png" alt="spectoflow dashboard — Board overview" width="880">
21
+ </p>
22
+
19
23
  An **agent-agnostic** spec-driven development framework with a **real-time local control plane**.
20
24
  You speak in plain language; the framework classifies your intent and runs the right workflow. No
21
25
  ceremonial command to start.
22
26
 
23
- **Works with whichever coding agent you have** Claude Code, Codex, Cursor, Gemini CLI, OpenCode,
24
- Kiro CLI, Antigravity, and Kimi CLI are auto-detected at `init`. The dashboard's topbar always shows
25
- the **active agent**, front and center, with a switcher: pick another and it's verified as genuinely
26
- installed before activating (a red **"No agent found"** if none is) — it never silently activates
27
- something that isn't there. (Kimi CLI doesn't yet ship a non-interactive run mode, so spectoflow can
28
- set it as active but won't try to spawn it the agent pickers that launch a run grey it out rather
29
- than hide it.)
27
+ **Works with whichever coding agent you have.** `init` auto-detects what's installed; the dashboard's
28
+ topbar always shows the **active agent**, front and center, with a switcher pick another and it's
29
+ verified as genuinely installed before activating (a red **"No agent found"** if none is), never
30
+ silently activating something that isn't there.
31
+
32
+ > Note the terminology: "agent" is overloaded here on purpose, matching how the ecosystem uses the
33
+ > word. The table below is about **coding-agent CLIs** — the tool you already run in your terminal
34
+ > (Claude Code, Copilot CLI, …). spectoflow's own **team personas** (developer, qa-engineer, …) are a
35
+ > different, unrelated use of "agent" — see [Agents vs skills](#agents-vs-skills) further down.
36
+
37
+ ## Supported coding agents
38
+
39
+ | Coding agent | Headless run | Docs |
40
+ |---|---|---|
41
+ | Claude Code | ✓ | [code.claude.com/docs/en/cli-reference](https://code.claude.com/docs/en/cli-reference) |
42
+ | Codex | ✓ | [developers.openai.com/codex/cli/reference](https://developers.openai.com/codex/cli/reference) |
43
+ | Cursor CLI | ✓ | [cursor.com/docs/cli/overview](https://cursor.com/docs/cli/overview) |
44
+ | Gemini CLI | ✓ | [github.com/google-gemini/gemini-cli](https://github.com/google-gemini/gemini-cli) |
45
+ | OpenCode | ✓ | [opencode.ai/docs/cli](https://opencode.ai/docs/cli/) |
46
+ | Kiro CLI | ✓ | [kiro.dev/docs/cli/headless](https://kiro.dev/docs/cli/headless/) |
47
+ | Antigravity | ✓ | [antigravity.google/docs/cli/headless](https://antigravity.google/docs/cli/headless/) |
48
+ | GitHub Copilot CLI | ✓ | [docs.github.com/copilot/…/about-copilot-cli](https://docs.github.com/copilot/concepts/agents/about-copilot-cli) |
49
+ | Amazon Q Developer CLI | ✓ | [docs.aws.amazon.com/amazonq/…/command-line-chat](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-chat.html) |
50
+ | Factory Droid CLI | ✓ | [docs.factory.ai/droid-exec/overview](https://docs.factory.ai/droid-exec/overview) |
51
+ | Auggie CLI (Augment Code) | ✓ | [docs.augmentcode.com/cli/overview](https://docs.augmentcode.com/cli/overview) |
52
+ | Goose CLI (Block) | ✓ | [block.github.io/goose](https://block.github.io/goose/) |
53
+ | Kimi CLI | detectable, manual only | [github.com/MoonshotAI/kimi-cli](https://github.com/MoonshotAI/kimi-cli) |
54
+
55
+ "Headless run" means spectoflow can actually spawn it non-interactively (`Run`/`Orchestrate`/
56
+ `Summarize` work). Kimi CLI has no confirmed one-shot mode as of this writing — spectoflow still
57
+ detects it and lets you set it as the active agent, it just won't try to spawn it (the pickers that
58
+ launch a run grey it out rather than hide it); drive it yourself in its own terminal meanwhile. The
59
+ same live table, with your own install status, is in the dashboard's **Documentation** tab. Missing
60
+ one you use? `config.json → runners` still takes any custom command by hand — see
61
+ [`templates/README.md`](templates/README.md) — or open an issue.
30
62
 
31
63
  ## Install
32
64
 
@@ -75,8 +107,8 @@ spectoflow --help (-h) show help (append -h to any com
75
107
  `.claude/commands/spectoflow.md`.
76
108
  - `.spectoflow/runtime.json` is gitignored (volatile execution state).
77
109
 
78
- `init` **auto-detects your installed agent(s)** (probes PATH for `claude`, `codex`, `cursor-agent`,
79
- `gemini`, and existing `.claude`/`.codex`/… dirs): it writes shims for each, sets the active agent in
110
+ `init` **auto-detects your installed agent(s)** (probes PATH for each of the 13 supported CLIs above,
111
+ and existing `.claude`/`.codex`/… dirs): it writes shims for each, sets the active agent in
80
112
  `config.json`, and seeds the runner commands. Override with `--agent=claude,codex`; if nothing is
81
113
  detected it falls back to claude + codex.
82
114
 
@@ -140,52 +172,72 @@ spectoflow dashboard # → http://localhost:4319 (or --port=
140
172
 
141
173
  `spectoflow dashboard` is the simple way — it prints the URL and won't double-start (it detects a
142
174
  dashboard already running on the port). `spectoflow status` tells you whether one is up. Running the
143
- `server.js` directly still works for a manual/embedded setup. Zero dependencies, updates live via SSE +
144
- file watching. Eight tabs behind a dense icon-tab header (brand logo · subtitle · a slim
145
- global-progress meter · a sync dot · a **Run** quick-action · a **settings** gear): **Board** (the
146
- control-room Overview KPI cards, a status donut, a **scope-vs-delivered area curve**, a
147
- workflow-at-a-glance strip, per-phase progress bars, filter chips + search plus the phase board),
148
- **Requests** (tasks awaiting you — `to_validate`/`to_analyze`), **Attention** (points the agent raised
149
- via a `::spectoflow attention msg=…` sentinel or that you noted edit/resolve/delete, or **validate →
150
- task**), **Backlog** (a flat sortable/filterable, paginated table of every task, defaulting to open
151
- work), **Workflow** (the pipeline as step cards — click one to enable/disable it, which edits
152
- `workflow.md`), **Agents & Skills** (enriched cards that open a full-body markdown drawer), **Chat** (a
153
- full-height group-chat panel), and **Info** (a project-at-a-glance summary). URLs are real routes
154
- (`/board`, `/backlog/T-012`, …). Charts are zero-dep, hand-rolled SVG in `dashboard/public/charts.js`
155
- (donut/area/bars/ring, animated, `prefers-reduced-motion`-aware), and every aggregate is computed
156
- client-side.
157
-
158
- **Designs & theme.** The dashboard ships **switchable designs** (Settings *Dashboard design*):
159
- **Control Room** (violet), **Obsidian Ops** (near-black lime/cyan, mono), **Neon Command**
160
- (glassmorphism aurora), and **Mission Control** (indigo control panel). Each works in light and dark
161
- (the moon toggle). A design is a `data-design`
162
- skina scoped CSS token block plus a one-line entry in `dashboard/public/designs.js`, so adding one
163
- is trivial. Fonts are **self-hosted** (`dashboard/public/fonts/*.woff2`), keeping the dashboard fully
164
- offline and dependency-free. Your choice persists per viewer (localStorage) and as the project
165
- default (`config.design`).
166
-
167
- A floating **💬 chat widget** (bottom-right, redesigned) and the **Chat** tab render the same
168
- `runtime.messages` log via a shared `renderChatLog()`, so they never drift. A running agent identifies
169
- itself by printing `::spectoflow role=… kind=… msg=…` sentinels, which become labelled messages
170
- (analyst / developer / qa …); other output streams raw. The board refreshes live as it edits plans.
171
- Either surface can also **Orchestrate** the enabled workflow: each step runs its agent, gated by mode
172
- + policy. The Chat tab can also **Summarize** the recent log into one digest (via the active agent)
173
- or **Clear** it outright, when it's grown long. The Agents & Skills drawer is served by the one
174
- read-only endpoint, `GET /api/agentfile?path=` (scoped to `.spectoflow/agents/**` +
175
- `.spectoflow/skills/**`, path-traversal-safe)the framework's only other server surface is
176
- unchanged.
177
-
178
- **Customize.** Settings **Customize** lets you extend the project's own spectoflow install: add a
175
+ `server.js` directly still works for a manual/embedded setup. Zero dependencies, updates live via SSE
176
+ + file watching.
177
+
178
+ The header bar always shows the brand, the **active agent**, autonomy mode, language, a global-progress
179
+ meter, a sync dot, and a **Run** quick-action. Ten tabs:
180
+
181
+ - **Board** the control-room Overview (compact KPI cards, a status donut, a **scope-vs-delivered
182
+ area curve**, a workflow-at-a-glance strip, per-phase progress bars, filter chips + search) plus the
183
+ phase board.
184
+ - **Chat** a full-height group-chat panel with **Summarize** / **Clear**.
185
+ - **Requests** tasks awaiting you (`to_validate` / `to_analyze`).
186
+ - **Attention** points the agent raised (a `::spectoflow attention msg=…` sentinel) or that you
187
+ noted yourself — edit / resolve / delete, or **validate task**.
188
+ - **Backlog** — a flat sortable/filterable, paginated table of every task, defaulting to open work.
189
+ - **Workflow** — the pipeline as step cards; click one to enable/disable it, which edits `workflow.md`.
190
+ - **Agents & Skills** enriched cards that open a full-body markdown drawer.
191
+ - **Info** a project-at-a-glance summary.
192
+ - **Documentation** the live supported-agents table (your own install status + links) plus the CLI
193
+ command reference.
194
+ - **Personalize** autonomy mode, language, design, the active agent, and **Extend spectoflow** (see
195
+ *Customize* below).
196
+
197
+ URLs are real routes (`/board`, `/backlog/T-012`, …). Charts are zero-dep, hand-rolled SVG in
198
+ `dashboard/public/charts.js` (donut/area/bars/ring, animated, `prefers-reduced-motion`-aware), and
199
+ every aggregate is computed client-side.
200
+
201
+ ### Designs & theme
202
+
203
+ The dashboard ships **switchable designs** (Personalize *Dashboard design*): **Spectral Console**
204
+ (dark-first, ⌘K palette the default), **Orbit** (light, circular radial menu), **Control Room**
205
+ (violet), **Obsidian Ops** (near-black lime/cyan, mono), **Neon Command** (glassmorphism aurora), and
206
+ **Mission Control** (indigo control panel). Each works in light and dark (the moon toggle). A design
207
+ is a `data-design` skin a scoped CSS token block plus a one-line entry in
208
+ `dashboard/public/designs.js`, so adding one is trivial. Fonts are **self-hosted**
209
+ (`dashboard/public/fonts/*.woff2`), keeping the dashboard fully offline and dependency-free. Your
210
+ choice persists per viewer (localStorage) and as the project default (`config.design`).
211
+
212
+ ### Chat
213
+
214
+ A floating **💬 chat widget** and the **Chat** tab render the same `runtime.messages` log via a shared
215
+ `renderChatLog()`, so they never drift. A running agent identifies itself by printing `::spectoflow
216
+ role=… kind=… msg=…` sentinels, which become labelled messages (analyst / developer / qa …); other
217
+ output streams raw. The board refreshes live as it edits plans. Either surface can also
218
+ **Orchestrate** the enabled workflow (each step runs its agent, gated by mode + policy), **Summarize**
219
+ the recent log into one digest via the active agent, or **Clear** it outright. The Agents & Skills
220
+ drawer is served by the one read-only endpoint, `GET /api/agentfile?path=` (scoped to
221
+ `.spectoflow/agents/**` + `.spectoflow/skills/**`, path-traversal-safe) — the framework's only other
222
+ server surface is unchanged.
223
+
224
+ ### Customize
225
+
226
+ Personalize → **Extend spectoflow** lets you extend the project's own spectoflow install: add a
179
227
  dashboard, a skill, or an agent by describing what you want (or hit **Auto** to have the agent survey
180
- the project and propose candidates), and it clarifies first if the ask is ambiguous. Dashboards are
181
- never raw HTML — they're a small **declarative block spec** (`markdown`, `kpi-row`, `chart-bars`,
182
- `chart-donut`, `table`, `list`, `stat-tile-row`) rendered by the same components the built-in Board
183
- uses, so a generated dashboard automatically matches whatever design is active, in both themes, and
184
- keeps matching if you switch designs later. Blocks can bind live to project stats (`bind:
185
- "phases.0.pct"`) or hold a static value. Generated skills and agents follow the same gold-standard
186
- shape as the shipped ones, cite real domain standards (OWASP, WCAG, C4/ADR, …) instead of generic
187
- advice, and are marked `origin: user-generated` so they're easy to tell apart in the UI. The same
188
- generators are available from the terminal:
228
+ the project and propose candidates) it clarifies first if the ask is ambiguous.
229
+
230
+ - Dashboards are never raw HTML they're a small **declarative block spec** (`markdown`, `kpi-row`,
231
+ `chart-bars`, `chart-donut`, `table`, `list`, `stat-tile-row`) rendered by the same components the
232
+ built-in Board uses, so a generated dashboard automatically matches whatever design is active, in
233
+ both themes, and keeps matching if you switch designs later. Blocks can bind live to project stats
234
+ (`bind: "phases.0.pct"`) or hold a static value.
235
+ - Generated skills and agents follow the same gold-standard shape as the shipped ones, cite real
236
+ domain standards (OWASP, WCAG, C4/ADR, …) instead of generic advice, and are marked `origin:
237
+ user-generated` so they're easy to tell apart in the UI.
238
+
239
+ The same generators are available from the terminal — each streams the agent's run live and exits
240
+ with its status, the same pipeline the dashboard's Generate/Auto buttons use:
189
241
 
190
242
  ```bash
191
243
  spectoflow skill create "reviews PRs for accessibility" # or: --auto to propose candidates
@@ -193,9 +245,6 @@ spectoflow agent create "owns accessibility review" # or: --auto
193
245
  spectoflow dashboard create "a KPI overview for support" # or: --auto
194
246
  ```
195
247
 
196
- Each streams the agent's run live and exits with its status — the same pipeline the dashboard's
197
- Generate/Auto buttons use, just from a shell.
198
-
199
248
  ## Agents vs skills
200
249
 
201
250
  Agents (`.spectoflow/agents/`) are **stable team personas** (Product Manager, Developer, QA Engineer…).
@@ -205,25 +254,38 @@ runs a skill. Improve a skill without touching the agent.
205
254
  Agents and skills follow real domain standards, cited in-file — TDD, OWASP ASVS/Top 10, C4/ADR,
206
255
  INVEST, Playwright E2E, Conventional Commits, and more — not generic one-liners.
207
256
 
208
- **Clarify before acting.** spectoflow is an **expert analyst, not an order-taker**. When a request is
209
- vague ("login displays badly, users can't sign in"), an always-on **Clarify reflex** — in the agent's
210
- memory (`AGENTS.md`) and backed by the `clarify` skill reflects it back and asks **one targeted
211
- question at a time**, each with a recommendation anchored in the project's goals and best practices,
212
- until the need is crisp; then it runs the normal workflow. It's additive: it feeds the router, never
213
- replaces it, and it's mode-aware.
214
-
215
- **End-to-end tests run headed, in the real browser, by default.** `write-e2e-tests` defaults to
216
- **Playwright lib, `--headed`** for its own local runs — the browser window is visible so a flow that
217
- "passes" for the wrong reason gets caught, not just a bare pass/fail line. `--ui` mode is used for
218
- authoring a flow or chasing a failure interactively. It only steps down — to headless, then
219
- **Playwright MCP**, then the client's native browser tooling (e.g. Claude Code's Chrome extension), then
220
- writing the spec and raising a `need` when you asked for something else or headed genuinely can't
221
- launch, and it **always says why** via the `::spectoflow` sentinel, never a silent switch. CI keeps
222
- running the committed suite headless that's the pipeline's job, not a fallback. `init` idempotently
223
- wires a `playwright` entry into the target project's `.mcp.json` (and `.cursor/mcp.json` for Cursor) so
224
- the MCP rung works out of the box — `npx` fetches the server on first use, so spectoflow stays zero-dep
225
- (the config lives in *your* project). The durable artifact is always the committed `*.spec.ts`; the
226
- Workflow tab's End-to-end step shows this policy in its dashboard popover.
257
+ ### Clarify before acting
258
+
259
+ spectoflow is an **expert analyst, not an order-taker**. When a request is vague ("login displays
260
+ badly, users can't sign in"), an always-on **Clarify reflex** in the agent's memory (`AGENTS.md`)
261
+ and backed by the `clarify` skill reflects it back and asks **one targeted question at a time**,
262
+ each with a recommendation anchored in the project's goals and best practices, until the need is
263
+ crisp; then it runs the normal workflow. It's additive: it feeds the router, never replaces it, and
264
+ it's mode-aware.
265
+
266
+ ### End-to-end tests run headed, in the real browser, by default
267
+
268
+ `write-e2e-tests` defaults to **Playwright lib, `--headed`** for its own local runs the browser
269
+ window is visible so a flow that "passes" for the wrong reason gets caught, not just a bare pass/fail
270
+ line. `--ui` mode is used for authoring a flow or chasing a failure interactively. It only steps down
271
+ when you asked for something else or headed genuinely can't launch and it **always says why** via
272
+ the `::spectoflow` sentinel, never a silent switch:
273
+
274
+ 1. Playwright lib, headed (default)
275
+ 2. `--ui` (interactive authoring/debugging)
276
+ 3. Playwright lib, headless
277
+ 4. **Playwright MCP**
278
+ 5. the client's native browser tooling (e.g. Claude Code's Chrome extension)
279
+ 6. write the spec and raise a `need`
280
+
281
+ CI keeps running the committed suite headless — that's the pipeline's job, not a fallback. `init`
282
+ idempotently wires a `playwright` entry into the target project's `.mcp.json` (and
283
+ `.cursor/mcp.json` for Cursor) so the MCP rung works out of the box — `npx` fetches the server on
284
+ first use, so spectoflow stays zero-dep (the config lives in *your* project). The durable artifact is
285
+ always the committed `*.spec.ts`; the Workflow tab's End-to-end step shows this policy in its
286
+ dashboard popover.
287
+
288
+ ### Keeping spec and code honest
227
289
 
228
290
  A `governance` capability adds a **Spec Source Guardian** (skill `audit-source`): it keeps the spec
229
291
  (intent) and the code/tests (reality) coherent — flagging drift in both directions, never auto-fixing,
@@ -233,12 +295,30 @@ drift helper (`lib/spec-drift.js`) and an opt-in Claude Code `Stop` hook (`hooks
233
295
  ## Language
234
296
 
235
297
  `.spectoflow/config.json` → `language` (default `en`, incl. code comments). Switchable from the CLI
236
- (`config.json`) or the dashboard's **settings** gear (mode + language).
298
+ (`config.json`), the dashboard's **Personalize** tab, or the topbar's language select directly.
237
299
 
238
300
  ## Studied, not copied
239
301
 
240
- Structure informed by spec-kit, OpenSpec (markdown + per-agent adapters), and BMAD (agent-personas).
241
- spectoflow keeps the good ideas, removes the ceremony, and adds a real-time control plane.
302
+ spectoflow's design was informed by three open-source projects worth knowing about in their own
303
+ right credit where it's due:
304
+
305
+ - **[GitHub spec-kit](https://github.com/github/spec-kit)** — the spec-driven workflow itself
306
+ (spec → plan → tasks) and its own multi-agent integration list, which shaped how spectoflow thinks
307
+ about agent-agnosticism.
308
+ - **[OpenSpec](https://github.com/Fission-AI/OpenSpec)** by Fission AI — markdown as the source of
309
+ truth and the per-agent adapter pattern (a thin native entry file per coding agent, pointing back
310
+ to one canonical brain) that `lib/adapters.js` is built on.
311
+ - **[BMAD-METHOD](https://github.com/bmad-code-org/BMAD-METHOD)** — stable, named agent personas
312
+ (PM, Architect, Developer, QA, …) as the right way to give an AI coding agent a consistent role,
313
+ rather than one undifferentiated prompt.
314
+
315
+ spectoflow keeps what worked, drops the ceremony each of those needs from you, and adds what none of
316
+ them had: a real-time local dashboard (board, chat, live agent-run tracking) and a genuinely
317
+ exhaustive, individually-verified agent-CLI compatibility list — see [Supported coding
318
+ agents](#supported-coding-agents) above.
319
+
320
+ If you use spec-kit, OpenSpec, or BMAD-METHOD today, spectoflow is worth a look; if you're evaluating
321
+ spectoflow, those three are worth a look too.
242
322
 
243
323
  ## License & author
244
324
 
package/bin/spectoflow.js CHANGED
@@ -474,10 +474,12 @@ ${c.dim('Docs:')} https://github.com/georgesmomo/spectoflow`);
474
474
  const HELP = {
475
475
  init: `${c.bold('spectoflow init')} ${c.dim('[dir] [--agent=a,b]')}\n
476
476
  Scaffold spectoflow into <dir> (default: current directory).
477
- Auto-detects installed agents (${c.dim('claude, codex, cursor, gemini')}) and writes their entry
478
- shims; override with ${c.g('--agent=claude,codex')}. Also wires ${c.bold('Playwright MCP')} into the
479
- project's ${c.dim('.mcp.json')} (idempotent never touches an existing entry).
480
- ${c.dim('An existing CLAUDE.md is preserved as CLAUDE.md.tomerge for you to merge on first run.')}`,
477
+ Auto-detects installed agents (${c.dim('claude, codex, cursor, gemini, opencode, kiro, antigravity,')}
478
+ ${c.dim('copilot, amazon-q, droid, auggie, goose, kimi')}) and writes their entry shims; override
479
+ with ${c.g('--agent=claude,codex')}. Also wires ${c.bold('Playwright MCP')} into the project's
480
+ ${c.dim('.mcp.json')} (idempotent never touches an existing entry).
481
+ ${c.dim('An existing CLAUDE.md is preserved as CLAUDE.md.tomerge for you to merge on first run.')}
482
+ ${c.dim('Full list with docs links: the dashboard\'s Documentation tab, or the README.')}`,
481
483
  update: `${c.bold('spectoflow update')} ${c.dim('[--dry-run] [--force|-f]')}\n
482
484
  Refresh framework-owned files (engine, dashboard, default agents & skills, AGENTS.md, policy…)
483
485
  to this CLI's version, ${c.bold('preserving your work')}: config.json, workflow.md, specs/, plans/
package/lib/adapters.js CHANGED
@@ -68,6 +68,10 @@ Argument: \`$ARGUMENTS\`
68
68
 
69
69
  // Priority order = which agent becomes the default when several are detected.
70
70
  //
71
+ // `docsUrl` points at that agent's own official CLI docs — cited from the research pass that set
72
+ // `runner`/`headless`, never guessed; it's surfaced verbatim in the dashboard's Documentation tab so
73
+ // "which agent is this and where do I read about it" has a real answer, not just an id.
74
+ //
71
75
  // `headless: false` marks an agent spectoflow can DETECT and let you set as active, but never spawn
72
76
  // itself — no confirmed non-interactive one-shot mode (prompt as trailing arg → stdout → exit) as of
73
77
  // this research pass, so `runner` is null on purpose (a fabricated command would just fail silently
@@ -75,6 +79,16 @@ Argument: \`$ARGUMENTS\`
75
79
  // for it, rather than hiding it — you can drive it yourself in its own terminal meanwhile, and
76
80
  // `config.json → runners` is still yours to hand-wire a command into if one ships later.
77
81
  //
82
+ // A runner string with more than one flag deliberately puts the trailing prompt right after whichever
83
+ // flag takes a value (e.g. `copilot -s --allow-all-tools -p`, not `copilot -p --allow-all-tools`) —
84
+ // runner.js spawns `[...parts.slice(1), prompt]`, appending the prompt as the LAST token, so a
85
+ // value-taking flag must be the last one in the string or the prompt lands on the wrong flag.
86
+ //
87
+ // Researched against both github.com/Fission-AI/OpenSpec/blob/main/docs/supported-tools.md and
88
+ // github.github.io/spec-kit/reference/integrations.html (broad agent-compatibility lists) — those
89
+ // pages document IDE/slash-command integration, not headless CLI support, so every entry below was
90
+ // independently verified against that tool's own primary docs regardless of appearing there.
91
+ //
78
92
  // Left out of KNOWN_AGENTS entirely (not even headless:false) — researched, not re-added on a whim:
79
93
  // DeepSeek Harness (deepseek-ai/deepseek-harness). It has no single installable binary to detect —
80
94
  // it's invoked as `npx @deepseek-ai/dsh <subcommand>`, is explicitly a local WEB-APP framework (not a
@@ -85,63 +99,127 @@ Argument: \`$ARGUMENTS\`
85
99
  const REGISTRY = [
86
100
  {
87
101
  id: 'claude',
102
+ label: 'Claude Code',
88
103
  entries: [
89
104
  { path: 'CLAUDE.md', content: CLAUDE_MD },
90
105
  { path: '.claude/commands/spectoflow.md', content: SLASH_CMD },
91
106
  ],
92
107
  runner: 'claude -p --permission-mode acceptEdits',
93
108
  headless: true,
109
+ docsUrl: 'https://code.claude.com/docs/en/cli-reference',
94
110
  detect: { bin: 'claude', dirs: ['.claude'] },
95
111
  },
96
112
  {
97
113
  id: 'codex',
114
+ label: 'Codex',
98
115
  entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }],
99
116
  runner: 'codex exec',
100
117
  headless: true,
118
+ docsUrl: 'https://developers.openai.com/codex/cli/reference',
101
119
  detect: { bin: 'codex', dirs: ['.codex'] },
102
120
  },
103
121
  {
104
122
  id: 'cursor',
123
+ label: 'Cursor',
105
124
  entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }],
106
125
  runner: 'cursor-agent -p',
107
126
  headless: true,
127
+ docsUrl: 'https://cursor.com/docs/cli/overview',
108
128
  detect: { bin: 'cursor-agent', dirs: ['.cursor'] },
109
129
  },
110
130
  {
111
131
  id: 'gemini',
132
+ label: 'Gemini CLI',
112
133
  entries: [{ path: 'GEMINI.md', content: GEMINI_MD }],
113
134
  runner: 'gemini -p',
114
135
  headless: true,
136
+ docsUrl: 'https://github.com/google-gemini/gemini-cli',
115
137
  detect: { bin: 'gemini', dirs: ['.gemini'] },
116
138
  },
117
139
  {
118
140
  id: 'opencode',
141
+ label: 'OpenCode',
119
142
  entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }],
120
143
  runner: 'opencode run --quiet',
121
144
  headless: true,
145
+ docsUrl: 'https://opencode.ai/docs/cli/',
122
146
  detect: { bin: 'opencode', dirs: ['.opencode'] },
123
147
  },
124
148
  {
125
149
  id: 'kiro',
150
+ label: 'Kiro CLI',
126
151
  entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }],
127
152
  runner: 'kiro-cli chat --no-interactive --trust-all-tools',
128
153
  headless: true,
154
+ docsUrl: 'https://kiro.dev/docs/cli/headless/',
129
155
  detect: { bin: 'kiro-cli', dirs: ['.kiro'] },
130
156
  },
131
157
  {
132
158
  id: 'antigravity',
159
+ label: 'Antigravity',
133
160
  entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }],
134
161
  runner: 'agy -p',
135
162
  headless: true,
136
- detect: { bin: 'agy' },
163
+ docsUrl: 'https://antigravity.google/docs/cli/headless/',
164
+ detect: { bin: 'agy', dirs: [] },
137
165
  },
138
166
  {
139
167
  id: 'kimi',
168
+ label: 'Kimi CLI',
140
169
  entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }],
141
170
  runner: null, // no confirmed non-interactive one-shot mode — see the note above REGISTRY
142
171
  headless: false,
172
+ docsUrl: 'https://github.com/MoonshotAI/kimi-cli',
143
173
  detect: { bin: 'kimi', dirs: [] },
144
174
  },
175
+ {
176
+ id: 'copilot',
177
+ label: 'GitHub Copilot CLI',
178
+ entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }], // also reads CLAUDE.md/GEMINI.md and its own .github/copilot-instructions.md natively
179
+ runner: 'copilot -s --allow-all-tools -p',
180
+ headless: true,
181
+ docsUrl: 'https://docs.github.com/copilot/concepts/agents/about-copilot-cli',
182
+ // No detect.dirs on purpose: `.github/` is common on any project with GitHub Actions, whether or
183
+ // not Copilot CLI is set up — using it as a signal would falsely mark Copilot "installed" on
184
+ // countless unrelated projects. PATH detection (the bin) only.
185
+ detect: { bin: 'copilot', dirs: [] },
186
+ },
187
+ {
188
+ id: 'amazon-q',
189
+ label: 'Amazon Q Developer CLI',
190
+ entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }], // NOT confirmed native — Q wires memory via `.amazonq/rules/**/*.md` referenced in its own agent config; this pointer is a harmless, forward-compatible default in the meantime
191
+ runner: 'q chat --no-interactive --trust-all-tools',
192
+ headless: true,
193
+ docsUrl: 'https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-chat.html',
194
+ detect: { bin: 'q', dirs: ['.amazonq'] },
195
+ },
196
+ {
197
+ id: 'droid',
198
+ label: 'Factory Droid CLI',
199
+ entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }],
200
+ runner: 'droid exec',
201
+ headless: true,
202
+ docsUrl: 'https://docs.factory.ai/droid-exec/overview',
203
+ detect: { bin: 'droid', dirs: ['.factory'] },
204
+ },
205
+ {
206
+ id: 'auggie',
207
+ label: 'Auggie CLI',
208
+ entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }],
209
+ runner: 'auggie --quiet --print',
210
+ headless: true,
211
+ docsUrl: 'https://docs.augmentcode.com/cli/overview',
212
+ detect: { bin: 'auggie', dirs: ['.augment'] },
213
+ },
214
+ {
215
+ id: 'goose',
216
+ label: 'Goose CLI',
217
+ entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }], // memory-file convention not confirmed AGENTS.md-native — same harmless-default treatment as amazon-q
218
+ runner: 'goose run -t',
219
+ headless: true,
220
+ docsUrl: 'https://block.github.io/goose/',
221
+ detect: { bin: 'goose', dirs: ['.goose'] },
222
+ },
145
223
  ];
146
224
 
147
225
  const byId = (id) => REGISTRY.find((a) => a.id === id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spectoflow",
3
- "version": "0.20.1",
3
+ "version": "0.21.1",
4
4
  "description": "Agent-agnostic spec-driven development framework + real-time local control plane. Markdown artifacts, intent router, workflow-by-scope.",
5
5
  "keywords": [
6
6
  "spec-driven-development",
@@ -28,6 +28,8 @@ sit at the project root and just point back here.
28
28
  ```
29
29
  - **See what you got:** `spectoflow list` (agents, skills & workflow at a glance), or `spectoflow
30
30
  agents` / `spectoflow skills` / `spectoflow workflow`. Append `-h` to any command for its help.
31
+ (These "agents" are spectoflow's own team personas below, not the coding-agent CLI you're typing
32
+ into — see the dashboard's **Documentation** tab for which coding-agent CLIs spectoflow can drive.)
31
33
  - **Change how it runs** in the dashboard's **Personalize** tab (the **active agent**, always shown
32
34
  in the topbar too — switching is verified against what's actually installed before it activates —
33
35
  plus autonomy mode, output language, and the dashboard **design**), or by editing `config.json`.
@@ -151,7 +151,7 @@ function render(){
151
151
  if(meter) meter.title=`${t('kpi.globalProgress')}: ${s.pct}% (${s.done}/${s.total} ${t('kpi.tasksLabel')})`;
152
152
  renderOverview(); renderBoard(); renderBacklog(); renderWorkflow(); renderTeam();
153
153
  renderChatLog($('#chatLog')); renderChatLog($('#chatTabLog'));
154
- renderSidebar(); renderRequests(); renderAttention(); renderInfo(); renderSettings();
154
+ renderSidebar(); renderRequests(); renderAttention(); renderInfo(); renderDocs(); renderSettings();
155
155
  renderCustomDashboards(); // adds/removes nav tabs + panels before applyActiveTab() below reads them
156
156
  applyActiveTab(); // re-apply the current tab so an SSE-driven re-render never resets to Board
157
157
  applyI18nStatic(); // re-translate the static markup (nav, headers, placeholders…) for this tick's language
@@ -264,7 +264,7 @@ function renderOverview(){
264
264
 
265
265
  // KPI row
266
266
  const kpis=el('div','kpi-row');
267
- kpis.append(kpiCard(t('kpi.globalProgress'), ring(s.pct,72), `${s.done}/${s.total} ${t('kpi.tasksLabel')}`, cssv('--s-done')));
267
+ kpis.append(kpiCard(t('kpi.globalProgress'), ring(s.pct,52), `${s.done}/${s.total} ${t('kpi.tasksLabel')}`, cssv('--s-done')));
268
268
  kpis.append(kpiCard(t('status.in_progress'), numBlock(s.byStatus.in_progress||0,'var(--s-in_progress)'), t('kpi.tasksLabel'), cssv('--s-in_progress')));
269
269
  kpis.append(kpiCard(t('status.to_validate'), numBlock(s.byStatus.to_validate||0,'var(--s-to_validate)'), t('kpi.awaitingReview'), cssv('--s-to_validate')));
270
270
  const r=s.running||{};
@@ -967,7 +967,7 @@ async function czSubmit(kind,description,agent){
967
967
  // A custom dashboard (Customize page) gets its own tab id "custom:<id>" and its own URL shape
968
968
  // /custom/<id> — kept out of ROUTES (a fixed list) since the set of custom ids is dynamic; recognized
969
969
  // by a dedicated branch in tabFromPath()/navigateTab() instead.
970
- const ROUTES=['board','requests','attention','backlog','workflow','team','chat','info','settings'];
970
+ const ROUTES=['board','requests','attention','backlog','workflow','team','chat','info','docs','settings'];
971
971
  function tabFromPath(){
972
972
  const s=location.pathname.split('/').filter(Boolean);
973
973
  if(s[0]==='custom'&&s[1]) return 'custom:'+decodeURIComponent(s[1]);
@@ -1046,7 +1046,11 @@ function mdLite(raw){
1046
1046
  // 2) escape HTML — everything below builds markup only from this escaped text
1047
1047
  text=escHtml(text);
1048
1048
  // 3) convert a small markdown subset to HTML
1049
- const inline=s=> s.replace(/`([^`]+)`/g,'<code>$1</code>').replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>');
1049
+ const inline=s=> s
1050
+ .replace(/`([^`]+)`/g,'<code>$1</code>')
1051
+ .replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>')
1052
+ .replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g,'<a href="$2" target="_blank" rel="noopener">$1</a>') // [text](url)
1053
+ .replace(/(^|[^"'>])(https?:\/\/[^\s<]+)/g,'$1<a href="$2" target="_blank" rel="noopener">$2</a>'); // bare https://… URLs, skipping ones already inside an href/text we just built
1050
1054
  const lines=text.split('\n');
1051
1055
  let html='', inCode=false, codeBuf=[], listBuf=[], paraBuf=[];
1052
1056
  const flushPara=()=>{ if(paraBuf.length){ html+='<p>'+paraBuf.join(' ')+'</p>'; paraBuf=[]; } };
@@ -1165,6 +1169,73 @@ function renderInfo(){
1165
1169
  box.append(infoSection(t('info.workflowLabel'),'workflow',wfList));
1166
1170
  }
1167
1171
 
1172
+ // ---- Documentation tab: which coding agents spectoflow can drive (with links to each one's own
1173
+ // docs, sourced from the same registry the topbar switcher reads) + the CLI command reference.
1174
+ // Never build this table from anything but P.knownAgents/P.installedAgents — that data comes from a
1175
+ // real PATH/config-dir test on the server, never a guess spectoflow hasn't verified. ----
1176
+ function linkEl(text,url){ const a=document.createElement('a'); a.href=url; a.target='_blank'; a.rel='noopener'; a.textContent=text; return a; }
1177
+ function docsAgentsTable(){
1178
+ const known=P.knownAgents||[], installed=P.installedAgents||[];
1179
+ const wrap=el('div','table-scroll'); const tbl=el('table','backlog-table');
1180
+ const thead=el('thead'); const htr=el('tr');
1181
+ [t('docs.colAgent'),t('docs.colStatus'),t('docs.colDocs')].forEach(c=>htr.append(el('th',null,c)));
1182
+ thead.append(htr); tbl.append(thead);
1183
+ const tbody=el('tbody');
1184
+ known.forEach(a=>{
1185
+ const tr=el('tr');
1186
+ tr.append(el('td',null,a.label));
1187
+ const statusTd=el('td');
1188
+ const isInstalled=installed.includes(a.id);
1189
+ const chip=el('span','chip s-'+(isInstalled?'done':'todo'), isInstalled?t('docs.installed'):t('docs.notInstalled'));
1190
+ statusTd.append(chip);
1191
+ if(!a.headless){ statusTd.append(document.createTextNode(' ')); statusTd.append(el('span','docs-manual-badge',t('docs.manualOnly'))); }
1192
+ tr.append(statusTd);
1193
+ const docsTd=el('td');
1194
+ if(a.docsUrl) docsTd.append(linkEl(t('docs.officialDocs'),a.docsUrl));
1195
+ tr.append(docsTd);
1196
+ tbody.append(tr);
1197
+ });
1198
+ tbl.append(tbody); wrap.append(tbl);
1199
+ return wrap;
1200
+ }
1201
+ function docsCommandsTable(){
1202
+ const rows=[
1203
+ ['spectoflow init [dir] [--agent=a,b]', t('docs.cmd.init')],
1204
+ ['spectoflow update [--dry-run] [--force|-f]', t('docs.cmd.update')],
1205
+ ['spectoflow status', t('docs.cmd.status')],
1206
+ ['spectoflow dashboard [--port=NNNN]', t('docs.cmd.dashboard')],
1207
+ ['spectoflow dashboard status|stop|restart', t('docs.cmd.dashboardSub')],
1208
+ ['spectoflow dashboard create "…" | --auto', t('docs.cmd.dashboardCreate')],
1209
+ ['spectoflow skill create "…" | --auto', t('docs.cmd.skillCreate')],
1210
+ ['spectoflow agent create "…" | --auto', t('docs.cmd.agentCreate')],
1211
+ ['spectoflow list', t('docs.cmd.list')],
1212
+ ['spectoflow agents · skills · workflow', t('docs.cmd.explore')],
1213
+ ];
1214
+ const wrap=el('div','table-scroll'); const tbl=el('table','backlog-table');
1215
+ const thead=el('thead'); const htr=el('tr');
1216
+ [t('docs.colCommand'),t('docs.colWhatItDoes')].forEach(c=>htr.append(el('th',null,c)));
1217
+ thead.append(htr); tbl.append(thead);
1218
+ const tbody=el('tbody');
1219
+ rows.forEach(([cmd,desc])=>{
1220
+ const tr=el('tr');
1221
+ const cmdTd=el('td'); const code=el('code',null,cmd); cmdTd.append(code); tr.append(cmdTd);
1222
+ tr.append(el('td',null,desc));
1223
+ tbody.append(tr);
1224
+ });
1225
+ tbl.append(tbody); wrap.append(tbl);
1226
+ return wrap;
1227
+ }
1228
+ function renderDocs(){
1229
+ const box=$('#docsBody'); if(!box) return;
1230
+ box.innerHTML='';
1231
+ box.append(infoSection(t('docs.agentsTitle'),'agents',docsAgentsTable()));
1232
+ box.append(infoSection(t('docs.commandsTitle'),'run',docsCommandsTable()));
1233
+ const note=el('p','docs-note');
1234
+ note.append(document.createTextNode(t('docs.moreNote')+' '));
1235
+ note.append(linkEl('github.com/georgesmomo/spectoflow','https://github.com/georgesmomo/spectoflow'));
1236
+ box.append(note);
1237
+ }
1238
+
1168
1239
  function openDrawer(id,keep){
1169
1240
  // named `task`, not `t` — `t` is the global translation function (see i18n.js) and this whole
1170
1241
  // function calls it repeatedly below; shadowing it with a task variable would break every call.
@@ -20,7 +20,7 @@
20
20
  const I18N = {
21
21
  en: {
22
22
  'nav.board':'Board','nav.requests':'Requests','nav.attention':'Attention','nav.backlog':'Backlog',
23
- 'nav.workflow':'Workflow','nav.team':'Agents & Skills','nav.chat':'Chat','nav.info':'Info','nav.settings':'Personalize',
23
+ 'nav.workflow':'Workflow','nav.team':'Agents & Skills','nav.chat':'Chat','nav.info':'Info','nav.docs':'Documentation','nav.settings':'Personalize',
24
24
  'status.todo':'To do','status.in_progress':'In progress','status.to_validate':'To validate',
25
25
  'status.to_analyze':'To analyze','status.done':'Done','status.blocked':'Blocked',
26
26
  'filter.all':'All','filter.open':'Open','filter.resolved':'Resolved',
@@ -114,10 +114,25 @@ en: {
114
114
  'customize.add.dashboard':'Add dashboard','customize.add.skill':'Add skill','customize.add.agent':'Add agent',
115
115
  'customize.empty.dashboard':'No custom dashboards yet.','customize.empty.skill':'No custom skills yet.','customize.empty.agent':'No custom agents yet.',
116
116
  'customize.describePh':'Describe what you want…','customize.auto':'Auto','customize.generate':'Generate','customize.blocksSub':'block(s)',
117
+ 'docs.title':'Documentation','docs.sub':'Which coding agents spectoflow can drive, and the commands that drive them.',
118
+ 'docs.agentsTitle':'Supported agents','docs.commandsTitle':'Commands',
119
+ 'docs.colAgent':'Agent','docs.colStatus':'Status','docs.colDocs':'Docs','docs.colCommand':'Command','docs.colWhatItDoes':'What it does',
120
+ 'docs.installed':'Installed','docs.notInstalled':'Not installed','docs.manualOnly':'Manual only','docs.officialDocs':'Official docs ↗',
121
+ 'docs.moreNote':'Full reference, source and license on GitHub:',
122
+ 'docs.cmd.init':'Scaffold a project (auto-detects agents; wires Playwright MCP).',
123
+ 'docs.cmd.update':'Refresh framework files to this kit version, preserving your work.',
124
+ 'docs.cmd.status':'Progress + whether the dashboard is running.',
125
+ 'docs.cmd.dashboard':'Start the control plane in the background.',
126
+ 'docs.cmd.dashboardSub':'Check, stop, or restart the running dashboard.',
127
+ 'docs.cmd.dashboardCreate':'Generate a custom dashboard from the terminal.',
128
+ 'docs.cmd.skillCreate':'Generate a project-specific skill from the terminal.',
129
+ 'docs.cmd.agentCreate':'Generate a project-specific agent persona from the terminal.',
130
+ 'docs.cmd.list':'Agents, skills and the workflow at a glance.',
131
+ 'docs.cmd.explore':'List the team personas, the procedures, or the pipeline steps.',
117
132
  },
118
133
  fr: {
119
134
  'nav.board':'Tableau','nav.requests':'Demandes','nav.attention':'Attention','nav.backlog':'Backlog',
120
- 'nav.workflow':'Workflow','nav.team':'Agents et compétences','nav.chat':'Chat','nav.info':'Infos','nav.settings':'Personnalisation',
135
+ 'nav.workflow':'Workflow','nav.team':'Agents et compétences','nav.chat':'Chat','nav.info':'Infos','nav.docs':'Documentation','nav.settings':'Personnalisation',
121
136
  'status.todo':'À faire','status.in_progress':'En cours','status.to_validate':'À valider',
122
137
  'status.to_analyze':'À analyser','status.done':'Terminé','status.blocked':'Bloqué',
123
138
  'filter.all':'Tous','filter.open':'Ouverts','filter.resolved':'Résolus',
@@ -211,10 +226,25 @@ fr: {
211
226
  'customize.add.dashboard':'Ajouter un dashboard','customize.add.skill':'Ajouter une compétence','customize.add.agent':'Ajouter un agent',
212
227
  'customize.empty.dashboard':'Aucun dashboard personnalisé pour l’instant.','customize.empty.skill':'Aucune compétence personnalisée pour l’instant.','customize.empty.agent':'Aucun agent personnalisé pour l’instant.',
213
228
  'customize.describePh':'Décrivez ce que vous voulez…','customize.auto':'Auto','customize.generate':'Générer','customize.blocksSub':'bloc(s)',
229
+ 'docs.title':'Documentation','docs.sub':'Les agents de code que spectoflow peut piloter, et les commandes qui les pilotent.',
230
+ 'docs.agentsTitle':'Agents pris en charge','docs.commandsTitle':'Commandes',
231
+ 'docs.colAgent':'Agent','docs.colStatus':'Statut','docs.colDocs':'Docs','docs.colCommand':'Commande','docs.colWhatItDoes':'Ce que ça fait',
232
+ 'docs.installed':'Installé','docs.notInstalled':'Non installé','docs.manualOnly':'Manuel uniquement','docs.officialDocs':'Doc officielle ↗',
233
+ 'docs.moreNote':'Référence complète, source et licence sur GitHub :',
234
+ 'docs.cmd.init':'Initialise un projet (détecte les agents automatiquement ; câble Playwright MCP).',
235
+ 'docs.cmd.update':'Met à jour les fichiers du framework vers cette version, en préservant vos modifications.',
236
+ 'docs.cmd.status':'Progression + si le dashboard tourne.',
237
+ 'docs.cmd.dashboard':'Démarre le plan de contrôle en arrière-plan.',
238
+ 'docs.cmd.dashboardSub':'Vérifie, arrête ou redémarre le dashboard en cours.',
239
+ 'docs.cmd.dashboardCreate':'Génère un dashboard personnalisé depuis le terminal.',
240
+ 'docs.cmd.skillCreate':'Génère une compétence propre au projet depuis le terminal.',
241
+ 'docs.cmd.agentCreate':'Génère un agent (persona) propre au projet depuis le terminal.',
242
+ 'docs.cmd.list':'Agents, compétences et workflow en un coup d’œil.',
243
+ 'docs.cmd.explore':'Liste les personas de l’équipe, les procédures ou les étapes du pipeline.',
214
244
  },
215
245
  es: {
216
246
  'nav.board':'Tablero','nav.requests':'Solicitudes','nav.attention':'Atención','nav.backlog':'Backlog',
217
- 'nav.workflow':'Workflow','nav.team':'Agentes y habilidades','nav.chat':'Chat','nav.info':'Info','nav.settings':'Personalizar',
247
+ 'nav.workflow':'Workflow','nav.team':'Agentes y habilidades','nav.chat':'Chat','nav.info':'Info','nav.docs':'Documentación','nav.settings':'Personalizar',
218
248
  'status.todo':'Por hacer','status.in_progress':'En curso','status.to_validate':'Por validar',
219
249
  'status.to_analyze':'Por analizar','status.done':'Hecho','status.blocked':'Bloqueado',
220
250
  'filter.all':'Todos','filter.open':'Abiertos','filter.resolved':'Resueltos',
@@ -308,10 +338,25 @@ es: {
308
338
  'customize.add.dashboard':'Añadir dashboard','customize.add.skill':'Añadir habilidad','customize.add.agent':'Añadir agente',
309
339
  'customize.empty.dashboard':'Aún no hay dashboards personalizados.','customize.empty.skill':'Aún no hay habilidades personalizadas.','customize.empty.agent':'Aún no hay agentes personalizados.',
310
340
  'customize.describePh':'Describe lo que quieres…','customize.auto':'Auto','customize.generate':'Generar','customize.blocksSub':'bloque(s)',
341
+ 'docs.title':'Documentación','docs.sub':'Qué agentes de código puede pilotar spectoflow, y los comandos que los pilotan.',
342
+ 'docs.agentsTitle':'Agentes compatibles','docs.commandsTitle':'Comandos',
343
+ 'docs.colAgent':'Agente','docs.colStatus':'Estado','docs.colDocs':'Docs','docs.colCommand':'Comando','docs.colWhatItDoes':'Qué hace',
344
+ 'docs.installed':'Instalado','docs.notInstalled':'No instalado','docs.manualOnly':'Solo manual','docs.officialDocs':'Docs oficial ↗',
345
+ 'docs.moreNote':'Referencia completa, código fuente y licencia en GitHub:',
346
+ 'docs.cmd.init':'Crea un proyecto (detecta agentes automáticamente; conecta Playwright MCP).',
347
+ 'docs.cmd.update':'Actualiza los archivos del framework a esta versión, preservando tu trabajo.',
348
+ 'docs.cmd.status':'Progreso + si el dashboard está en ejecución.',
349
+ 'docs.cmd.dashboard':'Inicia el panel de control en segundo plano.',
350
+ 'docs.cmd.dashboardSub':'Consulta, detiene o reinicia el dashboard en ejecución.',
351
+ 'docs.cmd.dashboardCreate':'Genera un dashboard personalizado desde la terminal.',
352
+ 'docs.cmd.skillCreate':'Genera una habilidad propia del proyecto desde la terminal.',
353
+ 'docs.cmd.agentCreate':'Genera un agente (persona) propio del proyecto desde la terminal.',
354
+ 'docs.cmd.list':'Agentes, habilidades y workflow de un vistazo.',
355
+ 'docs.cmd.explore':'Lista los personas del equipo, los procedimientos o los pasos del pipeline.',
311
356
  },
312
357
  de: {
313
358
  'nav.board':'Board','nav.requests':'Anfragen','nav.attention':'Hinweise','nav.backlog':'Backlog',
314
- 'nav.workflow':'Workflow','nav.team':'Agenten & Skills','nav.chat':'Chat','nav.info':'Info','nav.settings':'Personalisieren',
359
+ 'nav.workflow':'Workflow','nav.team':'Agenten & Skills','nav.chat':'Chat','nav.info':'Info','nav.docs':'Dokumentation','nav.settings':'Personalisieren',
315
360
  'status.todo':'Offen','status.in_progress':'In Arbeit','status.to_validate':'Zu prüfen',
316
361
  'status.to_analyze':'Zu analysieren','status.done':'Erledigt','status.blocked':'Blockiert',
317
362
  'filter.all':'Alle','filter.open':'Offen','filter.resolved':'Erledigt',
@@ -405,10 +450,25 @@ de: {
405
450
  'customize.add.dashboard':'Dashboard hinzufügen','customize.add.skill':'Skill hinzufügen','customize.add.agent':'Agent hinzufügen',
406
451
  'customize.empty.dashboard':'Noch keine eigenen Dashboards.','customize.empty.skill':'Noch keine eigenen Skills.','customize.empty.agent':'Noch keine eigenen Agenten.',
407
452
  'customize.describePh':'Beschreiben Sie, was Sie wollen…','customize.auto':'Auto','customize.generate':'Generieren','customize.blocksSub':'Block/Blöcke',
453
+ 'docs.title':'Dokumentation','docs.sub':'Welche Coding-Agenten spectoflow steuern kann, und die Befehle dafür.',
454
+ 'docs.agentsTitle':'Unterstützte Agenten','docs.commandsTitle':'Befehle',
455
+ 'docs.colAgent':'Agent','docs.colStatus':'Status','docs.colDocs':'Docs','docs.colCommand':'Befehl','docs.colWhatItDoes':'Was er macht',
456
+ 'docs.installed':'Installiert','docs.notInstalled':'Nicht installiert','docs.manualOnly':'Nur manuell','docs.officialDocs':'Offizielle Docs ↗',
457
+ 'docs.moreNote':'Vollständige Referenz, Quellcode und Lizenz auf GitHub:',
458
+ 'docs.cmd.init':'Projekt aufsetzen (erkennt Agenten automatisch; verdrahtet Playwright MCP).',
459
+ 'docs.cmd.update':'Framework-Dateien auf diese Version aktualisieren, eigene Änderungen bleiben erhalten.',
460
+ 'docs.cmd.status':'Fortschritt + ob das Dashboard läuft.',
461
+ 'docs.cmd.dashboard':'Startet die Kontrollzentrale im Hintergrund.',
462
+ 'docs.cmd.dashboardSub':'Prüft, stoppt oder startet das laufende Dashboard neu.',
463
+ 'docs.cmd.dashboardCreate':'Erzeugt ein individuelles Dashboard über das Terminal.',
464
+ 'docs.cmd.skillCreate':'Erzeugt einen projektspezifischen Skill über das Terminal.',
465
+ 'docs.cmd.agentCreate':'Erzeugt eine projektspezifische Agenten-Persona über das Terminal.',
466
+ 'docs.cmd.list':'Agenten, Skills und Workflow auf einen Blick.',
467
+ 'docs.cmd.explore':'Listet die Team-Personas, die Verfahren oder die Pipeline-Schritte.',
408
468
  },
409
469
  pt: {
410
470
  'nav.board':'Painel','nav.requests':'Pedidos','nav.attention':'Atenção','nav.backlog':'Backlog',
411
- 'nav.workflow':'Workflow','nav.team':'Agentes e habilidades','nav.chat':'Chat','nav.info':'Info','nav.settings':'Personalizar',
471
+ 'nav.workflow':'Workflow','nav.team':'Agentes e habilidades','nav.chat':'Chat','nav.info':'Info','nav.docs':'Documentação','nav.settings':'Personalizar',
412
472
  'status.todo':'A fazer','status.in_progress':'Em curso','status.to_validate':'Por validar',
413
473
  'status.to_analyze':'Por analisar','status.done':'Concluído','status.blocked':'Bloqueado',
414
474
  'filter.all':'Todos','filter.open':'Abertos','filter.resolved':'Resolvidos',
@@ -502,10 +562,25 @@ pt: {
502
562
  'customize.add.dashboard':'Adicionar dashboard','customize.add.skill':'Adicionar habilidade','customize.add.agent':'Adicionar agente',
503
563
  'customize.empty.dashboard':'Ainda sem dashboards personalizados.','customize.empty.skill':'Ainda sem habilidades personalizadas.','customize.empty.agent':'Ainda sem agentes personalizados.',
504
564
  'customize.describePh':'Descreva o que quer…','customize.auto':'Auto','customize.generate':'Gerar','customize.blocksSub':'bloco(s)',
565
+ 'docs.title':'Documentação','docs.sub':'Quais agentes de código o spectoflow consegue conduzir, e os comandos que os conduzem.',
566
+ 'docs.agentsTitle':'Agentes suportados','docs.commandsTitle':'Comandos',
567
+ 'docs.colAgent':'Agente','docs.colStatus':'Estado','docs.colDocs':'Docs','docs.colCommand':'Comando','docs.colWhatItDoes':'O que faz',
568
+ 'docs.installed':'Instalado','docs.notInstalled':'Não instalado','docs.manualOnly':'Só manual','docs.officialDocs':'Docs oficial ↗',
569
+ 'docs.moreNote':'Referência completa, código-fonte e licença no GitHub:',
570
+ 'docs.cmd.init':'Cria um projeto (deteta agentes automaticamente; liga o Playwright MCP).',
571
+ 'docs.cmd.update':'Atualiza os ficheiros do framework para esta versão, preservando o seu trabalho.',
572
+ 'docs.cmd.status':'Progresso + se o dashboard está em execução.',
573
+ 'docs.cmd.dashboard':'Inicia o painel de controlo em segundo plano.',
574
+ 'docs.cmd.dashboardSub':'Verifica, para ou reinicia o dashboard em execução.',
575
+ 'docs.cmd.dashboardCreate':'Gera um dashboard personalizado a partir do terminal.',
576
+ 'docs.cmd.skillCreate':'Gera uma competência própria do projeto a partir do terminal.',
577
+ 'docs.cmd.agentCreate':'Gera um agente (persona) próprio do projeto a partir do terminal.',
578
+ 'docs.cmd.list':'Agentes, competências e workflow de relance.',
579
+ 'docs.cmd.explore':'Lista os personas da equipa, os procedimentos ou os passos do pipeline.',
505
580
  },
506
581
  it: {
507
582
  'nav.board':'Bacheca','nav.requests':'Richieste','nav.attention':'Attenzione','nav.backlog':'Backlog',
508
- 'nav.workflow':'Workflow','nav.team':'Agenti e competenze','nav.chat':'Chat','nav.info':'Info','nav.settings':'Personalizza',
583
+ 'nav.workflow':'Workflow','nav.team':'Agenti e competenze','nav.chat':'Chat','nav.info':'Info','nav.docs':'Documentazione','nav.settings':'Personalizza',
509
584
  'status.todo':'Da fare','status.in_progress':'In corso','status.to_validate':'Da convalidare',
510
585
  'status.to_analyze':'Da analizzare','status.done':'Fatto','status.blocked':'Bloccato',
511
586
  'filter.all':'Tutti','filter.open':'Aperti','filter.resolved':'Risolti',
@@ -599,6 +674,21 @@ it: {
599
674
  'customize.add.dashboard':'Aggiungi dashboard','customize.add.skill':'Aggiungi skill','customize.add.agent':'Aggiungi agente',
600
675
  'customize.empty.dashboard':'Ancora nessuna dashboard personalizzata.','customize.empty.skill':'Ancora nessuna skill personalizzata.','customize.empty.agent':'Ancora nessun agente personalizzato.',
601
676
  'customize.describePh':'Descrivi cosa vuoi…','customize.auto':'Auto','customize.generate':'Genera','customize.blocksSub':'blocco/i',
677
+ 'docs.title':'Documentazione','docs.sub':'Quali agenti di codice spectoflow può guidare, e i comandi che li guidano.',
678
+ 'docs.agentsTitle':'Agenti supportati','docs.commandsTitle':'Comandi',
679
+ 'docs.colAgent':'Agente','docs.colStatus':'Stato','docs.colDocs':'Docs','docs.colCommand':'Comando','docs.colWhatItDoes':'Cosa fa',
680
+ 'docs.installed':'Installato','docs.notInstalled':'Non installato','docs.manualOnly':'Solo manuale','docs.officialDocs':'Docs ufficiale ↗',
681
+ 'docs.moreNote':'Riferimento completo, codice sorgente e licenza su GitHub:',
682
+ 'docs.cmd.init':'Crea un progetto (rileva gli agenti automaticamente; collega Playwright MCP).',
683
+ 'docs.cmd.update':'Aggiorna i file del framework a questa versione, preservando il tuo lavoro.',
684
+ 'docs.cmd.status':'Avanzamento + se il dashboard è in esecuzione.',
685
+ 'docs.cmd.dashboard':'Avvia il pannello di controllo in background.',
686
+ 'docs.cmd.dashboardSub':'Controlla, ferma o riavvia il dashboard in esecuzione.',
687
+ 'docs.cmd.dashboardCreate':'Genera una dashboard personalizzata dal terminale.',
688
+ 'docs.cmd.skillCreate':'Genera una skill specifica del progetto dal terminale.',
689
+ 'docs.cmd.agentCreate':'Genera un agente (persona) specifico del progetto dal terminale.',
690
+ 'docs.cmd.list':'Agenti, skill e workflow a colpo d’occhio.',
691
+ 'docs.cmd.explore':'Elenca le persona del team, le procedure o i passaggi della pipeline.',
602
692
  },
603
693
  };
604
694
 
@@ -25,6 +25,8 @@
25
25
  attention: wrap('<line x1="4.5" y1="2.4" x2="4.5" y2="15.6"/><path d="M4.5 3.4h8.2l-1.7 2.6 1.7 2.6H4.5z"/>'),
26
26
  // gear — settings (proper cog with teeth)
27
27
  settings: wrap('<circle cx="9" cy="9" r="2.4"/><path d="M9 1.6v2.2M9 14.2v2.2M16.4 9h-2.2M3.8 9H1.6M14.2 3.8l-1.55 1.55M5.35 12.65 3.8 14.2M14.2 14.2l-1.55-1.55M5.35 5.35 3.8 3.8"/>'),
28
+ // open book — documentation
29
+ docs: wrap('<path d="M9 5.4C7.4 4.2 5.2 3.7 2.8 4v9c2.4-.3 4.6.2 6.2 1.4 1.6-1.2 3.8-1.7 6.2-1.4V4c-2.4-.3-4.6.2-6.2 1.4z"/><line x1="9" y1="5.4" x2="9" y2="14.4"/>'),
28
30
  // crescent moon — theme toggle
29
31
  theme: wrap('<path d="M14.6 10.8A5.6 5.6 0 0 1 7.2 3.4 5.7 5.7 0 1 0 14.6 10.8z"/>'),
30
32
  };
@@ -57,6 +57,7 @@
57
57
  <button class="tab" data-tab="workflow" data-i18n-title="nav.workflow" title="Workflow"><span class="tab-ico" data-icon="workflow"></span><span class="tab-label" data-i18n="nav.workflow">Workflow</span></button>
58
58
  <button class="tab" data-tab="team" data-i18n-title="nav.team" title="Agents &amp; Skills"><span class="tab-ico" data-icon="agents"></span><span class="tab-label" data-i18n="nav.team">Agents &amp; Skills</span></button>
59
59
  <button class="tab" data-tab="info" data-i18n-title="nav.info" title="Info"><span class="tab-ico" data-icon="info"></span><span class="tab-label" data-i18n="nav.info">Info</span></button>
60
+ <button class="tab" data-tab="docs" data-i18n-title="nav.docs" title="Documentation"><span class="tab-ico" data-icon="docs"></span><span class="tab-label" data-i18n="nav.docs">Documentation</span></button>
60
61
  <button class="tab" data-tab="settings" data-i18n-title="nav.settings" title="Personalize"><span class="tab-ico" data-icon="settings"></span><span class="tab-label" data-i18n="nav.settings">Personalize</span></button>
61
62
  </nav>
62
63
  <div class="top-right">
@@ -233,6 +234,15 @@
233
234
  </div>
234
235
  </section>
235
236
 
237
+ <!-- DOCUMENTATION (agent compatibility + CLI command reference — filled by renderDocs() in app.js) -->
238
+ <section class="panel" data-panel="docs">
239
+ <div class="info-wrap">
240
+ <h2 class="panel-title" data-i18n="docs.title">Documentation</h2>
241
+ <p class="panel-sub" data-i18n="docs.sub">Which coding agents spectoflow can drive, and the commands that drive them.</p>
242
+ <div class="info-grid docs-grid" id="docsBody"></div>
243
+ </div>
244
+ </section>
245
+
236
246
  <!-- SETTINGS (change autonomy mode + output language → config.json) -->
237
247
  <section class="panel" data-panel="settings">
238
248
  <div class="settings-wrap">
@@ -97,17 +97,17 @@ body { background:var(--bg); color:var(--ink); font-family:var(--sans); font-siz
97
97
 
98
98
  /* overview: KPI cards + status donut + workflow strip + phase bars */
99
99
  .overview { padding:22px 24px 6px; display:flex; flex-direction:column; gap:14px; }
100
- .kpi-row { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; }
101
- .kpi { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:14px 16px; display:flex; flex-direction:column; gap:8px; transition:box-shadow .15s; }
100
+ .kpi-row { display:grid; grid-template-columns:repeat(4,1fr); gap:9px; }
101
+ .kpi { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:10px 13px; display:flex; flex-direction:column; gap:4px; transition:box-shadow .15s; }
102
102
  .kpi:hover { box-shadow:var(--shadow); }
103
- .kpi-label { font-size:11px; text-transform:uppercase; letter-spacing:.08em; color:var(--faint); font-weight:700; }
103
+ .kpi-label { font-size:10px; text-transform:uppercase; letter-spacing:.07em; color:var(--faint); font-weight:700; }
104
104
  .kpi-body { display:flex; align-items:center; }
105
- .kpi-num { font-family:var(--mono); font-size:30px; font-weight:700; line-height:1; color:var(--ink); }
106
- .kpi-num.is-text { font-size:17px; text-transform:capitalize; }
107
- .kpi-sub { font-size:11.5px; color:var(--muted); }
105
+ .kpi-num { font-family:var(--mono); font-size:23px; font-weight:700; line-height:1; color:var(--ink); }
106
+ .kpi-num.is-text { font-size:14px; text-transform:capitalize; }
107
+ .kpi-sub { font-size:10.5px; color:var(--muted); }
108
108
 
109
109
  .ring-wrap { position:relative; display:inline-flex; align-items:center; justify-content:center; }
110
- .ring-wrap .ring-label { position:absolute; font-family:var(--mono); font-size:13px; font-weight:700; color:var(--ink); }
110
+ .ring-wrap .ring-label { position:absolute; font-family:var(--mono); font-size:10.5px; font-weight:700; color:var(--ink); }
111
111
  .ring-svg circle { transition:stroke-dashoffset .5s ease; }
112
112
 
113
113
  .ocard { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:16px 18px; }
@@ -556,6 +556,14 @@ body.booting .ring-svg circle:last-of-type { transform-origin:center; animation:
556
556
  .settings-select:focus { outline:2px solid var(--signal); outline-offset:1px; }
557
557
  .settings-field-hint { font-size:12px; }
558
558
  .settings-field-hint.is-empty { color:var(--s-blocked); }
559
+ .docs-grid { grid-template-columns:1fr; } /* the built-in tables are wide — full width beats 2-up */
560
+ .docs-grid .backlog-table a { color:var(--signal); text-decoration:none; }
561
+ .docs-grid .backlog-table a:hover { text-decoration:underline; }
562
+ .docs-grid .backlog-table code { font-family:var(--mono); font-size:12px; }
563
+ .docs-manual-badge { font-size:10px; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); border:1px solid var(--line); border-radius:999px; padding:2px 7px; }
564
+ .docs-note { margin:14px 2px 0; font-size:12.5px; color:var(--muted); }
565
+ .docs-note a { color:var(--signal); text-decoration:none; }
566
+ .docs-note a:hover { text-decoration:underline; }
559
567
  .settings-saved { align-self:flex-start; font-size:12px; color:var(--s-done); font-family:var(--mono); }
560
568
  .settings-saved[hidden] { display:none; }
561
569
  .settings-readonly { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); overflow:hidden; }
@@ -35,7 +35,7 @@ function project(){
35
35
  // Known vs. actually-installed agents — the topbar switcher needs both: the full list to offer,
36
36
  // and which ones are real (bin on PATH, or the project already has that agent's config dir) so it
37
37
  // can refuse to activate one that isn't there.
38
- p.knownAgents = agentsRegistry.KNOWN_AGENTS.map((a) => ({ id: a.id, label: a.label, headless: a.headless }));
38
+ p.knownAgents = agentsRegistry.KNOWN_AGENTS.map((a) => ({ id: a.id, label: a.label, headless: a.headless, docsUrl: a.docsUrl }));
39
39
  p.installedAgents = agentsRegistry.installedAgents(ROOT);
40
40
  return p;
41
41
  }
@@ -11,17 +11,25 @@ const path = require('path');
11
11
 
12
12
  // headless:false = detectable and selectable as the active agent, but spectoflow never spawns it
13
13
  // itself (no confirmed non-interactive one-shot mode) — Run/Orchestrate/Summarize stay disabled for
14
- // it client-side; runner/summarize.js also refuse server-side (defense in depth). See the longer
15
- // rationale above lib/adapters.js's REGISTRY, the richer install-time twin of this list.
14
+ // it client-side; runner/summarize.js also refuse server-side (defense in depth). `docsUrl` is that
15
+ // agent's own official CLI docs, surfaced verbatim in the dashboard's Documentation tab. See the
16
+ // longer rationale above lib/adapters.js's REGISTRY, the richer install-time twin of this list —
17
+ // including why DeepSeek Harness isn't here at all, and why some runner strings order their flags
18
+ // the way they do (the trailing prompt must land right after whichever flag takes a value).
16
19
  const KNOWN_AGENTS = [
17
- { id: 'claude', label: 'Claude Code', bin: 'claude', dirs: ['.claude'], runner: 'claude -p --permission-mode acceptEdits', headless: true },
18
- { id: 'codex', label: 'Codex', bin: 'codex', dirs: ['.codex'], runner: 'codex exec', headless: true },
19
- { id: 'cursor', label: 'Cursor', bin: 'cursor-agent', dirs: ['.cursor'], runner: 'cursor-agent -p', headless: true },
20
- { id: 'gemini', label: 'Gemini CLI', bin: 'gemini', dirs: ['.gemini'], runner: 'gemini -p', headless: true },
21
- { id: 'opencode', label: 'OpenCode', bin: 'opencode', dirs: ['.opencode'], runner: 'opencode run --quiet', headless: true },
22
- { id: 'kiro', label: 'Kiro CLI', bin: 'kiro-cli', dirs: ['.kiro'], runner: 'kiro-cli chat --no-interactive --trust-all-tools', headless: true },
23
- { id: 'antigravity', label: 'Antigravity', bin: 'agy', dirs: [], runner: 'agy -p', headless: true },
24
- { id: 'kimi', label: 'Kimi CLI', bin: 'kimi', dirs: [], runner: null, headless: false },
20
+ { id: 'claude', label: 'Claude Code', bin: 'claude', dirs: ['.claude'], runner: 'claude -p --permission-mode acceptEdits', headless: true, docsUrl: 'https://code.claude.com/docs/en/cli-reference' },
21
+ { id: 'codex', label: 'Codex', bin: 'codex', dirs: ['.codex'], runner: 'codex exec', headless: true, docsUrl: 'https://developers.openai.com/codex/cli/reference' },
22
+ { id: 'cursor', label: 'Cursor', bin: 'cursor-agent', dirs: ['.cursor'], runner: 'cursor-agent -p', headless: true, docsUrl: 'https://cursor.com/docs/cli/overview' },
23
+ { id: 'gemini', label: 'Gemini CLI', bin: 'gemini', dirs: ['.gemini'], runner: 'gemini -p', headless: true, docsUrl: 'https://github.com/google-gemini/gemini-cli' },
24
+ { id: 'opencode', label: 'OpenCode', bin: 'opencode', dirs: ['.opencode'], runner: 'opencode run --quiet', headless: true, docsUrl: 'https://opencode.ai/docs/cli/' },
25
+ { id: 'kiro', label: 'Kiro CLI', bin: 'kiro-cli', dirs: ['.kiro'], runner: 'kiro-cli chat --no-interactive --trust-all-tools', headless: true, docsUrl: 'https://kiro.dev/docs/cli/headless/' },
26
+ { id: 'antigravity', label: 'Antigravity', bin: 'agy', dirs: [], runner: 'agy -p', headless: true, docsUrl: 'https://antigravity.google/docs/cli/headless/' },
27
+ { id: 'kimi', label: 'Kimi CLI', bin: 'kimi', dirs: [], runner: null, headless: false, docsUrl: 'https://github.com/MoonshotAI/kimi-cli' },
28
+ { id: 'copilot', label: 'GitHub Copilot CLI', bin: 'copilot', dirs: [], runner: 'copilot -s --allow-all-tools -p', headless: true, docsUrl: 'https://docs.github.com/copilot/concepts/agents/about-copilot-cli' },
29
+ { id: 'amazon-q', label: 'Amazon Q Developer CLI', bin: 'q', dirs: ['.amazonq'], runner: 'q chat --no-interactive --trust-all-tools', headless: true, docsUrl: 'https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-chat.html' },
30
+ { id: 'droid', label: 'Factory Droid CLI', bin: 'droid', dirs: ['.factory'], runner: 'droid exec', headless: true, docsUrl: 'https://docs.factory.ai/droid-exec/overview' },
31
+ { id: 'auggie', label: 'Auggie CLI', bin: 'auggie', dirs: ['.augment'], runner: 'auggie --quiet --print', headless: true, docsUrl: 'https://docs.augmentcode.com/cli/overview' },
32
+ { id: 'goose', label: 'Goose CLI', bin: 'goose', dirs: ['.goose'], runner: 'goose run -t', headless: true, docsUrl: 'https://block.github.io/goose/' },
25
33
  ];
26
34
 
27
35
  // Is `bin` an executable resolvable on PATH? On win32, an extension from PATHEXT is required, so we