repo-harness 0.1.2 → 0.1.4
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.md +1 -1
- package/CLAUDE.md +1 -1
- package/README.md +62 -8
- package/README.zh-CN.md +34 -7
- package/SKILL.md +22 -0
- package/assets/hooks/lib/workflow-state.sh +55 -12
- package/assets/hooks/post-bash.sh +116 -2
- package/assets/hooks/prompt-guard.sh +451 -135
- package/assets/initializer-question-pack.v4.json +207 -1
- package/assets/initializer-question-pack.v4.schema.json +40 -3
- package/assets/partials/04-project-structure.partial.md +10 -0
- package/assets/partials/07-footer.partial.md +3 -3
- package/assets/partials/08-orchestration.partial.md +1 -1
- package/assets/partials-agents/04-task-protocol.partial.md +4 -4
- package/assets/partials-agents/06-quality-safety.partial.md +2 -2
- package/assets/plan-map.json +44 -0
- package/assets/project-structures/ai-native-product-copilot.txt +21 -0
- package/assets/project-structures/ai-native-runtime-console.txt +25 -0
- package/assets/project-structures/ai-native-sidecar-kernel.txt +19 -0
- package/assets/reference-configs/evaluator-rubric.md +1 -1
- package/assets/reference-configs/external-tooling.md +14 -0
- package/assets/reference-configs/harness-overview.md +7 -7
- package/assets/reference-configs/release-deploy.md +8 -0
- package/assets/reference-configs/sprint-contracts.md +2 -2
- package/assets/skill-commands/repo-harness-scaffold/SKILL.md +13 -3
- package/assets/templates/contract.template.md +8 -8
- package/assets/templates/helpers/archive-workflow.sh +5 -1
- package/assets/templates/helpers/capture-plan.sh +14 -13
- package/assets/templates/helpers/check-deploy-sql-order.sh +12 -0
- package/assets/templates/helpers/codex-handoff-resume.sh +40 -8
- package/assets/templates/helpers/contract-worktree.sh +25 -1
- package/assets/templates/helpers/ensure-task-workflow.sh +19 -19
- package/assets/templates/helpers/new-plan.sh +20 -12
- package/assets/templates/helpers/plan-to-todo.sh +49 -17
- package/assets/templates/helpers/refresh-current-status.sh +6 -2
- package/assets/templates/plan.template.md +11 -11
- package/assets/templates/tech-stack.template.md +14 -0
- package/docs/reference-configs/evaluator-rubric.md +1 -1
- package/docs/reference-configs/external-tooling.md +14 -0
- package/docs/reference-configs/harness-overview.md +7 -7
- package/docs/reference-configs/hook-operations.md +9 -0
- package/docs/reference-configs/release-deploy.md +8 -0
- package/docs/reference-configs/sprint-contracts.md +2 -2
- package/package.json +1 -1
- package/scripts/archive-workflow.sh +5 -1
- package/scripts/assemble-template.ts +140 -0
- package/scripts/capture-plan.sh +14 -13
- package/scripts/check-deploy-sql-order.sh +12 -0
- package/scripts/codex-handoff-resume.sh +40 -8
- package/scripts/contract-worktree.sh +25 -1
- package/scripts/ensure-task-workflow.sh +19 -19
- package/scripts/initializer-question-pack.ts +28 -0
- package/scripts/lib/project-init-lib.sh +19 -19
- package/scripts/new-plan.sh +20 -12
- package/scripts/plan-to-todo.sh +49 -17
- package/scripts/refresh-current-status.sh +6 -2
- package/src/cli/commands/prompt-guard-decision.ts +88 -0
- package/src/cli/commands/status.ts +1 -1
- package/src/cli/hook/prompt-guard-decision.ts +238 -0
- package/src/cli/hook-entry.ts +8 -1
- package/src/cli/index.ts +11 -1
package/AGENTS.md
CHANGED
|
@@ -21,7 +21,7 @@ This repository self-hosts the `repo-harness` contract, formerly `repo-harness-s
|
|
|
21
21
|
## Operating Rules
|
|
22
22
|
|
|
23
23
|
- Sync `tasks/` whenever substantive repo changes are made.
|
|
24
|
-
- Use `tasks/notes/<
|
|
24
|
+
- Use `tasks/notes/<plan-stem>.notes.md` only for non-obvious slice decisions, deviations, tradeoffs, and open questions; `<plan-stem>` is the active plan filename without `plan-` and `.md` (for example `20260531-0045-governance-workflow`). Do not use notes as durable memory or a task log, and archive/promote them deliberately when the slice closes.
|
|
25
25
|
- Treat `.ai/hooks/` as the shared repo-local hook implementation; user-level `~/.claude/settings.json` and `~/.codex/hooks.json` are the host adapters.
|
|
26
26
|
- Keep the umbrella hierarchy explicit: architecture owns stable truth, capability contracts own local agent context, `tasks/workstreams/<domain>/<capability>/` owns durable progress, and `tasks/todo.md` owns only deferred medium/long-term goals with tradeoff and revisit trigger.
|
|
27
27
|
- Treat `.ai/context/capabilities.json` as the source of truth for capability prefixes; `agent-context-blocks.txt` and nested agent files are compatibility inputs only.
|
package/CLAUDE.md
CHANGED
|
@@ -21,7 +21,7 @@ This repository self-hosts the `repo-harness` contract, formerly `repo-harness-s
|
|
|
21
21
|
## Operating Rules
|
|
22
22
|
|
|
23
23
|
- Sync `tasks/` whenever substantive repo changes are made.
|
|
24
|
-
- Use `tasks/notes/<
|
|
24
|
+
- Use `tasks/notes/<plan-stem>.notes.md` only for non-obvious slice decisions, deviations, tradeoffs, and open questions; `<plan-stem>` is the active plan filename without `plan-` and `.md` (for example `20260531-0045-governance-workflow`). Do not use notes as durable memory or a task log, and archive/promote them deliberately when the slice closes.
|
|
25
25
|
- Treat `.ai/hooks/` as the shared repo-local hook implementation; user-level `~/.claude/settings.json` and `~/.codex/hooks.json` are the host adapters.
|
|
26
26
|
- Keep the umbrella hierarchy explicit: architecture owns stable truth, capability contracts own local agent context, `tasks/workstreams/<domain>/<capability>/` owns durable progress, and `tasks/todo.md` owns only deferred medium/long-term goals with tradeoff and revisit trigger.
|
|
27
27
|
- Treat `.ai/context/capabilities.json` as the source of truth for capability prefixes; `agent-context-blocks.txt` and nested agent files are compatibility inputs only.
|
package/README.md
CHANGED
|
@@ -45,6 +45,16 @@ The design has three layers:
|
|
|
45
45
|
the current repo's `.ai/hooks/*` scripts only when
|
|
46
46
|
`.ai/harness/workflow-contract.json` exists.
|
|
47
47
|
|
|
48
|
+
For `UserPromptSubmit`, the public adapter contract stays
|
|
49
|
+
`repo-harness-hook UserPromptSubmit --route default`. The CLI route registry
|
|
50
|
+
dispatches that route to `.ai/hooks/prompt-guard.sh`. The shell hook remains the
|
|
51
|
+
repo-local adapter for host JSON parsing, workflow file reads, capture side
|
|
52
|
+
effects, quality gate rendering, and host-safe stdout/stderr. The prompt intent
|
|
53
|
+
and workflow-state decision is handled by the TypeScript decision engine behind
|
|
54
|
+
`repo-harness-hook prompt-guard-decide`, which returns one action enum from an
|
|
55
|
+
explicit decision table. That split keeps host configuration stable while moving
|
|
56
|
+
the brittle classifier/state-machine layer out of shell conditionals.
|
|
57
|
+
|
|
48
58
|
The core invariant is that durable truth lives in the repo, not in a chat
|
|
49
59
|
thread. Hooks are accelerators and guardrails; the authority remains the
|
|
50
60
|
file-backed plan, contract, review, checks, and handoff artifacts.
|
|
@@ -67,9 +77,9 @@ flowchart TD
|
|
|
67
77
|
|
|
68
78
|
Approve --> Project["Project plan into execution<br/>capture-plan.sh --execute<br/>or plan-to-todo.sh --plan"]
|
|
69
79
|
Project --> Active["Active markers<br/>.ai/harness/active-plan<br/>.ai/harness/active-worktree"]
|
|
70
|
-
Project --> Contract["Sprint contract<br/>tasks/contracts/task-slug.contract.md"]
|
|
71
|
-
Project --> ReviewFile["Review file<br/>tasks/reviews/task-slug.review.md"]
|
|
72
|
-
Project --> Notes["Task notes<br/>tasks/notes/task-slug.notes.md"]
|
|
80
|
+
Project --> Contract["Sprint contract<br/>tasks/contracts/YYYYMMDD-HHMM-task-slug.contract.md"]
|
|
81
|
+
Project --> ReviewFile["Review file<br/>tasks/reviews/YYYYMMDD-HHMM-task-slug.review.md"]
|
|
82
|
+
Project --> Notes["Task notes<br/>tasks/notes/YYYYMMDD-HHMM-task-slug.notes.md"]
|
|
73
83
|
|
|
74
84
|
Contract --> WorktreePolicy{"Contract worktree required?"}
|
|
75
85
|
WorktreePolicy -->|yes| Checkout["Checkout isolated worktree<br/>contract-worktree.sh start --plan<br/>branch codex/task-slug"]
|
|
@@ -111,11 +121,12 @@ npx -y repo-harness init
|
|
|
111
121
|
```
|
|
112
122
|
|
|
113
123
|
The npm package release line is `0.1.x`; generated workflow compatibility is
|
|
114
|
-
tracked separately as the `5.x` model line. The `0.1.
|
|
124
|
+
tracked separately as the `5.x` model line. The `0.1.4` package publishes the
|
|
115
125
|
renamed `repo-harness` CLI, user-level Claude/Codex hook adapter bootstrap,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
npm
|
|
126
|
+
AI-native scaffold overlays, the typed prompt-guard decision engine, plan-stem
|
|
127
|
+
task artifact naming, Waza runtime skill sync, `diagram-design` sync, and the
|
|
128
|
+
release gate used by maintainers before npm publish. When working from a source
|
|
129
|
+
checkout instead of npm, run:
|
|
119
130
|
|
|
120
131
|
```bash
|
|
121
132
|
git clone https://github.com/Ancienttwo/repo-harness.git ~/Projects/repo-harness
|
|
@@ -194,6 +205,23 @@ before applying anything.
|
|
|
194
205
|
- Codex must mark `~/.codex/hooks.json` as trusted in Codex Settings before those hooks run.
|
|
195
206
|
- Debug in this order: user-level adapter config -> `repo-harness-hook` (or fallback `repo-harness hook`) -> route registry -> `.ai/hooks/*`.
|
|
196
207
|
|
|
208
|
+
Prompt guard has one extra internal step:
|
|
209
|
+
|
|
210
|
+
```mermaid
|
|
211
|
+
flowchart LR
|
|
212
|
+
Host["Claude/Codex UserPromptSubmit"] --> Adapter["user-level adapter"]
|
|
213
|
+
Adapter --> CLI["repo-harness-hook UserPromptSubmit --route default"]
|
|
214
|
+
CLI --> Route["route registry"]
|
|
215
|
+
Route --> Shell[".ai/hooks/prompt-guard.sh"]
|
|
216
|
+
Shell --> Decision["repo-harness-hook prompt-guard-decide<br/>TypeScript decision table"]
|
|
217
|
+
Decision --> Action["single action enum"]
|
|
218
|
+
Action --> Shell
|
|
219
|
+
Shell --> HostOutput["host-safe allow, advice, block, or done gate output"]
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The shell layer still owns filesystem authority and side effects. TypeScript owns
|
|
223
|
+
only the classifier plus `intent x plan state` decision table.
|
|
224
|
+
|
|
197
225
|
## Hook Failure Playbook
|
|
198
226
|
|
|
199
227
|
When a hook blocks work, start with the structured output in the terminal. The core
|
|
@@ -222,7 +250,7 @@ Most common guards:
|
|
|
222
250
|
|
|
223
251
|
## Current Release
|
|
224
252
|
|
|
225
|
-
- npm package: `repo-harness@0.1.
|
|
253
|
+
- npm package: `repo-harness@0.1.4`
|
|
226
254
|
- Generated workflow compatibility: `5.2.3`
|
|
227
255
|
- GitHub repository: `Ancienttwo/repo-harness`
|
|
228
256
|
- Release history: [`docs/CHANGELOG.md`](docs/CHANGELOG.md)
|
|
@@ -270,6 +298,23 @@ Most common guards:
|
|
|
270
298
|
- durable capability progress -> `tasks/workstreams/`
|
|
271
299
|
- release history -> `docs/CHANGELOG.md`
|
|
272
300
|
|
|
301
|
+
## Acknowledgements and Tooling Dependencies
|
|
302
|
+
|
|
303
|
+
`repo-harness` is built around a small set of external skills and repos that
|
|
304
|
+
proved useful while this project was being designed, debugged, and released.
|
|
305
|
+
They are acknowledged here because they shaped the workflow contract, but they
|
|
306
|
+
are not all bundled product dependencies.
|
|
307
|
+
|
|
308
|
+
| Tool or repo | Used for | Dependency shape |
|
|
309
|
+
| --- | --- | --- |
|
|
310
|
+
| gstack skills, including `document-release`, `office-hours`, `plan-eng-review`, and `plan-design-review` | Product discovery, plan review, design review, and post-ship documentation hygiene | External operator workflow; advisory by default |
|
|
311
|
+
| Waza skills, including `think`, `hunt`, `check`, `health`, `design`, `learn`, `read`, and `write` | Daily planning, bug hunts, verification, health checks, and Codex-first skill sync | Installed through the skills CLI into host skill roots |
|
|
312
|
+
| `diagram-design` | Human-readable architecture and system-flow diagrams when Mermaid is not enough | Runtime-referenced skill, not vendored into generated repos |
|
|
313
|
+
| `gbrain` | Knowledge sync, handoff retrieval, and long-form repo memory | Optional external CLI and index |
|
|
314
|
+
| CodeGraph (`@colbymchenry/codegraph`) | Symbol-aware navigation, impact tracing, and readiness checks for this self-host repo | Dev dependency in this repo; generated repos stay global-MCP-first unless policy opts in |
|
|
315
|
+
| Bun | Source checkout execution, tests, template assembly, and release checks | Required local runtime for maintainers |
|
|
316
|
+
| `commander` | `repo-harness` CLI command parsing | Runtime npm dependency |
|
|
317
|
+
|
|
273
318
|
## Action Command Skills
|
|
274
319
|
|
|
275
320
|
Source-owned command skill facades live in `assets/skill-commands/`. They keep
|
|
@@ -284,6 +329,15 @@ and tests:
|
|
|
284
329
|
project or module scaffold. `hooks-init`, `docs-init`, and `create-project-dirs`
|
|
285
330
|
are internal steps, not public commands.
|
|
286
331
|
|
|
332
|
+
`repo-harness-scaffold` keeps the A-K plan catalog as the project-type authority
|
|
333
|
+
and adds AI-native app structure through an optional `ai_native_profile` overlay.
|
|
334
|
+
The default profile is `none`, so existing scaffold output remains unchanged.
|
|
335
|
+
When selected, profiles such as `runtime-console`, `product-copilot`, and
|
|
336
|
+
`sidecar-kernel` document the AG-UI event boundary, assistant-ui or CopilotKit
|
|
337
|
+
UI runtime, Bun/Hono gateway, shared contracts, observability, and MCP/HTTP
|
|
338
|
+
sidecar rules without installing model providers or making Python, Go, Rust, or
|
|
339
|
+
A2UI mandatory defaults.
|
|
340
|
+
|
|
287
341
|
Use `repo-harness-capability` when the harness already exists and only selected
|
|
288
342
|
capability boundaries should be added. It updates `.ai/context/capabilities.json`,
|
|
289
343
|
syncs the requested local `AGENTS.md` / `CLAUDE.md` contract files, and validates
|
package/README.zh-CN.md
CHANGED
|
@@ -40,6 +40,15 @@ repo-local hooks,然后验证这些 workflow surfaces 仍然一致。
|
|
|
40
40
|
repo 是否存在 `.ai/harness/workflow-contract.json`;没有 opt in 就静默退出,有 opt in
|
|
41
41
|
才进入当前仓库的 `.ai/hooks/*`。
|
|
42
42
|
|
|
43
|
+
对 `UserPromptSubmit` 来说,公开 adapter contract 仍然是
|
|
44
|
+
`repo-harness-hook UserPromptSubmit --route default`。CLI route registry 会把这个
|
|
45
|
+
route dispatch 到 `.ai/hooks/prompt-guard.sh`。Shell hook 继续负责 host JSON 解析、
|
|
46
|
+
workflow 文件读取、plan capture 副作用、quality gate 渲染,以及 host-safe
|
|
47
|
+
stdout/stderr。Prompt intent 和 workflow state 的决策交给
|
|
48
|
+
`repo-harness-hook prompt-guard-decide` 背后的 TypeScript decision engine;它从显式
|
|
49
|
+
decision table 里返回一个 action enum。这样 host 配置不变,但最容易出错的
|
|
50
|
+
classifier/state-machine 层不再散落在 shell 条件分支里。
|
|
51
|
+
|
|
43
52
|
核心不变量:持久事实在仓库里,不在聊天窗口里。Hooks 只是加速器和 guardrail;
|
|
44
53
|
真正的 authority 是 plan、contract、review、checks 和 handoff 这些文件。
|
|
45
54
|
|
|
@@ -60,9 +69,9 @@ flowchart TD
|
|
|
60
69
|
|
|
61
70
|
Approve --> Project["投射到执行面<br/>capture-plan.sh --execute<br/>或 plan-to-todo.sh --plan"]
|
|
62
71
|
Project --> Active["Active markers<br/>.ai/harness/active-plan<br/>.ai/harness/active-worktree"]
|
|
63
|
-
Project --> Contract["Sprint contract<br/>tasks/contracts/task-slug.contract.md"]
|
|
64
|
-
Project --> ReviewFile["Review file<br/>tasks/reviews/task-slug.review.md"]
|
|
65
|
-
Project --> Notes["Task notes<br/>tasks/notes/task-slug.notes.md"]
|
|
72
|
+
Project --> Contract["Sprint contract<br/>tasks/contracts/YYYYMMDD-HHMM-task-slug.contract.md"]
|
|
73
|
+
Project --> ReviewFile["Review file<br/>tasks/reviews/YYYYMMDD-HHMM-task-slug.review.md"]
|
|
74
|
+
Project --> Notes["Task notes<br/>tasks/notes/YYYYMMDD-HHMM-task-slug.notes.md"]
|
|
66
75
|
|
|
67
76
|
Contract --> WorktreePolicy{"是否需要 contract worktree?"}
|
|
68
77
|
WorktreePolicy -->|是| Checkout["Checkout 隔离 worktree<br/>contract-worktree.sh start --plan<br/>branch codex/task-slug"]
|
|
@@ -103,9 +112,10 @@ npx -y repo-harness init
|
|
|
103
112
|
```
|
|
104
113
|
|
|
105
114
|
npm package release line 是 `0.1.x`;生成的 workflow compatibility model line
|
|
106
|
-
单独以 `5.x` 追踪。`repo-harness@0.1.
|
|
107
|
-
user-level hook adapter bootstrap、
|
|
108
|
-
|
|
115
|
+
单独以 `5.x` 追踪。`repo-harness@0.1.4` 发布的是改名后的 CLI、Claude/Codex
|
|
116
|
+
user-level hook adapter bootstrap、AI-native scaffold overlays、typed prompt-guard
|
|
117
|
+
decision engine、plan-stem task artifact 命名、Waza runtime skill sync、
|
|
118
|
+
`diagram-design` sync,以及 maintainer 发布 npm 前使用的 release gate。
|
|
109
119
|
|
|
110
120
|
如果从源码 checkout 工作:
|
|
111
121
|
|
|
@@ -180,6 +190,23 @@ bun test
|
|
|
180
190
|
- Codex 必须在 Settings 里信任 `~/.codex/hooks.json`,hooks 才会执行。
|
|
181
191
|
- 调试顺序:user-level adapter config -> `repo-harness-hook` 或 fallback `repo-harness hook` -> route registry -> `.ai/hooks/*`。
|
|
182
192
|
|
|
193
|
+
Prompt guard 多一个内部步骤:
|
|
194
|
+
|
|
195
|
+
```mermaid
|
|
196
|
+
flowchart LR
|
|
197
|
+
Host["Claude/Codex UserPromptSubmit"] --> Adapter["user-level adapter"]
|
|
198
|
+
Adapter --> CLI["repo-harness-hook UserPromptSubmit --route default"]
|
|
199
|
+
CLI --> Route["route registry"]
|
|
200
|
+
Route --> Shell[".ai/hooks/prompt-guard.sh"]
|
|
201
|
+
Shell --> Decision["repo-harness-hook prompt-guard-decide<br/>TypeScript decision table"]
|
|
202
|
+
Decision --> Action["single action enum"]
|
|
203
|
+
Action --> Shell
|
|
204
|
+
Shell --> HostOutput["host-safe allow, advice, block, or done gate output"]
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Shell 层仍然拥有文件系统 authority 和副作用。TypeScript 只拥有 classifier 加
|
|
208
|
+
`intent x plan state` decision table。
|
|
209
|
+
|
|
183
210
|
## Hook Failure Playbook
|
|
184
211
|
|
|
185
212
|
hook block 工作时,先看 terminal 里的结构化输出。核心字段是
|
|
@@ -208,7 +235,7 @@ hook block 工作时,先看 terminal 里的结构化输出。核心字段是
|
|
|
208
235
|
|
|
209
236
|
## 当前 Release
|
|
210
237
|
|
|
211
|
-
- npm package:`repo-harness@0.1.
|
|
238
|
+
- npm package:`repo-harness@0.1.4`
|
|
212
239
|
- Generated workflow compatibility:`5.2.3`
|
|
213
240
|
- GitHub repository:`Ancienttwo/repo-harness`
|
|
214
241
|
- Release history:[`docs/CHANGELOG.md`](docs/CHANGELOG.md)
|
package/SKILL.md
CHANGED
|
@@ -138,6 +138,28 @@ Custom Presets (G-K):
|
|
|
138
138
|
- Plan J: AI coding agent / TUI
|
|
139
139
|
- Plan K: Fully custom configuration
|
|
140
140
|
|
|
141
|
+
## AI-native scaffold overlay
|
|
142
|
+
|
|
143
|
+
`repo-harness-scaffold` keeps A-K as the project-type catalog and uses
|
|
144
|
+
`ai_native_profile` as a separate overlay axis. The default profile is `none`,
|
|
145
|
+
so existing generated output stays on the selected A-K plan. Use an overlay only
|
|
146
|
+
when the generated app needs agent runtime, UI protocol, tool, sidecar, state,
|
|
147
|
+
or observability boundaries.
|
|
148
|
+
|
|
149
|
+
Supported profile IDs live in `assets/initializer-question-pack.v4.json`.
|
|
150
|
+
Generated structure overlays currently exist for:
|
|
151
|
+
|
|
152
|
+
- `runtime-console`: Vite 8 + assistant-ui, AG-UI event transport, Bun/Hono
|
|
153
|
+
agent gateway, run store, contracts, approvals, artifacts, and telemetry
|
|
154
|
+
- `product-copilot`: in-product copilot panel, AG-UI app-domain events, product
|
|
155
|
+
context loaders, business action tools, authorization and approval policies
|
|
156
|
+
- `sidecar-kernel`: Bun/Hono app gateway with Python, Go, or Rust kernels behind
|
|
157
|
+
MCP tools or narrow HTTP jobs
|
|
158
|
+
|
|
159
|
+
Do not turn the AI-native overlay into another lettered plan. Do not make A2UI,
|
|
160
|
+
Python, Go, Rust, Redis, ClickHouse, Temporal, object storage, vector DBs, model
|
|
161
|
+
providers, or tracing vendors mandatory defaults.
|
|
162
|
+
|
|
141
163
|
## Migration Rules
|
|
142
164
|
|
|
143
165
|
For legacy repos, migrate old document surfaces before refreshing templates.
|
|
@@ -275,7 +275,7 @@ get_todo_source_plan() {
|
|
|
275
275
|
awk -F': ' '/^\> \*\*Source Plan\*\*:/ {print $2; exit}' tasks/todo.md | xargs
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
|
|
278
|
+
workflow_plan_slug_from_path() {
|
|
279
279
|
local plan_file="$1"
|
|
280
280
|
local base slug
|
|
281
281
|
|
|
@@ -286,7 +286,43 @@ derive_contract_path() {
|
|
|
286
286
|
return 1
|
|
287
287
|
fi
|
|
288
288
|
|
|
289
|
-
printf '
|
|
289
|
+
printf '%s' "$slug"
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
workflow_plan_artifact_stem_from_path() {
|
|
293
|
+
local plan_file="$1"
|
|
294
|
+
local base stem
|
|
295
|
+
|
|
296
|
+
base="$(basename "$plan_file")"
|
|
297
|
+
stem="$(printf '%s' "$base" | sed -E 's/^plan-//; s/\.md$//')"
|
|
298
|
+
if [[ "$stem" =~ ^[0-9]{8}-[0-9]{4}-.+ ]]; then
|
|
299
|
+
printf '%s' "$stem"
|
|
300
|
+
return 0
|
|
301
|
+
fi
|
|
302
|
+
|
|
303
|
+
workflow_plan_slug_from_path "$plan_file"
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
workflow_preferred_or_legacy_path() {
|
|
307
|
+
local preferred="$1"
|
|
308
|
+
local legacy="$2"
|
|
309
|
+
|
|
310
|
+
if [[ -f "$preferred" ]] || [[ ! -f "$legacy" ]]; then
|
|
311
|
+
printf '%s' "$preferred"
|
|
312
|
+
else
|
|
313
|
+
printf '%s' "$legacy"
|
|
314
|
+
fi
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
derive_contract_path() {
|
|
318
|
+
local plan_file="$1"
|
|
319
|
+
local stem slug
|
|
320
|
+
|
|
321
|
+
stem="$(workflow_plan_artifact_stem_from_path "$plan_file" || true)"
|
|
322
|
+
slug="$(workflow_plan_slug_from_path "$plan_file" || true)"
|
|
323
|
+
[[ -n "$stem" && -n "$slug" ]] || return 1
|
|
324
|
+
|
|
325
|
+
workflow_preferred_or_legacy_path "tasks/contracts/${stem}.contract.md" "tasks/contracts/${slug}.contract.md"
|
|
290
326
|
}
|
|
291
327
|
|
|
292
328
|
workflow_plan_slug() {
|
|
@@ -296,7 +332,7 @@ workflow_plan_slug() {
|
|
|
296
332
|
return 1
|
|
297
333
|
fi
|
|
298
334
|
|
|
299
|
-
slug="$(
|
|
335
|
+
slug="$(workflow_plan_slug_from_path "$active_plan" || true)"
|
|
300
336
|
if [[ -n "$slug" ]]; then
|
|
301
337
|
printf '%s' "$slug"
|
|
302
338
|
return 0
|
|
@@ -930,7 +966,7 @@ workflow_contract_slug() {
|
|
|
930
966
|
local active_plan slug
|
|
931
967
|
active_plan="$(get_active_plan || true)"
|
|
932
968
|
[[ -n "$active_plan" ]] || return 1
|
|
933
|
-
slug="$(
|
|
969
|
+
slug="$(workflow_plan_slug_from_path "$active_plan" || true)"
|
|
934
970
|
[[ -n "$slug" ]] || return 1
|
|
935
971
|
printf '%s' "$slug"
|
|
936
972
|
}
|
|
@@ -945,18 +981,25 @@ workflow_active_contract() {
|
|
|
945
981
|
}
|
|
946
982
|
|
|
947
983
|
workflow_active_review() {
|
|
948
|
-
local slug
|
|
949
|
-
|
|
950
|
-
[[ -n "$
|
|
951
|
-
|
|
984
|
+
local active_plan stem slug reviews_dir
|
|
985
|
+
active_plan="$(get_active_plan || true)"
|
|
986
|
+
[[ -n "$active_plan" ]] || return 1
|
|
987
|
+
stem="$(workflow_plan_artifact_stem_from_path "$active_plan" || true)"
|
|
988
|
+
slug="$(workflow_plan_slug_from_path "$active_plan" || true)"
|
|
989
|
+
[[ -n "$stem" && -n "$slug" ]] || return 1
|
|
990
|
+
reviews_dir="$(workflow_repo_relative_path "$(workflow_policy_get '.tasks.reviews_dir' 'tasks/reviews')" 'tasks/reviews' 'tasks/')"
|
|
991
|
+
workflow_preferred_or_legacy_path "${reviews_dir}/${stem}.review.md" "${reviews_dir}/${slug}.review.md"
|
|
952
992
|
}
|
|
953
993
|
|
|
954
994
|
workflow_active_notes() {
|
|
955
|
-
local slug notes_dir
|
|
956
|
-
|
|
957
|
-
[[ -n "$
|
|
995
|
+
local active_plan stem slug notes_dir
|
|
996
|
+
active_plan="$(get_active_plan || true)"
|
|
997
|
+
[[ -n "$active_plan" ]] || return 1
|
|
998
|
+
stem="$(workflow_plan_artifact_stem_from_path "$active_plan" || true)"
|
|
999
|
+
slug="$(workflow_plan_slug_from_path "$active_plan" || true)"
|
|
1000
|
+
[[ -n "$stem" && -n "$slug" ]] || return 1
|
|
958
1001
|
notes_dir="$(workflow_repo_relative_path "$(workflow_policy_get '.tasks.notes_dir' 'tasks/notes')" 'tasks/notes' 'tasks/')"
|
|
959
|
-
|
|
1002
|
+
workflow_preferred_or_legacy_path "${notes_dir}/${stem}.notes.md" "${notes_dir}/${slug}.notes.md"
|
|
960
1003
|
}
|
|
961
1004
|
|
|
962
1005
|
workflow_checks_file() {
|
|
@@ -10,12 +10,44 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
10
10
|
# shellcheck source=/dev/null
|
|
11
11
|
. "$SCRIPT_DIR/lib/workflow-state.sh"
|
|
12
12
|
|
|
13
|
+
post_bash_set_tool_output_from_stdin() {
|
|
14
|
+
local parsed tmp
|
|
15
|
+
|
|
16
|
+
hook_read_stdin_once
|
|
17
|
+
[[ -n "${HOOK_STDIN_JSON:-}" ]] || return 1
|
|
18
|
+
|
|
19
|
+
if command -v jq >/dev/null 2>&1; then
|
|
20
|
+
if printf '%s' "$HOOK_STDIN_JSON" | jq -e 'has("tool_output") and .tool_output != null' >/dev/null 2>&1; then
|
|
21
|
+
IFS= read -r -d '' parsed < <(printf '%s' "$HOOK_STDIN_JSON" | jq -j '.tool_output' 2>/dev/null; printf '\0')
|
|
22
|
+
TOOL_OUTPUT="$parsed"
|
|
23
|
+
return 0
|
|
24
|
+
fi
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
command -v bun >/dev/null 2>&1 || return 1
|
|
28
|
+
tmp="$(mktemp "${TMPDIR:-/tmp}/post-bash-tool-output.XXXXXX")" || return 1
|
|
29
|
+
if JSON_INPUT="$HOOK_STDIN_JSON" bun -e '
|
|
30
|
+
const raw = process.env.JSON_INPUT ?? "";
|
|
31
|
+
const value = JSON.parse(raw).tool_output;
|
|
32
|
+
if (value == null) process.exit(1);
|
|
33
|
+
if (typeof value === "object") process.stdout.write(JSON.stringify(value));
|
|
34
|
+
else process.stdout.write(String(value));
|
|
35
|
+
' > "$tmp" 2>/dev/null; then
|
|
36
|
+
IFS= read -r -d '' parsed < <(cat "$tmp"; printf '\0')
|
|
37
|
+
TOOL_OUTPUT="$parsed"
|
|
38
|
+
rm -f "$tmp"
|
|
39
|
+
return 0
|
|
40
|
+
fi
|
|
41
|
+
rm -f "$tmp"
|
|
42
|
+
return 1
|
|
43
|
+
}
|
|
44
|
+
|
|
13
45
|
TOOL_OUTPUT="${1:-${TOOL_OUTPUT:-}}"
|
|
14
46
|
EXIT_CODE="${2:-${EXIT_CODE:-}}"
|
|
15
47
|
COMMAND_TEXT="$(hook_json_get '.tool_input.command' '')"
|
|
16
48
|
|
|
17
49
|
if [[ -z "$TOOL_OUTPUT" ]]; then
|
|
18
|
-
TOOL_OUTPUT="$(hook_json_get '.tool_output' '')"
|
|
50
|
+
post_bash_set_tool_output_from_stdin || TOOL_OUTPUT="$(hook_json_get '.tool_output' '')"
|
|
19
51
|
fi
|
|
20
52
|
if [[ -z "$EXIT_CODE" ]]; then
|
|
21
53
|
EXIT_CODE="$(hook_json_get '.exit_code' '')"
|
|
@@ -30,6 +62,35 @@ post_bash_output_line_count() {
|
|
|
30
62
|
printf '%s' "$output" | awk 'END { print NR }'
|
|
31
63
|
}
|
|
32
64
|
|
|
65
|
+
post_bash_output_byte_count() {
|
|
66
|
+
local output="$1"
|
|
67
|
+
printf '%s' "$output" | wc -c | tr -d '[:space:]'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
post_bash_sha256() {
|
|
71
|
+
local output="$1"
|
|
72
|
+
if command -v shasum >/dev/null 2>&1; then
|
|
73
|
+
printf '%s' "$output" | shasum -a 256 | awk '{ print $1 }'
|
|
74
|
+
return
|
|
75
|
+
fi
|
|
76
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
77
|
+
printf '%s' "$output" | sha256sum | awk '{ print $1 }'
|
|
78
|
+
return
|
|
79
|
+
fi
|
|
80
|
+
printf ''
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
post_bash_failure_signal() {
|
|
84
|
+
local output="$1"
|
|
85
|
+
[[ -n "$output" ]] || return 1
|
|
86
|
+
printf '%s\n' "$output" | grep -qEi "(^|[[:space:]])(FAIL|FAILED|failed)([[:space:]:,]|$)|Traceback|panic:|fatal:|error.*test"
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
post_bash_exit_failed() {
|
|
90
|
+
local exit_code="$1"
|
|
91
|
+
[[ -n "$exit_code" && "$exit_code" != "0" ]]
|
|
92
|
+
}
|
|
93
|
+
|
|
33
94
|
post_bash_broad_command() {
|
|
34
95
|
local command_text="$1"
|
|
35
96
|
local trimmed
|
|
@@ -63,9 +124,45 @@ if post_bash_broad_command "$COMMAND_TEXT"; then
|
|
|
63
124
|
recommended_next_tool="codegraph_context"
|
|
64
125
|
fi
|
|
65
126
|
output_line_count="$(post_bash_output_line_count "$TOOL_OUTPUT")"
|
|
127
|
+
raw_output_bytes="$(post_bash_output_byte_count "$TOOL_OUTPUT")"
|
|
128
|
+
failure_signal=false
|
|
129
|
+
if post_bash_failure_signal "$TOOL_OUTPUT"; then
|
|
130
|
+
failure_signal=true
|
|
131
|
+
fi
|
|
132
|
+
rtk_available=false
|
|
133
|
+
if command -v rtk >/dev/null 2>&1; then
|
|
134
|
+
rtk_available=true
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
LONG_OUTPUT_LINES=200
|
|
138
|
+
LONG_OUTPUT_BYTES=32768
|
|
139
|
+
verbosity_class="inline"
|
|
140
|
+
suggested_runner="inline"
|
|
141
|
+
raw_output_path=""
|
|
142
|
+
raw_output_sha256=""
|
|
143
|
+
|
|
144
|
+
if post_bash_exit_failed "$EXIT_CODE"; then
|
|
145
|
+
verbosity_class="failure"
|
|
146
|
+
suggested_runner="raw"
|
|
147
|
+
elif (( output_line_count >= LONG_OUTPUT_LINES || raw_output_bytes >= LONG_OUTPUT_BYTES )); then
|
|
148
|
+
verbosity_class="long"
|
|
149
|
+
if [[ "$broad_command" == "true" && "$rtk_available" == "true" ]]; then
|
|
150
|
+
suggested_runner="rtk"
|
|
151
|
+
else
|
|
152
|
+
suggested_runner="raw"
|
|
153
|
+
fi
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
if [[ "$verbosity_class" != "inline" ]]; then
|
|
157
|
+
output_dir="$(workflow_runs_dir)/bash-output"
|
|
158
|
+
mkdir -p "$output_dir"
|
|
159
|
+
raw_output_sha256="$(post_bash_sha256 "$TOOL_OUTPUT")"
|
|
160
|
+
raw_output_path="${output_dir}/post-bash-$(date '+%Y%m%dT%H%M%S')-$$-${raw_output_sha256:0:12}.log"
|
|
161
|
+
printf '%s' "$TOOL_OUTPUT" > "$raw_output_path"
|
|
162
|
+
fi
|
|
66
163
|
|
|
67
164
|
if [[ "$EXIT_CODE" != "0" ]]; then
|
|
68
|
-
if
|
|
165
|
+
if [[ "$failure_signal" == "true" ]]; then
|
|
69
166
|
echo "[PostBash] Tests failed. Reminder: failure = rewrite module, not patching."
|
|
70
167
|
fi
|
|
71
168
|
fi
|
|
@@ -78,6 +175,16 @@ if [[ -f "$checks_file" ]] && grep -Eq '"source"[[:space:]]*:[[:space:]]*"verify
|
|
|
78
175
|
fi
|
|
79
176
|
|
|
80
177
|
mkdir -p "$(dirname "$target_checks_file")"
|
|
178
|
+
if [[ -n "$raw_output_path" ]]; then
|
|
179
|
+
raw_output_path_json="\"$(hook_json_escape "$raw_output_path")\""
|
|
180
|
+
else
|
|
181
|
+
raw_output_path_json="null"
|
|
182
|
+
fi
|
|
183
|
+
if [[ -n "$raw_output_sha256" ]]; then
|
|
184
|
+
raw_output_sha256_json="\"$(hook_json_escape "$raw_output_sha256")\""
|
|
185
|
+
else
|
|
186
|
+
raw_output_sha256_json="null"
|
|
187
|
+
fi
|
|
81
188
|
cat > "$target_checks_file" <<EOF_CHECKS
|
|
82
189
|
{
|
|
83
190
|
"source": "post-bash",
|
|
@@ -86,6 +193,13 @@ cat > "$target_checks_file" <<EOF_CHECKS
|
|
|
86
193
|
"status": "$([[ "${EXIT_CODE:-0}" = "0" ]] && echo pass || echo fail)",
|
|
87
194
|
"broad_command": ${broad_command},
|
|
88
195
|
"output_line_count": ${output_line_count:-0},
|
|
196
|
+
"verbosity_class": "$(hook_json_escape "$verbosity_class")",
|
|
197
|
+
"suggested_runner": "$(hook_json_escape "$suggested_runner")",
|
|
198
|
+
"raw_output_path": ${raw_output_path_json},
|
|
199
|
+
"raw_output_bytes": ${raw_output_bytes:-0},
|
|
200
|
+
"raw_output_sha256": ${raw_output_sha256_json},
|
|
201
|
+
"failure_signal": ${failure_signal},
|
|
202
|
+
"rtk_available": ${rtk_available},
|
|
89
203
|
"recommended_next_tool": "$(hook_json_escape "$recommended_next_tool")",
|
|
90
204
|
"generated_at": "$(date '+%Y-%m-%dT%H:%M:%S%z')"
|
|
91
205
|
}
|