@ghl-ai/aw 0.1.44-beta.1 → 0.1.44-beta.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/c4/claudePluginRegistry.mjs +142 -0
- package/c4/codexConfig.mjs +148 -0
- package/c4/codexPromptInjector.mjs +187 -0
- package/c4/commandSurface.mjs +286 -0
- package/c4/detect.mjs +62 -0
- package/c4/diagnostics.mjs +536 -0
- package/c4/eccRegistryBridge.mjs +184 -0
- package/c4/ghCli.mjs +94 -0
- package/c4/gitAuth.mjs +384 -0
- package/c4/index.mjs +64 -0
- package/c4/jsonMerge.mjs +229 -0
- package/c4/mcpServer.mjs +160 -0
- package/c4/mcpSmokeProbe.mjs +201 -0
- package/c4/preflight.mjs +254 -0
- package/c4/repoLocalClaudeSettings.mjs +54 -0
- package/c4/repoLocalIgnore.mjs +157 -0
- package/c4/repoRootInstructions.mjs +166 -0
- package/c4/secrets.mjs +55 -0
- package/c4/slimRouter.mjs +472 -0
- package/cli.mjs +7 -0
- package/commands/c4.mjs +387 -0
- package/ecc.mjs +7 -72
- package/integrate.mjs +6 -6
- package/mcp.mjs +0 -1
- package/package.json +5 -3
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* c4/slimRouter.mjs — two slim-card variants + per-prompt ECC delegation
|
|
3
|
+
* + AW_SLIM_ROUTER=0 escape hatch.
|
|
4
|
+
*
|
|
5
|
+
* Why two cards: Claude Code Web invokes skills via the Skill tool (`aw:plan`,
|
|
6
|
+
* colon-separated), Cursor reads the SKILL.md file (`aw-plan`, hyphen-separated).
|
|
7
|
+
* Card text diverges only on invocation syntax, not policy. Both stay under
|
|
8
|
+
* SLIM_CARD_HARD_CEILING_BYTES (6144) so they fit in SessionStart inline-context.
|
|
9
|
+
*
|
|
10
|
+
* Why ECC delegation per-prompt: the slim card is SessionStart-only. The
|
|
11
|
+
* per-prompt reminder (UserPromptSubmit on Claude, beforeSubmitPrompt on
|
|
12
|
+
* Cursor) reuses ECC's existing scripts. Cursor's beforeSubmitPrompt is a
|
|
13
|
+
* request-rewrite hook that REQUIRES valid JSON output; using ECC's
|
|
14
|
+
* Node-adapter `before-submit-prompt.sh` is the only correct wiring (the
|
|
15
|
+
* shared text helper would emit plain text and break Cursor).
|
|
16
|
+
*
|
|
17
|
+
* Why an escape hatch: if the slim card breaks something in production,
|
|
18
|
+
* AW_SLIM_ROUTER=0 falls back to the full ECC SessionStart hook. No card,
|
|
19
|
+
* no slim-hook script — just the un-modified ECC entry-point.
|
|
20
|
+
*
|
|
21
|
+
* Contract: spec.md::§"c4/slimRouter.mjs", §"Slim card content".
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { mkdirSync, writeFileSync, chmodSync } from 'node:fs';
|
|
25
|
+
import { dirname, join } from 'node:path';
|
|
26
|
+
import { claudeHooksMerge, jsonMergeWithDedup } from './jsonMerge.mjs';
|
|
27
|
+
|
|
28
|
+
export const SLIM_CARD_HARD_CEILING_BYTES = 6144;
|
|
29
|
+
|
|
30
|
+
export const REQUIRED_ENFORCEMENT_PHRASES = [
|
|
31
|
+
'Naming a route is not invoking it.',
|
|
32
|
+
'BEFORE any other',
|
|
33
|
+
'routing miss',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
export const REQUIRED_STAGE_MARKERS_CLAUDE = [
|
|
37
|
+
'aw:review',
|
|
38
|
+
'aw:plan',
|
|
39
|
+
'aw:build',
|
|
40
|
+
'Hard Gate',
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
export const REQUIRED_STAGE_MARKERS_CURSOR = [
|
|
44
|
+
'aw-review',
|
|
45
|
+
'aw-plan',
|
|
46
|
+
'aw-build',
|
|
47
|
+
'Hard Gate',
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
51
|
+
* Card text — copied verbatim from spec.md::§"Slim card content".
|
|
52
|
+
* Trailing newline is intentional: Claude/Cursor parse the file as a single
|
|
53
|
+
* markdown document.
|
|
54
|
+
* ───────────────────────────────────────────────────────────────────────── */
|
|
55
|
+
|
|
56
|
+
export const SLIM_CARD_CLAUDE = `# AW Router (Compact) — using-aw-skills
|
|
57
|
+
|
|
58
|
+
You have the AW Agentic Workspace engine. This compact router replaces the
|
|
59
|
+
full using-aw-skills SKILL.md so it fits inside Claude Code Web's
|
|
60
|
+
SessionStart inline cap. The full skill is still available via the Skill
|
|
61
|
+
tool as \`aw:using-aw-skills\` for deep cases.
|
|
62
|
+
|
|
63
|
+
## Hard Gate (MUST — in order, before any substantive response)
|
|
64
|
+
|
|
65
|
+
1. Classify the user's intent (one line).
|
|
66
|
+
2. Pick the SMALLEST correct stage skill from the table below.
|
|
67
|
+
3. Invoke it via \`Skill(skill="aw:<stage>")\` BEFORE any other Skill, Edit,
|
|
68
|
+
Write, or Bash call that performs substantive work.
|
|
69
|
+
4. Follow that stage skill's contract — do not improvise.
|
|
70
|
+
|
|
71
|
+
Naming a route is not invoking it. Read = call the Skill tool.
|
|
72
|
+
|
|
73
|
+
## Intent → Stage Skill (tier 1/2 — invoke these first)
|
|
74
|
+
|
|
75
|
+
| Intent signal in the prompt | Stage skill |
|
|
76
|
+
|---|---|
|
|
77
|
+
| review, "review this PR", findings, governance, "is this ready" | \`aw:review\` |
|
|
78
|
+
| plan, design, refactor strategy, propose approach | \`aw:plan\` |
|
|
79
|
+
| build, implement, write code, add feature, TDD | \`aw:build\` |
|
|
80
|
+
| bug, crash, alert, regression, debug, investigate | \`aw:investigate\` |
|
|
81
|
+
| test, QA, e2e, coverage, regression suite | \`aw:test\` |
|
|
82
|
+
| deploy, release, rollout, staging | \`aw:deploy\` |
|
|
83
|
+
| ship, launch, rollback readiness, closeout | \`aw:ship\` |
|
|
84
|
+
| feature end-to-end (research → ship) | \`aw:feature\` |
|
|
85
|
+
| agent/skill/command/rule authoring | \`aw:adk\` |
|
|
86
|
+
|
|
87
|
+
## Tier-3 domain skills are delegated, not invoked directly
|
|
88
|
+
|
|
89
|
+
\`platform-core-pr-review\`, \`platform-services-development\`,
|
|
90
|
+
\`platform-frontend-*\`, \`platform-sdet-*\`, \`platform-infra-*\`, etc. — these
|
|
91
|
+
are domain transports. The stage skill (\`aw:review\`, \`aw:build\`, …) decides
|
|
92
|
+
when to call them. Invoking a tier-3 skill before its parent stage is a
|
|
93
|
+
routing miss.
|
|
94
|
+
|
|
95
|
+
## Single-reviewer fallback (review only)
|
|
96
|
+
|
|
97
|
+
You may skip \`aw:review\` and call \`platform-core-pr-review\` directly ONLY
|
|
98
|
+
if ALL of the following hold:
|
|
99
|
+
|
|
100
|
+
- the diff is < 50 lines AND touches a single file
|
|
101
|
+
- no auth, payment, schema, or public API surface
|
|
102
|
+
- assessed risk = Low
|
|
103
|
+
- the user explicitly requested single-reviewer mode
|
|
104
|
+
|
|
105
|
+
If any condition fails, route through \`aw:review\`.
|
|
106
|
+
|
|
107
|
+
## Red flags (you skipped the gate)
|
|
108
|
+
|
|
109
|
+
- "I know the route" without calling \`Skill(skill="aw:<stage>")\`
|
|
110
|
+
- Pattern-matching a URL/keyword (e.g. PR link → pr-review) past the gate
|
|
111
|
+
- Calling a \`platform-*\` skill before its parent \`aw:*\` stage skill
|
|
112
|
+
- Single-reviewer review for >1 file or >50 lines without explicit request
|
|
113
|
+
|
|
114
|
+
## Rule reminders (load on demand from disk)
|
|
115
|
+
|
|
116
|
+
Platform rules live under \`.aw/.aw_rules/platform/\` — search in this order:
|
|
117
|
+
\`./.aw/.aw_rules/platform/\`, \`~/.aw/.aw_rules/platform/\`, \`~/.aw_rules/platform/\`,
|
|
118
|
+
\`~/.aw/.aw_registry/.aw_rules/platform/\`. Read \`universal/AGENTS.md\` and
|
|
119
|
+
\`security/AGENTS.md\` first, then the touched-domain \`AGENTS.md\` plus
|
|
120
|
+
\`references/*.md\` on demand.
|
|
121
|
+
`;
|
|
122
|
+
|
|
123
|
+
export const SLIM_CARD_CURSOR = `# AW Router (Compact) — using-aw-skills
|
|
124
|
+
|
|
125
|
+
You have the AW Agentic Workspace engine. This compact router replaces the
|
|
126
|
+
full using-aw-skills SKILL.md so it fits inside the harness's SessionStart
|
|
127
|
+
inline-context cap. The full skill is still available on disk at
|
|
128
|
+
\`~/.cursor/skills/using-aw-skills/SKILL.md\` for deep cases.
|
|
129
|
+
|
|
130
|
+
## Hard Gate (MUST — in order, before any substantive response)
|
|
131
|
+
|
|
132
|
+
1. Classify the user's intent (one line).
|
|
133
|
+
2. Pick the SMALLEST correct stage skill from the table below.
|
|
134
|
+
3. Read \`~/.cursor/skills/aw-<stage>/SKILL.md\` BEFORE any other Read, Edit,
|
|
135
|
+
Write, Shell, or MCP call that performs substantive work.
|
|
136
|
+
4. Follow that stage skill's contract — do not improvise.
|
|
137
|
+
|
|
138
|
+
Naming a route is not invoking it. Read = open the SKILL.md file.
|
|
139
|
+
|
|
140
|
+
## Intent → Stage Skill (tier 1/2 — read these first)
|
|
141
|
+
|
|
142
|
+
| Intent signal in the prompt | Stage skill |
|
|
143
|
+
|---|---|
|
|
144
|
+
| review, "review this PR", findings, governance, "is this ready" | \`aw-review\` |
|
|
145
|
+
| plan, design, refactor strategy, propose approach | \`aw-plan\` |
|
|
146
|
+
| build, implement, write code, add feature, TDD | \`aw-build\` |
|
|
147
|
+
| bug, crash, alert, regression, debug, investigate | \`aw-investigate\` |
|
|
148
|
+
| test, QA, e2e, coverage, regression suite | \`aw-test\` |
|
|
149
|
+
| deploy, release, rollout, staging | \`aw-deploy\` |
|
|
150
|
+
| ship, launch, rollback readiness, closeout | \`aw-ship\` |
|
|
151
|
+
| feature end-to-end (research → ship) | \`aw-feature\` |
|
|
152
|
+
| agent/skill/command/rule authoring | \`aw-adk\` |
|
|
153
|
+
|
|
154
|
+
## Tier-3 domain skills are delegated, not invoked directly
|
|
155
|
+
|
|
156
|
+
\`platform-core-pr-review\`, \`platform-services-development\`,
|
|
157
|
+
\`platform-frontend-*\`, \`platform-sdet-*\`, \`platform-infra-*\`, etc. — these
|
|
158
|
+
are domain transports. The stage skill (\`aw-review\`, \`aw-build\`, …) decides
|
|
159
|
+
when to call them. Reading a tier-3 skill before its parent stage is a
|
|
160
|
+
routing miss.
|
|
161
|
+
|
|
162
|
+
## Single-reviewer fallback (review only)
|
|
163
|
+
|
|
164
|
+
You may skip \`aw-review\` and use \`platform-core-pr-review\` directly ONLY
|
|
165
|
+
if ALL of the following hold:
|
|
166
|
+
|
|
167
|
+
- the diff is < 50 lines AND touches a single file
|
|
168
|
+
- no auth, payment, schema, or public API surface
|
|
169
|
+
- assessed risk = Low
|
|
170
|
+
- the user explicitly requested single-reviewer mode
|
|
171
|
+
|
|
172
|
+
If any condition fails, route through \`aw-review\`.
|
|
173
|
+
|
|
174
|
+
## Red flags (you skipped the gate)
|
|
175
|
+
|
|
176
|
+
- "I know the route" without reading \`aw-<stage>/SKILL.md\`
|
|
177
|
+
- Pattern-matching a URL/keyword (e.g. PR link → pr-review) past the gate
|
|
178
|
+
- Reading a \`platform-*\` skill before its parent \`aw-*\` stage skill
|
|
179
|
+
- Single-reviewer review for >1 file or >50 lines without explicit request
|
|
180
|
+
|
|
181
|
+
## Rule reminders (load on demand from disk)
|
|
182
|
+
|
|
183
|
+
Platform rules live under \`.aw/.aw_rules/platform/\` — search in this order:
|
|
184
|
+
\`./.aw/.aw_rules/platform/\`, \`~/.aw/.aw_rules/platform/\`, \`~/.aw_rules/platform/\`,
|
|
185
|
+
\`~/.aw/.aw_registry/.aw_rules/platform/\`. Read \`universal/AGENTS.md\` and
|
|
186
|
+
\`security/AGENTS.md\` first, then the touched-domain \`AGENTS.md\` plus
|
|
187
|
+
\`references/*.md\` on demand.
|
|
188
|
+
`;
|
|
189
|
+
|
|
190
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
191
|
+
* Hook scripts — embedded as bash one-liners that emit fixed JSON envelopes.
|
|
192
|
+
* ───────────────────────────────────────────────────────────────────────── */
|
|
193
|
+
|
|
194
|
+
function buildClaudeSlimHookScript(cardPath) {
|
|
195
|
+
// Export CARD_PATH so the python child sees it via os.environ. The python
|
|
196
|
+
// body is inside a SINGLE-QUOTED heredoc so no shell expansion happens —
|
|
197
|
+
// the path is read from the env, not interpolated, which avoids quoting
|
|
198
|
+
// hazards if cardPath contains shell metacharacters.
|
|
199
|
+
return `#!/usr/bin/env bash
|
|
200
|
+
# AW C4 SLIM ROUTER (Claude SessionStart) v1
|
|
201
|
+
set -euo pipefail
|
|
202
|
+
export CARD_PATH="${cardPath}"
|
|
203
|
+
if [ ! -f "$CARD_PATH" ]; then
|
|
204
|
+
printf '%s' '{}'
|
|
205
|
+
exit 0
|
|
206
|
+
fi
|
|
207
|
+
python3 -c '
|
|
208
|
+
import json, os
|
|
209
|
+
card = open(os.environ["CARD_PATH"]).read()
|
|
210
|
+
print(json.dumps({
|
|
211
|
+
"hookSpecificOutput": {
|
|
212
|
+
"hookEventName": "SessionStart",
|
|
213
|
+
"additionalContext": card,
|
|
214
|
+
}
|
|
215
|
+
}))
|
|
216
|
+
'
|
|
217
|
+
`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function buildCursorSlimHookScript(cardPath) {
|
|
221
|
+
// Cursor uses the simpler top-level snake_case form. Same env-passing
|
|
222
|
+
// strategy as the Claude variant.
|
|
223
|
+
return `#!/usr/bin/env bash
|
|
224
|
+
# AW C4 SLIM ROUTER (Cursor sessionStart) v1
|
|
225
|
+
set -euo pipefail
|
|
226
|
+
export CARD_PATH="${cardPath}"
|
|
227
|
+
if [ ! -f "$CARD_PATH" ]; then
|
|
228
|
+
printf '%s' '{}'
|
|
229
|
+
exit 0
|
|
230
|
+
fi
|
|
231
|
+
python3 -c '
|
|
232
|
+
import json, os
|
|
233
|
+
card = open(os.environ["CARD_PATH"]).read()
|
|
234
|
+
print(json.dumps({"additional_context": card}))
|
|
235
|
+
'
|
|
236
|
+
`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
240
|
+
* Public API
|
|
241
|
+
* ───────────────────────────────────────────────────────────────────────── */
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Validate + return the slim card for a harness.
|
|
245
|
+
*
|
|
246
|
+
* @param {'claude-web'|'cursor-cloud'} harness
|
|
247
|
+
* @returns {{ card: string, bytes: number }}
|
|
248
|
+
*/
|
|
249
|
+
export function buildSlimRouterCard(harness) {
|
|
250
|
+
let card;
|
|
251
|
+
let stageMarkers;
|
|
252
|
+
if (harness === 'claude-web') {
|
|
253
|
+
card = SLIM_CARD_CLAUDE;
|
|
254
|
+
stageMarkers = REQUIRED_STAGE_MARKERS_CLAUDE;
|
|
255
|
+
} else if (harness === 'cursor-cloud') {
|
|
256
|
+
card = SLIM_CARD_CURSOR;
|
|
257
|
+
stageMarkers = REQUIRED_STAGE_MARKERS_CURSOR;
|
|
258
|
+
} else {
|
|
259
|
+
throw new Error(`buildSlimRouterCard: unsupported harness "${harness}"`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const bytes = Buffer.byteLength(card, 'utf8');
|
|
263
|
+
if (bytes > SLIM_CARD_HARD_CEILING_BYTES) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
`Slim card for ${harness} is ${bytes} bytes, exceeds ${SLIM_CARD_HARD_CEILING_BYTES} ceiling`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
for (const phrase of REQUIRED_ENFORCEMENT_PHRASES) {
|
|
270
|
+
if (!card.includes(phrase)) {
|
|
271
|
+
throw new Error(`Slim card for ${harness} missing enforcement phrase: "${phrase}"`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
for (const marker of stageMarkers) {
|
|
275
|
+
if (!card.includes(marker)) {
|
|
276
|
+
throw new Error(`Slim card for ${harness} missing stage marker: "${marker}"`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return { card, bytes };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function ensureDir(d) {
|
|
284
|
+
mkdirSync(d, { recursive: true });
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function writeExecutable(path, content) {
|
|
288
|
+
ensureDir(dirname(path));
|
|
289
|
+
writeFileSync(path, content, { mode: 0o755 });
|
|
290
|
+
try { chmodSync(path, 0o755); } catch { /* best-effort */ }
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function isEscaped(envVarName) {
|
|
294
|
+
const val = process.env[envVarName];
|
|
295
|
+
return val === '0';
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Install the slim router for a harness.
|
|
300
|
+
*
|
|
301
|
+
* @param {'claude-web'|'cursor-cloud'} harness
|
|
302
|
+
* @param {string} home
|
|
303
|
+
* @param {{ escapeEnvVar?: string }} [opts]
|
|
304
|
+
* @returns {{
|
|
305
|
+
* cardPath: string,
|
|
306
|
+
* sessionHookPath: string,
|
|
307
|
+
* promptHookCommand: string,
|
|
308
|
+
* registeredIn: string,
|
|
309
|
+
* escaped: boolean,
|
|
310
|
+
* }}
|
|
311
|
+
*/
|
|
312
|
+
export function installSlimRouter(harness, home, opts = {}) {
|
|
313
|
+
if (harness !== 'claude-web' && harness !== 'cursor-cloud') {
|
|
314
|
+
throw new Error(`installSlimRouter: unsupported harness "${harness}"`);
|
|
315
|
+
}
|
|
316
|
+
if (!home || typeof home !== 'string') {
|
|
317
|
+
throw new Error('installSlimRouter: home is required');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const escapeEnvVar = opts.escapeEnvVar ?? 'AW_SLIM_ROUTER';
|
|
321
|
+
const escaped = isEscaped(escapeEnvVar);
|
|
322
|
+
|
|
323
|
+
if (harness === 'claude-web') {
|
|
324
|
+
return installClaudeSlim({ home, escaped });
|
|
325
|
+
}
|
|
326
|
+
return installCursorSlim({ home, escaped });
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function installClaudeSlim({ home, escaped }) {
|
|
330
|
+
const cardPath = join(home, '.claude/aw-router-card.md');
|
|
331
|
+
const sessionHookPath = join(home, '.claude/hooks/aw-slim-session-start.sh');
|
|
332
|
+
const settingsPath = join(home, '.claude/settings.json');
|
|
333
|
+
|
|
334
|
+
// Ensure the registry-config parent dir exists in BOTH modes — escape mode
|
|
335
|
+
// skips the card write that would otherwise create it, but settings.json
|
|
336
|
+
// still needs to land somewhere.
|
|
337
|
+
ensureDir(dirname(settingsPath));
|
|
338
|
+
|
|
339
|
+
// Per-prompt: ECC's existing rule-reminder script (NOT a card re-emitter).
|
|
340
|
+
const promptHookCommand =
|
|
341
|
+
`bash -lc 'exec bash "\${CLAUDE_PLUGIN_ROOT:-$HOME/.claude}/scripts/hooks/session-start-rules-context.sh"'`;
|
|
342
|
+
|
|
343
|
+
if (!escaped) {
|
|
344
|
+
ensureDir(dirname(cardPath));
|
|
345
|
+
writeFileSync(cardPath, SLIM_CARD_CLAUDE, { mode: 0o600 });
|
|
346
|
+
|
|
347
|
+
const hookScript = buildClaudeSlimHookScript(cardPath);
|
|
348
|
+
writeExecutable(sessionHookPath, hookScript);
|
|
349
|
+
|
|
350
|
+
const sessionCommand = `bash -lc 'exec bash "${sessionHookPath}"'`;
|
|
351
|
+
|
|
352
|
+
claudeHooksMerge({
|
|
353
|
+
settingsPath,
|
|
354
|
+
eventName: 'SessionStart',
|
|
355
|
+
matcher: 'startup|clear|compact',
|
|
356
|
+
command: sessionCommand,
|
|
357
|
+
description: 'AW slim router (SessionStart)',
|
|
358
|
+
commandPatterns: [
|
|
359
|
+
'aw-slim-session-start.sh',
|
|
360
|
+
'hooks/session-start',
|
|
361
|
+
],
|
|
362
|
+
});
|
|
363
|
+
} else {
|
|
364
|
+
// Escape: register the full ECC SessionStart entry-point.
|
|
365
|
+
const fullEcc =
|
|
366
|
+
`bash -lc 'exec bash "\${CLAUDE_PLUGIN_ROOT:-$HOME/.claude}/hooks/session-start"'`;
|
|
367
|
+
claudeHooksMerge({
|
|
368
|
+
settingsPath,
|
|
369
|
+
eventName: 'SessionStart',
|
|
370
|
+
matcher: 'startup|clear|compact',
|
|
371
|
+
command: fullEcc,
|
|
372
|
+
description: 'AW full ECC SessionStart (slim escape hatch)',
|
|
373
|
+
commandPatterns: [
|
|
374
|
+
'aw-slim-session-start.sh',
|
|
375
|
+
'hooks/session-start',
|
|
376
|
+
],
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Per-prompt hook is registered in BOTH default and escape modes — the
|
|
381
|
+
// reminder is independent of the slim card.
|
|
382
|
+
claudeHooksMerge({
|
|
383
|
+
settingsPath,
|
|
384
|
+
eventName: 'UserPromptSubmit',
|
|
385
|
+
command: promptHookCommand,
|
|
386
|
+
description: 'AW per-prompt rules reminder (ECC delegation)',
|
|
387
|
+
commandPatterns: ['session-start-rules-context.sh'],
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
return {
|
|
391
|
+
cardPath,
|
|
392
|
+
sessionHookPath,
|
|
393
|
+
promptHookCommand,
|
|
394
|
+
registeredIn: settingsPath,
|
|
395
|
+
escaped,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function installCursorSlim({ home, escaped }) {
|
|
400
|
+
const cardPath = join(home, '.cursor/aw-router-card.md');
|
|
401
|
+
const sessionHookPath = join(home, '.cursor/hooks/aw-slim-session-start.sh');
|
|
402
|
+
const hooksJsonPath = join(home, '.cursor/hooks.json');
|
|
403
|
+
|
|
404
|
+
// Same rationale as installClaudeSlim — escape mode skips the card write.
|
|
405
|
+
ensureDir(dirname(hooksJsonPath));
|
|
406
|
+
|
|
407
|
+
// (G1) The Node-adapter wrapper that satisfies Cursor's request-rewrite
|
|
408
|
+
// JSON contract. NEVER delegate to shared/user-prompt-submit.sh directly.
|
|
409
|
+
const promptHookCommand =
|
|
410
|
+
`bash -lc 'f="$PWD/.cursor/hooks/before-submit-prompt.sh"; ` +
|
|
411
|
+
`[ -f "$f" ] || f="$HOME/.cursor/hooks/before-submit-prompt.sh"; exec bash "$f"'`;
|
|
412
|
+
|
|
413
|
+
if (!escaped) {
|
|
414
|
+
ensureDir(dirname(cardPath));
|
|
415
|
+
writeFileSync(cardPath, SLIM_CARD_CURSOR, { mode: 0o600 });
|
|
416
|
+
|
|
417
|
+
const hookScript = buildCursorSlimHookScript(cardPath);
|
|
418
|
+
writeExecutable(sessionHookPath, hookScript);
|
|
419
|
+
|
|
420
|
+
const sessionCommand = `bash -lc 'exec bash "${sessionHookPath}"'`;
|
|
421
|
+
|
|
422
|
+
jsonMergeWithDedup({
|
|
423
|
+
filePath: hooksJsonPath,
|
|
424
|
+
jsonPointer: '/hooks/sessionStart',
|
|
425
|
+
newEntry: {
|
|
426
|
+
command: sessionCommand,
|
|
427
|
+
event: 'sessionStart',
|
|
428
|
+
description: 'AW slim router (sessionStart)',
|
|
429
|
+
},
|
|
430
|
+
commandPatterns: [
|
|
431
|
+
'aw-slim-session-start.sh',
|
|
432
|
+
'.cursor/hooks/session-start.sh',
|
|
433
|
+
],
|
|
434
|
+
});
|
|
435
|
+
} else {
|
|
436
|
+
const fullEcc =
|
|
437
|
+
`bash -lc 'f="$PWD/.cursor/hooks/session-start.sh"; ` +
|
|
438
|
+
`[ -f "$f" ] || f="$HOME/.cursor/hooks/session-start.sh"; exec bash "$f"'`;
|
|
439
|
+
jsonMergeWithDedup({
|
|
440
|
+
filePath: hooksJsonPath,
|
|
441
|
+
jsonPointer: '/hooks/sessionStart',
|
|
442
|
+
newEntry: {
|
|
443
|
+
command: fullEcc,
|
|
444
|
+
event: 'sessionStart',
|
|
445
|
+
description: 'AW full ECC sessionStart (slim escape hatch)',
|
|
446
|
+
},
|
|
447
|
+
commandPatterns: [
|
|
448
|
+
'aw-slim-session-start.sh',
|
|
449
|
+
'.cursor/hooks/session-start.sh',
|
|
450
|
+
],
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
jsonMergeWithDedup({
|
|
455
|
+
filePath: hooksJsonPath,
|
|
456
|
+
jsonPointer: '/hooks/beforeSubmitPrompt',
|
|
457
|
+
newEntry: {
|
|
458
|
+
command: promptHookCommand,
|
|
459
|
+
event: 'beforeSubmitPrompt',
|
|
460
|
+
description: 'AW per-prompt rules reminder (ECC Node-adapter)',
|
|
461
|
+
},
|
|
462
|
+
commandPatterns: ['before-submit-prompt.sh'],
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
cardPath,
|
|
467
|
+
sessionHookPath,
|
|
468
|
+
promptHookCommand,
|
|
469
|
+
registeredIn: hooksJsonPath,
|
|
470
|
+
escaped,
|
|
471
|
+
};
|
|
472
|
+
}
|
package/cli.mjs
CHANGED
|
@@ -27,6 +27,7 @@ const COMMANDS = {
|
|
|
27
27
|
daemon: () => import('./commands/daemon.mjs').then(m => m.daemonCommand),
|
|
28
28
|
telemetry: () => import('./commands/telemetry.mjs').then(m => m.telemetryCommand),
|
|
29
29
|
'slack-sim': () => import('./commands/slack-sim.mjs').then(m => m.slackSimCommand),
|
|
30
|
+
c4: () => import('./commands/c4.mjs').then(m => m.c4Command),
|
|
30
31
|
};
|
|
31
32
|
|
|
32
33
|
function parseArgs(argv) {
|
|
@@ -115,6 +116,12 @@ function printHelp() {
|
|
|
115
116
|
cmd('aw slack-sim run <scenario>', 'Replay Slack-like scenarios against real runtime'),
|
|
116
117
|
cmd('aw slack-sim list-scenarios', 'List built-in Slack simulator scenarios'),
|
|
117
118
|
|
|
119
|
+
sec('Cloud'),
|
|
120
|
+
cmd('aw c4', 'Bootstrap AW for cloud harnesses (Claude/Cursor/Codex)'),
|
|
121
|
+
cmd('aw c4 --dry-run', 'Run preflight + auth setup without installing'),
|
|
122
|
+
cmd('aw c4 --diagnose', 'Self-test post-init wiring (always exits 0)'),
|
|
123
|
+
cmd('aw c4 --harness <name>', 'Override harness detection (cursor-cloud|codex-web|claude-web)'),
|
|
124
|
+
|
|
118
125
|
sec('Settings'),
|
|
119
126
|
cmd('aw telemetry status', 'Show telemetry status'),
|
|
120
127
|
cmd('aw telemetry disable', 'Opt out of anonymous analytics'),
|