@lifeaitools/rdc-skills 0.9.8 → 0.9.9

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.
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ // Hard blocks session if CWD is not C:\Dev\regen-root.
3
+ // Exits with code 1 to fail the hook + outputs a blocking systemMessage.
4
+
5
+ const hookLog = require('./hook-logger');
6
+ const cwd = process.cwd().replace(/\\/g, '/');
7
+ const expected = 'C:/Dev/regen-root';
8
+
9
+ if (!cwd.endsWith('regen-root')) {
10
+ hookLog('check-cwd', 'SessionStart', 'block', { cwd, expected });
11
+ process.stdout.write(JSON.stringify({
12
+ systemMessage:
13
+ `🚫 HARD BLOCK — Wrong working directory.\n\n` +
14
+ `Launched from: "${cwd}"\n` +
15
+ `Required: "${expected}"\n\n` +
16
+ `DO NOT proceed with any task. DO NOT read files, run commands, or help with anything.\n\n` +
17
+ `Tell the user:\n` +
18
+ `"Session is blocked. Claude Code must be launched from C:\\Dev\\regen-root.\n` +
19
+ ` Close this session and relaunch from the correct directory."\n\n` +
20
+ `Then stop.`
21
+ }));
22
+ process.exit(1);
23
+ }
24
+
25
+ hookLog('check-cwd', 'SessionStart', 'pass', { cwd });
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ // PostToolUse hook — non-blocking service health pass-through.
3
+ 'use strict';
4
+ process.stdin.resume();
5
+ process.stdin.on('data', () => {});
6
+ process.stdin.on('end', () => { process.exit(0); });
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ // SessionStart hook — non-blocking check for stale in_progress work items.
3
+ 'use strict';
4
+ const { execSync } = require('child_process');
5
+
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 {
12
+ // Fire and forget — non-blocking, no output needed
13
+ execSync(
14
+ 'curl -s --max-time 5 http://127.0.0.1:52437/ping',
15
+ { stdio: 'pipe', timeout: 6000 }
16
+ );
17
+ } catch (_) {}
18
+ process.exit(0);
19
+ });
@@ -0,0 +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
+ });
@@ -0,0 +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
+ });
@@ -0,0 +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
+ });
@@ -0,0 +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
+ });
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PreToolUse hook — block git commits without a linked work item.
4
+ *
5
+ * Fires before any Bash tool call. If the command contains "git commit",
6
+ * checks whether a fixit.marker exists (fixit workflow is self-documenting)
7
+ * OR the commit message references a work item ID or conventional type.
8
+ *
9
+ * BLOCKS the commit if:
10
+ * - No fixit.marker exists
11
+ * - Commit message has no work item reference AND no conventional commit type
12
+ *
13
+ * A "work item reference" is any of:
14
+ * - A UUID pattern (work item ID)
15
+ * - "#<issue>" reference
16
+ * - Conventional commit prefix: feat/fix/chore/refactor/test/docs/style/perf/ci/build
17
+ */
18
+
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+ const hookLog = require('./hook-logger');
22
+
23
+ const MARKER_FILE = path.join(
24
+ process.env.USERPROFILE || process.env.HOME || 'C:/Users/DaveLadouceur',
25
+ '.claude',
26
+ 'fixit.marker'
27
+ );
28
+
29
+ const CONVENTIONAL_TYPES = /^(feat|fix|chore|refactor|test|docs|style|perf|ci|build|revert)(\(.+\))?:/i;
30
+ const UUID_PATTERN = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
31
+ const ISSUE_REF = /#[a-zA-Z0-9-]+/;
32
+
33
+ function main() {
34
+ let input = '';
35
+ process.stdin.resume();
36
+ process.stdin.setEncoding('utf8');
37
+
38
+ process.stdin.on('data', chunk => { input += chunk; });
39
+ process.stdin.on('end', () => {
40
+ let toolInput;
41
+ try {
42
+ toolInput = JSON.parse(input);
43
+ } catch {
44
+ process.exit(0);
45
+ }
46
+
47
+ if (toolInput.tool_name !== 'Bash') {
48
+ process.exit(0);
49
+ }
50
+
51
+ const command = toolInput.tool_input?.command || '';
52
+ if (!command.includes('git commit')) {
53
+ process.exit(0);
54
+ }
55
+
56
+ // fixit.marker means this is a self-documenting fixit session — allow
57
+ if (fs.existsSync(MARKER_FILE)) {
58
+ hookLog('require-work-item', 'PreToolUse', 'pass-fixit', {});
59
+ process.exit(0);
60
+ }
61
+
62
+ // Extract -m "..." message from command
63
+ const msgMatch = command.match(/-m\s+["']([^"']+)["']/s) ||
64
+ command.match(/-m\s+"([\s\S]+?)"\s*(?:&&|$)/);
65
+ const msg = msgMatch ? msgMatch[1] : command;
66
+
67
+ if (CONVENTIONAL_TYPES.test(msg.trim()) || UUID_PATTERN.test(msg) || ISSUE_REF.test(msg)) {
68
+ hookLog('require-work-item', 'PreToolUse', 'pass', { msg: msg.slice(0, 80) });
69
+ process.exit(0);
70
+ }
71
+
72
+ hookLog('require-work-item', 'PreToolUse', 'warn', { msg: msg.slice(0, 80) });
73
+ // Warn only — never hard-block commits. A missing work item is informational,
74
+ // not a blocker. Conventional commit format is sufficient self-documentation.
75
+ process.stdout.write(JSON.stringify({
76
+ systemMessage: `⚠️ Commit has no work item reference or conventional commit type.\n` +
77
+ `Preferred format: fix(<scope>): <message> — proceeding anyway.`
78
+ }));
79
+ process.exit(0);
80
+ });
81
+ }
82
+
83
+ main();
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ // PostCompact hook — write a brief for session restart context.
3
+ 'use strict';
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const HOME = process.env.USERPROFILE || process.env.HOME;
7
+ const BRIEF = path.join(HOME, '.claude', 'restart-brief.md');
8
+
9
+ let input = '';
10
+ process.stdin.resume();
11
+ process.stdin.setEncoding('utf8');
12
+ process.stdin.on('data', c => { input += c; });
13
+ process.stdin.on('end', () => {
14
+ try {
15
+ const ts = new Date().toISOString();
16
+ fs.writeFileSync(BRIEF, `# Restart Brief\nGenerated: ${ts}\n\nContext compacted. Resume from work items queue.\n`);
17
+ } catch (_) {}
18
+ process.exit(0);
19
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/rdc-skills",
3
- "version": "0.9.8",
3
+ "version": "0.9.9",
4
4
  "description": "RDC typed-agent dispatch skill suite for Claude Code \u00e2\u20ac\u201d plan, build, review, overnight builds",
5
5
  "keywords": [
6
6
  "claude-code",