superwiki 0.1.5 → 0.1.7
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +36 -3
- package/commands/board.md +5 -0
- package/commands/run.md +5 -0
- package/package.json +1 -1
- package/skills/sw-board/SKILL.md +28 -0
- package/skills/sw-config/SKILL.md +5 -3
- package/skills/sw-implement/SKILL.md +8 -4
- package/skills/sw-init/assets/agents-block.md +7 -0
- package/skills/sw-init/assets/sw.mjs +90 -2
- package/skills/sw-init/scripts/init.mjs +16 -1
- package/skills/sw-lint/SKILL.md +1 -0
- package/skills/sw-plan/SKILL.md +6 -2
- package/skills/sw-run/SKILL.md +86 -0
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Measured on a real project with 165 tasks, converted from a single markdown inde
|
|
|
29
29
|
|
|
30
30
|
| | Before | After |
|
|
31
31
|
| --- | --- | --- |
|
|
32
|
-
| Read at the start of every session | 197 KB index |
|
|
32
|
+
| Read at the start of every session | 197 KB index | 7.7 KB index (the 97 open tasks, a line each) + 2.7 KB of rules |
|
|
33
33
|
| Read to start one task | the index, then the task's section | one file, 2 KB at the median |
|
|
34
34
|
| Marking a task done | a status cell, plus a ✅ at every reference to it (median 12 places) | one frontmatter line |
|
|
35
35
|
|
|
@@ -39,7 +39,7 @@ Measured on a real project with 165 tasks, converted from a single markdown inde
|
|
|
39
39
|
|
|
40
40
|
```text
|
|
41
41
|
docs/
|
|
42
|
-
index.md catalog of the wiki, one line per page
|
|
42
|
+
index.md the open tasks, then the catalog of the wiki, one line per page
|
|
43
43
|
log.md append-only history
|
|
44
44
|
raw/ your sources, never modified
|
|
45
45
|
wiki/ pages the agent writes
|
|
@@ -50,6 +50,31 @@ docs/
|
|
|
50
50
|
|
|
51
51
|
`AGENTS.md` gets a short block of rules so the agent maintains the vault in every session, with or without a command.
|
|
52
52
|
|
|
53
|
+
You can follow the work without the viewer: `index.md` opens with the task list, written from the task files. Each open task is a line that links to its file; finished ones are listed by id.
|
|
54
|
+
|
|
55
|
+
```markdown
|
|
56
|
+
## Tasks
|
|
57
|
+
|
|
58
|
+
ready 9 · in progress 1 · blocked 18 · done 20
|
|
59
|
+
|
|
60
|
+
**In progress**
|
|
61
|
+
|
|
62
|
+
- [[B-20]] Portfolio sync · M5
|
|
63
|
+
|
|
64
|
+
**Ready**
|
|
65
|
+
|
|
66
|
+
- [[F-01]] Frontend skeleton and guards · M0
|
|
67
|
+
- [[B-18]] Valuation · M4
|
|
68
|
+
|
|
69
|
+
**Blocked**
|
|
70
|
+
|
|
71
|
+
- [[B-21]] Journal and thesis gates · M5 · waits on B-20
|
|
72
|
+
|
|
73
|
+
**Done (20)** [[B-01]] [[B-02]] [[B-03]] ...
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
A task's status still lives only in its own file. The list is a view: the agent rewrites it with `sw.mjs board` whenever a task changes, and `lint` says when it has fallen behind.
|
|
77
|
+
|
|
53
78
|
## Install
|
|
54
79
|
|
|
55
80
|
Requires Node 18 or newer.
|
|
@@ -170,8 +195,10 @@ A project's `docs/` folder is plain markdown and keeps working as an Obsidian va
|
|
|
170
195
|
| `sw-ingest` | file a source into the wiki |
|
|
171
196
|
| `sw-plan` | plan a task with the planner subagent and get your approval |
|
|
172
197
|
| `sw-implement` | run a task with the implementer subagent, have it reviewed if the task asks for that, and record the result |
|
|
198
|
+
| `sw-run` | work through several tasks in a row, unattended: plan, implement, review and record each, and stop when one needs you |
|
|
173
199
|
| `sw-explain` | explain a task: what, why, dependencies, what it unblocks |
|
|
174
200
|
| `sw-triage` | for a problem: seen before? lessons, likely causes |
|
|
201
|
+
| `sw-board` | refresh the task list in `index.md` from the task files |
|
|
175
202
|
| `sw-lint` | structural checks by script, semantic review on request |
|
|
176
203
|
| `sw-visualize` | open the viewer |
|
|
177
204
|
| `sw-stats` | what the current session has cost: tokens, context, steps and tool calls, per agent |
|
|
@@ -193,6 +220,9 @@ Shown as typed in Claude Code. In Codex, write `$sw-plan` instead of `/sw-plan`.
|
|
|
193
220
|
|
|
194
221
|
/sw-implement P-15 run P-15; refuses if a dependency is not done
|
|
195
222
|
/sw-implement continue what is in progress, or pick a ready task
|
|
223
|
+
/sw-run the backend tasks, commit after each
|
|
224
|
+
one task after another without asking at each step;
|
|
225
|
+
stops when a task needs you, and reports what it decided
|
|
196
226
|
|
|
197
227
|
/sw-explain M-06 what M-06 is, why it exists, what it waits on and unblocks
|
|
198
228
|
/sw-triage photo uploads hang at 100% on mobile since yesterday
|
|
@@ -201,6 +231,7 @@ Shown as typed in Claude Code. In Codex, write `$sw-plan` instead of `/sw-plan`.
|
|
|
201
231
|
file a source and summarise it into the wiki
|
|
202
232
|
|
|
203
233
|
/sw-config plan with opus, implement with sonnet, review with opus
|
|
234
|
+
/sw-board refresh the task list in index.md after you edited tasks by hand
|
|
204
235
|
/sw-lint check links, frontmatter and task dependencies
|
|
205
236
|
/sw-visualize open the task board and the wiki in the browser
|
|
206
237
|
/sw-stats what this session has cost so far, per agent
|
|
@@ -263,7 +294,8 @@ node docs/.sw/sw.mjs check P-15 # can it start or finish, what is op
|
|
|
263
294
|
node docs/.sw/sw.mjs explain P-15 # dependencies, what it unblocks, plan, area guide
|
|
264
295
|
node docs/.sw/sw.mjs search sync timeout # where something is mentioned
|
|
265
296
|
node docs/.sw/sw.mjs next-id P # next free id in an area
|
|
266
|
-
node docs/.sw/sw.mjs
|
|
297
|
+
node docs/.sw/sw.mjs board # rewrite the task list in index.md from the task files
|
|
298
|
+
node docs/.sw/sw.mjs lint # broken links, bad frontmatter, dependency errors, a stale task list
|
|
267
299
|
node docs/.sw/sw.mjs stats # tokens, context and steps of the agent session here
|
|
268
300
|
node docs/.sw/sw.mjs doctor # what that session carried before it read anything
|
|
269
301
|
node docs/.sw/sw.mjs serve --open # the viewer, reading files live
|
|
@@ -281,6 +313,7 @@ Edit sources in `src/`:
|
|
|
281
313
|
| File | What it is |
|
|
282
314
|
| --- | --- |
|
|
283
315
|
| `src/core.js` | the vault model, derived task state, lint and search; shared by the CLI and the viewer |
|
|
316
|
+
| `src/board.js` | the task list in `index.md` |
|
|
284
317
|
| `src/sessions.js` | finds the record an agent keeps of a session |
|
|
285
318
|
| `src/stats.js` | reduces a session record to cost per agent |
|
|
286
319
|
| `src/doctor.js` | reduces a session record to what the session started with |
|
package/commands/run.md
ADDED
package/package.json
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sw-board
|
|
3
|
+
description: Use when the user wants the task list in docs/index.md refreshed, updated or rebuilt, says the index is out of date after editing tasks by hand, or invokes sw-board or sw:board.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# sw-board
|
|
7
|
+
|
|
8
|
+
Rewrites the task list at the top of `docs/index.md` from the task files. One command; do not read the task files or write the list yourself.
|
|
9
|
+
|
|
10
|
+
Run from the project root:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
node docs/.sw/sw.mjs board
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
It prints whether the list changed and the counts. Tell the user that, in one line. Only its own section of `index.md` is rewritten; what the user wrote around it stays.
|
|
17
|
+
|
|
18
|
+
The list is a view. A task's status lives in the frontmatter of `docs/tasks/<ID>.md`: to change what the list shows, change the task file and run the command again. Never edit the list by hand.
|
|
19
|
+
|
|
20
|
+
## If it fails
|
|
21
|
+
|
|
22
|
+
| Output | Do |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| `unknown command board` | the project's `docs/.sw/sw.mjs` is older than this skill; offer to run sw-init, which updates it and writes the list |
|
|
25
|
+
| `this vault has no task module` | say so; sw-init with `--tasks` adds it |
|
|
26
|
+
| `docs/index.md is missing` | offer to run sw-init |
|
|
27
|
+
|
|
28
|
+
If the user asks what is ready, what blocks a task or how many are done, answer with `node docs/.sw/sw.mjs ready|check <ID>|status` instead of reading the list.
|
|
@@ -37,9 +37,11 @@ No tool lets a skill change the model of the running session, so the skills hand
|
|
|
37
37
|
|
|
38
38
|
When the user asks to set a model:
|
|
39
39
|
|
|
40
|
-
1.
|
|
41
|
-
2.
|
|
42
|
-
3.
|
|
40
|
+
1. **See what is set**: `show`.
|
|
41
|
+
2. **Settle role, tool and model.** Ask only for what is missing. If they name a model without a tool, infer the tool from the model family and say which you chose. Use the model name exactly as that tool spells it; do not translate names between tools.
|
|
42
|
+
3. **Change only what differs.** If the role already runs that model, change nothing and say so. A tool's short name and the full name of its current version (`opus` and the newest Opus) are the same model: do not rewrite one into the other.
|
|
43
|
+
4. **Run the `model` command** and show its output.
|
|
44
|
+
5. **Say when it takes effect**: a tool picks up new agent files when its next session starts.
|
|
43
45
|
|
|
44
46
|
After Superwiki itself is updated, run `sync` once: the roles' instructions are part of the agent files.
|
|
45
47
|
|
|
@@ -25,7 +25,10 @@ Run commands from the project root. `<skill-dir>` is the directory this SKILL.md
|
|
|
25
25
|
| `plan: none` | fine for a small task: one area, three "Done when" items or fewer, nothing open in its notes, a few files. For anything larger, recommend sw-plan first and let the user choose |
|
|
26
26
|
| `review: required (...)` | remember it for step 7 |
|
|
27
27
|
|
|
28
|
-
3. **Mark it started** before any work:
|
|
28
|
+
3. **Mark it started** before any work:
|
|
29
|
+
- in the frontmatter of `docs/tasks/<ID>.md`, `status: in-progress` and `started:` today;
|
|
30
|
+
- in `docs/log.md`, a new entry `## [date] task | <ID> started`, in the layout its last entries use;
|
|
31
|
+
- `node docs/.sw/sw.mjs board`, so the task list in `index.md` shows it.
|
|
29
32
|
4. **Checks that need the environment.** If the task has a plan, look for `needs:` in it: `grep -n 'needs:' docs/plans/<ID>-plan.md`. Each hit is a check that starts services or changes data. Ask the user which of them may run; without a yes, none.
|
|
30
33
|
5. **Dispatch the implementer** (how: "Dispatching" below). Its prompt is the task id, the project root if it is not your working directory, and which `needs:` checks it may run. Do not paste the plan into the prompt; it reads the files.
|
|
31
34
|
6. **Judge the report.** Its `Requirements:` list must name every "Done when" item and every scope, state or constraint item of the task; compare it with the task file.
|
|
@@ -36,7 +39,7 @@ Run commands from the project root. `<skill-dir>` is the directory this SKILL.md
|
|
|
36
39
|
- `Verdict: pass`: go on. Pass `important` and `minor` findings to the user in your report; they do not block.
|
|
37
40
|
- `Verdict: changes needed`: dispatch the implementer again with the blocking findings, word for word, then the reviewer again with the files changed since. After two rounds that still end in `changes needed`, stop and put the findings to the user.
|
|
38
41
|
- Do not review the change yourself in place of the reviewer, and do not argue a blocking finding away. If you think a finding is wrong, say so to the user and let them decide.
|
|
39
|
-
8. **Record the outcome
|
|
42
|
+
8. **Record the outcome**, then run `node docs/.sw/sw.mjs board`.
|
|
40
43
|
|
|
41
44
|
| Outcome | Task file | Log entry |
|
|
42
45
|
| --- | --- | --- |
|
|
@@ -50,7 +53,7 @@ Run commands from the project root. `<skill-dir>` is the directory this SKILL.md
|
|
|
50
53
|
10. **File what else was learned.** These are separate offers: act on each only when the user says yes to that one.
|
|
51
54
|
- A report held a decision or constraint the wiki should keep: offer a wiki page (`type: decision` or `concept`), added to `index.md`.
|
|
52
55
|
- The task fixed a problem whose cause is now known, or the review caught a defect worth remembering: offer a `type: lesson` page (Symptom, Cause, Fix, How to notice it earlier); sw-triage finds these later.
|
|
53
|
-
- A report named follow-up work: offer to create the tasks.
|
|
56
|
+
- A report named follow-up work, or the review left `important` findings open: offer to create the tasks. A finding that lives only in the log is forgotten.
|
|
54
57
|
11. **Report** to the user, in this order. Commit only if the user asks.
|
|
55
58
|
- the outcome;
|
|
56
59
|
- each requirement with its evidence, and anything that differs from the task;
|
|
@@ -77,6 +80,7 @@ The same table serves both roles: `sw-implementer` with `implementer.md`, `sw-re
|
|
|
77
80
|
- Accepting a `differs` item on the user's behalf. A sensible alternative is still not what the task asked for.
|
|
78
81
|
- Skipping the review on a task that requires it, or doing it yourself in the same context that judged the implementation.
|
|
79
82
|
- Starting work before the task file says `in-progress`. If the session dies, nobody knows the task was touched.
|
|
80
|
-
- Letting a subagent edit the task file or the
|
|
83
|
+
- Letting a subagent edit the task file, the log or the task list. One writer for status: you.
|
|
84
|
+
- Editing the task list in `index.md` by hand. It is written from the task files; change the task file and run `board`.
|
|
81
85
|
- Reading the plan or the code "to follow along". The subagents already paid for that.
|
|
82
86
|
- Running the next task in the same session out of momentum.
|
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
|
|
4
4
|
`docs/` is a wiki you write and keep current, and an Obsidian vault the user reads.
|
|
5
5
|
|
|
6
|
+
{{#tasks}}
|
|
7
|
+
- `docs/index.md`: the open tasks, then the catalog, one line per wiki page. Read it first, then open only the pages you need.
|
|
8
|
+
{{/tasks}}
|
|
9
|
+
{{^tasks}}
|
|
6
10
|
- `docs/index.md`: catalog, one line per wiki page. Read it first, then open only the pages you need.
|
|
11
|
+
{{/tasks}}
|
|
7
12
|
- `docs/log.md`: append-only. Add `## [YYYY-MM-DD] <kind> | <title>` at the end; read it with `tail`, never whole.
|
|
8
13
|
- `docs/raw/`: sources. Read, never modify.
|
|
9
14
|
- `docs/wiki/`: flat, one page per topic, frontmatter `type:` and one-line `summary:`.
|
|
@@ -27,6 +32,7 @@ Wiki:
|
|
|
27
32
|
Tasks:
|
|
28
33
|
|
|
29
34
|
- A task's status lives only in its frontmatter. Before you start: `status: in-progress` and `started:`. When its "Done when" list is met: `status: done` and `finished:`. Each change of status gets a `task` entry in `log.md`.
|
|
35
|
+
- The task list in `index.md` is written from the task files. After you add a task or change a task's status, title, milestone or dependencies, run `node docs/.sw/sw.mjs board`. Never edit that list by hand.
|
|
30
36
|
- Do not start a task while any of its `deps` is not done.
|
|
31
37
|
- If `explain <ID>` names an area guide (`docs/wiki/guide-<area>.md`), read it before you change code for the task: where things are, patterns, how to verify. Afterwards add the facts it was missing, one line each.
|
|
32
38
|
- Work that belongs to no task (a quick fix, a small request) needs no task file. Append one `change` entry to `log.md` instead: what changed and why, in a line.
|
|
@@ -38,6 +44,7 @@ Skills. Use these without being asked. For work in this vault they come before a
|
|
|
38
44
|
{{#tasks}}
|
|
39
45
|
- Planning a task, or the user asks what to work on next: `sw-plan`.
|
|
40
46
|
- Implementing a task: `sw-implement`. A change that needs no plan and touches one or two files may be done directly, under the task rules above.
|
|
47
|
+
- Several tasks in a row without the user at each step: `sw-run`.
|
|
41
48
|
- A question about a task (what, why, what it blocks): `sw-explain`.
|
|
42
49
|
{{/tasks}}
|
|
43
50
|
- A bug, failure or unexpected behavior is reported: `sw-triage` first, before any debugging.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Generated by scripts/build.mjs from src/core.js, src/sessions.js, src/stats.js, src/doctor.js, src/cli.js. Do not edit.
|
|
2
|
+
// Generated by scripts/build.mjs from src/core.js, src/board.js, src/sessions.js, src/stats.js, src/doctor.js, src/cli.js. Do not edit.
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import { basename, dirname, join, resolve as resolvePath, sep } from 'node:path';
|
|
@@ -322,6 +322,75 @@ export function guideFor(vault, area) {
|
|
|
322
322
|
return vault.pages.find(p => p.folder === 'wiki' && p.data.type === 'guide' && key(p.data.area ?? '') === key(area)) || null;
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
+
// The task board: the open tasks as a section of index.md, so a person can follow the work in the
|
|
326
|
+
// vault itself, without the viewer. It is a view, written from the task files by `sw.mjs board`
|
|
327
|
+
// and never edited by hand; a task's status still lives only in its own frontmatter.
|
|
328
|
+
// Open tasks get a line each. Finished ones are listed by id only, so the section stays small as
|
|
329
|
+
// a project grows: index.md is the one file every session reads.
|
|
330
|
+
|
|
331
|
+
const BOARD_START = '<!-- sw:board:start (written by `sw.mjs board`; do not edit) -->';
|
|
332
|
+
const BOARD_END = '<!-- sw:board:end -->';
|
|
333
|
+
const BOARD_BLOCK = /<!-- sw:board:start[^\n]*-->\n[\s\S]*?<!-- sw:board:end -->/;
|
|
334
|
+
const REFRESH = 'run `node docs/.sw/sw.mjs board`';
|
|
335
|
+
|
|
336
|
+
// Sections in reading order: what is being worked on, what can start, what waits.
|
|
337
|
+
const OPEN_STATES = [['progress', 'In progress'], ['ready', 'Ready'], ['blocked', 'Blocked']];
|
|
338
|
+
|
|
339
|
+
function openLine(vault, task) {
|
|
340
|
+
// A dependency that does not exist is lint's finding, not something a task waits on.
|
|
341
|
+
const waiting = task.openDeps.filter(id => taskOf(vault, id));
|
|
342
|
+
return [
|
|
343
|
+
`- [[${task.id}]] ${task.title}`,
|
|
344
|
+
...(task.milestone ? [task.milestone] : []),
|
|
345
|
+
...(task.state === 'blocked' && waiting.length ? [`waits on ${waiting.join(', ')}`] : []),
|
|
346
|
+
].join(' · ');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// The board for this vault, markers included.
|
|
350
|
+
export function taskBoard(vault) {
|
|
351
|
+
const { total } = summary(vault);
|
|
352
|
+
const counts = [
|
|
353
|
+
`ready ${total.ready}`, `in progress ${total.progress}`, `blocked ${total.blocked}`, `done ${total.done}`,
|
|
354
|
+
...(total.cancelled ? [`cancelled ${total.cancelled}`] : []),
|
|
355
|
+
];
|
|
356
|
+
const sections = OPEN_STATES
|
|
357
|
+
.map(([state, heading]) => [heading, tasksIn(vault, state)])
|
|
358
|
+
.filter(([, tasks]) => tasks.length)
|
|
359
|
+
.map(([heading, tasks]) => `**${heading}**\n\n${tasks.map(task => openLine(vault, task)).join('\n')}`);
|
|
360
|
+
// Finished tasks are looked up, not worked through: by id, not in running order.
|
|
361
|
+
const done = tasksIn(vault, 'done').map(task => task.id).sort((a, b) => a.localeCompare(b, 'en', { numeric: true })).map(id => `[[${id}]]`);
|
|
362
|
+
const paragraphs = [
|
|
363
|
+
'## Tasks',
|
|
364
|
+
total.total ? counts.join(' · ') : 'No tasks yet.',
|
|
365
|
+
...sections,
|
|
366
|
+
...(done.length ? [`**Done (${done.length})** ${done.join(' ')}`] : []),
|
|
367
|
+
];
|
|
368
|
+
return `${BOARD_START}\n${paragraphs.join('\n\n')}\n${BOARD_END}`;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export const boardIn = indexText => (String(indexText).match(BOARD_BLOCK) || [null])[0];
|
|
372
|
+
|
|
373
|
+
// index.md with this board in it: in place of the one it has, otherwise right under the title.
|
|
374
|
+
export function indexWithBoard(indexText, board) {
|
|
375
|
+
const text = String(indexText);
|
|
376
|
+
if (BOARD_BLOCK.test(text)) return text.replace(BOARD_BLOCK, () => board);
|
|
377
|
+
const title = text.match(/^# .*\n?/);
|
|
378
|
+
if (!title) return `${board}\n\n${text}`;
|
|
379
|
+
const rest = text.slice(title[0].length).replace(/^\n+/, '');
|
|
380
|
+
return `${title[0].trimEnd()}\n\n${board}\n${rest ? `\n${rest}` : ''}`;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// A lint finding when index.md does not show the tasks as they are now, else null.
|
|
384
|
+
export function boardFinding(vault) {
|
|
385
|
+
if (!vault.index) return null; // a missing index is reported on its own
|
|
386
|
+
const current = boardIn(vault.index.body);
|
|
387
|
+
if (!current && !vault.tasks.size) return null; // a vault without tasks needs no board
|
|
388
|
+
if (current === taskBoard(vault)) return null;
|
|
389
|
+
return current
|
|
390
|
+
? { level: 'warn', code: 'stale-board', path: 'index.md', message: `the task list is out of date; ${REFRESH}` }
|
|
391
|
+
: { level: 'warn', code: 'missing-board', path: 'index.md', message: `the tasks are not listed; ${REFRESH}` };
|
|
392
|
+
}
|
|
393
|
+
|
|
325
394
|
// Finds the record an agent tool keeps of the session working in a project.
|
|
326
395
|
// Claude Code, Codex and Copilot CLI each write every session to disk in their own layout; this
|
|
327
396
|
// lists a project's sessions as { tool, id, modified, current, source } and picks one. What a
|
|
@@ -913,6 +982,7 @@ const HELP = `sw <command> [--docs <dir>] [--json]
|
|
|
913
982
|
explain <ID> a task's dependencies, what it blocks and unblocks, its plan and linked pages
|
|
914
983
|
search <words> pages and log entries that mention the words, best match first
|
|
915
984
|
next-id <AREA> next free task id for an area (numbers are never reused)
|
|
985
|
+
board rewrite the task list in docs/index.md from the task files
|
|
916
986
|
lint structural checks; exit code 1 on errors
|
|
917
987
|
stats what the agent session here has cost so far: steps, context and tokens per agent
|
|
918
988
|
doctor what that session carried before it read anything: rule files, skill and tool lists
|
|
@@ -1128,8 +1198,25 @@ function nextIdCommand({ vault, args }) {
|
|
|
1128
1198
|
return { data: { id }, text: id };
|
|
1129
1199
|
}
|
|
1130
1200
|
|
|
1201
|
+
// The task list in index.md is a view of the task files; this writes it again from them.
|
|
1202
|
+
function boardCommand({ docs, vault }) {
|
|
1203
|
+
if (!existsSync(join(docs, 'tasks'))) return { error: 'this vault has no task module (docs/tasks); sw-init --tasks adds it', code: 1 };
|
|
1204
|
+
const path = join(docs, 'index.md');
|
|
1205
|
+
if (!existsSync(path)) return { error: 'docs/index.md is missing; run sw-init', code: 1 };
|
|
1206
|
+
const before = readFileSync(path, 'utf8');
|
|
1207
|
+
const after = indexWithBoard(before, taskBoard(vault));
|
|
1208
|
+
const changed = after !== before;
|
|
1209
|
+
if (changed) writeFileSync(path, after);
|
|
1210
|
+
const { total } = summary(vault);
|
|
1211
|
+
return {
|
|
1212
|
+
data: { changed, ready: total.ready, inProgress: total.progress, blocked: total.blocked, done: total.done },
|
|
1213
|
+
text: `board: docs/index.md ${changed ? 'updated' : 'unchanged'} ready ${total.ready} in-progress ${total.progress} blocked ${total.blocked} done ${total.done}`,
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1131
1217
|
function lintCommand({ vault }) {
|
|
1132
|
-
const
|
|
1218
|
+
const board = boardFinding(vault);
|
|
1219
|
+
const findings = [...lint(vault), ...(board ? [board] : [])];
|
|
1133
1220
|
const errors = findings.filter(f => f.level === 'error').length;
|
|
1134
1221
|
return {
|
|
1135
1222
|
data: findings,
|
|
@@ -1272,6 +1359,7 @@ const COMMANDS = {
|
|
|
1272
1359
|
explain: { run: explain, needsVault: true },
|
|
1273
1360
|
search: { run: searchCommand, needsVault: true },
|
|
1274
1361
|
'next-id': { run: nextIdCommand, needsVault: true },
|
|
1362
|
+
board: { run: boardCommand, needsVault: true },
|
|
1275
1363
|
lint: { run: lintCommand, needsVault: true },
|
|
1276
1364
|
stats: { run: statsCommand, needsVault: false },
|
|
1277
1365
|
doctor: { run: doctorCommand, needsVault: false },
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Scaffolds docs/ as a Superwiki vault. Safe to re-run: user content is kept, tool files are
|
|
3
3
|
// replaced with this version, and the report says which was which.
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
4
5
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
5
6
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
6
7
|
import { fileURLToPath } from 'node:url';
|
|
@@ -11,6 +12,7 @@ const MANAGED_BLOCK = /<!-- sw:start[\s\S]*?<!-- sw:end -->/;
|
|
|
11
12
|
// What a vault consists of at the top of docs/. Anything else there belongs to someone else.
|
|
12
13
|
const VAULT_ENTRIES = ['index.md', 'log.md', 'raw', 'wiki', 'tasks', 'plans', 'viewer.html'];
|
|
13
14
|
const ROLES = ['plan', 'implement', 'review'];
|
|
15
|
+
const NEW_INDEX = '# Index\n\n## Wiki\n\nCatalog of the wiki: one line per page, `- [[file-name]]: summary`, grouped by type.\n';
|
|
14
16
|
|
|
15
17
|
const HELP = `init.mjs [--root <dir>] [--tasks | --no-tasks] [--areas "M=Mobile,B=Backend"]
|
|
16
18
|
|
|
@@ -116,7 +118,7 @@ function writeVault(docs, tasks, report) {
|
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
const today = new Date().toISOString().slice(0, 10);
|
|
119
|
-
report.keep(join(docs, 'index.md'),
|
|
121
|
+
report.keep(join(docs, 'index.md'), NEW_INDEX);
|
|
120
122
|
report.keep(join(docs, 'log.md'), `# Log\n\nAppend-only. Entry format: \`## [YYYY-MM-DD] kind | title\`.\n\n## [${today}] init | Superwiki vault created\n`);
|
|
121
123
|
|
|
122
124
|
// The viewer snapshot and the local server's address are per-machine and regenerated on demand.
|
|
@@ -128,6 +130,18 @@ function writeVault(docs, tasks, report) {
|
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
132
|
|
|
133
|
+
// The task list in index.md is the one part of that file the tool owns. The vault's own CLI
|
|
134
|
+
// writes it, so an upgraded vault gets the list in the format of the version just installed.
|
|
135
|
+
function writeTaskBoard(docs, report) {
|
|
136
|
+
const index = join(docs, 'index.md');
|
|
137
|
+
const cli = join(docs, '.sw', 'sw.mjs');
|
|
138
|
+
if (!existsSync(cli)) return; // already reported as missing
|
|
139
|
+
const before = readFileSync(index, 'utf8');
|
|
140
|
+
const run = spawnSync(process.execPath, [cli, 'board', '--docs', docs], { encoding: 'utf8' });
|
|
141
|
+
if (run.status !== 0) return report.line('note', `the task list in docs/index.md was not written: ${run.stderr.trim()}`);
|
|
142
|
+
report.line(readFileSync(index, 'utf8') === before ? 'unchanged' : 'updated', 'docs/index.md (task list)');
|
|
143
|
+
}
|
|
144
|
+
|
|
131
145
|
function writeAgentRules(root, tasks, report) {
|
|
132
146
|
const block = schemaBlock(tasks);
|
|
133
147
|
const agentsPath = join(root, 'AGENTS.md');
|
|
@@ -174,6 +188,7 @@ function main(argv) {
|
|
|
174
188
|
const hadTaskFiles = !previous && existsSync(join(docs, 'tasks'));
|
|
175
189
|
const report = createReport(root);
|
|
176
190
|
writeVault(docs, tasks, report);
|
|
191
|
+
if (tasks) writeTaskBoard(docs, report);
|
|
177
192
|
const config = buildConfig(previous, { root, tasks, areas: options.areas });
|
|
178
193
|
report.write(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
179
194
|
writeAgentRules(root, tasks, report);
|
package/skills/sw-lint/SKILL.md
CHANGED
|
@@ -17,6 +17,7 @@ Two passes. The first is a script and costs almost nothing. The second reads pag
|
|
|
17
17
|
|
|
18
18
|
| Finding | Fix |
|
|
19
19
|
|---|---|
|
|
20
|
+
| `stale-board`, `missing-board` | `node docs/.sw/sw.mjs board`: it rewrites the task list in `index.md` from the task files. Never edit that list by hand |
|
|
20
21
|
| `not-in-index` | add `- [[page]]: summary` to `index.md`, using the page's `summary:` |
|
|
21
22
|
| `missing-field` `summary` | copy it from the page's line in `index.md`; if there is none, read the page and write it |
|
|
22
23
|
| `missing-field` `type` | read that page, write the field |
|
package/skills/sw-plan/SKILL.md
CHANGED
|
@@ -14,7 +14,10 @@ Needs the task module (`docs/tasks/`). If it is missing, say so and offer sw-ini
|
|
|
14
14
|
1. **Pick the task.**
|
|
15
15
|
- Id given: `node docs/.sw/sw.mjs check <ID>`, then read `docs/tasks/<ID>.md`. Status `done` or `cancelled`: stop and ask what the user wants. A plan already exists: this run revises it; say so. Open deps do not prevent planning.
|
|
16
16
|
- No id, existing work: `node docs/.sw/sw.mjs ready`, and let the user choose.
|
|
17
|
-
- New work: agree on title, area and dependencies with the user, get the id from `node docs/.sw/sw.mjs next-id <AREA
|
|
17
|
+
- New work: agree on title, area and dependencies with the user, and get the id from `node docs/.sw/sw.mjs next-id <AREA>`. Then:
|
|
18
|
+
- write `docs/tasks/<ID>.md` from `docs/.sw/templates/task.md` with `status: todo`, a "Goal" and a "Done when" list;
|
|
19
|
+
- append `## [date] task | <ID> created` to `docs/log.md`;
|
|
20
|
+
- run `node docs/.sw/sw.mjs board`, so the task list in `index.md` shows it.
|
|
18
21
|
2. **Does it need a plan?** Judge from the task file alone. A task is small when all of these hold: one area, three "Done when" items or fewer, nothing left open in its notes, and the change it describes is confined to a few files. A small task needs no plan: say so and offer `sw-implement <ID>` directly. Go on with planning only if the user wants a plan anyway, or the task is not small.
|
|
19
22
|
3. **Clarify.** Ask the user only what the task file leaves open about scope or intent, one question at a time, each with your recommendation. Add the answers to the task's "Notes" now. Do not read code to find questions; the planner surfaces the technical ones. Skip this when nothing is open.
|
|
20
23
|
4. **Dispatch the planner.** Its prompt is: the task id, today's date, the project root if it is not your working directory, and any feedback from an earlier round. It writes the plan file as a draft and returns a short message.
|
|
@@ -36,7 +39,7 @@ Needs the task module (`docs/tasks/`). If it is missing, say so and offer sw-ini
|
|
|
36
39
|
- in the area guide, if `node docs/.sw/sw.mjs explain <ID>` names one, the planner's `Guide:` lines, one line per fact;
|
|
37
40
|
- in `docs/log.md`, a new entry `## [date] plan | <ID>`, in the layout the log's last entries use.
|
|
38
41
|
|
|
39
|
-
Then run `node docs/.sw/sw.mjs lint`.
|
|
42
|
+
Then run `node docs/.sw/sw.mjs lint`. If it reports `stale-board`, a task's title, milestone or dependencies changed along the way: run `node docs/.sw/sw.mjs board`.
|
|
40
43
|
7. **Stop.** Do not start implementing. Tell the user the plan is approved and that sw-implement `<ID>` runs it.
|
|
41
44
|
|
|
42
45
|
## Common mistakes
|
|
@@ -46,3 +49,4 @@ Needs the task module (`docs/tasks/`). If it is missing, say so and offer sw-ini
|
|
|
46
49
|
- Editing the plan yourself. If something in it is wrong, send it back to the planner.
|
|
47
50
|
- Setting the task to `in-progress`. Planning does not change status.
|
|
48
51
|
- Planning several tasks in one plan file. One task, one plan; shared design goes to a `type: decision` wiki page that the plans link.
|
|
52
|
+
- Adding the new task to the list in `index.md` by hand. `board` writes that list from the task files.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sw-run
|
|
3
|
+
description: Use when the user wants several Superwiki tasks worked through one after another without being asked at each step (run the backlog, do all tasks of an area, keep going until done or blocked, act as orchestrator), or invokes sw-run or sw:run.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# sw-run
|
|
7
|
+
|
|
8
|
+
Works through tasks in order, unattended: for each one, plan it if it needs a plan, implement it, have it reviewed if it requires that, record it, and go on to the next. You are the orchestrator. Every piece of work goes to a subagent with a clean context; your session holds only the queue, the reports and the decisions.
|
|
9
|
+
|
|
10
|
+
Each task is run exactly as sw-implement runs it. This skill adds what a run of many needs: answers agreed once at the start, decisions you make in the user's place and write down, and rules for when to stop.
|
|
11
|
+
|
|
12
|
+
Run commands from the project root.
|
|
13
|
+
|
|
14
|
+
## Before the first task
|
|
15
|
+
|
|
16
|
+
1. **Scope.** Which tasks: an area, a list of ids, or everything that becomes ready. Take it from the user's message. Default order is the order of `node docs/.sw/sw.mjs ready`.
|
|
17
|
+
2. **Standing answers.** The user will not be asked again, so these are settled now. Ask only for the ones their message leaves open, in one question:
|
|
18
|
+
|
|
19
|
+
| Question | If the user does not say |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| May plans be approved without them? | yes; that is what unattended means |
|
|
22
|
+
| Which `needs:` checks (starting services, changing data) may run? | none |
|
|
23
|
+
| Commit after each task? Push? | no commit, no push |
|
|
24
|
+
| Go on with other tasks when one stops? | no; stop the run |
|
|
25
|
+
|
|
26
|
+
3. **Check that the run can do what was agreed**, before any task is touched:
|
|
27
|
+
- `node docs/.sw/sw.mjs lint`: errors are fixed or reported first.
|
|
28
|
+
- Commits were agreed: run one harmless git command. If the project's settings or the tool refuse git, say so now. Do not start a run whose terms cannot be met, and do not look for another way to run the refused command.
|
|
29
|
+
- The working tree holds changes that belong to no task of this run: say what they are and ask once whether to leave them or commit them first.
|
|
30
|
+
4. **A clean session per task.** You cannot open a new session; a fresh subagent per role is the clean context. If the user asked for sessions, say this is how it is done.
|
|
31
|
+
|
|
32
|
+
## Each task
|
|
33
|
+
|
|
34
|
+
1. **Next task**: `node docs/.sw/sw.mjs ready`. A task in progress comes first, then the first ready task in scope. None left: go to "The report".
|
|
35
|
+
2. **Run it as sw-implement does**: gate, mark it started, checks that need the environment, implementer, judge the report, review if required, record, task list. Load sw-implement once and follow it for every task. What differs in a run:
|
|
36
|
+
|
|
37
|
+
| In sw-implement or sw-plan | In a run |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| a task that is not small: "recommend sw-plan and let the user choose" | plan it. Mark the task started first, then dispatch the planner as sw-plan step 4 says |
|
|
40
|
+
| the planner's `Questions` go to the user | answer each with the planner's assumed answer, unless the task file, a wiki page or a recorded lesson says otherwise (`node docs/.sw/sw.mjs search <words>`). Write every answer into the task's "Notes" as `decided without the user: ...` |
|
|
41
|
+
| the plan waits for approval | approve it, if that was agreed, with a log entry that says it was approved under the run's standing answer |
|
|
42
|
+
| a `differs` item is the user's call | send it back to the implementer once with the task's wording. Still differs: stop |
|
|
43
|
+
| offers after a task (wiki pages, lessons, follow-up tasks) | do not ask and do not act. List them in the report |
|
|
44
|
+
|
|
45
|
+
3. **Close the task.** Commit, if agreed: only the files this task changed, message `<ID>: <title>`. Push only if agreed. A refused commit or push stops the run.
|
|
46
|
+
4. **Check your own size**: `node docs/.sw/sw.mjs stats`, row `main`. If its `peak` is above 200k tokens, stop here, between tasks: every further step would pay for all of it. Tell the user to start a new session and run sw-run again; the queue is in the files, so nothing is lost.
|
|
47
|
+
|
|
48
|
+
## When to stop
|
|
49
|
+
|
|
50
|
+
Stop the run, leave the task `in-progress` with what is open in its "Notes", and report. Do not skip the task and take the next one unless that was agreed and the next task does not depend on it.
|
|
51
|
+
|
|
52
|
+
- A requirement is `not met` or still `differs` after one more round with the implementer.
|
|
53
|
+
- The review still says `changes needed` after two rounds.
|
|
54
|
+
- The task cannot be verified without a `needs:` check that was not allowed.
|
|
55
|
+
- A commit or push that was agreed is refused.
|
|
56
|
+
- `lint` reports an error after the task was recorded.
|
|
57
|
+
- Your `peak` is above 200k (this one is between tasks, with nothing left open).
|
|
58
|
+
|
|
59
|
+
## Keeping the run cheap
|
|
60
|
+
|
|
61
|
+
A run pays for the orchestrator's context once per step, for every task. What keeps it small:
|
|
62
|
+
|
|
63
|
+
- **A fresh subagent for every task and every role.** Never continue the agent that planned or implemented the previous task.
|
|
64
|
+
- **Prompts as the role files ask**: task id, date, project root, the checks it may run, the standing answers that concern it. No reading lists and no project summary; each role knows what to read.
|
|
65
|
+
- **Reports, not files.** Do not read code, plans or the other tasks' files. `ready`, `check` and `explain` answer what you need about the queue.
|
|
66
|
+
- **One load of each skill.** Do not load a skill again to re-read it.
|
|
67
|
+
- **Edit frontmatter with your edit tool**, not with `sed` or another text substitution: a pattern that does not match fails silently and the status is then wrong without an error.
|
|
68
|
+
|
|
69
|
+
## The report
|
|
70
|
+
|
|
71
|
+
At the end, or when the run stops:
|
|
72
|
+
|
|
73
|
+
1. A table: task, outcome, review verdict, commit.
|
|
74
|
+
2. **Decisions made without the user**, one line each with the task id. This is the list the user must read.
|
|
75
|
+
3. What stopped the run, if it stopped, and what would let it continue.
|
|
76
|
+
4. Offers and open findings collected along the way.
|
|
77
|
+
5. `node docs/.sw/sw.mjs stats`, as printed.
|
|
78
|
+
6. What is ready next.
|
|
79
|
+
|
|
80
|
+
## Common mistakes
|
|
81
|
+
|
|
82
|
+
- Asking the user mid-run something the standing answers cover, or not asking at the start something they do not.
|
|
83
|
+
- Deciding a `needs:` check may run because the run is unattended. Unattended means fewer questions, not more permission.
|
|
84
|
+
- Piling several tasks into one uncommitted tree after a commit was refused.
|
|
85
|
+
- Running one long subagent that plans, implements and reviews a task. Measured on a real project, such an agent reached a context of almost a million tokens and sent over ten times what the three separate roles sent on another task of the same project.
|
|
86
|
+
- Carrying on past 200k because the next task looks small.
|