acstack 0.0.0-stage → 0.47.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.
Files changed (143) hide show
  1. package/.claude-plugin/plugin.json +19 -0
  2. package/LICENSE +21 -0
  3. package/README.md +389 -2
  4. package/THIRD_PARTY_NOTICES +14 -0
  5. package/TROUBLESHOOTING.md +225 -0
  6. package/agents/codex/frontend-verifier.toml +352 -0
  7. package/agents/codex/task-checker.toml +345 -0
  8. package/agents/codex/task-executor.toml +157 -0
  9. package/agents/codex/test-writer.toml +149 -0
  10. package/agents/frontend-verifier.md +351 -0
  11. package/agents/task-checker.md +344 -0
  12. package/agents/task-executor.md +156 -0
  13. package/agents/test-writer.md +148 -0
  14. package/assets/playwriter-bridge/build-launcher.ps1 +32 -0
  15. package/assets/playwriter-bridge/doctor.ps1 +450 -0
  16. package/assets/playwriter-bridge/launcher-src.cs +131 -0
  17. package/assets/playwriter-bridge/opener.js +330 -0
  18. package/assets/playwriter-bridge/opener.ps1 +63 -0
  19. package/assets/playwriter-bridge/register-tasks.ps1 +328 -0
  20. package/assets/playwriter-bridge/relay.ps1 +50 -0
  21. package/assets/playwriter-bridge/stop.ps1 +125 -0
  22. package/assets/playwriter-bridge/sync.ps1 +271 -0
  23. package/assets/playwriter-bridge/tunnel.ps1 +325 -0
  24. package/assets/playwriter-bridge/vm-free-port.sh +166 -0
  25. package/assets/terminal-workspace/client-keybindings.md +87 -0
  26. package/assets/terminal-workspace/config.example.yaml +329 -0
  27. package/assets/terminal-workspace/setup.sh +4317 -0
  28. package/cli/README.md +47 -0
  29. package/cli/bin/acstack.js +3 -0
  30. package/cli/dist/index.js +25984 -0
  31. package/cli/dist/index.js.map +7 -0
  32. package/cli/package.json +59 -0
  33. package/docs/adr/0001-ste-writing-standard.md +16 -0
  34. package/docs/adr/0002-version-gate-not-auto-bump.md +35 -0
  35. package/docs/adr/0003-handoff-ack-command.md +19 -0
  36. package/docs/adr/0004-dev-setup-per-host.md +13 -0
  37. package/docs/adr/0005-two-orchestrators-per-host.md +25 -0
  38. package/docs/adr/0006-document-opener-service.md +89 -0
  39. package/docs/adr/0007-lead-check-reads-the-transcript.md +87 -0
  40. package/docs/adr/0008-cli-audits-skill-interviews.md +37 -0
  41. package/docs/adr/0009-verify-the-write-not-the-writer.md +51 -0
  42. package/docs/adr/0010-promote-dev-to-main-by-fast-forward.md +115 -0
  43. package/docs/adr/0011-node-header-is-the-edge-encoding.md +20 -0
  44. package/docs/adr/0012-node-kind-is-a-closed-set-of-five.md +31 -0
  45. package/docs/adr/0013-the-diagram-renderer-is-pure.md +23 -0
  46. package/docs/adr/0014-reading-a-node-hides-its-generated-diagram.md +29 -0
  47. package/docs/adr/0015-a-node-id-links-to-its-issue-through-an-anchor.md +37 -0
  48. package/docs/adr/0016-the-map-root-carries-no-status-of-its-own.md +21 -0
  49. package/docs/adr/0017-call-impeccable-copy-only-its-questions.md +13 -0
  50. package/docs/adr/index.md +17 -0
  51. package/docs/config-reference.md +572 -0
  52. package/docs/dev-setups.md +164 -0
  53. package/docs/migrate-0.8.md +28 -0
  54. package/docs/playwriter-bridge-windows.md +15 -0
  55. package/docs/terminal-workspace-keys.md +137 -0
  56. package/docs/terminal-workspace.md +1774 -0
  57. package/docs/workflows.md +309 -0
  58. package/hooks/ac-writing-style-reminder.sh +86 -0
  59. package/hooks/context-pressure.sh +583 -0
  60. package/hooks/hooks.json +50 -0
  61. package/hooks/session-start-handoff.sh +165 -0
  62. package/hooks/session-start.sh +214 -0
  63. package/orchestrator/AGENTS.md +532 -0
  64. package/output-styles/ac-writing-style.md +286 -0
  65. package/package.json +50 -4
  66. package/pi/agents/frontend-verifier.md +357 -0
  67. package/pi/agents/task-checker.md +348 -0
  68. package/pi/agents/task-executor.md +160 -0
  69. package/pi/agents/test-writer.md +154 -0
  70. package/pi/extension.ts +723 -0
  71. package/profiles/fullstack-python.yaml +29 -0
  72. package/profiles/node-typescript.yaml +23 -0
  73. package/profiles/python.yaml +24 -0
  74. package/skills/archive/SKILL.md +160 -0
  75. package/skills/bootstrap/SKILL.md +109 -0
  76. package/skills/bridge/HANDOFF.md +93 -0
  77. package/skills/bridge/SKILL-VM.md +292 -0
  78. package/skills/bridge/SKILL-WINDOWS.md +526 -0
  79. package/skills/bridge/SKILL.md +121 -0
  80. package/skills/browser-verify/SKILL.md +229 -0
  81. package/skills/code-review/SKILL.md +44 -0
  82. package/skills/commit/SKILL.md +106 -0
  83. package/skills/compact-and-continue/SKILL.md +241 -0
  84. package/skills/explore/SKILL.md +149 -0
  85. package/skills/grilling/SKILL.md +134 -0
  86. package/skills/handoff/SKILL.md +250 -0
  87. package/skills/handoff/launch-herdr.md +197 -0
  88. package/skills/handoff/launch-without-herdr.md +30 -0
  89. package/skills/handoff/lead-check.md +59 -0
  90. package/skills/handoff/outcomes.md +162 -0
  91. package/skills/handoff/running-agent.md +50 -0
  92. package/skills/handoff/watch.md +115 -0
  93. package/skills/host/SKILL.md +247 -0
  94. package/skills/host/detect-host.sh +297 -0
  95. package/skills/host/herdr.md +79 -0
  96. package/skills/host/install-herdr-block.sh +467 -0
  97. package/skills/host/install-orca.sh +512 -0
  98. package/skills/host/notes.md +97 -0
  99. package/skills/host/orca.md +160 -0
  100. package/skills/host/questions.md +163 -0
  101. package/skills/host/report.md +42 -0
  102. package/skills/implement/SKILL.md +230 -0
  103. package/skills/implement/decision-log.md +62 -0
  104. package/skills/implement/design-pass.md +32 -0
  105. package/skills/implement/relaxed-tdd.md +28 -0
  106. package/skills/map/SKILL.md +250 -0
  107. package/skills/map/frontend.md +227 -0
  108. package/skills/map/trackers/files.md +74 -0
  109. package/skills/map/trackers/github.md +180 -0
  110. package/skills/map/visuals.md +387 -0
  111. package/skills/new-worktree/SKILL.md +155 -0
  112. package/skills/new-worktree/detect-orchestrator.sh +328 -0
  113. package/skills/new-worktree/detect-orchestrator.test.sh +894 -0
  114. package/skills/new-worktree/herdr.md +84 -0
  115. package/skills/new-worktree/orca.md +101 -0
  116. package/skills/pre-commit-review/SKILL.md +242 -0
  117. package/skills/pre-commit-review/auto-fix.md +19 -0
  118. package/skills/pre-commit-review/interactive.md +35 -0
  119. package/skills/quick-fix/SKILL.md +125 -0
  120. package/skills/record-context/SKILL.md +106 -0
  121. package/skills/setup/SKILL.md +250 -0
  122. package/skills/setup/browser.md +123 -0
  123. package/skills/setup/design-review.md +97 -0
  124. package/skills/setup/first-run.md +114 -0
  125. package/skills/setup/report.md +68 -0
  126. package/skills/setup/row-fixes.md +196 -0
  127. package/skills/setup/terminal-workspace.md +153 -0
  128. package/skills/sync/SKILL.md +171 -0
  129. package/skills/teardown-worktree/SKILL.md +245 -0
  130. package/skills/teardown-worktree/detector.md +39 -0
  131. package/skills/teardown-worktree/herdr.md +113 -0
  132. package/skills/teardown-worktree/orca.md +61 -0
  133. package/skills/terminal-workspace/SKILL.md +249 -0
  134. package/skills/terminal-workspace/components.md +36 -0
  135. package/skills/terminal-workspace/config-choices.md +69 -0
  136. package/skills/terminal-workspace/emulator-keys.md +48 -0
  137. package/skills/terminal-workspace/headless-clipboard.md +175 -0
  138. package/skills/terminal-workspace/turn-off-and-update.md +104 -0
  139. package/skills/test-conventions/SKILL.md +59 -0
  140. package/skills/visual-docs/SKILL.md +246 -0
  141. package/skills/writing/SKILL.md +231 -0
  142. package/skills/writing/examples.md +153 -0
  143. package/skills/writing/vocabulary.md +96 -0
