@lifeaitools/rdc-skills 0.9.32 → 0.9.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +25 -2
- package/MANIFEST.md +4 -0
- package/commands/build.md +2 -2
- package/commands/fixit.md +14 -2
- package/commands/workitems.md +6 -1
- package/guides/agent-bootstrap.md +71 -8
- package/guides/agents/backend.md +2 -2
- package/guides/agents/data.md +1 -1
- package/guides/agents/frontend.md +1 -1
- package/guides/agents/setup.md +4 -2
- package/guides/engineering-behavior.md +43 -0
- package/hooks/foreground-process-gate.js +109 -0
- package/hooks/hook-logger.js +25 -0
- package/hooks/run-hidden-hook.ps1 +47 -0
- package/hooks/work-item-exit-gate.js +297 -0
- package/package.json +3 -3
- package/rules/work-items-rpc.md +56 -7
- package/scripts/install-rdc-skills.js +75 -9
- package/scripts/install.ps1 +17 -11
- package/scripts/self-test.mjs +14 -0
- package/skills/build/SKILL.md +13 -9
- package/skills/co-develop/SKILL.md +182 -0
- package/skills/collab/SKILL.md +1 -1
- package/skills/deploy/SKILL.md +1 -1
- package/skills/design/SKILL.md +3 -3
- package/skills/fixit/SKILL.md +19 -5
- package/skills/fs-mcp/SKILL.md +131 -0
- package/skills/handoff/SKILL.md +1 -1
- package/skills/help/SKILL.md +4 -2
- package/skills/overnight/SKILL.md +3 -3
- package/skills/plan/SKILL.md +4 -3
- package/skills/preplan/SKILL.md +1 -1
- package/skills/prototype/SKILL.md +1 -1
- package/skills/release/SKILL.md +2 -2
- package/skills/report/SKILL.md +1 -1
- package/skills/review/SKILL.md +5 -3
- package/skills/self-test/SKILL.md +1 -1
- package/skills/status/SKILL.md +3 -1
- package/skills/watch/SKILL.md +1 -1
- package/skills/workitems/SKILL.md +7 -2
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rdc",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.34",
|
|
4
4
|
"description": "RDC typed-agent dispatch skill suite for Claude Code — plan, build, review, overnight unattended builds with work-item tracking and TDD enforcement.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "LIFEAI",
|
|
7
7
|
"email": "support@lifeai.tools"
|
|
8
8
|
},
|
|
9
9
|
"homepage": "https://github.com/LIFEAI/rdc-skills",
|
|
10
|
-
"repository": "https://github.com/LIFEAI/rdc-skills"
|
|
10
|
+
"repository": "https://github.com/LIFEAI/rdc-skills",
|
|
11
|
+
"skills": [
|
|
12
|
+
"build",
|
|
13
|
+
"co-develop",
|
|
14
|
+
"collab",
|
|
15
|
+
"deploy",
|
|
16
|
+
"design",
|
|
17
|
+
"fixit",
|
|
18
|
+
"fs-mcp",
|
|
19
|
+
"handoff",
|
|
20
|
+
"help",
|
|
21
|
+
"overnight",
|
|
22
|
+
"plan",
|
|
23
|
+
"preplan",
|
|
24
|
+
"prototype",
|
|
25
|
+
"release",
|
|
26
|
+
"report",
|
|
27
|
+
"review",
|
|
28
|
+
"self-test",
|
|
29
|
+
"status",
|
|
30
|
+
"terminal-config",
|
|
31
|
+
"watch",
|
|
32
|
+
"workitems"
|
|
33
|
+
]
|
|
11
34
|
}
|
package/MANIFEST.md
CHANGED
|
@@ -47,6 +47,7 @@ All skills have been converted from project-specific to generic/portable:
|
|
|
47
47
|
├── fixit/SKILL.md
|
|
48
48
|
├── status/SKILL.md
|
|
49
49
|
├── report/SKILL.md
|
|
50
|
+
├── co-develop/SKILL.md [peer-aware Claude/Codex co-development]
|
|
50
51
|
├── collab/SKILL.md [claude.ai ↔ Claude Code relay]
|
|
51
52
|
├── handoff/SKILL.md
|
|
52
53
|
├── prototype/SKILL.md
|
|
@@ -92,6 +93,9 @@ All skills have been converted from project-specific to generic/portable:
|
|
|
92
93
|
### Quality (1)
|
|
93
94
|
- rdc:self-test — static lint (tier1) + smoke tests (tier2)
|
|
94
95
|
|
|
96
|
+
### Collaboration (1)
|
|
97
|
+
- rdc:co-develop — peer-aware Claude/Codex co-development over clauth
|
|
98
|
+
|
|
95
99
|
### Agent Guides (10, in `guides/agents/`) — [agent-only — dispatched by rdc:build, not user-invocable]
|
|
96
100
|
- frontend — React, UI, Tailwind
|
|
97
101
|
- backend — API, database, auth
|
package/commands/build.md
CHANGED
|
@@ -132,7 +132,7 @@ Read the task title and description, then:
|
|
|
132
132
|
9. **As agents complete:**
|
|
133
133
|
- Verify commit landed on the development branch
|
|
134
134
|
- Push to origin *(skip if `$RDC_TEST=1` — echo `[RDC_TEST] skipping git push` instead)*
|
|
135
|
-
-
|
|
135
|
+
- Ensure the agent submitted `implementation_report.codeflow_post`, then set the work item to `review`; the validator closes `done`
|
|
136
136
|
- Continue to next wave
|
|
137
137
|
|
|
138
138
|
**If an agent fails:**
|
|
@@ -142,7 +142,7 @@ Read the task title and description, then:
|
|
|
142
142
|
BUILD_STATUS: { wave, tasks_done, tasks_failed, commits, escalated: true }
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
-
10. **Final verification gate (mandatory — before marking epic done):**
|
|
145
|
+
10. **Final verification gate (mandatory — before marking work or epic done):**
|
|
146
146
|
Dispatch the verify agent (see `guides/agents/verify.md`) across every package/app touched in this build.
|
|
147
147
|
The Iron Law: **NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE.**
|
|
148
148
|
- Run `npx vitest run --dir <pkg>` fresh for each touched package
|
package/commands/fixit.md
CHANGED
|
@@ -87,11 +87,23 @@ else
|
|
|
87
87
|
fi
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
### 6.
|
|
90
|
+
### 6. Report, review, close, and clean up
|
|
91
91
|
|
|
92
92
|
```sql
|
|
93
|
+
SELECT submit_implementation_report('<id>'::uuid,
|
|
94
|
+
'{"tldr":"<one sentence>","assumptions":[],"deviations":[],"uncertainty":[],"detail":"<what was fixed>","flags":[],"transactional":false,"memory_records":[],"codeflow_post":{"agent_session_id":"<agent-session-id>","summary":"<what changed and why>","files_changed":["<path>"],"verification":["<command/evidence>"],"commit":"<hash optional>"}}'::jsonb
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
SELECT update_work_item_status('<id>'::uuid, 'review',
|
|
98
|
+
'["Fixed via rdc:fixit; ready for validation"]'::jsonb,
|
|
99
|
+
'<agent-session-id>',
|
|
100
|
+
'agent'
|
|
101
|
+
);
|
|
102
|
+
|
|
93
103
|
SELECT update_work_item_status('<id>'::uuid, 'done',
|
|
94
|
-
'["
|
|
104
|
+
'["Validator verified rdc:fixit report, CodeFlow post, and checklist evidence"]'::jsonb,
|
|
105
|
+
'<validator-session-id>',
|
|
106
|
+
'validator'
|
|
95
107
|
);
|
|
96
108
|
```
|
|
97
109
|
|
package/commands/workitems.md
CHANGED
|
@@ -66,10 +66,15 @@ Est: <hours>',
|
|
|
66
66
|
|
|
67
67
|
```sql
|
|
68
68
|
SELECT update_work_item_status('<uuid>'::uuid, 'in_progress');
|
|
69
|
-
SELECT update_work_item_status('<uuid>'::uuid, '
|
|
69
|
+
SELECT update_work_item_status('<uuid>'::uuid, 'review', '["Implementation complete; ready for validator"]'::jsonb, '<agent-session-id>', 'agent');
|
|
70
|
+
SELECT update_work_item_status('<uuid>'::uuid, 'done', '["Validator verified implementation report, CodeFlow post, and checklist evidence"]'::jsonb, '<validator-session-id>', 'validator');
|
|
70
71
|
SELECT update_work_item_status('<uuid>'::uuid, 'blocked', '["Why it is blocked"]'::jsonb);
|
|
71
72
|
```
|
|
72
73
|
|
|
74
|
+
Non-epic `done` is a validator-only close and requires an existing
|
|
75
|
+
`implementation_report.codeflow_post`, checked required checklist evidence, and
|
|
76
|
+
originating-agent tick audit.
|
|
77
|
+
|
|
73
78
|
## Read Tasks in an Epic
|
|
74
79
|
|
|
75
80
|
```sql
|
|
@@ -10,6 +10,10 @@ You are a subagent dispatched by the rdc:build supervisor. You have a specific
|
|
|
10
10
|
scope (files, package, feature) that will be in your prompt. Stay in that scope.
|
|
11
11
|
NEVER modify files outside it.
|
|
12
12
|
|
|
13
|
+
Read `guides/engineering-behavior.md` next when it is available. It defines the
|
|
14
|
+
RDC implementation posture for assumptions, minimal changes, surgical scope,
|
|
15
|
+
verification evidence, and escalation.
|
|
16
|
+
|
|
13
17
|
---
|
|
14
18
|
|
|
15
19
|
## Credentials — Daemon Access Pattern
|
|
@@ -86,6 +90,18 @@ Never run `pnpm build` or equivalent full builds locally — they consume excess
|
|
|
86
90
|
Type-check only: `npx tsc --noEmit --project <path>/tsconfig.json`
|
|
87
91
|
Run tests only for modified packages: modify tests in isolation, not whole suite.
|
|
88
92
|
|
|
93
|
+
### No Foreground Windows
|
|
94
|
+
|
|
95
|
+
Agent-launched processes must not steal focus. This is a hard local-operator
|
|
96
|
+
rule, not a preference.
|
|
97
|
+
|
|
98
|
+
- Playwright must run headless. Do not use `--headed`, `--ui`, `codegen`, `open`, `show-report`, or `PWDEBUG=1` in agent sessions.
|
|
99
|
+
- Use list/dot/json reporters and saved trace/report artifacts instead of opening the Playwright UI.
|
|
100
|
+
- PowerShell helpers must use `-WindowStyle Hidden -NonInteractive`, or a hidden wrapper.
|
|
101
|
+
- `Start-Process` must include `-WindowStyle Hidden` or `-WindowStyle Minimized`.
|
|
102
|
+
- `cmd /c start` must use `/min` for intentionally visible tools or `/b` for background work.
|
|
103
|
+
- Node/cmd/ps1 helpers launched by hooks must go through the RDC hidden hook runner.
|
|
104
|
+
|
|
89
105
|
Check the project overlay for specific language, package manager, and build constraints.
|
|
90
106
|
|
|
91
107
|
---
|
|
@@ -183,36 +199,83 @@ If you discover that fixing your assigned task would also require changing files
|
|
|
183
199
|
|
|
184
200
|
---
|
|
185
201
|
|
|
186
|
-
## ⛔ Implementation Report Contract
|
|
202
|
+
## ⛔ Implementation Report + CodeFlow Exit Contract
|
|
187
203
|
|
|
188
|
-
Every agent MUST follow this protocol before
|
|
204
|
+
Every implementation agent MUST follow this protocol before moving a work item
|
|
205
|
+
to `review`. Agents do not close non-epic work as `done`; validators close it
|
|
206
|
+
after fresh verification.
|
|
189
207
|
|
|
190
208
|
### Step 1 — Tick checklist items as you complete them
|
|
191
209
|
|
|
192
210
|
```sql
|
|
193
|
-
SELECT update_checklist_item(
|
|
211
|
+
SELECT update_checklist_item(
|
|
212
|
+
'<work-item-id>'::uuid,
|
|
213
|
+
'item-id',
|
|
214
|
+
true,
|
|
215
|
+
'<agent-session-id>',
|
|
216
|
+
'agent',
|
|
217
|
+
'rpc'
|
|
218
|
+
);
|
|
194
219
|
```
|
|
195
220
|
|
|
196
|
-
Call this for each item AS you complete it — not all at once at the end.
|
|
221
|
+
Call this for each item AS you complete it — not all at once at the end. The
|
|
222
|
+
database records every tick in `work_item_checklist_events`. Supervisor and
|
|
223
|
+
validator re-ticks are rejected by the exit gate.
|
|
197
224
|
|
|
198
225
|
### Step 2 — Submit implementation report BEFORE marking done
|
|
199
226
|
|
|
200
227
|
```sql
|
|
201
228
|
SELECT submit_implementation_report(
|
|
202
229
|
'<work-item-id>'::uuid,
|
|
203
|
-
'{
|
|
230
|
+
'{
|
|
231
|
+
"tldr":"...",
|
|
232
|
+
"assumptions":[],
|
|
233
|
+
"deviations":[],
|
|
234
|
+
"uncertainty":[],
|
|
235
|
+
"detail":"...",
|
|
236
|
+
"flags":[],
|
|
237
|
+
"codeflow_post":{
|
|
238
|
+
"agent_session_id":"<agent-session-id>",
|
|
239
|
+
"summary":"...",
|
|
240
|
+
"files_changed":["path/to/file"],
|
|
241
|
+
"verification":["command or evidence"],
|
|
242
|
+
"commit":"<optional commit hash>"
|
|
243
|
+
}
|
|
244
|
+
}'::jsonb
|
|
204
245
|
);
|
|
205
246
|
```
|
|
206
247
|
|
|
207
248
|
Returns `{ flags_count, deviations_count, has_deviations }`. Include this signal in your `AGENT_COMPLETE` report.
|
|
208
249
|
|
|
209
|
-
### Step 3 —
|
|
250
|
+
### Step 3 — Move to review
|
|
210
251
|
|
|
211
252
|
```sql
|
|
212
|
-
SELECT update_work_item_status(
|
|
253
|
+
SELECT update_work_item_status(
|
|
254
|
+
'<work-item-id>'::uuid,
|
|
255
|
+
'review',
|
|
256
|
+
'["Implementation complete; ready for validator"]'::jsonb,
|
|
257
|
+
'<agent-session-id>',
|
|
258
|
+
'agent'
|
|
259
|
+
);
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
If the report or `codeflow_post` is missing, the database raises EXCEPTION.
|
|
263
|
+
|
|
264
|
+
### Validator close — mark done only after fresh review
|
|
265
|
+
|
|
266
|
+
```sql
|
|
267
|
+
SELECT update_work_item_status(
|
|
268
|
+
'<work-item-id>'::uuid,
|
|
269
|
+
'done',
|
|
270
|
+
'["Validator verified implementation report, CodeFlow post, and checklist evidence"]'::jsonb,
|
|
271
|
+
'<validator-session-id>',
|
|
272
|
+
'validator'
|
|
273
|
+
);
|
|
213
274
|
```
|
|
214
275
|
|
|
215
|
-
If any `required: true` checklist item is still unchecked,
|
|
276
|
+
If any `required: true` checklist item is still unchecked, was re-ticked by a
|
|
277
|
+
supervisor/validator, or was ticked by a different session than the originating
|
|
278
|
+
agent, the DB and PreToolUse hook reject the close.
|
|
216
279
|
|
|
217
280
|
### Supervisor workflow
|
|
218
281
|
|
package/guides/agents/backend.md
CHANGED
|
@@ -57,8 +57,8 @@ SELECT insert_work_item(
|
|
|
57
57
|
p_source := 'agent'
|
|
58
58
|
);
|
|
59
59
|
|
|
60
|
-
--
|
|
61
|
-
SELECT update_work_item_status('<uuid>'::uuid, '
|
|
60
|
+
-- Implementation agents submit report + CodeFlow post, then move to review
|
|
61
|
+
SELECT update_work_item_status('<uuid>'::uuid, 'review', '["Implementation complete; ready for validator"]'::jsonb, '<agent-session-id>', 'agent');
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
**NEVER write raw INSERT/UPDATE against work items.**
|
package/guides/agents/data.md
CHANGED
|
@@ -37,7 +37,7 @@ After `apply_migration`: always verify with queries.
|
|
|
37
37
|
```sql
|
|
38
38
|
SELECT get_open_epics();
|
|
39
39
|
SELECT insert_work_item(p_title := 'Task', p_priority := 'high', p_labels := ARRAY['data'], p_source := 'agent');
|
|
40
|
-
SELECT update_work_item_status('<uuid>'::uuid, '
|
|
40
|
+
SELECT update_work_item_status('<uuid>'::uuid, 'review', '["Implementation complete; ready for validator"]'::jsonb, '<agent-session-id>', 'agent');
|
|
41
41
|
SELECT get_work_items_by_epic('<epic-uuid>'::uuid);
|
|
42
42
|
```
|
|
43
43
|
|
|
@@ -88,5 +88,5 @@ Never hardcode hex values in components. Always use CSS variables from the app's
|
|
|
88
88
|
- NEVER overlap with other agents on the same files
|
|
89
89
|
- NEVER modify files outside your assigned scope
|
|
90
90
|
- After completing: commit with descriptive message, push *(skip push if `$RDC_TEST=1` — echo `[RDC_TEST] skipping git push` instead)*
|
|
91
|
-
-
|
|
91
|
+
- Submit `implementation_report.codeflow_post`, then update work item to `review` via `update_work_item_status(..., p_actor_role := 'agent')`; validators close `done`
|
|
92
92
|
- Write tests FIRST — red → implement → green
|
package/guides/agents/setup.md
CHANGED
|
@@ -142,7 +142,7 @@ Use `mcp__claude_ai_Supabase__execute_sql` — no `project_id` needed.
|
|
|
142
142
|
## Work Items
|
|
143
143
|
SELECT get_open_epics();
|
|
144
144
|
SELECT insert_work_item(p_title := '...', p_priority := 'high');
|
|
145
|
-
SELECT update_work_item_status('<id>'::uuid, '
|
|
145
|
+
SELECT update_work_item_status('<id>'::uuid, 'review', '["Implementation complete; ready for validator"]'::jsonb, '<agent-session-id>', 'agent');
|
|
146
146
|
Create work items BEFORE starting work.
|
|
147
147
|
|
|
148
148
|
## Completion Report
|
|
@@ -206,12 +206,14 @@ agents, manages git, enforces hooks, and ships to production.
|
|
|
206
206
|
|
|
207
207
|
| Capability | Tool |
|
|
208
208
|
|-----------|------|
|
|
209
|
-
| Read/write repo files | `fs_read`, `fs_write`, `fs_glob`, `fs_grep` |
|
|
209
|
+
| Read/write/import repo files | `fs_read`, `fs_write`, `fs_write_chunk`, `fs_ingest_url`, `fs_import_git_files`, `fs_glob`, `fs_grep` |
|
|
210
210
|
| Query Supabase | Supabase MCP `execute_sql` |
|
|
211
211
|
| GitHub operations | Github Proxy MCP |
|
|
212
212
|
| Credentials | clauth MCP `clauth_get` |
|
|
213
213
|
| Trigger Claude Code | `monkey_dispatch` MCP |
|
|
214
214
|
|
|
215
|
+
For filesystem decisions, load `rdc:fs-mcp`. It defines when to use direct FS writes, chunked writes, URL ingest, or GitHub-branch import into the dirty local monorepo.
|
|
216
|
+
|
|
215
217
|
## Two-Claude Relay
|
|
216
218
|
|
|
217
219
|
Two channels to communicate with Claude Code:
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Engineering Behavior
|
|
2
|
+
|
|
3
|
+
Use this with `agent-bootstrap.md` for implementation and review work. These
|
|
4
|
+
rules adapt general coding-agent hygiene into the RDC work-item contract.
|
|
5
|
+
|
|
6
|
+
## Before Editing
|
|
7
|
+
|
|
8
|
+
- State material assumptions in the work-item report; ask or block when the
|
|
9
|
+
ambiguity changes architecture, data shape, security, or user-visible scope.
|
|
10
|
+
- Prefer the smallest change that satisfies the assigned checklist rows.
|
|
11
|
+
- Do not add features, abstractions, configurability, or fallback behavior that
|
|
12
|
+
is not required by the work item.
|
|
13
|
+
- If a simpler path exists than the apparent request, report the tradeoff before
|
|
14
|
+
widening the implementation.
|
|
15
|
+
|
|
16
|
+
## While Editing
|
|
17
|
+
|
|
18
|
+
- Stay inside the assigned files, package, route, or work-item boundary.
|
|
19
|
+
- Match the local style and contracts already in the touched files.
|
|
20
|
+
- Do not reformat, rename, or refactor adjacent code unless the checklist row
|
|
21
|
+
explicitly requires it.
|
|
22
|
+
- Clean up only the unused imports, variables, files, or branches created by
|
|
23
|
+
your own change.
|
|
24
|
+
- If existing code looks dead or wrong but is outside scope, list it as a
|
|
25
|
+
blocker or follow-up. Do not remove it.
|
|
26
|
+
|
|
27
|
+
## Verification
|
|
28
|
+
|
|
29
|
+
- Every completed row needs evidence: test output, route probe, SQL result,
|
|
30
|
+
screenshot artifact, type-check output, CLI transcript, or reviewer citation.
|
|
31
|
+
- Finding an existing file is not evidence. Verify the required behavior.
|
|
32
|
+
- Tick each `decomp-*` and `test-*` checklist item immediately after proving
|
|
33
|
+
that exact behavior. Do not batch ticks at the end.
|
|
34
|
+
- Record assumptions, deviations, uncertainty, blockers, files changed, and
|
|
35
|
+
verification in `submit_implementation_report()` before moving to `review`.
|
|
36
|
+
|
|
37
|
+
## Escalation
|
|
38
|
+
|
|
39
|
+
- Stop and report `BLOCKED` when the fix requires files outside scope, a broader
|
|
40
|
+
architectural choice, a missing credential, or a second repeated failure.
|
|
41
|
+
- In unattended mode, choose the most conservative valid path only when the
|
|
42
|
+
acceptance criteria remain unchanged; otherwise escalate through the advisor
|
|
43
|
+
path required by the active skill.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* PreToolUse hook — block focus-stealing foreground process launches.
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const hookLog = require('./hook-logger');
|
|
8
|
+
|
|
9
|
+
function readStdin() {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
let input = '';
|
|
12
|
+
process.stdin.setEncoding('utf8');
|
|
13
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
14
|
+
process.stdin.on('end', () => resolve(input));
|
|
15
|
+
process.stdin.resume();
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function block(message, details = {}) {
|
|
20
|
+
hookLog('foreground-process-gate', 'PreToolUse', 'block', details);
|
|
21
|
+
process.stdout.write(JSON.stringify({
|
|
22
|
+
systemMessage: `HARD BLOCK — Foreground process launch rejected.\n\n${message}`,
|
|
23
|
+
}));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function pass(details = {}) {
|
|
28
|
+
hookLog('foreground-process-gate', 'PreToolUse', 'pass', details);
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function toolText(raw) {
|
|
33
|
+
if (typeof raw.tool_input?.command === 'string') return raw.tool_input.command;
|
|
34
|
+
if (typeof raw.tool_input === 'string') return raw.tool_input;
|
|
35
|
+
try { return JSON.stringify(raw.tool_input || raw); } catch { return ''; }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function hasHiddenIntent(command) {
|
|
39
|
+
return /-WindowStyle\s+Hidden/i.test(command) ||
|
|
40
|
+
/-WindowStyle\s+Minimized/i.test(command) ||
|
|
41
|
+
/windowsHide\s*:\s*true/i.test(command) ||
|
|
42
|
+
/CreateNoWindow\s*=\s*\$?true/i.test(command) ||
|
|
43
|
+
/Start-Job\b/i.test(command) ||
|
|
44
|
+
/--background\b/i.test(command) ||
|
|
45
|
+
/\bHEADLESS\s*=\s*(1|true)\b/i.test(command) ||
|
|
46
|
+
/\bCI\s*=\s*(1|true)\b/i.test(command);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function checkPlaywright(command) {
|
|
50
|
+
if (!/\b(playwright|@playwright\/test)\b/i.test(command)) return;
|
|
51
|
+
|
|
52
|
+
if (/\bplaywright\s+(show-report|codegen|open)\b/i.test(command)) {
|
|
53
|
+
block(
|
|
54
|
+
'Playwright report/codegen/open launches foreground UI. Use trace/report files as artifacts, or run a hidden/background smoke wrapper.',
|
|
55
|
+
{ kind: 'playwright-ui' },
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (/(^|[\s;&|])(--headed|--ui|PWDEBUG\s*=\s*1|PWDEBUG\s*=\s*true)(?=$|[\s;&|])/i.test(command)) {
|
|
60
|
+
block(
|
|
61
|
+
'Playwright must run headless in agent sessions. Remove `--headed`, `--ui`, and `PWDEBUG=1`; use `--reporter=list` or an artifact trace instead.',
|
|
62
|
+
{ kind: 'playwright-headed' },
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function checkPowerShell(command) {
|
|
68
|
+
if (!/\bStart-Process\b/i.test(command)) return;
|
|
69
|
+
if (hasHiddenIntent(command)) return;
|
|
70
|
+
block(
|
|
71
|
+
'`Start-Process` must include `-WindowStyle Hidden` or `-WindowStyle Minimized` for agent-launched node/cmd/ps1/test processes.',
|
|
72
|
+
{ kind: 'start-process' },
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function checkCmdStart(command) {
|
|
77
|
+
if (!/\bcmd(?:\.exe)?\s+\/c\s+start\b/i.test(command)) return;
|
|
78
|
+
if (/\bcmd(?:\.exe)?\s+\/c\s+start\s+(""|''|`"")?\s*\/(?:min|b)\b/i.test(command)) return;
|
|
79
|
+
block(
|
|
80
|
+
'`cmd /c start` must use `/min` for visible tools or `/b`/a hidden wrapper for background tools.',
|
|
81
|
+
{ kind: 'cmd-start' },
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function checkDirectShellLaunch(command) {
|
|
86
|
+
if (hasHiddenIntent(command)) return;
|
|
87
|
+
if (/\bpowershell(?:\.exe)?\b[^|\n]*(?:-File\s+[^|\n]*\.ps1|\.ps1\b)/i.test(command)) {
|
|
88
|
+
block(
|
|
89
|
+
'PowerShell script launches from agent tooling must use `-WindowStyle Hidden -NonInteractive` or a hidden wrapper.',
|
|
90
|
+
{ kind: 'powershell-ps1' },
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function main() {
|
|
96
|
+
let raw;
|
|
97
|
+
try { raw = JSON.parse(await readStdin()); } catch { process.exit(0); }
|
|
98
|
+
const command = toolText(raw);
|
|
99
|
+
if (!command) pass({ reason: 'no-command' });
|
|
100
|
+
|
|
101
|
+
checkPlaywright(command);
|
|
102
|
+
checkPowerShell(command);
|
|
103
|
+
checkCmdStart(command);
|
|
104
|
+
checkDirectShellLaunch(command);
|
|
105
|
+
|
|
106
|
+
pass({ reason: 'clean' });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
main().catch((e) => block(`Foreground process gate crashed: ${e.message}`));
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const logPath = path.join(os.homedir(), '.claude', 'hook-events.jsonl');
|
|
8
|
+
|
|
9
|
+
module.exports = function hookLog(hook, event, verdict, details = {}) {
|
|
10
|
+
try {
|
|
11
|
+
fs.mkdirSync(path.dirname(logPath), { recursive: true });
|
|
12
|
+
fs.appendFileSync(
|
|
13
|
+
logPath,
|
|
14
|
+
JSON.stringify({
|
|
15
|
+
ts: new Date().toISOString(),
|
|
16
|
+
hook,
|
|
17
|
+
event,
|
|
18
|
+
verdict,
|
|
19
|
+
details,
|
|
20
|
+
}) + '\n',
|
|
21
|
+
);
|
|
22
|
+
} catch (_) {
|
|
23
|
+
// Hooks should never fail just because telemetry could not be written.
|
|
24
|
+
}
|
|
25
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[Parameter(Mandatory = $true)]
|
|
3
|
+
[string]$HookScript
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
$ErrorActionPreference = "Stop"
|
|
7
|
+
|
|
8
|
+
$nodeCmd = Get-Command node.exe -ErrorAction SilentlyContinue
|
|
9
|
+
if (-not $nodeCmd) {
|
|
10
|
+
$nodeCmd = Get-Command node -ErrorAction SilentlyContinue
|
|
11
|
+
}
|
|
12
|
+
$node = if ($nodeCmd) { $nodeCmd.Source } else { $null }
|
|
13
|
+
if (-not $node) {
|
|
14
|
+
$node = if ($nodeCmd) { $nodeCmd.Path } else { $null }
|
|
15
|
+
}
|
|
16
|
+
if (-not $node) {
|
|
17
|
+
[Console]::Error.WriteLine("run-hidden-hook: node was not found on PATH")
|
|
18
|
+
exit 127
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
$stdinText = [Console]::In.ReadToEnd()
|
|
22
|
+
|
|
23
|
+
$psi = [System.Diagnostics.ProcessStartInfo]::new()
|
|
24
|
+
$psi.FileName = $node
|
|
25
|
+
$psi.UseShellExecute = $false
|
|
26
|
+
$psi.CreateNoWindow = $true
|
|
27
|
+
$psi.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
|
|
28
|
+
$psi.RedirectStandardInput = $true
|
|
29
|
+
$psi.RedirectStandardOutput = $true
|
|
30
|
+
$psi.RedirectStandardError = $true
|
|
31
|
+
$psi.Arguments = "`"$HookScript`""
|
|
32
|
+
|
|
33
|
+
$proc = [System.Diagnostics.Process]::new()
|
|
34
|
+
$proc.StartInfo = $psi
|
|
35
|
+
$null = $proc.Start()
|
|
36
|
+
|
|
37
|
+
$proc.StandardInput.Write($stdinText)
|
|
38
|
+
$proc.StandardInput.Close()
|
|
39
|
+
|
|
40
|
+
$stdout = $proc.StandardOutput.ReadToEnd()
|
|
41
|
+
$stderr = $proc.StandardError.ReadToEnd()
|
|
42
|
+
$proc.WaitForExit()
|
|
43
|
+
|
|
44
|
+
if ($stdout) { [Console]::Out.Write($stdout) }
|
|
45
|
+
if ($stderr) { [Console]::Error.Write($stderr) }
|
|
46
|
+
|
|
47
|
+
exit $proc.ExitCode
|