kaizen-agent 1.43.0 → 1.43.2
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/agents/kaizen-reviewer.md +1 -1
- package/cli/board.test.ts +3 -2
- package/cli/browser.test.ts +1 -1
- package/cli/browser.ts +12 -7
- package/cli/pty.test.ts +10 -0
- package/cli/pty.ts +15 -6
- package/cli/settings.test.ts +38 -0
- package/cli/settings.ts +37 -30
- package/cli/state.test.ts +25 -1
- package/cli/state.ts +32 -2
- package/cli/web.ts +11 -1
- package/package.json +1 -1
- package/skills/kaizen/README.md +0 -4
- package/skills/kaizen/SKILL.md +4 -1
- package/skills/kaizen/config.default.yml +2 -51
- package/skills/kaizen/docs.md +19 -5
- package/skills/kaizen/settings.md +10 -3
- package/skills/kaizen/spec-build.md +3 -2
- package/skills/kaizen/spec-main.md +68 -5
- package/skills/kaizen/spec-plan.md +95 -1
- package/skills/kaizen/spec-review.md +2 -1
- package/skills/kaizen-help/SKILL.md +1 -5
- package/web/board/agent-term.ts +1 -1
- package/web/board/api.ts +2 -0
- package/web/board/cards.ts +50 -7
- package/web/board/git.ts +1 -1
- package/web/board/icebox.ts +1 -1
- package/web/board/keys.ts +2 -0
- package/web/board/live.ts +3 -2
- package/web/board/projects.ts +2 -2
- package/web/board/settings.ts +47 -12
- package/web/board/terms.ts +297 -0
- package/web/board/views.ts +19 -5
- package/web/board.html +29 -2
- package/web/board.js +25 -24
- package/web/notebook.js +1 -1
- package/web/notes.html +2 -0
- package/web/term.js +1 -1
|
@@ -58,45 +58,7 @@ auto_fix:
|
|
|
58
58
|
# knob in the file. A third round rarely closes what two could not.
|
|
59
59
|
max_iterations: 2
|
|
60
60
|
|
|
61
|
-
track:
|
|
62
|
-
# The kind of work a run is doing, which decides what "deliverable",
|
|
63
|
-
# "verification", and "finding" mean for every stage. It is INFERRED from the
|
|
64
|
-
# request by the planner and stated back in the plan's Framing for the user to
|
|
65
|
-
# correct at the approval — the user is never asked to categorize their own request.
|
|
66
|
-
# Set this only to pin a default for a repo that is always one kind of work
|
|
67
|
-
# (e.g. a docs repo). auto (or empty) infers per run.
|
|
68
|
-
default: auto
|
|
69
|
-
|
|
70
61
|
review:
|
|
71
|
-
# Checks that run in every track.
|
|
72
|
-
checks:
|
|
73
|
-
- correctness
|
|
74
|
-
- plan_conformance
|
|
75
|
-
# Additional checks per track, chosen by what the run actually is. The planner may
|
|
76
|
-
# name a track not listed here; the reviewer then derives its checks from the
|
|
77
|
-
# track's "what would make it wrong".
|
|
78
|
-
# Each check below is one question the reviewer asks; the line beside it says which.
|
|
79
|
-
track_checks:
|
|
80
|
-
# security - no new way in, no secret or user data exposed
|
|
81
|
-
# reuse_simplification - nothing rebuilt that the codebase already had
|
|
82
|
-
# test_coverage - the changed behaviour has a check that fails when it breaks
|
|
83
|
-
software: [security, reuse_simplification, test_coverage]
|
|
84
|
-
# blast_radius and regression are universal — see spec-review.md — not listed here.
|
|
85
|
-
# completeness - every part the piece promises is actually present
|
|
86
|
-
# claim_accuracy - every checkable claim traced to a source
|
|
87
|
-
# reader_usability - its intended reader can act on it without asking
|
|
88
|
-
writing: [completeness, claim_accuracy, reader_usability]
|
|
89
|
-
# audience_fit - written for the people who will actually read it
|
|
90
|
-
# voice_consistency - reads as the same voice as the surrounding material
|
|
91
|
-
communication: [claim_accuracy, audience_fit, voice_consistency]
|
|
92
|
-
# prerequisites - everything needed before step one is stated
|
|
93
|
-
# rollback - a way back exists for every step that changes something
|
|
94
|
-
# executability - someone else can run it start to finish unaided
|
|
95
|
-
operations: [prerequisites, rollback, executability]
|
|
96
|
-
# evidence_support - the conclusion follows from evidence shown, not asserted
|
|
97
|
-
# selection_bias - the evidence was not picked to fit the answer
|
|
98
|
-
# alternatives - the readings that were rejected are named and why
|
|
99
|
-
research: [evidence_support, selection_bias, alternatives]
|
|
100
62
|
# Let the reviewer append durable lessons to .kaizen/memory.md.
|
|
101
63
|
write_memory: true
|
|
102
64
|
# Every stage of every future run reads memory.md, so cap it. Over this, the reviewer
|
|
@@ -108,20 +70,12 @@ backlog:
|
|
|
108
70
|
# not take, findings below the fix threshold, and debts deferred at an approval.
|
|
109
71
|
# Written by the main thread at the plan approval and again at the final approval.
|
|
110
72
|
enabled: true
|
|
111
|
-
# Per run, beside its plan and review — provenance without a central file to sync.
|
|
112
|
-
file: 06-backlog.md
|
|
113
|
-
# Items are open | done | rejected. Only open ones are printed by /kaizen backlog;
|
|
114
|
-
# closed ones stay in their run's file as record.
|
|
115
|
-
statuses: [open, done, rejected]
|
|
116
|
-
# Before state.keep_runs prunes a run, its still-open items move here, relative to
|
|
117
|
-
# state.dir. Done and rejected items are pruned with the run.
|
|
118
|
-
orphan_file: backlog.md
|
|
119
73
|
|
|
120
74
|
state:
|
|
121
75
|
dir: .kaizen
|
|
122
76
|
# Keep this many completed runs before pruning the oldest. `kaizen prune` lists
|
|
123
77
|
# what would go and `kaizen prune --yes` deletes it, after rescuing open backlog
|
|
124
|
-
# items into backlog.
|
|
78
|
+
# items into backlog.md beside memory.md, so it never destroys open work.
|
|
125
79
|
keep_runs: 20
|
|
126
80
|
|
|
127
81
|
git:
|
|
@@ -130,9 +84,6 @@ git:
|
|
|
130
84
|
# pushes. false: never commit, push, or open a PR unless the user explicitly asks.
|
|
131
85
|
# Ignored when the project is not a git repository.
|
|
132
86
|
auto_commit: false
|
|
133
|
-
# Create a branch before the implement stage when on the default branch.
|
|
134
|
-
# Ignored when the project is not a git repository.
|
|
135
|
-
branch_before_implement: true
|
|
136
87
|
# Build each run on its own branch (kaizen/<run-id>) in its own git worktree under
|
|
137
88
|
# .kaizen/wt/, so runs and people working at the same time never share a folder. Done
|
|
138
89
|
# lands the branch by pull request (gh) or a rebase and push. Ignored when the
|
|
@@ -163,7 +114,7 @@ agent:
|
|
|
163
114
|
# auto picks Claude Code if installed, otherwise the first agent found on PATH.
|
|
164
115
|
default: auto
|
|
165
116
|
# on: a board decision ends the run's waiting session before continuing its
|
|
166
|
-
# conversation, and abandoned or done runs (after
|
|
117
|
+
# conversation, and abandoned or done runs (after 1 minute) have theirs ended.
|
|
167
118
|
# Only sessions the board or dashboard launched are touched. off: nothing is
|
|
168
119
|
# ended, and a resumed conversation opens beside the window still waiting.
|
|
169
120
|
close: on
|
package/skills/kaizen/docs.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Kaizen — docs
|
|
2
2
|
|
|
3
3
|
Read for `/kaizen docs` and `/kaizen docs lint`. Nothing else needs this file, except
|
|
4
|
-
the
|
|
5
|
-
during a run.
|
|
4
|
+
the paragraphs in `spec-build.md` and `spec-review.md` that keep the vault current
|
|
5
|
+
during a run, and the *Docs gate* in `spec-main.md`, which reads *Update* step 4 and
|
|
6
|
+
*Lint* before a run is done.
|
|
6
7
|
|
|
7
8
|
`/kaizen docs` writes a project's documentation into the notebook as a small vault an
|
|
8
9
|
agent reads before it plans or builds: an **Index** listing every note with one line on
|
|
@@ -107,7 +108,9 @@ updated: YYYY-MM-DD
|
|
|
107
108
|
2. Split it into 5–12 areas a newcomer would name — by what the parts do, not by
|
|
108
109
|
folder alone. Fewer for a small project.
|
|
109
110
|
3. Write one note per area in the shape above, then `Index.md`, then `Log.md` with the
|
|
110
|
-
first line.
|
|
111
|
+
first line. Every source file lands in some note's `sources` — the area's helpers,
|
|
112
|
+
small screens and scripts too, not only its main files — named directly or through a
|
|
113
|
+
folder that holds it (see *Coverage* below).
|
|
111
114
|
4. Write the pointer (below).
|
|
112
115
|
5. Lint, and report.
|
|
113
116
|
|
|
@@ -123,8 +126,9 @@ updated: YYYY-MM-DD
|
|
|
123
126
|
rewrite the sections they affect — never `## Notes` — and set `updated`.
|
|
124
127
|
3. Every `sources` path is gone: move the note to `Project/Archive/` and drop it from
|
|
125
128
|
the Index. Never delete a note.
|
|
126
|
-
4. A
|
|
127
|
-
|
|
129
|
+
4. A source file no note covers (see *Coverage*): add it to the `sources` of the note
|
|
130
|
+
whose area it belongs to, and mention it there; where it fits no area (a new top-level
|
|
131
|
+
folder, a new entry point), write a note for it.
|
|
128
132
|
5. Refresh the Index lines whose description changed, append one Log line naming every
|
|
129
133
|
note touched, refresh the pointer, lint, and report.
|
|
130
134
|
|
|
@@ -137,10 +141,20 @@ Checks, reported and not silently fixed:
|
|
|
137
141
|
- every note has `description`, `tags`, `sources` and `updated` (the Index and Log need
|
|
138
142
|
no `sources`), and is listed in the Index;
|
|
139
143
|
- every `sources` path exists;
|
|
144
|
+
- **uncovered**: source files no note's `sources` names or holds, listed one per line;
|
|
140
145
|
- **stale**: notes whose `sources` changed after their `updated` day, found as in
|
|
141
146
|
*Update* step 1;
|
|
142
147
|
- `Index.md` is under 60 lines.
|
|
143
148
|
|
|
149
|
+
## Coverage
|
|
150
|
+
|
|
151
|
+
A **source file** is one the repository tracks (`git ls-files`; outside git, every file
|
|
152
|
+
not ignored), leaving out lockfiles, generated or built output, vendored code, binaries,
|
|
153
|
+
and bulk data folders. A note covers a file when its `sources` names the file or a folder
|
|
154
|
+
that holds it, so a data folder such as `public/quran` is covered by naming the folder.
|
|
155
|
+
Lint's *uncovered* list is what makes this checkable: a file no note covers is also a file
|
|
156
|
+
whose changes *stale* never sees, so the vault falls behind without saying so.
|
|
157
|
+
|
|
144
158
|
## The pointer
|
|
145
159
|
|
|
146
160
|
What makes an agent open the Index first. One block, identical everywhere:
|
|
@@ -23,17 +23,24 @@ nearest first, so a change lands globally unless this project keeps its own conf
|
|
|
23
23
|
| `auto_fix.max_iterations` | `1`–`5` | Fix and recheck rounds before what is left is escalated |
|
|
24
24
|
| `review.write_memory` | `true`, `false` | Let the reviewer append lessons to `memory.md` |
|
|
25
25
|
| `git.auto_commit` | `true`, `false` | Commit the work when a run finishes |
|
|
26
|
-
| `git.branch_before_implement` | `true`, `false` | Branch before building when on the default branch |
|
|
27
26
|
| `git.worktree_per_run` | `true`, `false` | Build each run on its own branch in its own worktree and land it by pull request |
|
|
28
27
|
| `ui.mouse` | `false`, `true` | Click to select, click again to act; while on, the terminal cannot select text |
|
|
29
28
|
| `subagents.model` | `inherit`, `opus`, `sonnet`, `haiku` | Model the stage subagents run on (Claude Code only) |
|
|
30
29
|
| `subagents.effort` | `inherit`, `low`, `medium`, `high` | Reasoning effort for the stage subagents (Claude Code only) |
|
|
31
30
|
| `sources.every` | `off`, `1h`, `6h`, `24h` | How often the web board checks request sources for new requests, into the icebox, while it runs |
|
|
32
|
-
| `sources.agent` | `ask`, `chatgpt`, `agent` | Who
|
|
31
|
+
| `sources.agent` | `ask`, `chatgpt`, `agent` | Who picks the posts that fit from social sources when Gather is pressed: the ChatGPT plan signed in from the Icebox, or your agent on `/kaizen gather` |
|
|
33
32
|
| `sources.model` | text | Model the ChatGPT plan gathers with; empty takes the first it lists |
|
|
34
33
|
| `sources.effort` | `low`, `medium`, `high` | Reasoning effort of that model while gathering |
|
|
35
34
|
| `agent.default` | `auto`, `claude`, `codex`, `agy`, `opencode`, `gemini` | Coding agent to launch from dashboard backlog |
|
|
36
35
|
|
|
36
|
+
Some settings only have an effect while another stands a certain way: the approvals
|
|
37
|
+
while `mode` is `approve`, `build.executor` and `subagents.*` while `runner` is `full`,
|
|
38
|
+
`auto_fix.min_severity` and `auto_fix.max_iterations` while `auto_fix.enabled` is
|
|
39
|
+
`true`, `sources.model` and `sources.effort` while `sources.agent` is `chatgpt`. When
|
|
40
|
+
printing one of these that has no effect now, say so beside its value. Still offer it:
|
|
41
|
+
a value set now applies once the other setting changes, and `/kaizen full` overrides
|
|
42
|
+
the runner for one run.
|
|
43
|
+
|
|
37
44
|
Rules for writing the file:
|
|
38
45
|
|
|
39
46
|
- **Change the value in place.** `config.yml` ships with a comment above every key
|
|
@@ -46,7 +53,7 @@ Rules for writing the file:
|
|
|
46
53
|
- After writing, print the setting and its new value, and nothing else. No summary of
|
|
47
54
|
the whole file.
|
|
48
55
|
|
|
49
|
-
Settings not in the table —
|
|
56
|
+
Settings not in the table — such as `state.dir` and the per-tool
|
|
50
57
|
`agent.<cmd>.model` / `agent.<cmd>.effort` the board launches a session with — are edited by
|
|
51
58
|
hand in `config.yml`, which is commented throughout. Say so rather than offering a
|
|
52
59
|
picker with thirty options.
|
|
@@ -86,8 +86,9 @@ the note, correct what the change made untrue or left out — never the `## Note
|
|
|
86
86
|
section, which is the user's — and set its `updated` to today. Refresh the note's line
|
|
87
87
|
in `Index.md` if its `description` changed, and append one line to `Log.md`:
|
|
88
88
|
`- YYYY-MM-DD run <id> — <notes touched>`. Name the notes touched in `03-impl.md`. A
|
|
89
|
-
change no note covers is not written up here:
|
|
90
|
-
|
|
89
|
+
change no note covers is not written up here: list it in `03-impl.md` as "no note
|
|
90
|
+
covers `<path>`" — the main thread's *Docs gate* in `spec-main.md` documents it before
|
|
91
|
+
the run is done. No Index, nothing to do.
|
|
91
92
|
|
|
92
93
|
**Read the plan's File manifest instead of searching.** The planner already located
|
|
93
94
|
every path this run touches. Search the material only for what the manifest does not
|
|
@@ -97,13 +97,48 @@ tab from staying empty; the reviewer flagging it later is a second net, not the
|
|
|
97
97
|
|
|
98
98
|
---
|
|
99
99
|
|
|
100
|
+
## Docs gate
|
|
101
|
+
|
|
102
|
+
Before a run can be `done` — in every runner and every mode, `auto` included — bring
|
|
103
|
+
the project's docs vault up to date with what the run changed. Apply it once the review
|
|
104
|
+
is settled and before the final approval report, so the user approves the notes with
|
|
105
|
+
the work; where the user's answer sends findings back to be fixed, apply it again over
|
|
106
|
+
those fixes before setting `stage: "done"`. In a run with a `worktree`, edit the notes
|
|
107
|
+
in the worktree, where the vault is committed with the code. Where
|
|
108
|
+
`notes/Project/Index.md` does not exist in the state directory, there is no vault and
|
|
109
|
+
nothing to do. The builder already updates the notes it can see during the build; this
|
|
110
|
+
is the step that catches what it could not: fix iterations after it, and changes no
|
|
111
|
+
note covers.
|
|
112
|
+
|
|
113
|
+
1. **What changed:** every path named in `03-impl.md` and in each
|
|
114
|
+
`05-iterations/*-fix.md`, leaving out `.kaizen/` itself.
|
|
115
|
+
2. **Covered:** for each note whose `sources` include one of those paths (a folder in
|
|
116
|
+
`sources` covers everything under it), check the note against the changed files.
|
|
117
|
+
Where the change made it untrue or left something out, correct it as *Keep the
|
|
118
|
+
project's docs vault current* in [`spec-build.md`](spec-build.md) says — never the
|
|
119
|
+
`## Notes` section — set `updated` to today, and append the `Log.md` line, unless the
|
|
120
|
+
builder already wrote this run's line, in which case add the notes to it.
|
|
121
|
+
3. **Not covered:** a changed path no note's `sources` reach. Add it to the `sources`
|
|
122
|
+
of the note whose area it belongs to and describe it there; where it belongs to no
|
|
123
|
+
area, write a note for it as step 4 of *Update* in [`docs.md`](docs.md) says and
|
|
124
|
+
list it in `Index.md`. A path that is not part of the project's material — a lock
|
|
125
|
+
file, a generated bundle — needs no note.
|
|
126
|
+
4. **Lint** the notes touched, as *Lint* in [`docs.md`](docs.md) lists, and fix what
|
|
127
|
+
this run broke.
|
|
128
|
+
|
|
129
|
+
Name the notes touched in the final report; the commit at done includes them. A run
|
|
130
|
+
whose changes leave every note true touches nothing and says so in one line.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
100
134
|
## Final approval
|
|
101
135
|
|
|
102
136
|
Controlled by `approvals.review`, skipped in `auto`.
|
|
103
137
|
|
|
104
138
|
Report: what was built (for a code run, show its Preview proof), verification results, findings fixed, findings still open,
|
|
105
139
|
every criterion the reviewer answered `CANNOT VERIFY` with the check it says a human
|
|
106
|
-
must make,
|
|
140
|
+
must make, anything the builder noted as out of scope, and the notes the *Docs gate*
|
|
141
|
+
touched. Where the run used `runner: lite`, say
|
|
107
142
|
so in that report — the reviewer saw the work, and nobody should read a same-session
|
|
108
143
|
review as an independent one because the output looked the same.
|
|
109
144
|
Later build requests in the same session follow *Follow-ups are runs too* in [`spec.md`](spec.md): a revise of this run until it is `done`, then a new linked run.
|
|
@@ -133,10 +168,10 @@ Set `stage: "done"` in `state.json` once the user accepts.
|
|
|
133
168
|
|
|
134
169
|
**Commit at done, when `git.auto_commit` is `true`.** Setting `stage: "done"` — the
|
|
135
170
|
user approving at review, or an `auto` run finishing — is then the ask to commit, under
|
|
136
|
-
either runner. Commit only the files this run changed (the paths `03-impl.md` names
|
|
137
|
-
that `git status` shows changed), by path:
|
|
138
|
-
`git commit -m <message> -- <paths>`, never the whole
|
|
139
|
-
the same checkout. The message is `03-impl.md`'s `## Commit`. In a run with a
|
|
171
|
+
either runner. Commit only the files this run changed (the paths `03-impl.md` names
|
|
172
|
+
and the notes the *Docs gate* touched, that `git status` shows changed), by path:
|
|
173
|
+
`git add -A -- <paths>` then `git commit -m <message> -- <paths>`, never the whole
|
|
174
|
+
tree, since other sessions edit the same checkout. The message is `03-impl.md`'s `## Commit`. In a run with a
|
|
140
175
|
`worktree`, commit there. Then write `runs/<id>/07-commit.md` as `commit: <short sha>`
|
|
141
176
|
and `pushed: no`, so the board shows it committed and offers the push. Never push. A
|
|
142
177
|
failed commit is reported as printed; the run is still `done`. With the setting
|
|
@@ -156,3 +191,31 @@ When a child run reaches `done`, the board appends `Child <child id> done: <titl
|
|
|
156
191
|
the parent's `notes.md` by itself. The parent documents it from there: if it is still in
|
|
157
192
|
flight its next stage reads `notes.md`; if it is already `done`, the entry is the record.
|
|
158
193
|
Never reopen a finished parent.
|
|
194
|
+
|
|
195
|
+
**A roadmap's increments are filed at its approval.** When the approved plan has a
|
|
196
|
+
`## Roadmap` (see *A whole application is a roadmap* in `spec-plan.md`), the same write
|
|
197
|
+
that records the approval files increments 2 to N, in order, from the project folder:
|
|
198
|
+
`kaizen card add "<title> (roadmap #N · after #a, #b)" --parent <run id>`, with
|
|
199
|
+
`any time` in place of `after …` where it has no prerequisite and its `blocked: <reason>`
|
|
200
|
+
kept in the text where it has one. Increment 1 is this run's own build. Nothing starts
|
|
201
|
+
by itself: the user starts an increment from its card when they choose, and the parent's
|
|
202
|
+
card lists its children with their status. An increment cut at the approval is not
|
|
203
|
+
filed; it goes to the backlog like anything else cut there.
|
|
204
|
+
|
|
205
|
+
## Discovery rounds
|
|
206
|
+
|
|
207
|
+
A roadmap plan's Discovery and any run's REA offer reach the user through the main
|
|
208
|
+
thread, under every runner: each round's questions are asked with the tool's structured
|
|
209
|
+
question mechanism (AskUserQuestion in Claude Code), all of a round's questions in one
|
|
210
|
+
call, each with the planner's recommended option first. Under `runner: full`, the
|
|
211
|
+
planner stops after writing a round; dispatch it again with the answers, recorded in
|
|
212
|
+
`02-approval.md` as that round's decisions, until it writes a plan with no round left
|
|
213
|
+
to ask, then present that plan at the plan approval as usual. Answers are not an
|
|
214
|
+
approval: the run stays at `stage: "plan"` with `awaiting: null` throughout, and
|
|
215
|
+
`awaiting: "approvals.plan"` is set only once `01-plan.md` has a `## Roadmap` — a round
|
|
216
|
+
written to that file is not a plan to approve, and a tool resuming the run re-asks its
|
|
217
|
+
questions. Where the user says yes to REA before the plan exists, run the study here,
|
|
218
|
+
write what it found to the run's `notes.md`, and dispatch the planner with it. Where
|
|
219
|
+
REA has to be installed first, the install in *Offer REA* (`spec-plan.md`) is the main
|
|
220
|
+
thread's too: doctor, the dry-run shown to the user, setup only on their approval,
|
|
221
|
+
then the restart, after which the run resumes at `stage: "plan"` from `state.json`.
|
|
@@ -27,6 +27,39 @@ different work behind each, nothing available to break the tie — and even then
|
|
|
27
27
|
its recommendation with the question. Questions the existing material can answer are
|
|
28
28
|
never asked; that is the planner's own work.
|
|
29
29
|
|
|
30
|
+
**Offer REA for what exists but is not here.** In every run and every track: when the
|
|
31
|
+
request or what came with it names something that already exists but whose source is
|
|
32
|
+
not at hand — an app or site to copy, replace or integrate with, an APK or IPA, a
|
|
33
|
+
binary, a package, a file format, a device or its firmware, a protocol or a capture —
|
|
34
|
+
and REA's tools are connected in the session, the plan asks once:
|
|
35
|
+
`Q: Study <the thing> with REA first?`, with `yes` (recommended where how it works
|
|
36
|
+
inside decides the design) and `no`. Under `runner: lite` or an inline planner, a yes
|
|
37
|
+
given before the plan is written lets the planner run REA itself and plan from what it
|
|
38
|
+
found. Under `full`, where the planner has no REA tools, the main thread runs the study
|
|
39
|
+
on a yes, writes what it found to the run's `notes.md`, and dispatches the planner
|
|
40
|
+
again; a yes given at the plan approval instead makes the study the build's first
|
|
41
|
+
step. What REA
|
|
42
|
+
returns is cited with its limitations, as *the track* in `spec.md` already requires.
|
|
43
|
+
REA not connected: ask instead
|
|
44
|
+
`Q: REA isn't installed — set it up first?`, with `help me install it` (recommended
|
|
45
|
+
where how it works inside decides the design), `I'll install it myself` and `skip
|
|
46
|
+
REA`. On *help me install it* the main thread follows REA's own setup: it runs
|
|
47
|
+
`npx -y rea-agents@<version> doctor --client <client> --json` — the version pinned,
|
|
48
|
+
as REA's own guide pins it, taken from `npm view rea-agents version` and shown to the
|
|
49
|
+
user; the client from the run's `agent` (`claude` → `claude_code`, `codex` → `codex`,
|
|
50
|
+
`agy` → `antigravity`, `opencode` → `opencode`, `gemini` → `gemini_cli`, `cursor` →
|
|
51
|
+
`cursor`) — then `setup --client <client> --dry-run --json` with the same version, shows the user the exact files it
|
|
52
|
+
would change, and runs `setup --client <client> --yes` only after they approve it —
|
|
53
|
+
setup writes the agent's own configuration, so it is never run unseen, in any mode.
|
|
54
|
+
On *I'll install it myself* it gives that same command and waits. Either way the new
|
|
55
|
+
tools appear only after the agent restarts: say so, and the run resumes from its
|
|
56
|
+
`state.json`, the yes to REA recorded beforehand in `02-approval.md` so the resumed
|
|
57
|
+
session studies the thing rather than asking again. REA's prerequisites (Node, and
|
|
58
|
+
Java, Ghidra or JADX for some targets) are named where doctor reports them missing,
|
|
59
|
+
never installed by the run. On *skip REA*, or when setup fails, the plan says so
|
|
60
|
+
once and claims about that thing's internals are marked unverified. Nothing of the
|
|
61
|
+
kind named: REA is not mentioned.
|
|
62
|
+
|
|
30
63
|
**Building from nothing is the exception: settle the foundation first.** When the
|
|
31
64
|
track is a new product or app, there is no material to answer the questions that
|
|
32
65
|
decide everything after them, and "wrong" for this track is a wrong foundation — the
|
|
@@ -39,6 +72,67 @@ materially different builds — put to the user as an *Open question* with optio
|
|
|
39
72
|
all in one round, before anything is built. Three quick questions and a default for
|
|
40
73
|
the rest is how a run ends up writing the second app instead of the first.
|
|
41
74
|
|
|
75
|
+
**A whole application is a roadmap, not a run.** When the track is a new product or
|
|
76
|
+
app — web, mobile, PWA, desktop, anything — and it covers more than one independent
|
|
77
|
+
module, one run cannot hold it: the plan, the diff and the review all outgrow what a
|
|
78
|
+
reader can check. A single screen or a single feature stays one run. The Framing says
|
|
79
|
+
in one line that the plan is a roadmap, so the user can refuse it. A roadmap plan adds
|
|
80
|
+
three sections right after the Goal, each written for a person, and the
|
|
81
|
+
foundation round above becomes up to three rounds:
|
|
82
|
+
|
|
83
|
+
- **`## Discovery`** — the planner goes deep before it designs. It reads everything the
|
|
84
|
+
user brought first (decks, forms, sheets, screenshots, an existing repo, REA's
|
|
85
|
+
findings), then asks only what is still open, in up to three rounds of `Q:` pick
|
|
86
|
+
lists, each question with a recommended option: (1) purpose, who uses it and in what
|
|
87
|
+
roles, what it replaces; (2) the flows of each module and their domain rules;
|
|
88
|
+
(3) the foundation choices, among them how it ships, as a multi-select — full CI/CD
|
|
89
|
+
(tests and deploy on every merge), Docker (an image and a compose file that run
|
|
90
|
+
anywhere), a managed platform (Vercel, Fly, Railway or similar), mobile app ready
|
|
91
|
+
(store builds with signing, for example Capacitor or Expo/EAS), or a simple manual
|
|
92
|
+
deploy — and, where the app has a screen and no design system exists, its look and
|
|
93
|
+
feel, asked as item 7 *Design direction* below says: the feel (for example rich and
|
|
94
|
+
animated, calm and simple, enterprise and dense, gen-z and playful) and how much
|
|
95
|
+
motion. A round with nothing left to ask is skipped. Under `runner: full` the
|
|
96
|
+
planner writes `01-plan.md` with the Framing, `## Discovery` so far and the round's
|
|
97
|
+
questions as *Open questions*, and no `## Roadmap`, then stops; that file is a
|
|
98
|
+
round, not a plan. The main thread asks them and dispatches it again with the
|
|
99
|
+
answers. Under
|
|
100
|
+
`lite` or inline, the session asks them directly. The section records the roles, the
|
|
101
|
+
main flows, the things the app keeps track of and how they relate, a glossary of
|
|
102
|
+
the domain's words, and every answer given.
|
|
103
|
+
- **`## Foundation`** — a decision with its reason, derived from Discovery, for each
|
|
104
|
+
of: platform; frontend (framework, UI kit, state, the design direction chosen);
|
|
105
|
+
backend (runtime, framework, API style); the contract between them (REST, RPC or
|
|
106
|
+
realtime, and where its types live); hosting and deploy, as chosen in discovery;
|
|
107
|
+
database (engine, where it runs, the main tables or collections and their
|
|
108
|
+
relations, how migrations are done); auth and access; repo layout and conventions.
|
|
109
|
+
Every increment builds on it.
|
|
110
|
+
- **`## Roadmap`** — the increments, numbered, one line each:
|
|
111
|
+
`N. <title> — <what it delivers> — after: #a, #b` (or `any time`), with
|
|
112
|
+
`— blocked: <reason>` when something outside the work holds it. Increment 1 is always
|
|
113
|
+
the skeleton: the scaffold, the stack wired, the database schema and migration setup,
|
|
114
|
+
an auth stub, and a deploy of the empty app the way the Foundation ships (in its
|
|
115
|
+
Docker image, through its pipeline, as a store test build). Every feature increment
|
|
116
|
+
after it is one feature end to end — its screens, its API, its tables and migration,
|
|
117
|
+
and its tests — so each can be tried on its own; splitting by layer ("all the APIs", then "all the screens")
|
|
118
|
+
is only for an app with no UI or no backend. Each increment is sized to one ordinary
|
|
119
|
+
run. Where real people will use the app, the last increment is going live: what
|
|
120
|
+
the deploy choice still needs (the full pipeline, the production image, store
|
|
121
|
+
listings and release builds), backups, monitoring and error tracking, and the
|
|
122
|
+
production deploy. A last line says which must go in order and which can start any
|
|
123
|
+
time.
|
|
124
|
+
|
|
125
|
+
The rest of a roadmap plan — Work list, Steps, File manifest, gate — covers increment 1
|
|
126
|
+
only; it is this run's build. The others become child cards at the approval (see
|
|
127
|
+
*Child cards* in `spec-main.md`).
|
|
128
|
+
|
|
129
|
+
**An increment run builds inside its parent's decisions.** A request with a `Parent:`
|
|
130
|
+
line whose parent run's `01-plan.md` has a `## Roadmap` is an increment. Its planner
|
|
131
|
+
reads that plan's Discovery, Foundation and its own Roadmap line before anything else.
|
|
132
|
+
They bind it: a deviation from the Foundation is an *Open question* to the user, and a
|
|
133
|
+
change to the roadmap is an *Out of scope suggestion*, never a silent edit of the
|
|
134
|
+
parent's plan.
|
|
135
|
+
|
|
42
136
|
The goals stay the user's. The planner may propose a better solution to what was
|
|
43
137
|
asked, or argue that the request as stated will not achieve what the user wants, but
|
|
44
138
|
it may not widen the goal on its own. Anything beyond the request goes in *Out of
|
|
@@ -132,7 +226,7 @@ questions that are open. An alternative rejected gets a line saying what and why
|
|
|
132
226
|
a case against it.
|
|
133
227
|
|
|
134
228
|
**Write the parts a person reads for a person.** Framing, the Work list, Goal, Scope,
|
|
135
|
-
Risks and Out of scope suggestions are read by someone deciding whether to approve,
|
|
229
|
+
Discovery, Foundation, Roadmap, Risks and Out of scope suggestions are read by someone deciding whether to approve,
|
|
136
230
|
often on a narrow panel. Write them in everyday words: short sentences, one idea per
|
|
137
231
|
bullet, things named by what the user sees ("the chat bubble", "the Plan tab") rather
|
|
138
232
|
than by function, file, CSS value or gate id. Code names, `path:line` and gate ids
|
|
@@ -125,7 +125,8 @@ and no criterion is `CANNOT VERIFY`; otherwise
|
|
|
125
125
|
`; <n> cannot verify` when any criterion was — even when every count is 0, so an open
|
|
126
126
|
human check never reads as a clean pass.
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
Where `review.write_memory` is true (the default), the reviewer also appends any
|
|
129
|
+
durable lesson about this material to `memory.md` —
|
|
129
130
|
a recurring bug pattern, a non-obvious constraint, a convention worth keeping. Not
|
|
130
131
|
run-specific detail; only what the next run would want to know.
|
|
131
132
|
|
|
@@ -31,7 +31,7 @@ anything.
|
|
|
31
31
|
| `/kaizen backlog` | Print open backlog items across all runs, grouped by source run, rescued items last under `Orphaned` |
|
|
32
32
|
| `/kaizen gather` | Read request sources kaizen cannot read by itself; their requests land in the icebox, nothing starts |
|
|
33
33
|
| `/kaizen discover` | Group what is in the icebox and promote the ideas you pick; nothing starts |
|
|
34
|
-
| `/kaizen docs [lint]` | Write or refresh the project's docs vault in the notebook, which agents read first; `lint` only reports what is stale or
|
|
34
|
+
| `/kaizen docs [lint]` | Write or refresh the project's docs vault in the notebook, which agents read first; `lint` only reports what is stale, broken or uncovered |
|
|
35
35
|
| `/kaizen approve` | Approve whatever the current run is waiting on |
|
|
36
36
|
| `/kaizen reject <reason>` | Reject it; the reason decides revise (back to planner) vs kill (abandon) |
|
|
37
37
|
| `/kaizen abort` | Mark the current run abandoned |
|
|
@@ -88,15 +88,11 @@ Each iteration is written to `runs/<id>/05-iterations/`.
|
|
|
88
88
|
| `auto_fix.enabled` | `true` | Auto-fix findings vs. escalate everything |
|
|
89
89
|
| `auto_fix.min_severity` | `high` | Threshold for auto-fix (`critical\|high\|medium\|low`) |
|
|
90
90
|
| `auto_fix.max_iterations` | `2` | Fix/recheck rounds before escalating remaining findings |
|
|
91
|
-
| `track.default` | `auto` (inferred per run) | Pin a track for a repo that is always one kind of work |
|
|
92
91
|
| `review.write_memory` | `true` | Let the reviewer append lessons to `.kaizen/memory.md` |
|
|
93
92
|
| `backlog.enabled` | `true` | Capture what a run deliberately did not do |
|
|
94
|
-
| `backlog.file` | `06-backlog.md` | Per-run backlog file |
|
|
95
|
-
| `backlog.orphan_file` | `backlog.md` | Where a pruned run's open items are rescued to |
|
|
96
93
|
| `state.dir` | `.kaizen` | State directory |
|
|
97
94
|
| `state.keep_runs` | `20` | Completed runs kept before pruning oldest (rescues open backlog first) |
|
|
98
95
|
| `git.auto_commit` | `false` | `true`: commit the run's changed files when it is done; never pushes |
|
|
99
|
-
| `git.branch_before_implement` | `true` | Branch before implement stage when on the default branch |
|
|
100
96
|
| `subagents.model` | `inherit` | Model the stage subagents run on (opus/sonnet/haiku, Claude Code only) |
|
|
101
97
|
| `subagents.effort` | `inherit` | Reasoning effort for the stage subagents (low/medium/high, Claude Code only) |
|
|
102
98
|
| `sources.every` | `24h` | How often the web board checks request sources for new ideas (`off\|1h\|6h\|24h`) |
|
package/web/board/agent-term.ts
CHANGED
|
@@ -11,7 +11,7 @@ let termKey = null,
|
|
|
11
11
|
termOff = null,
|
|
12
12
|
termLib = null;
|
|
13
13
|
export const where = (r) => (r.pty ? "running in its card" : "opened in your terminal");
|
|
14
|
-
const termLoad = () =>
|
|
14
|
+
export const termLoad = () =>
|
|
15
15
|
(termLib ||= new Promise((ok, no) => {
|
|
16
16
|
const l = document.createElement("link");
|
|
17
17
|
l.rel = "stylesheet";
|
package/web/board/api.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { key, render } from "./cards.ts";
|
|
|
3
3
|
import { $ } from "./dom.ts";
|
|
4
4
|
import { close, ideaBody, open, openRun } from "./panel.ts";
|
|
5
5
|
import { notifyUser, toast } from "./toast.ts";
|
|
6
|
+
import { openTerms, termsShowing } from "./terms.ts";
|
|
6
7
|
|
|
7
8
|
// The server writes notifier() from state.ts over a marker in board.html: one rule for both UIs.
|
|
8
9
|
declare const due: (key: string, say: string | null, done: boolean, now: number) => boolean;
|
|
@@ -36,6 +37,7 @@ export async function load(quiet) {
|
|
|
36
37
|
}
|
|
37
38
|
// A redraw under a form being typed in would reset its fields and its focus.
|
|
38
39
|
if (!(quiet && store.mode && $("board").querySelector(".inl input"))) render();
|
|
40
|
+
if (termsShowing()) openTerms();
|
|
39
41
|
// Run on an idea closed the panel: open the card on its agent once the board knows the session.
|
|
40
42
|
if (store.wantTerm && !store.openKey && !store.mode) {
|
|
41
43
|
const n = store.S.cards.find((x) => key(x) === store.wantTerm && x.pty);
|
package/web/board/cards.ts
CHANGED
|
@@ -237,6 +237,7 @@ function card(c) {
|
|
|
237
237
|
} else {
|
|
238
238
|
items.push(["open", actionOf(c), ico("open")]);
|
|
239
239
|
items.push(["archive", "Archive", ico("archive")]);
|
|
240
|
+
if (c.column === 1 && c.status !== "abandoned") items.push(["toidea", "Back to idea", ico("restore")]);
|
|
240
241
|
if (c.status === "waiting" || c.status === "running" || c.status === "stalled") {
|
|
241
242
|
items.push(null, ["reject", "Abandon", ico("x"), "danger"]);
|
|
242
243
|
}
|
|
@@ -267,6 +268,12 @@ function card(c) {
|
|
|
267
268
|
toggleArchive(c, a === "archive");
|
|
268
269
|
return;
|
|
269
270
|
}
|
|
271
|
+
if (a === "toidea") {
|
|
272
|
+
const r = await post("/to-idea", { dir: c.state, id: c.id });
|
|
273
|
+
if (r.ok) toast("Back in Ideas");
|
|
274
|
+
load();
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
270
277
|
store.mode = a;
|
|
271
278
|
store.modeKey = k;
|
|
272
279
|
render();
|
|
@@ -302,7 +309,7 @@ function inlineForm(c) {
|
|
|
302
309
|
li.className = "inl";
|
|
303
310
|
const dir = c.state;
|
|
304
311
|
if (store.mode === "edit") {
|
|
305
|
-
li.innerHTML = html`<input value="${c.text}"><textarea placeholder="
|
|
312
|
+
li.innerHTML = html`<input value="${c.text}"><textarea placeholder="Anything the planner should know">${c.notes || ""}</textarea><div class="q dim" id="clong" hidden>The first line is over 72 characters; logs will cut it short.</div><div class="row"><button class="btn ghost" data-a="c">Cancel</button><button class="btn pri" data-a="s">Save</button></div>`;
|
|
306
313
|
li.querySelector("[data-a=s]").onclick = async () => {
|
|
307
314
|
const next = li.querySelector("input").value.trim(),
|
|
308
315
|
notes = li.querySelector("textarea").value.trim();
|
|
@@ -314,7 +321,7 @@ function inlineForm(c) {
|
|
|
314
321
|
}
|
|
315
322
|
if (store.mode === "reject") {
|
|
316
323
|
const idea = c.kind !== "run";
|
|
317
|
-
li.innerHTML = html`<div class="q">${c.kind === "backlog" ? "Not doing this backlog item?" : idea ? "Reject this idea?" : "Abandon this run?"} The reason is recorded.</div><input placeholder="
|
|
324
|
+
li.innerHTML = html`<div class="q">${c.kind === "backlog" ? "Not doing this backlog item?" : idea ? "Reject this idea?" : "Abandon this run?"} The reason is recorded.</div><input placeholder="Why not?"><div class="row"><button class="btn ghost" data-a="c">Keep it</button><button class="btn warn" data-a="s">${idea ? "Reject" : "Abandon"}</button></div>`;
|
|
318
325
|
li.querySelector("[data-a=s]").onclick = async () => {
|
|
319
326
|
const why = li.querySelector("input").value.trim();
|
|
320
327
|
if (!why) {
|
|
@@ -450,7 +457,7 @@ async function startRun(c, kind) {
|
|
|
450
457
|
const backlog = c.kind === "backlog" ? { source: c.source, n: c.n } : undefined;
|
|
451
458
|
const r = await post("/run", { dir: c.state, text: c.text, backlog, kind, agent: p ? p.agent : undefined, yolo });
|
|
452
459
|
if (r.ok) {
|
|
453
|
-
toast((kind === "full" ? "Full" : "Lite") + (yolo ? " yolo" : "") + " run " + where(r) + ": " + via(r.agent));
|
|
460
|
+
toast((kind === "full" ? "Full" : kind === "lite" ? "Lite" : "Started") + (yolo ? " yolo" : "") + " run " + where(r) + ": " + via(r.agent));
|
|
454
461
|
// A backlog card has no terminal of its own; the run's card gets it once the run exists.
|
|
455
462
|
if (r.pty && !backlog) store.wantTerm = key(c);
|
|
456
463
|
}
|
|
@@ -461,12 +468,13 @@ const HINT = { 1: "Ideas you run are planned here", 2: "Approved plans are built
|
|
|
461
468
|
|
|
462
469
|
// The new-idea form, wired into f: inline at the top of Ideas, or in a modal.
|
|
463
470
|
function ideaForm(f, close) {
|
|
464
|
-
f.innerHTML = html`${store.all ? html`<div class="combo" id="ndc"><button type="button" class="sel" id="newdir" aria-label="Project for this idea" aria-haspopup="listbox" aria-expanded="false"></button><div class="menu-pop" hidden><input class="sel" aria-label="Search projects" placeholder="Search projects…" spellcheck="false" autocomplete="off"><div id="nds" role="listbox"></div></div></div>` : ""}<input id="newidea" placeholder="What
|
|
471
|
+
f.innerHTML = html`${store.all ? html`<div class="combo" id="ndc"><button type="button" class="sel" id="newdir" aria-label="Project for this idea" aria-haspopup="listbox" aria-expanded="false"></button><div class="menu-pop" hidden><input class="sel" aria-label="Search projects" placeholder="Search projects…" spellcheck="false" autocomplete="off"><div id="nds" role="listbox"></div></div></div>` : ""}<input id="newidea" placeholder="What do you want to build?"><textarea id="newnotes" placeholder="Anything the planner should know (optional)"></textarea><div class="att" id="newatt"></div><input type="file" id="newfile" multiple hidden><div class="row"><button type="button" class="ib" id="newpick" aria-label="Attach files" title="Attach files">${ico("clip")}</button><button type="button" class="btn ghost" id="nc">Cancel</button><div class="split"><button type="button" class="btn pri" id="ns">Add</button><button type="button" class="btn pri" id="nm" aria-label="More ways to add" aria-haspopup="menu" aria-expanded="false">${ico("chev")}</button><div class="menu-pop" role="menu" hidden><button type="button" role="menuitem" id="nr" title="Add the idea and run it now">${ico("play")}Start run</button></div></div></div>`;
|
|
465
472
|
const inp = f.querySelector("#newidea"),
|
|
466
473
|
files = [];
|
|
467
474
|
draft(inp, "f:new:0");
|
|
468
475
|
draft(f.querySelector("textarea"), "f:new:1");
|
|
469
|
-
|
|
476
|
+
// run: add the idea, then launch it at once with the project's runner and agent.
|
|
477
|
+
const save = async (run) => {
|
|
470
478
|
const text = inp.value.trim();
|
|
471
479
|
if (!text) return;
|
|
472
480
|
const notes = f.querySelector("textarea").value.trim();
|
|
@@ -481,6 +489,11 @@ function ideaForm(f, close) {
|
|
|
481
489
|
const n = await post("/note", { dir, for: text, text: "", files: enc });
|
|
482
490
|
if (!n.ok) toast("Idea added, files not attached");
|
|
483
491
|
}
|
|
492
|
+
if (run) {
|
|
493
|
+
await load();
|
|
494
|
+
const c = store.S.cards.find((x) => x.kind === "idea" && x.text === text && (!dir || x.state === dir));
|
|
495
|
+
if (c) return startRun(c);
|
|
496
|
+
}
|
|
484
497
|
}
|
|
485
498
|
load();
|
|
486
499
|
};
|
|
@@ -503,7 +516,37 @@ function ideaForm(f, close) {
|
|
|
503
516
|
}
|
|
504
517
|
};
|
|
505
518
|
if (store.all) ideaCombo(f);
|
|
506
|
-
f.querySelector("#ns").onclick = save;
|
|
519
|
+
f.querySelector("#ns").onclick = () => save(false);
|
|
520
|
+
f.querySelector("#nr").onclick = () => save(true);
|
|
521
|
+
{
|
|
522
|
+
const mb = f.querySelector("#nm"),
|
|
523
|
+
menu = f.querySelector(".split .menu-pop");
|
|
524
|
+
// Focus moves into the menu only when it was opened from the keyboard.
|
|
525
|
+
const show = (on, kbd) => {
|
|
526
|
+
menu.hidden = !on;
|
|
527
|
+
mb.setAttribute("aria-expanded", String(on));
|
|
528
|
+
if (on && kbd) setTimeout(() => menu.querySelector("button").focus());
|
|
529
|
+
};
|
|
530
|
+
mb.onclick = (e) => {
|
|
531
|
+
e.stopPropagation();
|
|
532
|
+
show(menu.hidden, e.detail === 0);
|
|
533
|
+
};
|
|
534
|
+
menu.onkeydown = (e) => {
|
|
535
|
+
if (e.key === "Escape") {
|
|
536
|
+
e.preventDefault();
|
|
537
|
+
e.stopPropagation();
|
|
538
|
+
show(false);
|
|
539
|
+
mb.focus();
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
f.addEventListener("click", (e) => {
|
|
543
|
+
if (!menu.hidden && !e.target.closest(".split")) show(false);
|
|
544
|
+
});
|
|
545
|
+
const sp = f.querySelector(".split");
|
|
546
|
+
sp.addEventListener("focusout", (e) => {
|
|
547
|
+
if (!sp.contains(e.relatedTarget)) show(false);
|
|
548
|
+
});
|
|
549
|
+
}
|
|
507
550
|
f.querySelector("#nc").onclick = close;
|
|
508
551
|
{
|
|
509
552
|
const m = mic([inp, f.querySelector("textarea")]);
|
|
@@ -512,7 +555,7 @@ function ideaForm(f, close) {
|
|
|
512
555
|
inp.onkeydown = (e) => {
|
|
513
556
|
if (e.key === "Enter") {
|
|
514
557
|
e.preventDefault();
|
|
515
|
-
save();
|
|
558
|
+
save(false);
|
|
516
559
|
}
|
|
517
560
|
if (e.key === "Escape") {
|
|
518
561
|
e.preventDefault();
|
package/web/board/git.ts
CHANGED
|
@@ -31,7 +31,7 @@ export function gitBtn(c) {
|
|
|
31
31
|
export function confirmCommit(c, msg) {
|
|
32
32
|
const g = store.runDoc.git,
|
|
33
33
|
foot = $("pfoot");
|
|
34
|
-
foot.innerHTML = html`<div class="inl" style="flex:1"><div class="q">Commit ${g.files.length} changed file${g.files.length === 1 ? "" : "s"} on ${g.branch || "this branch"} and push to ${g.remote}?</div><div class="q dim">Only the files this run's report names.${g.others ? ` ${g.others} other changed file${g.others === 1 ? " is" : "s are"} left alone.` : ""}</div><pre class="files">${g.files.join("\n")}</pre>${g.shared && g.shared.length ? html`<div class="q">These files also hold other runs' changes, and a file is committed whole:</div><pre class="files">${g.shared.join("\n")}</pre>` : ""}<textarea aria-label="Commit message" placeholder="
|
|
34
|
+
foot.innerHTML = html`<div class="inl" style="flex:1"><div class="q">Commit ${g.files.length} changed file${g.files.length === 1 ? "" : "s"} on ${g.branch || "this branch"} and push to ${g.remote}?</div><div class="q dim">Only the files this run's report names.${g.others ? ` ${g.others} other changed file${g.others === 1 ? " is" : "s are"} left alone.` : ""}</div><pre class="files">${g.files.join("\n")}</pre>${g.shared && g.shared.length ? html`<div class="q">These files also hold other runs' changes, and a file is committed whole:</div><pre class="files">${g.shared.join("\n")}</pre>` : ""}<textarea aria-label="Commit message" placeholder="Describe what changed">${msg || commitMsg(store.runDoc.impl) || c.title || c.text}</textarea><div class="q dim" id="clong" hidden>The first line is over 72 characters; logs will cut it short.</div><div class="row"><button class="btn ghost" data-a="c">Cancel</button><button class="btn pri" data-a="s">Commit & push</button></div></div>`;
|
|
35
35
|
const inp = foot.querySelector("textarea"),
|
|
36
36
|
go = foot.querySelector("[data-a=s]");
|
|
37
37
|
inp.focus();
|
package/web/board/icebox.ts
CHANGED
|
@@ -975,7 +975,7 @@ export async function openSources() {
|
|
|
975
975
|
if (!dir && !showable().length) return toast("Pick a project first");
|
|
976
976
|
// The new-idea form, cut down: the request, files, dictation and, in All projects, the project.
|
|
977
977
|
const dl = modal(
|
|
978
|
-
`${all ? `<div class="combo" id="ndc"><button type="button" class="sel" id="newdir" aria-label="Project for this request" aria-haspopup="listbox" aria-expanded="false"></button><div class="menu-pop" hidden><input class="sel" aria-label="Search projects" placeholder="Search projects…" spellcheck="false" autocomplete="off"><div id="nds" role="listbox"></div></div></div>` : ""}<textarea id="newidea" placeholder="
|
|
978
|
+
`${all ? `<div class="combo" id="ndc"><button type="button" class="sel" id="newdir" aria-label="Project for this request" aria-haspopup="listbox" aria-expanded="false"></button><div class="menu-pop" hidden><input class="sel" aria-label="Search projects" placeholder="Search projects…" spellcheck="false" autocomplete="off"><div id="nds" role="listbox"></div></div></div>` : ""}<textarea id="newidea" placeholder="What did someone ask for?" aria-label="Request"></textarea><div class="att"></div><input type="file" multiple hidden><div class="row"><button type="button" class="ib" id="newpick" aria-label="Attach files" title="Attach files">${ico("clip")}</button><button type="button" class="btn ghost" value="">Cancel</button><button class="btn pri">Add</button></div>`,
|
|
979
979
|
);
|
|
980
980
|
const f = dl.querySelector("form"),
|
|
981
981
|
inp = f.querySelector("textarea"),
|