@lifeaitools/rdc-skills 0.24.37 → 0.24.39
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/settings.json +15 -15
- package/.claude-plugin/marketplace.json +21 -21
- package/.claude-plugin/plugin.json +1371 -1371
- package/.github/workflows/publish.yml +34 -34
- package/.github/workflows/self-test.yml +58 -58
- package/CHANGELOG.md +310 -310
- package/LICENSE +21 -21
- package/MANIFEST.md +221 -221
- package/README.md +377 -377
- package/README.sandbox.md +3 -3
- package/assets/watcher/viewer.html +164 -164
- package/bin/rdc-skills-mcp.mjs +316 -316
- package/commands/build.md +183 -183
- package/commands/collab.md +180 -180
- package/commands/deploy.md +152 -152
- package/commands/design.md +31 -31
- package/commands/edit.md +28 -28
- package/commands/fixit.md +124 -124
- package/commands/handoff.md +173 -173
- package/commands/help.md +95 -95
- package/commands/overnight.md +220 -220
- package/commands/plan.md +158 -158
- package/commands/preplan.md +131 -131
- package/commands/prototype.md +145 -145
- package/commands/release.md +49 -49
- package/commands/report.md +99 -99
- package/commands/review.md +120 -120
- package/commands/self-test.md +113 -113
- package/commands/status.md +86 -86
- package/commands/watch.md +98 -98
- package/commands/workitems.md +137 -137
- package/git-sha.json +1 -1
- package/guides/agent-bootstrap.md +295 -295
- package/guides/agents/backend.md +104 -104
- package/guides/agents/content.md +94 -94
- package/guides/agents/cs2.md +56 -56
- package/guides/agents/data.md +87 -87
- package/guides/agents/design.md +77 -77
- package/guides/agents/frontend.md +92 -92
- package/guides/agents/infrastructure.md +81 -81
- package/guides/agents/setup.md +281 -281
- package/guides/agents/verify.md +151 -151
- package/guides/agents/viz.md +106 -106
- package/guides/backend.md +146 -146
- package/guides/content.md +147 -147
- package/guides/cs2.md +190 -190
- package/guides/data.md +123 -123
- package/guides/design.md +116 -116
- package/guides/engineering-behavior.md +43 -43
- package/guides/escalation-protocol.md +125 -125
- package/guides/frontend.md +151 -151
- package/guides/history-md-spec.md +297 -297
- package/guides/infrastructure.md +179 -179
- package/guides/lessons-learned-spec.md +153 -153
- package/guides/output-contract.md +108 -108
- package/guides/publish-md-spec.md +289 -289
- package/guides/rdc-skills-startup.md +30 -30
- package/guides/verify.md +11 -11
- package/hooks/check-cwd.js +31 -31
- package/hooks/check-rdc-environment.js +164 -164
- package/hooks/check-services.js +6 -6
- package/hooks/check-stale-work-items.js +19 -19
- package/hooks/foreground-process-gate.js +128 -128
- package/hooks/gate-watchdog-selfcheck.js +257 -257
- package/hooks/hook-logger.js +25 -25
- package/hooks/lib/run-evidence-gate.mjs +241 -241
- package/hooks/no-stop-open-epics.js +127 -127
- package/hooks/post-tool-batch-gate.js +203 -203
- package/hooks/post-work-check.js +21 -21
- package/hooks/postcompact-log.js +13 -13
- package/hooks/precompact-log.js +13 -13
- package/hooks/rate-limit-retry.js +46 -46
- package/hooks/rdc-invocation-marker.js +157 -157
- package/hooks/rdc-output-contract-gate.js +94 -94
- package/hooks/require-work-item-on-commit.js +294 -294
- package/hooks/restart-brief.js +19 -19
- package/hooks/run-hidden-hook.ps1 +47 -47
- package/hooks/task-completed-gate.js +274 -274
- package/hooks/work-item-exit-gate.js +944 -944
- package/lib/catalog.mjs +236 -236
- package/lib/cloud-rewrite.mjs +155 -155
- package/package.json +56 -56
- package/rules/work-items-rpc.md +520 -520
- package/scaffold/templates/HISTORY.md.template +39 -39
- package/scaffold/templates/PUBLISH.md.template +21 -21
- package/scaffold/templates/brochure-studio-default.html +70 -70
- package/scripts/acceptance.mjs +502 -502
- package/scripts/fixtures/guides/bad-guide.md +15 -15
- package/scripts/fixtures/guides-clean/good-guide.md +16 -16
- package/scripts/install-rdc-skills.js +1289 -1289
- package/scripts/install.ps1 +202 -202
- package/scripts/install.sh +132 -132
- package/scripts/lib/assertions.mjs +287 -287
- package/scripts/lib/manifest-schema.mjs +754 -754
- package/scripts/lib/runner.mjs +465 -465
- package/scripts/lib/sandbox.mjs +435 -435
- package/scripts/prepack.mjs +32 -32
- package/scripts/rdc-brochure.mjs +464 -464
- package/scripts/rdc-design-cli.mjs +134 -134
- package/scripts/rebuild-mcp.mjs +107 -107
- package/scripts/self-test.mjs +1460 -1460
- package/scripts/stamp-git-sha.mjs +29 -29
- package/scripts/test-guide-validator.mjs +196 -196
- package/scripts/test-rdc-hooks.mjs +145 -145
- package/scripts/uninstall.ps1 +77 -77
- package/scripts/uninstall.sh +69 -69
- package/scripts/update.ps1 +43 -43
- package/scripts/update.sh +43 -43
- package/scripts/validate-place-histories.js +461 -461
- package/scripts/validate-publish-manifests.js +424 -424
- package/scripts/watch-init.mjs +100 -100
- package/skills/brochure/SKILL.md +107 -107
- package/skills/build/SKILL.md +563 -554
- package/skills/channel-formatter/SKILL.md +533 -533
- package/skills/co-develop/SKILL.md +196 -196
- package/skills/collab/SKILL.md +239 -239
- package/skills/convert/SKILL.md +140 -140
- package/skills/deploy/SKILL.md +541 -541
- package/skills/design/SKILL.md +211 -211
- package/skills/design/reference/ownership.md +16 -16
- package/skills/design/reference/rampa.md +92 -92
- package/skills/design/reference/studio-model.md +153 -153
- package/skills/edit/SKILL.md +98 -98
- package/skills/fixit/SKILL.md +165 -165
- package/skills/fs-mcp/SKILL.md +148 -148
- package/skills/handoff/SKILL.md +236 -200
- package/skills/help/SKILL.md +143 -143
- package/skills/housekeeping/SKILL.md +189 -189
- package/skills/lifeai-brochure-author/SKILL.md +340 -340
- package/skills/overnight/SKILL.md +251 -251
- package/skills/plan/SKILL.md +345 -314
- package/skills/preplan/SKILL.md +90 -90
- package/skills/prototype/SKILL.md +150 -150
- package/skills/rdc-brochurify/SKILL.md +245 -245
- package/skills/rdc-extract-verifier-rules/SKILL.md +191 -191
- package/skills/release/SKILL.md +140 -140
- package/skills/report/SKILL.md +100 -100
- package/skills/review/SKILL.md +152 -152
- package/skills/rpms-filemap/SKILL.cloud.md +111 -111
- package/skills/rpms-filemap/SKILL.md +111 -111
- package/skills/self-test/SKILL.md +132 -132
- package/skills/status/SKILL.md +99 -99
- package/skills/terminal-config/SKILL.md +62 -62
- package/skills/tests/MATRIX.md +54 -54
- package/skills/tests/README.md +47 -47
- package/skills/tests/rdc-brochure.test.json +34 -34
- package/skills/tests/rdc-build.test.json +36 -36
- package/skills/tests/rdc-channel-formatter.test.json +45 -45
- package/skills/tests/rdc-co-develop.test.json +29 -29
- package/skills/tests/rdc-collab.test.json +29 -29
- package/skills/tests/rdc-convert.test.json +35 -35
- package/skills/tests/rdc-deploy.test.json +30 -30
- package/skills/tests/rdc-design.test.json +27 -27
- package/skills/tests/rdc-edit.test.json +29 -29
- package/skills/tests/rdc-fixit.test.json +36 -36
- package/skills/tests/rdc-fs-mcp.test.json +36 -36
- package/skills/tests/rdc-handoff.test.json +28 -28
- package/skills/tests/rdc-help.test.json +29 -29
- package/skills/tests/rdc-housekeeping.test.json +31 -31
- package/skills/tests/rdc-lifeai-brochure-author.test.json +35 -35
- package/skills/tests/rdc-overnight.test.json +37 -37
- package/skills/tests/rdc-plan.test.json +27 -27
- package/skills/tests/rdc-preplan.test.json +31 -31
- package/skills/tests/rdc-prototype.test.json +28 -28
- package/skills/tests/rdc-rdc-brochurify.test.json +23 -23
- package/skills/tests/rdc-rdc-extract-verifier-rules.test.json +34 -34
- package/skills/tests/rdc-release.test.json +29 -29
- package/skills/tests/rdc-report.test.json +28 -28
- package/skills/tests/rdc-review.test.json +29 -29
- package/skills/tests/rdc-rpms-filemap.test.json +28 -28
- package/skills/tests/rdc-self-test.test.json +24 -24
- package/skills/tests/rdc-status.test.json +29 -29
- package/skills/tests/rdc-terminal-config.test.json +29 -29
- package/skills/tests/rdc-watch.test.json +24 -24
- package/skills/tests/rdc-workitems.test.json +27 -27
- package/skills/watch/SKILL.md +97 -97
- package/skills/workitems/SKILL.md +151 -151
- package/tests/acceptance.test.mjs +59 -59
- package/tests/channel-formatter.contract.test.mjs +251 -251
- package/tests/curl-surface.test.mjs +289 -289
- package/tests/harness-gates.test.mjs +325 -325
- package/tests/help-surface.test.mjs +61 -61
- package/tests/install-rdc-skills.test.mjs +49 -49
- package/tests/manifest-contract-fields.test.mjs +78 -78
- package/tests/mcp.test.mjs +271 -271
- package/tests/require-work-item-on-commit.test.mjs +162 -162
- package/tests/run-evidence-gate.test.mjs +82 -82
- package/tests/skill-test-matrix.test.mjs +66 -66
- package/tests/validate-skills.js +27 -27
- package/tests/work-item-exit-gate-l2.test.mjs +368 -368
- package/tests/work-item-exit-gate-l3.test.mjs +197 -197
package/hooks/post-work-check.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Stop hook — warn if work appears to have been done without a work item.
|
|
3
|
-
'use strict';
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
|
|
7
|
-
const HOME = process.env.USERPROFILE || process.env.HOME;
|
|
8
|
-
const LOG = path.join(HOME, '.claude', 'stop-log.jsonl');
|
|
9
|
-
|
|
10
|
-
function log(obj) {
|
|
11
|
-
try { fs.appendFileSync(LOG, JSON.stringify({ ts: new Date().toISOString(), ...obj }) + '\n'); } catch (_) {}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
let input = '';
|
|
15
|
-
process.stdin.resume();
|
|
16
|
-
process.stdin.setEncoding('utf8');
|
|
17
|
-
process.stdin.on('data', c => { input += c; });
|
|
18
|
-
process.stdin.on('end', () => {
|
|
19
|
-
log({ hook: 'post-work-check', stop: true });
|
|
20
|
-
process.exit(0);
|
|
21
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Stop hook — warn if work appears to have been done without a work item.
|
|
3
|
+
'use strict';
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const HOME = process.env.USERPROFILE || process.env.HOME;
|
|
8
|
+
const LOG = path.join(HOME, '.claude', 'stop-log.jsonl');
|
|
9
|
+
|
|
10
|
+
function log(obj) {
|
|
11
|
+
try { fs.appendFileSync(LOG, JSON.stringify({ ts: new Date().toISOString(), ...obj }) + '\n'); } catch (_) {}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let input = '';
|
|
15
|
+
process.stdin.resume();
|
|
16
|
+
process.stdin.setEncoding('utf8');
|
|
17
|
+
process.stdin.on('data', c => { input += c; });
|
|
18
|
+
process.stdin.on('end', () => {
|
|
19
|
+
log({ hook: 'post-work-check', stop: true });
|
|
20
|
+
process.exit(0);
|
|
21
|
+
});
|
package/hooks/postcompact-log.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// PostCompact hook — log compact completion.
|
|
3
|
-
'use strict';
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const LOG = (process.env.USERPROFILE || process.env.HOME) + '/.claude/stop-log.jsonl';
|
|
6
|
-
let input = '';
|
|
7
|
-
process.stdin.resume();
|
|
8
|
-
process.stdin.setEncoding('utf8');
|
|
9
|
-
process.stdin.on('data', c => { input += c; });
|
|
10
|
-
process.stdin.on('end', () => {
|
|
11
|
-
try { fs.appendFileSync(LOG, JSON.stringify({ ts: new Date().toISOString(), hook: 'postcompact' }) + '\n'); } catch (_) {}
|
|
12
|
-
process.exit(0);
|
|
13
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PostCompact hook — log compact completion.
|
|
3
|
+
'use strict';
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const LOG = (process.env.USERPROFILE || process.env.HOME) + '/.claude/stop-log.jsonl';
|
|
6
|
+
let input = '';
|
|
7
|
+
process.stdin.resume();
|
|
8
|
+
process.stdin.setEncoding('utf8');
|
|
9
|
+
process.stdin.on('data', c => { input += c; });
|
|
10
|
+
process.stdin.on('end', () => {
|
|
11
|
+
try { fs.appendFileSync(LOG, JSON.stringify({ ts: new Date().toISOString(), hook: 'postcompact' }) + '\n'); } catch (_) {}
|
|
12
|
+
process.exit(0);
|
|
13
|
+
});
|
package/hooks/precompact-log.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// PreCompact hook — log compact event.
|
|
3
|
-
'use strict';
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const LOG = (process.env.USERPROFILE || process.env.HOME) + '/.claude/stop-log.jsonl';
|
|
6
|
-
let input = '';
|
|
7
|
-
process.stdin.resume();
|
|
8
|
-
process.stdin.setEncoding('utf8');
|
|
9
|
-
process.stdin.on('data', c => { input += c; });
|
|
10
|
-
process.stdin.on('end', () => {
|
|
11
|
-
try { fs.appendFileSync(LOG, JSON.stringify({ ts: new Date().toISOString(), hook: 'precompact' }) + '\n'); } catch (_) {}
|
|
12
|
-
process.exit(0);
|
|
13
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PreCompact hook — log compact event.
|
|
3
|
+
'use strict';
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const LOG = (process.env.USERPROFILE || process.env.HOME) + '/.claude/stop-log.jsonl';
|
|
6
|
+
let input = '';
|
|
7
|
+
process.stdin.resume();
|
|
8
|
+
process.stdin.setEncoding('utf8');
|
|
9
|
+
process.stdin.on('data', c => { input += c; });
|
|
10
|
+
process.stdin.on('end', () => {
|
|
11
|
+
try { fs.appendFileSync(LOG, JSON.stringify({ ts: new Date().toISOString(), hook: 'precompact' }) + '\n'); } catch (_) {}
|
|
12
|
+
process.exit(0);
|
|
13
|
+
});
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Stop hook — detect rate limit and schedule retry via Windows Task Scheduler.
|
|
3
|
-
'use strict';
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const { execSync } = require('child_process');
|
|
7
|
-
|
|
8
|
-
const HOME = process.env.USERPROFILE || process.env.HOME;
|
|
9
|
-
const CLAUDE = path.join(HOME, '.claude');
|
|
10
|
-
const FLAG = path.join(CLAUDE, 'rate-limit.flag');
|
|
11
|
-
const LOG = path.join(CLAUDE, 'stop-log.jsonl');
|
|
12
|
-
const RUNNER = path.join(CLAUDE, 'hooks', 'rate-limit-retry.cmd');
|
|
13
|
-
|
|
14
|
-
function log(obj) {
|
|
15
|
-
try { fs.appendFileSync(LOG, JSON.stringify({ ts: new Date().toISOString(), ...obj }) + '\n'); } catch (_) {}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let input = '';
|
|
19
|
-
process.stdin.resume();
|
|
20
|
-
process.stdin.setEncoding('utf8');
|
|
21
|
-
process.stdin.on('data', c => { input += c; });
|
|
22
|
-
process.stdin.on('end', () => {
|
|
23
|
-
let data = {};
|
|
24
|
-
try { data = JSON.parse(input); } catch (_) {}
|
|
25
|
-
|
|
26
|
-
const isRateLimit =
|
|
27
|
-
(data.stop_reason === 'rate_limit') ||
|
|
28
|
-
JSON.stringify(data).toLowerCase().includes('rate limit') ||
|
|
29
|
-
JSON.stringify(data).toLowerCase().includes('rate_limit');
|
|
30
|
-
|
|
31
|
-
if (!isRateLimit) { process.exit(0); }
|
|
32
|
-
|
|
33
|
-
// Write flag and schedule retry in 1 hour
|
|
34
|
-
try { fs.writeFileSync(FLAG, JSON.stringify({ detected_at: new Date().toISOString() })); } catch (_) {}
|
|
35
|
-
|
|
36
|
-
const next = new Date(Date.now() + 60 * 60 * 1000);
|
|
37
|
-
const t = `${String(next.getHours()).padStart(2,'0')}:${String(next.getMinutes()).padStart(2,'0')}`;
|
|
38
|
-
try {
|
|
39
|
-
execSync(`schtasks /create /f /tn "ClaudeRateLimitRetry" /tr "\\"${RUNNER}\\"" /sc once /st ${t}`,
|
|
40
|
-
{ shell: 'cmd.exe', stdio: 'pipe' });
|
|
41
|
-
log({ hook: 'rate-limit-retry', scheduled: t });
|
|
42
|
-
} catch (e) {
|
|
43
|
-
log({ hook: 'rate-limit-retry', error: String(e.message).slice(0, 200) });
|
|
44
|
-
}
|
|
45
|
-
process.exit(0);
|
|
46
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Stop hook — detect rate limit and schedule retry via Windows Task Scheduler.
|
|
3
|
+
'use strict';
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const HOME = process.env.USERPROFILE || process.env.HOME;
|
|
9
|
+
const CLAUDE = path.join(HOME, '.claude');
|
|
10
|
+
const FLAG = path.join(CLAUDE, 'rate-limit.flag');
|
|
11
|
+
const LOG = path.join(CLAUDE, 'stop-log.jsonl');
|
|
12
|
+
const RUNNER = path.join(CLAUDE, 'hooks', 'rate-limit-retry.cmd');
|
|
13
|
+
|
|
14
|
+
function log(obj) {
|
|
15
|
+
try { fs.appendFileSync(LOG, JSON.stringify({ ts: new Date().toISOString(), ...obj }) + '\n'); } catch (_) {}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let input = '';
|
|
19
|
+
process.stdin.resume();
|
|
20
|
+
process.stdin.setEncoding('utf8');
|
|
21
|
+
process.stdin.on('data', c => { input += c; });
|
|
22
|
+
process.stdin.on('end', () => {
|
|
23
|
+
let data = {};
|
|
24
|
+
try { data = JSON.parse(input); } catch (_) {}
|
|
25
|
+
|
|
26
|
+
const isRateLimit =
|
|
27
|
+
(data.stop_reason === 'rate_limit') ||
|
|
28
|
+
JSON.stringify(data).toLowerCase().includes('rate limit') ||
|
|
29
|
+
JSON.stringify(data).toLowerCase().includes('rate_limit');
|
|
30
|
+
|
|
31
|
+
if (!isRateLimit) { process.exit(0); }
|
|
32
|
+
|
|
33
|
+
// Write flag and schedule retry in 1 hour
|
|
34
|
+
try { fs.writeFileSync(FLAG, JSON.stringify({ detected_at: new Date().toISOString() })); } catch (_) {}
|
|
35
|
+
|
|
36
|
+
const next = new Date(Date.now() + 60 * 60 * 1000);
|
|
37
|
+
const t = `${String(next.getHours()).padStart(2,'0')}:${String(next.getMinutes()).padStart(2,'0')}`;
|
|
38
|
+
try {
|
|
39
|
+
execSync(`schtasks /create /f /tn "ClaudeRateLimitRetry" /tr "\\"${RUNNER}\\"" /sc once /st ${t}`,
|
|
40
|
+
{ shell: 'cmd.exe', stdio: 'pipe' });
|
|
41
|
+
log({ hook: 'rate-limit-retry', scheduled: t });
|
|
42
|
+
} catch (e) {
|
|
43
|
+
log({ hook: 'rate-limit-retry', error: String(e.message).slice(0, 200) });
|
|
44
|
+
}
|
|
45
|
+
process.exit(0);
|
|
46
|
+
});
|
|
@@ -1,157 +1,157 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* UserPromptExpansion/UserPromptSubmit hook — mark active rdc:* invocations.
|
|
4
|
-
*
|
|
5
|
-
* This does not enforce compliance. It primes the turn with the RDC contract
|
|
6
|
-
* and leaves a session marker for rdc-output-contract-gate.js to enforce at
|
|
7
|
-
* Stop time.
|
|
8
|
-
*/
|
|
9
|
-
'use strict';
|
|
10
|
-
|
|
11
|
-
const fs = require('fs');
|
|
12
|
-
const os = require('os');
|
|
13
|
-
const path = require('path');
|
|
14
|
-
const hookLog = require('./hook-logger');
|
|
15
|
-
|
|
16
|
-
const RDC_COMMANDS = new Set([
|
|
17
|
-
'build',
|
|
18
|
-
'co-develop',
|
|
19
|
-
'collab',
|
|
20
|
-
'deploy',
|
|
21
|
-
'design',
|
|
22
|
-
'fixit',
|
|
23
|
-
'fs-mcp',
|
|
24
|
-
'handoff',
|
|
25
|
-
'help',
|
|
26
|
-
'overnight',
|
|
27
|
-
'plan',
|
|
28
|
-
'preplan',
|
|
29
|
-
'prototype',
|
|
30
|
-
'release',
|
|
31
|
-
'report',
|
|
32
|
-
'review',
|
|
33
|
-
'self-test',
|
|
34
|
-
'status',
|
|
35
|
-
'terminal-config',
|
|
36
|
-
'watch',
|
|
37
|
-
'workitems',
|
|
38
|
-
]);
|
|
39
|
-
|
|
40
|
-
function readStdin() {
|
|
41
|
-
return new Promise((resolve) => {
|
|
42
|
-
let input = '';
|
|
43
|
-
process.stdin.setEncoding('utf8');
|
|
44
|
-
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
45
|
-
process.stdin.on('end', () => resolve(input));
|
|
46
|
-
process.stdin.resume();
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function markerDir() {
|
|
51
|
-
return path.join(os.homedir(), '.claude', 'rdc-active');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function markerPath(sessionId) {
|
|
55
|
-
const safe = String(sessionId || 'unknown').replace(/[^a-zA-Z0-9_.-]/g, '_');
|
|
56
|
-
return path.join(markerDir(), `${safe}.json`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function normalizeCommandName(value) {
|
|
60
|
-
return String(value || '')
|
|
61
|
-
.trim()
|
|
62
|
-
.replace(/^\/+/, '')
|
|
63
|
-
.replace(/^rdc[:-]/i, '')
|
|
64
|
-
.toLowerCase();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function detectRdc(raw) {
|
|
68
|
-
const event = raw.hook_event_name || '';
|
|
69
|
-
if (event === 'UserPromptExpansion') {
|
|
70
|
-
if (raw.command_source && raw.command_source !== 'plugin') return null;
|
|
71
|
-
const command = normalizeCommandName(raw.command_name);
|
|
72
|
-
if (command === 'help' && !/^\/rdc[:-]help\b/i.test(String(raw.prompt || ''))) return null;
|
|
73
|
-
if (RDC_COMMANDS.has(command)) return command;
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (event === 'UserPromptSubmit') {
|
|
78
|
-
const prompt = String(raw.prompt || '').trim();
|
|
79
|
-
const m = prompt.match(/^\/rdc[:-]([a-z][a-z0-9-]*)\b/i);
|
|
80
|
-
if (!m) return null;
|
|
81
|
-
const command = normalizeCommandName(m[1]);
|
|
82
|
-
if (RDC_COMMANDS.has(command)) return command;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function writeMarker(raw, command) {
|
|
89
|
-
fs.mkdirSync(markerDir(), { recursive: true });
|
|
90
|
-
const p = markerPath(raw.session_id);
|
|
91
|
-
if (fs.existsSync(p)) {
|
|
92
|
-
try {
|
|
93
|
-
const existing = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
94
|
-
const started = Date.parse(existing.started_at || '');
|
|
95
|
-
const isRecentDuplicate = Number.isFinite(started) &&
|
|
96
|
-
Date.now() - started <= 1000 &&
|
|
97
|
-
existing.session_id === (raw.session_id || null) &&
|
|
98
|
-
existing.command === command;
|
|
99
|
-
if (isRecentDuplicate) {
|
|
100
|
-
return { ...existing, deduped: true };
|
|
101
|
-
}
|
|
102
|
-
} catch {}
|
|
103
|
-
}
|
|
104
|
-
const marker = {
|
|
105
|
-
session_id: raw.session_id || null,
|
|
106
|
-
command,
|
|
107
|
-
command_name: raw.command_name || null,
|
|
108
|
-
command_args: raw.command_args || null,
|
|
109
|
-
prompt: raw.prompt || null,
|
|
110
|
-
cwd: raw.cwd || null,
|
|
111
|
-
transcript_path: raw.transcript_path || null,
|
|
112
|
-
started_at: new Date().toISOString(),
|
|
113
|
-
hook_event_name: raw.hook_event_name || null,
|
|
114
|
-
};
|
|
115
|
-
fs.writeFileSync(p, JSON.stringify(marker, null, 2));
|
|
116
|
-
return marker;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function outputContext(eventName, command) {
|
|
120
|
-
const additionalContext = [
|
|
121
|
-
`RDC CONTRACT ACTIVE for /${command}.`,
|
|
122
|
-
'Before responding, follow the project-local guide contracts:',
|
|
123
|
-
'- .rdc/guides/output-contract.md: show one checklist for this invocation, update it as work progresses, and end with the required verdict line.',
|
|
124
|
-
'- .rdc/guides/engineering-behavior.md: assumptions, scope, evidence, deviations, blockers, and verification must be explicit.',
|
|
125
|
-
'Runtime enforcement is on emitted artifacts: the final assistant message must contain at least one checklist row and a verdict line.',
|
|
126
|
-
].join('\n');
|
|
127
|
-
|
|
128
|
-
process.stdout.write(JSON.stringify({
|
|
129
|
-
hookSpecificOutput: {
|
|
130
|
-
hookEventName: eventName,
|
|
131
|
-
additionalContext,
|
|
132
|
-
},
|
|
133
|
-
}));
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async function main() {
|
|
137
|
-
let raw;
|
|
138
|
-
try { raw = JSON.parse(await readStdin()); } catch { process.exit(0); }
|
|
139
|
-
const command = detectRdc(raw);
|
|
140
|
-
if (!command) process.exit(0);
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
const marker = writeMarker(raw, command);
|
|
144
|
-
hookLog('rdc-invocation-marker', raw.hook_event_name || 'unknown', marker.deduped ? 'deduped' : 'marked', {
|
|
145
|
-
command,
|
|
146
|
-
session_id: marker.session_id,
|
|
147
|
-
});
|
|
148
|
-
outputContext(raw.hook_event_name || 'UserPromptExpansion', command);
|
|
149
|
-
} catch (e) {
|
|
150
|
-
hookLog('rdc-invocation-marker', raw.hook_event_name || 'unknown', 'error', {
|
|
151
|
-
command,
|
|
152
|
-
error: e.message,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
main().catch(() => process.exit(0));
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* UserPromptExpansion/UserPromptSubmit hook — mark active rdc:* invocations.
|
|
4
|
+
*
|
|
5
|
+
* This does not enforce compliance. It primes the turn with the RDC contract
|
|
6
|
+
* and leaves a session marker for rdc-output-contract-gate.js to enforce at
|
|
7
|
+
* Stop time.
|
|
8
|
+
*/
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const hookLog = require('./hook-logger');
|
|
15
|
+
|
|
16
|
+
const RDC_COMMANDS = new Set([
|
|
17
|
+
'build',
|
|
18
|
+
'co-develop',
|
|
19
|
+
'collab',
|
|
20
|
+
'deploy',
|
|
21
|
+
'design',
|
|
22
|
+
'fixit',
|
|
23
|
+
'fs-mcp',
|
|
24
|
+
'handoff',
|
|
25
|
+
'help',
|
|
26
|
+
'overnight',
|
|
27
|
+
'plan',
|
|
28
|
+
'preplan',
|
|
29
|
+
'prototype',
|
|
30
|
+
'release',
|
|
31
|
+
'report',
|
|
32
|
+
'review',
|
|
33
|
+
'self-test',
|
|
34
|
+
'status',
|
|
35
|
+
'terminal-config',
|
|
36
|
+
'watch',
|
|
37
|
+
'workitems',
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
function readStdin() {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
let input = '';
|
|
43
|
+
process.stdin.setEncoding('utf8');
|
|
44
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
45
|
+
process.stdin.on('end', () => resolve(input));
|
|
46
|
+
process.stdin.resume();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function markerDir() {
|
|
51
|
+
return path.join(os.homedir(), '.claude', 'rdc-active');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function markerPath(sessionId) {
|
|
55
|
+
const safe = String(sessionId || 'unknown').replace(/[^a-zA-Z0-9_.-]/g, '_');
|
|
56
|
+
return path.join(markerDir(), `${safe}.json`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function normalizeCommandName(value) {
|
|
60
|
+
return String(value || '')
|
|
61
|
+
.trim()
|
|
62
|
+
.replace(/^\/+/, '')
|
|
63
|
+
.replace(/^rdc[:-]/i, '')
|
|
64
|
+
.toLowerCase();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function detectRdc(raw) {
|
|
68
|
+
const event = raw.hook_event_name || '';
|
|
69
|
+
if (event === 'UserPromptExpansion') {
|
|
70
|
+
if (raw.command_source && raw.command_source !== 'plugin') return null;
|
|
71
|
+
const command = normalizeCommandName(raw.command_name);
|
|
72
|
+
if (command === 'help' && !/^\/rdc[:-]help\b/i.test(String(raw.prompt || ''))) return null;
|
|
73
|
+
if (RDC_COMMANDS.has(command)) return command;
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (event === 'UserPromptSubmit') {
|
|
78
|
+
const prompt = String(raw.prompt || '').trim();
|
|
79
|
+
const m = prompt.match(/^\/rdc[:-]([a-z][a-z0-9-]*)\b/i);
|
|
80
|
+
if (!m) return null;
|
|
81
|
+
const command = normalizeCommandName(m[1]);
|
|
82
|
+
if (RDC_COMMANDS.has(command)) return command;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function writeMarker(raw, command) {
|
|
89
|
+
fs.mkdirSync(markerDir(), { recursive: true });
|
|
90
|
+
const p = markerPath(raw.session_id);
|
|
91
|
+
if (fs.existsSync(p)) {
|
|
92
|
+
try {
|
|
93
|
+
const existing = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
94
|
+
const started = Date.parse(existing.started_at || '');
|
|
95
|
+
const isRecentDuplicate = Number.isFinite(started) &&
|
|
96
|
+
Date.now() - started <= 1000 &&
|
|
97
|
+
existing.session_id === (raw.session_id || null) &&
|
|
98
|
+
existing.command === command;
|
|
99
|
+
if (isRecentDuplicate) {
|
|
100
|
+
return { ...existing, deduped: true };
|
|
101
|
+
}
|
|
102
|
+
} catch {}
|
|
103
|
+
}
|
|
104
|
+
const marker = {
|
|
105
|
+
session_id: raw.session_id || null,
|
|
106
|
+
command,
|
|
107
|
+
command_name: raw.command_name || null,
|
|
108
|
+
command_args: raw.command_args || null,
|
|
109
|
+
prompt: raw.prompt || null,
|
|
110
|
+
cwd: raw.cwd || null,
|
|
111
|
+
transcript_path: raw.transcript_path || null,
|
|
112
|
+
started_at: new Date().toISOString(),
|
|
113
|
+
hook_event_name: raw.hook_event_name || null,
|
|
114
|
+
};
|
|
115
|
+
fs.writeFileSync(p, JSON.stringify(marker, null, 2));
|
|
116
|
+
return marker;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function outputContext(eventName, command) {
|
|
120
|
+
const additionalContext = [
|
|
121
|
+
`RDC CONTRACT ACTIVE for /${command}.`,
|
|
122
|
+
'Before responding, follow the project-local guide contracts:',
|
|
123
|
+
'- .rdc/guides/output-contract.md: show one checklist for this invocation, update it as work progresses, and end with the required verdict line.',
|
|
124
|
+
'- .rdc/guides/engineering-behavior.md: assumptions, scope, evidence, deviations, blockers, and verification must be explicit.',
|
|
125
|
+
'Runtime enforcement is on emitted artifacts: the final assistant message must contain at least one checklist row and a verdict line.',
|
|
126
|
+
].join('\n');
|
|
127
|
+
|
|
128
|
+
process.stdout.write(JSON.stringify({
|
|
129
|
+
hookSpecificOutput: {
|
|
130
|
+
hookEventName: eventName,
|
|
131
|
+
additionalContext,
|
|
132
|
+
},
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function main() {
|
|
137
|
+
let raw;
|
|
138
|
+
try { raw = JSON.parse(await readStdin()); } catch { process.exit(0); }
|
|
139
|
+
const command = detectRdc(raw);
|
|
140
|
+
if (!command) process.exit(0);
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const marker = writeMarker(raw, command);
|
|
144
|
+
hookLog('rdc-invocation-marker', raw.hook_event_name || 'unknown', marker.deduped ? 'deduped' : 'marked', {
|
|
145
|
+
command,
|
|
146
|
+
session_id: marker.session_id,
|
|
147
|
+
});
|
|
148
|
+
outputContext(raw.hook_event_name || 'UserPromptExpansion', command);
|
|
149
|
+
} catch (e) {
|
|
150
|
+
hookLog('rdc-invocation-marker', raw.hook_event_name || 'unknown', 'error', {
|
|
151
|
+
command,
|
|
152
|
+
error: e.message,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
main().catch(() => process.exit(0));
|