spectoflow 0.20.0 → 0.21.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 CHANGED
@@ -20,11 +20,39 @@ An **agent-agnostic** spec-driven development framework with a **real-time local
20
20
  You speak in plain language; the framework classifies your intent and runs the right workflow. No
21
21
  ceremonial command to start.
22
22
 
23
- **Works with whichever coding agent you have** Claude Code, Codex, Cursor, Gemini CLI, OpenCode,
24
- Kiro CLI, and Antigravity are auto-detected at `init`. The dashboard's topbar always shows the
25
- **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.
23
+ **Works with whichever coding agent you have.** `init` auto-detects what's installed; the dashboard's
24
+ topbar always shows the **active agent**, front and center, with a switcher pick another and it's
25
+ verified as genuinely installed before activating (a red **"No agent found"** if none is), never
26
+ silently activating something that isn't there.
27
+
28
+ > Note the terminology: "agent" is overloaded here on purpose, matching how the ecosystem uses the
29
+ > word. The table below is about **coding-agent CLIs** — the tool you already run in your terminal
30
+ > (Claude Code, Copilot CLI, …). spectoflow's own **team personas** (developer, qa-engineer, …) are a
31
+ > different, unrelated use of "agent" — see [Agents vs skills](#agents-vs-skills) further down.
32
+
33
+ | Coding agent | Headless run | Docs |
34
+ |---|---|---|
35
+ | Claude Code | ✓ | [code.claude.com/docs/en/cli-reference](https://code.claude.com/docs/en/cli-reference) |
36
+ | Codex | ✓ | [developers.openai.com/codex/cli/reference](https://developers.openai.com/codex/cli/reference) |
37
+ | Cursor CLI | ✓ | [cursor.com/docs/cli/overview](https://cursor.com/docs/cli/overview) |
38
+ | Gemini CLI | ✓ | [github.com/google-gemini/gemini-cli](https://github.com/google-gemini/gemini-cli) |
39
+ | OpenCode | ✓ | [opencode.ai/docs/cli](https://opencode.ai/docs/cli/) |
40
+ | Kiro CLI | ✓ | [kiro.dev/docs/cli/headless](https://kiro.dev/docs/cli/headless/) |
41
+ | Antigravity | ✓ | [antigravity.google/docs/cli/headless](https://antigravity.google/docs/cli/headless/) |
42
+ | GitHub Copilot CLI | ✓ | [docs.github.com/copilot/…/about-copilot-cli](https://docs.github.com/copilot/concepts/agents/about-copilot-cli) |
43
+ | 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) |
44
+ | Factory Droid CLI | ✓ | [docs.factory.ai/droid-exec/overview](https://docs.factory.ai/droid-exec/overview) |
45
+ | Auggie CLI (Augment Code) | ✓ | [docs.augmentcode.com/cli/overview](https://docs.augmentcode.com/cli/overview) |
46
+ | Goose CLI (Block) | ✓ | [block.github.io/goose](https://block.github.io/goose/) |
47
+ | Kimi CLI | detectable, manual only | [github.com/MoonshotAI/kimi-cli](https://github.com/MoonshotAI/kimi-cli) |
48
+
49
+ "Headless run" means spectoflow can actually spawn it non-interactively (`Run`/`Orchestrate`/
50
+ `Summarize` work). Kimi CLI has no confirmed one-shot mode as of this writing — spectoflow still
51
+ detects it and lets you set it as the active agent, it just won't try to spawn it (the pickers that
52
+ launch a run grey it out rather than hide it); drive it yourself in its own terminal meanwhile. The
53
+ same live table, with your own install status, is in the dashboard's **Documentation** tab. Missing
54
+ one you use? `config.json → runners` still takes any custom command by hand — see
55
+ [`templates/README.md`](templates/README.md) — or open an issue.
28
56
 
29
57
  ## Install
30
58
 
@@ -139,16 +167,19 @@ spectoflow dashboard # → http://localhost:4319 (or --port=
139
167
  `spectoflow dashboard` is the simple way — it prints the URL and won't double-start (it detects a
140
168
  dashboard already running on the port). `spectoflow status` tells you whether one is up. Running the
141
169
  `server.js` directly still works for a manual/embedded setup. Zero dependencies, updates live via SSE +
142
- file watching. Eight tabs behind a dense icon-tab header (brand logo · subtitle · a slim
143
- global-progress meter · a sync dot · a **Run** quick-action · a **settings** gear): **Board** (the
144
- control-room Overview — KPI cards, a status donut, a **scope-vs-delivered area curve**, a
170
+ file watching. Ten tabs behind a dense icon-tab header (brand logo · subtitle · **active agent** ·
171
+ autonomy mode · language · a slim global-progress meter · a sync dot · a **Run** quick-action): **Board**
172
+ (the control-room Overview — compact KPI cards, a status donut, a **scope-vs-delivered area curve**, a
145
173
  workflow-at-a-glance strip, per-phase progress bars, filter chips + search — plus the phase board),
146
- **Requests** (tasks awaiting you `to_validate`/`to_analyze`), **Attention** (points the agent raised
147
- via a `::spectoflow attention msg=…` sentinel or that you noted edit/resolve/delete, or **validate →
148
- task**), **Backlog** (a flat sortable/filterable, paginated table of every task, defaulting to open
149
- work), **Workflow** (the pipeline as step cards — click one to enable/disable it, which edits
150
- `workflow.md`), **Agents & Skills** (enriched cards that open a full-body markdown drawer), **Chat** (a
151
- full-height group-chat panel), and **Info** (a project-at-a-glance summary). URLs are real routes
174
+ **Chat** (a full-height group-chat panel with **Summarize**/**Clear**), **Requests** (tasks awaiting
175
+ you `to_validate`/`to_analyze`), **Attention** (points the agent raised via a `::spectoflow attention
176
+ msg=…` sentinel or that you noted — edit/resolve/delete, or **validate task**), **Backlog** (a flat
177
+ sortable/filterable, paginated table of every task, defaulting to open work), **Workflow** (the
178
+ pipeline as step cards click one to enable/disable it, which edits `workflow.md`), **Agents &
179
+ Skills** (enriched cards that open a full-body markdown drawer), **Info** (a project-at-a-glance
180
+ summary), **Documentation** (the supported-agents table above, live with your own install status and
181
+ links, plus the CLI command reference), and **Personalize** (autonomy mode, language, design, the
182
+ active agent, and **Extend spectoflow** — described under *Customize* below). URLs are real routes
152
183
  (`/board`, `/backlog/T-012`, …). Charts are zero-dep, hand-rolled SVG in `dashboard/public/charts.js`
153
184
  (donut/area/bars/ring, animated, `prefers-reduced-motion`-aware), and every aggregate is computed
154
185
  client-side.
@@ -173,7 +204,8 @@ read-only endpoint, `GET /api/agentfile?path=` (scoped to `.spectoflow/agents/**
173
204
  `.spectoflow/skills/**`, path-traversal-safe) — the framework's only other server surface is
174
205
  unchanged.
175
206
 
176
- **Customize.** Settings → **Customize** lets you extend the project's own spectoflow install: add a
207
+ **Customize.** Personalize → **Extend spectoflow** lets you extend the project's own spectoflow
208
+ install: add a
177
209
  dashboard, a skill, or an agent by describing what you want (or hit **Auto** to have the agent survey
178
210
  the project and propose candidates), and it clarifies first if the ask is ambiguous. Dashboards are
179
211
  never raw HTML — they're a small **declarative block spec** (`markdown`, `kpi-row`, `chart-bars`,
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,56 +68,157 @@ Argument: \`$ARGUMENTS\`
68
68
 
69
69
  // Priority order = which agent becomes the default when several are detected.
70
70
  //
71
- // Considered and left out (researched, not re-added on a whim): Kimi Code CLI (MoonshotAI/kimi-cli)
72
- // and DeepSeek Harness (deepseek-ai/deepseek-harness) as of Sept 2026 neither ships a genuine
73
- // non-interactive one-shot mode (prompt-as-trailing-arg, stdout, exit) that runner.js's spawn model
74
- // needs; Kimi is interactive/ACP-only, DeepSeek Harness is a local web-app framework. Revisit if
75
- // either ships a headless flag a fabricated runner command would just fail silently for real users.
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
+ //
75
+ // `headless: false` marks an agent spectoflow can DETECT and let you set as active, but never spawn
76
+ // itself — no confirmed non-interactive one-shot mode (prompt as trailing arg → stdout → exit) as of
77
+ // this research pass, so `runner` is null on purpose (a fabricated command would just fail silently
78
+ // for real users). The dashboard still lists it and shows why Run/Orchestrate/Summarize are disabled
79
+ // for it, rather than hiding it — you can drive it yourself in its own terminal meanwhile, and
80
+ // `config.json → runners` is still yours to hand-wire a command into if one ships later.
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
+ //
92
+ // Left out of KNOWN_AGENTS entirely (not even headless:false) — researched, not re-added on a whim:
93
+ // DeepSeek Harness (deepseek-ai/deepseek-harness). It has no single installable binary to detect —
94
+ // it's invoked as `npx @deepseek-ai/dsh <subcommand>`, is explicitly a local WEB-APP framework (not a
95
+ // terminal chat/prompt tool like the others), and ships as an August-2026 "developer preview" whose
96
+ // APIs the project itself says will change. Detecting "is npx present" would not mean deepseek-harness
97
+ // is; it doesn't fit this registry's detection model. Revisit once it (or Kimi) publishes a real
98
+ // one-shot flag, or wire a custom command into config.json → runners by hand in the meantime.
76
99
  const REGISTRY = [
77
100
  {
78
101
  id: 'claude',
102
+ label: 'Claude Code',
79
103
  entries: [
80
104
  { path: 'CLAUDE.md', content: CLAUDE_MD },
81
105
  { path: '.claude/commands/spectoflow.md', content: SLASH_CMD },
82
106
  ],
83
107
  runner: 'claude -p --permission-mode acceptEdits',
108
+ headless: true,
109
+ docsUrl: 'https://code.claude.com/docs/en/cli-reference',
84
110
  detect: { bin: 'claude', dirs: ['.claude'] },
85
111
  },
86
112
  {
87
113
  id: 'codex',
114
+ label: 'Codex',
88
115
  entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }],
89
116
  runner: 'codex exec',
117
+ headless: true,
118
+ docsUrl: 'https://developers.openai.com/codex/cli/reference',
90
119
  detect: { bin: 'codex', dirs: ['.codex'] },
91
120
  },
92
121
  {
93
122
  id: 'cursor',
123
+ label: 'Cursor',
94
124
  entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }],
95
125
  runner: 'cursor-agent -p',
126
+ headless: true,
127
+ docsUrl: 'https://cursor.com/docs/cli/overview',
96
128
  detect: { bin: 'cursor-agent', dirs: ['.cursor'] },
97
129
  },
98
130
  {
99
131
  id: 'gemini',
132
+ label: 'Gemini CLI',
100
133
  entries: [{ path: 'GEMINI.md', content: GEMINI_MD }],
101
134
  runner: 'gemini -p',
135
+ headless: true,
136
+ docsUrl: 'https://github.com/google-gemini/gemini-cli',
102
137
  detect: { bin: 'gemini', dirs: ['.gemini'] },
103
138
  },
104
139
  {
105
140
  id: 'opencode',
141
+ label: 'OpenCode',
106
142
  entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }],
107
143
  runner: 'opencode run --quiet',
144
+ headless: true,
145
+ docsUrl: 'https://opencode.ai/docs/cli/',
108
146
  detect: { bin: 'opencode', dirs: ['.opencode'] },
109
147
  },
110
148
  {
111
149
  id: 'kiro',
150
+ label: 'Kiro CLI',
112
151
  entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }],
113
152
  runner: 'kiro-cli chat --no-interactive --trust-all-tools',
153
+ headless: true,
154
+ docsUrl: 'https://kiro.dev/docs/cli/headless/',
114
155
  detect: { bin: 'kiro-cli', dirs: ['.kiro'] },
115
156
  },
116
157
  {
117
158
  id: 'antigravity',
159
+ label: 'Antigravity',
118
160
  entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }],
119
161
  runner: 'agy -p',
120
- detect: { bin: 'agy' },
162
+ headless: true,
163
+ docsUrl: 'https://antigravity.google/docs/cli/headless/',
164
+ detect: { bin: 'agy', dirs: [] },
165
+ },
166
+ {
167
+ id: 'kimi',
168
+ label: 'Kimi CLI',
169
+ entries: [{ path: 'AGENTS.md', content: ROOT_AGENTS_MD }],
170
+ runner: null, // no confirmed non-interactive one-shot mode — see the note above REGISTRY
171
+ headless: false,
172
+ docsUrl: 'https://github.com/MoonshotAI/kimi-cli',
173
+ detect: { bin: 'kimi', dirs: [] },
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'] },
121
222
  },
122
223
  ];
123
224
 
@@ -148,7 +249,7 @@ function defaultRunners(agents) {
148
249
  const out = {};
149
250
  for (const id of agents) {
150
251
  const a = byId(id);
151
- if (a) out[id] = a.runner;
252
+ if (a && a.runner) out[id] = a.runner; // headless:false agents (e.g. kimi) have no runner to seed
152
253
  }
153
254
  return out;
154
255
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spectoflow",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
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`.
@@ -95,7 +95,8 @@ function setChat(open){
95
95
  $('#chat').setAttribute('aria-hidden', open?'false':'true');
96
96
  $('#chatFab').classList.toggle('is-open',open);
97
97
  try{ localStorage.setItem('spf-chat', open?'1':'0'); }catch{}
98
- if(open) setTimeout(()=>$('#runPrompt').focus(),60);
98
+ // Land where you can actually type, not wherever the log happened to be scrolled last.
99
+ if(open) setTimeout(()=>{ scrollChat($('#chatLog')); $('#runPrompt').focus(); },60);
99
100
  }
100
101
  // doRun/doOrchestrate default to the floating widget's textarea/select; the Chat tab has its own
101
102
  // #tabRunPrompt/#tabRunAgent (an id can't be shared by two elements) and passes them explicitly —
@@ -139,11 +140,10 @@ function render(){
139
140
  // in renderSettings() below (called on every render tick) and saved via saveSettings().
140
141
  // agent select — widget (#runAgent) and Chat tab (#tabRunAgent) each get their own populated
141
142
  // <select>, since an id can't be shared by two elements; same option list, same source of truth.
142
- const runners=Object.keys((c.runners)||{claude:1});
143
- [$('#runAgent'),$('#tabRunAgent')].forEach(sel=>{
144
- if(!sel) return;
145
- if(sel.options.length!==runners.length){ sel.innerHTML=''; runners.forEach(k=>{ const o=document.createElement('option'); o.value=k; o.textContent=k; sel.append(o); }); if(c.agent) sel.value=c.agent; }
146
- });
143
+ // Shows every known agent (not just ones already in config.json → runners — a freshly-installed
144
+ // agent works the first time, runner.js falls back to the registry default), disabling any that
145
+ // isn't genuinely installed or can't run headless (e.g. kimi).
146
+ [$('#runAgent'),$('#tabRunAgent')].forEach(sel=>fillAgentSelect(sel,P.knownAgents||[],runnableAgentIds(),c.agent));
147
147
  const s=SpectoStats.stats(P);
148
148
  const meterFill=$('#globalMeterFill');
149
149
  if(meterFill) meterFill.style.width=(s.pct||0)+'%';
@@ -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||{};
@@ -717,6 +717,12 @@ function setModeSelects(mode){ [$('#setMode'),$('#topMode')].forEach(s=>{ if(s)
717
717
  // the user activate an agent that isn't actually installed — options for a not-installed known agent
718
718
  // are present but disabled, and P.installedAgents itself comes from the server testing each agent's
719
719
  // real CLI (bin on PATH, or the project's own config dir), not a guess.
720
+ // ids that can actually be SPAWNED right now: installed AND headless-capable (a known agent like
721
+ // kimi can be "installed" and even the active agent, but nothing can run it non-interactively).
722
+ function runnableAgentIds(){
723
+ const installed=P.installedAgents||[];
724
+ return (P.knownAgents||[]).filter(a=>a.headless&&installed.includes(a.id)).map(a=>a.id);
725
+ }
720
726
  function fillAgentSelect(sel,known,installed,active){
721
727
  if(!sel) return;
722
728
  const empty=!installed.length;
@@ -936,9 +942,7 @@ function renderCustomize(){
936
942
  const form=el('div','cz-form');
937
943
  const ta=el('textarea','chat-ta'); ta.placeholder=t('customize.describePh');
938
944
  const sel=el('select','chat-agent');
939
- const runners=Object.keys((P.config&&P.config.runners)||{claude:1});
940
- runners.forEach((k)=>{ const o=document.createElement('option'); o.value=k; o.textContent=k; sel.append(o); });
941
- if(P.config&&P.config.agent) sel.value=P.config.agent;
945
+ fillAgentSelect(sel,P.knownAgents||[],runnableAgentIds(),P.config&&P.config.agent);
942
946
  const actions=el('div','cz-form-actions');
943
947
  const autoBtn=el('button','btn',t('customize.auto'));
944
948
  const goBtn=el('button','btn primary',t('customize.generate'));
@@ -963,7 +967,7 @@ async function czSubmit(kind,description,agent){
963
967
  // A custom dashboard (Customize page) gets its own tab id "custom:<id>" and its own URL shape
964
968
  // /custom/<id> — kept out of ROUTES (a fixed list) since the set of custom ids is dynamic; recognized
965
969
  // by a dedicated branch in tabFromPath()/navigateTab() instead.
966
- const ROUTES=['board','requests','attention','backlog','workflow','team','chat','info','settings'];
970
+ const ROUTES=['board','requests','attention','backlog','workflow','team','chat','info','docs','settings'];
967
971
  function tabFromPath(){
968
972
  const s=location.pathname.split('/').filter(Boolean);
969
973
  if(s[0]==='custom'&&s[1]) return 'custom:'+decodeURIComponent(s[1]);
@@ -978,6 +982,10 @@ function navigateTab(tabId,push){
978
982
  }
979
983
  applyActiveTab();
980
984
  closeNav(); // a tab pick closes the mobile menu
985
+ // Landing on Chat: go straight to where you can read the latest and type, not wherever the log
986
+ // was scrolled to last (only on an actual switch INTO the tab — applyActiveTab() alone runs on
987
+ // every SSE render tick too, and re-scrolling/re-focusing there would fight the user's typing).
988
+ if(tabId==='chat') setTimeout(()=>{ scrollChat($('#chatTabLog')); $('#tabRunPrompt').focus(); },60);
981
989
  }
982
990
  function closeNav(){ document.body.classList.remove('nav-open'); const nt=$('#navToggle'); if(nt) nt.setAttribute('aria-expanded','false'); }
983
991
 
@@ -1038,7 +1046,11 @@ function mdLite(raw){
1038
1046
  // 2) escape HTML — everything below builds markup only from this escaped text
1039
1047
  text=escHtml(text);
1040
1048
  // 3) convert a small markdown subset to HTML
1041
- 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
1042
1054
  const lines=text.split('\n');
1043
1055
  let html='', inCode=false, codeBuf=[], listBuf=[], paraBuf=[];
1044
1056
  const flushPara=()=>{ if(paraBuf.length){ html+='<p>'+paraBuf.join(' ')+'</p>'; paraBuf=[]; } };
@@ -1157,6 +1169,73 @@ function renderInfo(){
1157
1169
  box.append(infoSection(t('info.workflowLabel'),'workflow',wfList));
1158
1170
  }
1159
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
+
1160
1239
  function openDrawer(id,keep){
1161
1240
  // named `task`, not `t` — `t` is the global translation function (see i18n.js) and this whole
1162
1241
  // function calls it repeatedly below; shadowing it with a task variable would break every call.
@@ -1297,6 +1376,8 @@ $('#chatClose').addEventListener('click',()=> setChat(false));
1297
1376
  try{ if(localStorage.getItem('spf-chat')==='1') setChat(true); }catch{}
1298
1377
  $('#runBtn').addEventListener('click',()=>doRun());
1299
1378
  $('#orchBtn').addEventListener('click',()=>doOrchestrate());
1379
+ $('#widgetSummarizeBtn').addEventListener('click',()=>summarizeChat($('#runAgent')));
1380
+ $('#widgetClearBtn').addEventListener('click',clearChat);
1300
1381
  $('#runPrompt').addEventListener('keydown',e=>{ if((e.metaKey||e.ctrlKey)&&e.key==='Enter')doRun(); });
1301
1382
  // Chat tab — same doRun/doOrchestrate/approve, its own textarea/select (#tabRunPrompt/#tabRunAgent)
1302
1383
  $('#tabRunBtn').addEventListener('click',()=>doRun($('#tabRunPrompt'),$('#tabRunAgent')));
@@ -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">
@@ -201,16 +202,18 @@
201
202
  <h2 class="panel-title" data-i18n="nav.chat">Chat</h2>
202
203
  <p class="panel-sub" data-i18n="chat.tabSub">Full conversation with the runner — the same run as the widget, more room to read it.</p>
203
204
  </div>
204
- <div class="chat-tab-head-actions">
205
- <button id="tabSummarizeBtn" class="mini-btn" data-i18n-title="chat.summarizeTitle" data-i18n="action.summarize" title="Condense the recent activity into a summary">Summarize</button>
206
- <button id="tabClearBtn" class="mini-btn" data-i18n-title="chat.clearTitle" data-i18n="action.clear" title="Clear the chat log">Clear</button>
207
- <select id="tabRunAgent" class="chat-agent" title="Agent"></select>
208
- </div>
209
205
  </div>
210
206
  <div class="chat-tab-log" id="chatTabLog">
211
207
  <div class="chat-idle" data-i18n-html="chat.idle">Type a request — the agent runs headless in this project with full memory
212
208
  (<code>CLAUDE.md → AGENTS.md</code>) and updates the board live.</div>
213
209
  </div>
210
+ <!-- Toolbar right above the input, not up in the header — this is where you're looking
211
+ when you reach for Summarize/Clear or switch which agent the next message runs on. -->
212
+ <div class="chat-tab-toolbar">
213
+ <button id="tabSummarizeBtn" class="mini-btn" data-i18n-title="chat.summarizeTitle" data-i18n="action.summarize" title="Condense the recent activity into a summary">Summarize</button>
214
+ <button id="tabClearBtn" class="mini-btn" data-i18n-title="chat.clearTitle" data-i18n="action.clear" title="Clear the chat log">Clear</button>
215
+ <select id="tabRunAgent" class="chat-agent" title="Agent"></select>
216
+ </div>
214
217
  <div class="chat-tab-input">
215
218
  <textarea id="tabRunPrompt" class="chat-ta" data-i18n-ph="chat.inputPlaceholder" placeholder="e.g. Add a login feature with email + password"></textarea>
216
219
  <div class="chat-tab-actions">
@@ -231,6 +234,15 @@
231
234
  </div>
232
235
  </section>
233
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
+
234
246
  <!-- SETTINGS (change autonomy mode + output language → config.json) -->
235
247
  <section class="panel" data-panel="settings">
236
248
  <div class="settings-wrap">
@@ -306,13 +318,17 @@
306
318
  <span class="chat-title" data-i18n="chat.widgetTitle">Run an agent</span>
307
319
  <span class="chat-sub" data-i18n="chat.widgetSub">Quick access · full view in the Chat tab</span>
308
320
  </div>
309
- <select id="runAgent" class="chat-agent" title="Agent"></select>
310
321
  <button class="chat-close" id="chatClose" aria-label="Close">×</button>
311
322
  </div>
312
323
  <div class="chat-log" id="chatLog">
313
324
  <div class="chat-idle" data-i18n-html="chat.idle">Type a request — the agent runs headless in this project with full memory
314
325
  (<code>CLAUDE.md → AGENTS.md</code>) and updates the board live.</div>
315
326
  </div>
327
+ <div class="chat-toolbar">
328
+ <button id="widgetSummarizeBtn" class="mini-btn" data-i18n-title="chat.summarizeTitle" data-i18n="action.summarize" title="Condense the recent activity into a summary">Summarize</button>
329
+ <button id="widgetClearBtn" class="mini-btn" data-i18n-title="chat.clearTitle" data-i18n="action.clear" title="Clear the chat log">Clear</button>
330
+ <select id="runAgent" class="chat-agent" title="Agent"></select>
331
+ </div>
316
332
  <div class="chat-input">
317
333
  <textarea id="runPrompt" class="chat-ta" data-i18n-ph="chat.inputPlaceholder" placeholder="e.g. Add a login feature with email + password"></textarea>
318
334
  <button id="runBtn" class="btn primary chat-send" data-i18n="action.send">Send</button>
@@ -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; }
@@ -378,7 +378,8 @@ body { background:var(--bg); color:var(--ink); font-family:var(--sans); font-siz
378
378
  .chat-title { font-weight:700; font-size:15px; }
379
379
  .chat-sub { font-size:10px; color:var(--faint); text-transform:uppercase; letter-spacing:.04em; }
380
380
  .chat-agent { margin-left:auto; flex-shrink:0; font:inherit; font-size:12px; background:var(--surface); color:var(--ink); border:1px solid var(--line); border-radius:7px; padding:5px 8px; }
381
- .chat-close { width:28px; height:28px; flex-shrink:0; border:1px solid var(--line); background:var(--surface-2); color:var(--muted); border-radius:8px; font-size:17px; cursor:pointer; transition:border-color .15s,color .15s; }
381
+ .chat-toolbar { display:flex; align-items:center; gap:8px; padding:8px 14px; border-top:1px solid var(--line); flex-wrap:wrap; }
382
+ .chat-close { width:28px; height:28px; flex-shrink:0; margin-left:auto; border:1px solid var(--line); background:var(--surface-2); color:var(--muted); border-radius:8px; font-size:17px; cursor:pointer; transition:border-color .15s,color .15s; }
382
383
  .chat-close:hover { border-color:color-mix(in srgb,var(--s-blocked) 45%,var(--line)); color:var(--ink); }
383
384
  .chat-log { flex:1; overflow-y:auto; padding:16px; display:flex; flex-direction:column; gap:11px; }
384
385
  .chat-idle { color:var(--faint); font-style:italic; font-size:12.5px; }
@@ -414,10 +415,10 @@ body { background:var(--bg); color:var(--ink); font-family:var(--sans); font-siz
414
415
  .chat-tab-head { display:flex; align-items:flex-start; gap:14px; margin-bottom:14px; }
415
416
  .chat-tab-head .panel-title { margin:0; }
416
417
  .chat-tab-head .panel-sub { margin:2px 0 0; }
417
- .chat-tab-head-actions { display:flex; align-items:center; gap:8px; margin-left:auto; flex-shrink:0; flex-wrap:wrap; }
418
- .chat-tab-head .chat-agent { flex-shrink:0; }
419
418
  .chat-tab-log { flex:1; min-height:220px; overflow-y:auto; display:flex; flex-direction:column; gap:12px; background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:20px; box-shadow:var(--shadow); }
420
- .chat-tab-input { display:flex; gap:10px; margin-top:14px; }
419
+ .chat-tab-toolbar { display:flex; align-items:center; gap:8px; margin-top:10px; flex-wrap:wrap; }
420
+ .chat-tab-toolbar .chat-agent { margin-left:auto; flex-shrink:0; }
421
+ .chat-tab-input { display:flex; gap:10px; margin-top:10px; }
421
422
  .chat-tab-input .chat-ta { flex:1; min-height:56px; max-height:180px; }
422
423
  .chat-tab-actions { display:flex; flex-direction:column; gap:8px; flex-shrink:0; }
423
424
  .chat-tab-wrap .chat-warn { margin:8px 0 0; }
@@ -555,6 +556,14 @@ body.booting .ring-svg circle:last-of-type { transform-origin:center; animation:
555
556
  .settings-select:focus { outline:2px solid var(--signal); outline-offset:1px; }
556
557
  .settings-field-hint { font-size:12px; }
557
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; }
558
567
  .settings-saved { align-self:flex-start; font-size:12px; color:var(--s-done); font-family:var(--mono); }
559
568
  .settings-saved[hidden] { display:none; }
560
569
  .settings-readonly { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); overflow:hidden; }
@@ -9,6 +9,18 @@
9
9
  */
10
10
  const { spawn } = require('child_process');
11
11
  const store = require('../lib/store');
12
+ const agentsRegistry = require('../lib/agents-registry');
13
+
14
+ // The command to run `which`: config.json's own runners map first (an explicit user choice always
15
+ // wins), falling back to the registry's default for a known, headless-capable, genuinely-installed
16
+ // agent — so picking one from a per-message agent list works the first time, before it's ever been
17
+ // the project's "active agent" and had a runner seeded into config.json for it.
18
+ function resolveRunnerCommand(root, cfg, which, opts) {
19
+ if (cfg.runners && cfg.runners[which]) return cfg.runners[which];
20
+ const known = agentsRegistry.KNOWN_AGENTS.find((a) => a.id === which);
21
+ if (known && known.runner && agentsRegistry.isAgentInstalled(which, root, opts)) return known.runner;
22
+ return null;
23
+ }
12
24
 
13
25
  function runStart(root, run) {
14
26
  const rt = store.readRuntime(root); rt.agents = rt.agents || []; rt.agents.push(run); store.writeRuntime(root, rt);
@@ -50,7 +62,7 @@ function pushAttention(root, text, by) {
50
62
  function startRun(root, { prompt, agent, logPrompt = true }, emit) {
51
63
  const cfg = store.readConfig(root);
52
64
  const which = agent || cfg.agent || 'claude';
53
- const cmdStr = cfg.runners && cfg.runners[which];
65
+ const cmdStr = resolveRunnerCommand(root, cfg, which);
54
66
  if (!cmdStr) return { error: `No runner configured for "${which}".` };
55
67
  const parts = cmdStr.split(/\s+/).filter(Boolean);
56
68
  const runId = 'r' + Date.now().toString(36);
@@ -97,4 +109,4 @@ function startRun(root, { prompt, agent, logPrompt = true }, emit) {
97
109
  return { runId, child };
98
110
  }
99
111
 
100
- module.exports = { startRun };
112
+ module.exports = { startRun, resolveRunnerCommand };
@@ -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 }));
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
  }
@@ -62,8 +62,11 @@ function writeConfig(patch){
62
62
  }
63
63
  cfg.agent = id;
64
64
  // Seed a default runner if this agent was never configured (e.g. installed after init/update).
65
+ // A headless:false agent (e.g. kimi) has no runner to seed — it can still be the active agent,
66
+ // it just can't be spawned by Run/Orchestrate/Summarize (disabled client-side; runner.js and
67
+ // summarize.js also refuse server-side either way).
65
68
  const known = agentsRegistry.KNOWN_AGENTS.find((a) => a.id === id);
66
- if (known) { cfg.runners = cfg.runners || {}; if (!cfg.runners[id]) cfg.runners[id] = known.runner; }
69
+ if (known && known.runner) { cfg.runners = cfg.runners || {}; if (!cfg.runners[id]) cfg.runners[id] = known.runner; }
67
70
  }
68
71
  fs.writeFileSync(cp, JSON.stringify(cfg, null, 2) + '\n');
69
72
  return cfg;
@@ -6,6 +6,7 @@
6
6
  */
7
7
  const { spawn } = require('child_process');
8
8
  const store = require('../lib/store');
9
+ const { resolveRunnerCommand } = require('./runner');
9
10
 
10
11
  const DEFAULT_LIMIT = 40;
11
12
 
@@ -20,7 +21,7 @@ function formatLog(messages, limit = DEFAULT_LIMIT) {
20
21
  function runSummarize(root, { agent } = {}, emit) {
21
22
  const cfg = store.readConfig(root);
22
23
  const which = agent || cfg.agent || 'claude';
23
- const cmdStr = cfg.runners && cfg.runners[which];
24
+ const cmdStr = resolveRunnerCommand(root, cfg, which);
24
25
  if (!cmdStr) return { error: `No runner configured for "${which}".` };
25
26
 
26
27
  const rt = store.readRuntime(root);
@@ -9,14 +9,27 @@
9
9
  const fs = require('fs');
10
10
  const path = require('path');
11
11
 
12
+ // headless:false = detectable and selectable as the active agent, but spectoflow never spawns it
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). `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).
12
19
  const KNOWN_AGENTS = [
13
- { id: 'claude', label: 'Claude Code', bin: 'claude', dirs: ['.claude'], runner: 'claude -p --permission-mode acceptEdits' },
14
- { id: 'codex', label: 'Codex', bin: 'codex', dirs: ['.codex'], runner: 'codex exec' },
15
- { id: 'cursor', label: 'Cursor', bin: 'cursor-agent', dirs: ['.cursor'], runner: 'cursor-agent -p' },
16
- { id: 'gemini', label: 'Gemini CLI', bin: 'gemini', dirs: ['.gemini'], runner: 'gemini -p' },
17
- { id: 'opencode', label: 'OpenCode', bin: 'opencode', dirs: ['.opencode'], runner: 'opencode run --quiet' },
18
- { id: 'kiro', label: 'Kiro CLI', bin: 'kiro-cli', dirs: ['.kiro'], runner: 'kiro-cli chat --no-interactive --trust-all-tools' },
19
- { id: 'antigravity', label: 'Antigravity', bin: 'agy', dirs: [], runner: 'agy -p' },
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/' },
20
33
  ];
21
34
 
22
35
  // Is `bin` an executable resolvable on PATH? On win32, an extension from PATHEXT is required, so we