@@ -0,0 +1,309 @@
1
+ # ACStack workflows
2
+
3
+ *Every request ends the same way, with your specs updated from the diff. What changes is how many questions you settle first.*
4
+
5
+ ACStack routes a request down one of three paths. Every path ends at a commit that updates your specs.
6
+
7
+ - **The path depends on what is in your way**: nothing, a bug, or an unanswered question
8
+ - **A clear request goes straight to the four TDD agents** (see section 4)
9
+ - **A bug goes to root-cause analysis first** (see section 2)
10
+ - **A request with open questions becomes a to-do graph** you work through (see section 1)
11
+ - **The rest of this document** is the detail behind one box of the diagram below
12
+
13
+ ```mermaid
14
+ flowchart TD
15
+ R["A request<br/>(feature, bug, refactor)"] --> Q{"What stands<br/>in the way?"}
16
+ Q -->|"nothing – the way is clear"| I["Implement<br/>(implement)"]
17
+ Q -->|"it is broken"| F["Root-cause fix<br/>(quick-fix)"]
18
+ Q -->|"open decisions"| M["Map<br/>(chart, then work the frontier)"]
19
+ M -->|"work nodes"| I
20
+ I --> T["Build under TDD<br/>(4-phase pipeline)"]
21
+ F --> C["Commit<br/>(acstack:commit)"]
22
+ T --> C
23
+ C --> Y["Sync specs from the diff<br/>(domain-map.yaml)"]
24
+ Y --> L[("Living specs<br/>acstack/specs/")]
25
+ M -->|"every question answered"| A["Archive<br/>(check the decisions landed,<br/>then delete the nodes)"]
26
+ A --> L
27
+ ```
28
+
29
+ | Step in the diagram | Detail |
30
+ | --------------------- | --------- |
31
+ | Map, the to-do graph | section 1 |
32
+ | Implement | section 2 |
33
+ | Asking and recording | section 3 |
34
+ | Build under TDD | section 4 |
35
+ | Commit **and** sync | section 5 |
36
+ | Archive | section 6 |
37
+ | Living specs | section 7 |
38
+
39
+ The request box is a terminal, not a step. Section 5 covers two boxes because one command does both.
40
+
41
+ Section 8 describes this document. No box holds it.
42
+
43
+ ## 1. The map, meaning the to-do graph
44
+
45
+ *One kind of record replaces the fixed proposal, design, and tasks pipeline earlier versions used. Nothing stores the map itself. It is queries over the nodes.*
46
+
47
+ - **A map** is a set of small records called nodes
48
+ - **A node** is one question to settle or one piece of work to do
49
+ - `/acstack:map` is the entry point for planned work
50
+ - It charts a map when none exists, opening with a grill, a round of questions that fixes the destination and surfaces the fog
51
+ - The destination is what finished looks like
52
+ - It works the frontier instead when a map exists
53
+ - ACStack never sorts requests into sizes
54
+ - Nothing picks a different process for a big one
55
+ - There is one path, and only the node count changes
56
+
57
+ A node carries one of five statuses.
58
+
59
+ | Status | What it means |
60
+ | --- | --- |
61
+ | `fog` | nobody can state it precisely yet |
62
+ | `open` | ready to settle |
63
+ | `claimed` | someone is working it |
64
+ | `resolved` | settled |
65
+ | `out-of-scope` | deliberately dropped |
66
+
67
+ A node carries a `kind`, a `label`, and a `mode` too. Two link types connect the nodes.
68
+
69
+ - **`kind`** is one of five: `destination`, `notes`, `decision`, `research`, `work`
70
+ - It picks the node's shape in a diagram, never how the map routes it
71
+ - **`label`** is the node's short name for a diagram, at most four words
72
+ - **`mode`** says who settles the node: `hitl` for a human, `afk` for an agent alone
73
+ - **`answers`** names the one node whose answer raised this one
74
+ - The parent links form a tree, which gives you a reading order
75
+ - An agent packages a handoff by walking that tree
76
+ - **`blocked-by`** names any number of nodes that must resolve before this one starts
77
+ - The blocking edges form a directed acyclic graph, meaning the edges never loop back
78
+ - These edges tell you what you can work right now
79
+
80
+ Every node body carries a diagram, two at most. A childless `notes` node is the one exception.
81
+
82
+ - A node with children draws its own subtree, rendered by `acstack node diagram`
83
+ - Every other node draws what its question or its change touches, drawn by the agent
84
+ - A second mermaid fence under each diagram is its legend, showing only the cues that diagram uses
85
+ - On GitHub every node id in a diagram links to its issue
86
+ - `skills/map/visuals.md` holds the rules
87
+
88
+ ACStack works depth out from `answers`. Nobody declares it. The map itself is five queries.
89
+
90
+ | The question | The query |
91
+ | --- | --- |
92
+ | Where are we going? | the root node |
93
+ | What have we decided? | the `resolved` nodes, in the order the decisions happened |
94
+ | What can we work now? | the `open` nodes with no unresolved blockers |
95
+ | What is not yet specified? | the `fog` nodes |
96
+ | What did we drop? | the `out-of-scope` nodes |
97
+
98
+ Nodes live in a tracker. That is the storage layer, and you can swap it.
99
+
100
+ - A tracker provides four operations and nothing more: create, read, update, list
101
+ - GitHub issues are the default
102
+ - The first line of every issue body carries all of that node's edges
103
+ - One list call therefore returns the whole graph
104
+ - A sub-issue and a dependency mirror those edges, so GitHub's own interface draws the map
105
+ - Files under `acstack/maps/<name>/` are the fallback
106
+ - Frontmatter holds the edges, so those files carry no header line
107
+ - Frontier, claim, and resolve build on those four operations
108
+ - No tracker implements them itself
109
+
110
+ **Build no more process than the job needs.**
111
+
112
+ - You create a map only when the fog will outlive the session
113
+ - You settle one question in conversation
114
+ - That leaves an architecture decision record (ADR), not a map
115
+
116
+ ## 2. Implement
117
+
118
+ *A unit of work carries its own size. One node is a quick change.*
119
+
120
+ *Forty is a long effort. Nothing declares which.*
121
+
122
+ - `/acstack:implement` claims `afk` work nodes from the frontier when a map exists
123
+ - It treats the request itself as the work item when none does
124
+ - Either way, parallel explorer subagents run over the relevant code before anyone writes anything
125
+ - The command resolves paths at claim time
126
+ - A node describes behaviour
127
+ - It can sit unstarted for weeks
128
+ - The test-driven development (TDD) pipeline of section 4 runs to completion inside each claim
129
+ - A node only ever moves `open -> claimed -> resolved`
130
+ - The agent releases the claim when work stalls
131
+ - The node is plainly open again
132
+ - Progress is a query, not a checkbox file
133
+ - Resuming never means re-reading an effort end to end
134
+
135
+ `/acstack:quick-fix` stays separate. A broken thing is a different problem from an unanswered question.
136
+
137
+ - A bug has a root cause to find, not a decision to settle
138
+ - `/acstack:quick-fix` therefore never creates a node
139
+
140
+ ## 3. Asking the questions, and writing the answers down
141
+
142
+ *Three skills the agent reaches for itself, without you typing a command, carry the interviewing, the recording, and the writing.*
143
+
144
+ - **`grilling`** is the interview
145
+ - It works through your decisions one round at a time
146
+ - A round asks the whole frontier, every question whose prerequisites have resolved, numbered, each with a recommended answer
147
+ - It then recomputes the frontier and repeats
148
+ - Facts are the agent's job, never yours
149
+ - It sends any question a look at the environment would answer to a subagent instead
150
+ - Presentation follows `workflow.grilling.questions`, the host's question tool by default
151
+ - It falls back to inline prose past 4 questions, or for a question with no discrete options
152
+ - There is no question cap
153
+ - The set of answerable questions bounds itself
154
+ - **`record-context`** fires when a decision lands
155
+ - It writes an ADR under `docs/adr/` only when the decision is hard to reverse *and* surprising *and* a real trade-off
156
+ - It writes a glossary term when a term got settled, in root `CONTEXT.md` for cross-domain vocabulary
157
+ - It writes a domain term in `acstack/specs/<domain>/CONTEXT.md` instead
158
+ - It can write both, or neither
159
+ - ACStack generates the ADR index from the files
160
+ - Nobody hand-edits it
161
+ - **`writing`** holds the standard every durable artifact follows: node answers, ADRs, glossary entries, specs and handoffs
162
+ - The words to avoid live in `skills/writing/vocabulary.md`
163
+
164
+ ## 4. Build under TDD
165
+
166
+ *Four agents run in order. The rule that does the work: the test-writer never sees the implementation.*
167
+
168
+ ```mermaid
169
+ flowchart LR
170
+ subgraph P["Order set by workflow.tdd.strict"]
171
+ direction LR
172
+ W["test-writer<br/>(requirements only)"]
173
+ E["task-executor<br/>(cannot edit tests)"]
174
+ W -.->|"strict: tests first"| E
175
+ E -.->|"relaxed: code first"| W
176
+ end
177
+ P --> Fm["format<br/>(commands.format)"]
178
+ Fm --> K["task-checker<br/>(PASS / FAIL)"]
179
+ K -->|FAIL| E
180
+ K -->|PASS| V["frontend-verifier<br/>(real browser)"]
181
+ V -->|FAIL| E
182
+ ```
183
+
184
+ The separation is the mechanism, not a formality.
185
+
186
+ - **test-writer** sees requirements and acceptance criteria, never the implementation plan
187
+ - Tests then encode what should happen
188
+ - The implementation cannot shape them
189
+ - **task-executor** receives the failing tests and cannot modify anything in the test directory
190
+ - It has to make the specification pass instead of editing it
191
+ - **task-checker** is a binary gate
192
+ - It runs the task's tests, then the full suite for regressions
193
+ - It checks the test count against `.test-baseline` and audits mocks for circular assertions
194
+ - It confirms the executor left the tests alone
195
+ - **frontend-verifier** drives a real browser over the Chrome DevTools Protocol (CDP)
196
+ - It takes before and after screenshots
197
+ - It reports on that evidence
198
+
199
+ Which phases run, and in what order:
200
+
201
+ - A FAIL routes back to the executor with the reason
202
+ - Phases 1 and 4 are conditional
203
+ - You can skip test-writer for a config or docs change with no testable behaviour
204
+ - The frontend-verifier runs under three conditions: `acstack/project.yaml` configures a frontend, frontend files changed, and verification is on
205
+ - `workflow.tdd.strict: false` reverses phase 1 and phase 2
206
+ - The order becomes task-executor, then test-writer, then task-checker
207
+ - It drops no phase
208
+ - All three agents run
209
+ - Relaxed TDD means nobody writes the tests first, never that the work goes unverified
210
+
211
+ Relaxed weakens one wall, the one this section calls the important one.
212
+
213
+ - Under `false` the test-writer works beside an implementation it must not read, instead of one that does not exist yet
214
+ - Its independence then rests on its instructions rather than on context isolation
215
+ - That is the cost of relaxed TDD
216
+ - The checker pays for it too
217
+ - No run can show the new tests failing without the implementation
218
+ - It holds them to existing and passing instead
219
+
220
+ The format step sits immediately before phase 3.
221
+
222
+ - The pipeline runs `commands.format` over the files the work touched
223
+ - Under relaxed that puts the format step after the test-writer
224
+ - The new tests get formatted too
225
+ - A project whose `commands.format` is `null` gets no format step
226
+ - A non-zero exit gets reported
227
+ - It never counts as a failed implementation
228
+
229
+ ## 5. Commit and sync specs
230
+
231
+ *Spec sync runs on every commit on every path. It reads the diff. Your specs therefore describe what you built, not what you planned.*
232
+
233
+ `/acstack:commit` groups changes into MECE commits. MECE means mutually exclusive and collectively exhaustive: no change appears twice, and no change goes missing. Before writing them it does five things.
234
+
235
+ 1. Reads `acstack/domain-map.yaml` to map each changed file to a domain.
236
+ 2. Works out what the diff adds, modifies or removes, for each affected domain with a `spec.md`.
237
+ 3. Writes those entries into the domain's spec and stages them in the same commit.
238
+ 4. Flags source files that match no domain.
239
+ 5. Checks the glossaries.
240
+
241
+ On the glossary check:
242
+
243
+ - It says so when the diff renames or deletes an identifier matching a glossary term
244
+ - It never edits the glossary
245
+ - For specs the code wins
246
+ - For the glossary the human wins
247
+
248
+ Four cases stop sync.
249
+
250
+ - `workflow.spec_sync` set to `false`
251
+ - no domain map
252
+ - no file matching a domain
253
+ - a change that touches only docs and config
254
+
255
+ **Without `acstack/domain-map.yaml` this step does nothing, silently.**
256
+
257
+ - `/acstack:setup` generates that file
258
+ - `acstack config check` reports it as missing on a project set up before that existed
259
+
260
+ ## 6. Archive
261
+
262
+ *A map is working state, not a record you keep. Archive checks the decisions reached your specs, your decision notes, and your glossary, then deletes the nodes.*
263
+
264
+ - `/acstack:archive` checks for a cleared map: empty frontier, no fog, no claims
265
+ - It spot-checks that the specs, the decision notes and the glossary captured what the effort settled
266
+ - It then disposes of the nodes
267
+ - It deletes them by default
268
+ - It moves them to `acstack/archive/<date>-<name>/nodes/` when `workflow.maps.persist` is `true`
269
+ - Keeping nodes is off by default
270
+ - A kept map is a second copy of every decision
271
+ - The two drift apart
272
+ - On the GitHub tracker there is nothing to dispose
273
+ - A closed issue is already the archive
274
+ - Archive runs either way
275
+ - The user types `/acstack:archive`
276
+ - `/acstack:map` hands off to it once every node resolves
277
+ - The disposal step then asks you first
278
+ - Legacy `acstack/changes/` directories from the pre-map workflow still archive the old way
279
+ - An upgrade therefore never strands work
280
+
281
+ ## 7. Living specs
282
+
283
+ *What the project keeps. ACStack deletes a map once every question resolves. `acstack/specs/` outlives it.*
284
+
285
+ - Specs live at `acstack/specs/<domain>/spec.md`, organised by the domains in `domain-map.yaml`
286
+ - Each spec states numbered functional requirements (`FR-NNN`) in Given, When, Then form
287
+ - They are cumulative
288
+ - They describe only what the code already does
289
+ - A roadmap item in a living spec is a bug in the spec
290
+
291
+ Two rules keep them honest.
292
+
293
+ - **Two writers, one target**
294
+ - Commit-time sync handles incremental change
295
+ - Archive verifies a completed map left nothing behind
296
+ - Both converge on the same files
297
+ - **Fix it when you see it**
298
+ - Any agent that finds a spec contradicting the code corrects the spec immediately
299
+ - It rewrites wrong behaviour, appends missing requirements, and deletes stale references
300
+
301
+ `/acstack:bootstrap` generates the first set from an existing codebase. A project never starts from an empty directory.
302
+
303
+ ## 8. Where the design record lives
304
+
305
+ *This section describes the document, not the system. No diagram box holds it.*
306
+
307
+ - The reasoning behind this design lives in the issues labelled `wayfinder` on this repository
308
+ - That covers why one node type, why four tracker operations, and why no tiers
309
+ - Each issue is one decision with the reasoning that produced it
@@ -0,0 +1,86 @@
1
+ #!/bin/sh
2
+ # Per-turn reminder for the ac-writing-style output style.
3
+ #
4
+ # Built-in styles (Concise, Proactive) carry a `turnReminder` field that the CLI
5
+ # re-injects every turn. A file-based output style cannot set one: the
6
+ # frontmatter schema accepts only name, description, keep-coding-instructions
7
+ # and force-for-plugin. This hook supplies the equivalent.
8
+ #
9
+ # The reminder text is EXTRACTED from output-styles/ac-writing-style.md on every
10
+ # turn, never stored. A hand-typed copy drifts from the style the moment either
11
+ # one changes, and that is exactly what happened to the first version of this
12
+ # hook. Two parts come out of the file:
13
+ #
14
+ # 1. "## Who you are", top-level lines only, which is the persona
15
+ # 2. "## Before you send", in full, which is the checklist
16
+ #
17
+ # Output is JSON only, with suppressOutput true, so nothing reaches the user's
18
+ # transcript. The hook stays silent unless the active output style is this one.
19
+
20
+ set -u
21
+
22
+ # Drain stdin so the CLI never sees a broken pipe.
23
+ { command -p cat 2>/dev/null || cat; } >/dev/null 2>&1 || :
24
+
25
+ silent() { printf '{}\n'; exit 0; }
26
+
27
+ # --------------------------------------------------------------------------
28
+ # Gate: only speak when ac-writing-style is the active output style.
29
+ # --------------------------------------------------------------------------
30
+
31
+ read_style() {
32
+ [ -f "$1" ] || return 1
33
+ sed -n 's/.*"outputStyle"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$1" 2>/dev/null | head -1
34
+ }
35
+
36
+ # Project settings win over user settings, the same order the CLI resolves them.
37
+ style=$(read_style ".claude/settings.local.json" || true)
38
+ [ -n "${style:-}" ] || style=$(read_style ".claude/settings.json" || true)
39
+ [ -n "${style:-}" ] || style=$(read_style "${HOME}/.claude/settings.json" || true)
40
+
41
+ case "${style:-}" in
42
+ *ac-writing-style|*ac-agentic-coding-writing) ;;
43
+ *) silent ;;
44
+ esac
45
+
46
+ # --------------------------------------------------------------------------
47
+ # Locate the output style the reminder derives from.
48
+ # --------------------------------------------------------------------------
49
+
50
+ root="${CLAUDE_PLUGIN_ROOT:-}"
51
+ [ -n "$root" ] || root="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
52
+ style_file="${root}/output-styles/ac-writing-style.md"
53
+
54
+ [ -r "$style_file" ] || silent
55
+
56
+ # --------------------------------------------------------------------------
57
+ # Extract, escape, and emit.
58
+ # --------------------------------------------------------------------------
59
+
60
+ awk '
61
+ function esc(s) {
62
+ gsub(/\\/, "\\\\", s)
63
+ gsub(/"/, "\\\"", s)
64
+ gsub(/\t/, "\\t", s)
65
+ return s
66
+ }
67
+ function emit(s) { out = out esc(s) "\\n" }
68
+
69
+ # "## Who you are" holds the persona. Keep its top-level lines: the italic
70
+ # opener, the bold rules, and the unindented bullets. Drop the sub-bullets,
71
+ # because the reminder names each rule and the file itself argues for it.
72
+ /^## Who you are$/ { persona = 1; next }
73
+ /^## Shape: Minto pyramid$/ { persona = 0 }
74
+ persona && /^### / { emit(""); emit($0); next }
75
+ persona && /^[*-]/ { emit($0); next }
76
+ persona && /^\*\*/ { emit($0); next }
77
+
78
+ # "## Before you send" is the checklist, and it runs to the end of the file.
79
+ /^## Before you send$/ { checklist = 1; emit(""); emit($0); next }
80
+ checklist { emit($0); next }
81
+
82
+ END {
83
+ if (out == "") exit 1
84
+ printf "{\"suppressOutput\": true, \"hookSpecificOutput\": {\"hookEventName\": \"UserPromptSubmit\", \"additionalContext\": \"ac-writing-style reminder\\n\\n%s\"}}\n", out
85
+ }
86
+ ' "$style_file" || silent