@lifeaitools/rdc-skills 0.24.42 → 0.25.1

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.
Files changed (202) hide show
  1. package/.claude/settings.json +15 -15
  2. package/.claude-plugin/marketplace.json +21 -21
  3. package/.claude-plugin/plugin.json +1560 -1371
  4. package/.github/workflows/publish.yml +34 -34
  5. package/.github/workflows/self-test.yml +58 -58
  6. package/CHANGELOG.md +322 -310
  7. package/LICENSE +21 -21
  8. package/MANIFEST.md +224 -221
  9. package/README.md +377 -376
  10. package/README.sandbox.md +3 -3
  11. package/assets/watcher/viewer.html +164 -164
  12. package/bin/rdc-skills-mcp.mjs +316 -316
  13. package/commands/build.md +183 -183
  14. package/commands/collab.md +180 -180
  15. package/commands/deploy.md +152 -152
  16. package/commands/design.md +31 -31
  17. package/commands/edit.md +28 -28
  18. package/commands/fixit.md +150 -124
  19. package/commands/handoff.md +173 -173
  20. package/commands/help.md +95 -95
  21. package/commands/overnight.md +220 -220
  22. package/commands/plan.md +158 -158
  23. package/commands/preplan.md +131 -131
  24. package/commands/prototype.md +145 -145
  25. package/commands/release.md +49 -49
  26. package/commands/report.md +99 -99
  27. package/commands/review.md +120 -120
  28. package/commands/self-test.md +113 -113
  29. package/commands/status.md +86 -86
  30. package/commands/watch.md +98 -98
  31. package/commands/workitems.md +137 -137
  32. package/git-sha.json +1 -1
  33. package/guides/agent-bootstrap.md +295 -295
  34. package/guides/agents/backend.md +104 -104
  35. package/guides/agents/content.md +94 -94
  36. package/guides/agents/cs2.md +56 -56
  37. package/guides/agents/data.md +87 -87
  38. package/guides/agents/design.md +77 -77
  39. package/guides/agents/frontend.md +92 -92
  40. package/guides/agents/infrastructure.md +81 -81
  41. package/guides/agents/setup.md +281 -281
  42. package/guides/agents/verify.md +151 -151
  43. package/guides/agents/viz.md +106 -106
  44. package/guides/backend.md +146 -146
  45. package/guides/content.md +147 -147
  46. package/guides/cs2.md +190 -190
  47. package/guides/data.md +123 -123
  48. package/guides/design.md +116 -116
  49. package/guides/engineering-behavior.md +43 -43
  50. package/guides/escalation-protocol.md +125 -125
  51. package/guides/frontend.md +151 -151
  52. package/guides/history-md-spec.md +297 -297
  53. package/guides/infrastructure.md +179 -179
  54. package/guides/lessons-learned-spec.md +145 -151
  55. package/guides/output-contract.md +108 -108
  56. package/guides/publish-md-spec.md +289 -289
  57. package/guides/rdc-skills-startup.md +30 -30
  58. package/guides/verify.md +11 -11
  59. package/hooks/check-cwd.js +31 -31
  60. package/hooks/check-rdc-environment.js +164 -164
  61. package/hooks/check-services.js +6 -6
  62. package/hooks/check-stale-work-items.js +19 -19
  63. package/hooks/foreground-process-gate.js +128 -128
  64. package/hooks/gate-watchdog-selfcheck.js +257 -257
  65. package/hooks/hook-logger.js +25 -25
  66. package/hooks/lib/run-evidence-gate.mjs +241 -241
  67. package/hooks/no-stop-open-epics.js +127 -127
  68. package/hooks/post-tool-batch-gate.js +203 -203
  69. package/hooks/post-work-check.js +21 -21
  70. package/hooks/postcompact-log.js +13 -13
  71. package/hooks/precompact-log.js +13 -13
  72. package/hooks/rate-limit-retry.js +46 -46
  73. package/hooks/rdc-invocation-marker.js +157 -157
  74. package/hooks/rdc-output-contract-gate.js +94 -94
  75. package/hooks/require-work-item-on-commit.js +294 -294
  76. package/hooks/restart-brief.js +19 -19
  77. package/hooks/run-hidden-hook.ps1 +47 -47
  78. package/hooks/task-completed-gate.js +274 -274
  79. package/hooks/work-item-exit-gate.js +944 -944
  80. package/lib/catalog.mjs +236 -236
  81. package/lib/cloud-rewrite.mjs +155 -155
  82. package/package.json +57 -57
  83. package/rules/work-items-rpc.md +520 -520
  84. package/scaffold/templates/HISTORY.md.template +39 -39
  85. package/scaffold/templates/PUBLISH.md.template +21 -21
  86. package/scaffold/templates/brochure-studio-default.html +70 -70
  87. package/scripts/acceptance.mjs +502 -502
  88. package/scripts/fixtures/guides/bad-guide.md +15 -15
  89. package/scripts/fixtures/guides-clean/good-guide.md +16 -16
  90. package/scripts/install-rdc-skills.js +1401 -1289
  91. package/scripts/install.ps1 +202 -202
  92. package/scripts/install.sh +132 -132
  93. package/scripts/lib/assertions.mjs +287 -287
  94. package/scripts/lib/manifest-schema.mjs +754 -754
  95. package/scripts/lib/runner.mjs +465 -465
  96. package/scripts/lib/sandbox.mjs +435 -435
  97. package/scripts/prepack.mjs +32 -32
  98. package/scripts/rdc-brochure.mjs +482 -482
  99. package/scripts/rdc-design-cli.mjs +134 -134
  100. package/scripts/rebuild-mcp.mjs +107 -107
  101. package/scripts/self-test.mjs +1460 -1460
  102. package/scripts/stamp-git-sha.mjs +29 -29
  103. package/scripts/test-guide-validator.mjs +196 -196
  104. package/scripts/test-rdc-hooks.mjs +145 -145
  105. package/scripts/uninstall.ps1 +77 -77
  106. package/scripts/uninstall.sh +69 -69
  107. package/scripts/update.ps1 +43 -43
  108. package/scripts/update.sh +43 -43
  109. package/scripts/validate-place-histories.js +461 -461
  110. package/scripts/validate-publish-manifests.js +502 -424
  111. package/scripts/watch-init.mjs +100 -100
  112. package/skills/brochure/SKILL.md +107 -107
  113. package/skills/build/SKILL.md +578 -563
  114. package/skills/channel-formatter/SKILL.md +538 -533
  115. package/skills/co-develop/SKILL.md +196 -196
  116. package/skills/collab/SKILL.md +239 -239
  117. package/skills/convert/SKILL.md +167 -140
  118. package/skills/deploy/SKILL.md +541 -541
  119. package/skills/design/SKILL.md +211 -211
  120. package/skills/design/reference/ownership.md +16 -16
  121. package/skills/design/reference/rampa.md +92 -92
  122. package/skills/design/reference/studio-model.md +153 -153
  123. package/skills/edit/SKILL.md +98 -98
  124. package/skills/env/SKILL.md +141 -0
  125. package/skills/fixit/SKILL.md +203 -165
  126. package/skills/fs-mcp/SKILL.md +183 -148
  127. package/skills/handoff/SKILL.md +236 -236
  128. package/skills/help/SKILL.md +143 -143
  129. package/skills/housekeeping/SKILL.md +160 -219
  130. package/skills/lifeai-brochure-author/SKILL.md +340 -340
  131. package/skills/new-model/SKILL.md +49 -0
  132. package/skills/onramp/SKILL.md +1459 -0
  133. package/skills/overnight/SKILL.md +251 -251
  134. package/skills/plan/SKILL.md +345 -345
  135. package/skills/preplan/SKILL.md +90 -90
  136. package/skills/prototype/SKILL.md +150 -150
  137. package/skills/rdc-brochurify/SKILL.md +245 -245
  138. package/skills/rdc-extract-verifier-rules/SKILL.md +191 -191
  139. package/skills/regen-media/SKILL.md +94 -0
  140. package/skills/release/SKILL.md +140 -140
  141. package/skills/report/SKILL.md +100 -100
  142. package/skills/review/SKILL.md +160 -152
  143. package/skills/rpms-filemap/SKILL.cloud.md +111 -111
  144. package/skills/rpms-filemap/SKILL.md +111 -111
  145. package/skills/self-test/SKILL.md +132 -132
  146. package/skills/status/SKILL.md +99 -99
  147. package/skills/terminal-config/SKILL.md +62 -62
  148. package/skills/tests/MATRIX.md +55 -54
  149. package/skills/tests/README.md +47 -47
  150. package/skills/tests/onramp.test.json +87 -0
  151. package/skills/tests/rdc-brochure.test.json +34 -34
  152. package/skills/tests/rdc-build.test.json +36 -36
  153. package/skills/tests/rdc-channel-formatter.test.json +45 -45
  154. package/skills/tests/rdc-co-develop.test.json +29 -29
  155. package/skills/tests/rdc-collab.test.json +29 -29
  156. package/skills/tests/rdc-convert.test.json +35 -35
  157. package/skills/tests/rdc-deploy.test.json +30 -30
  158. package/skills/tests/rdc-design.test.json +27 -27
  159. package/skills/tests/rdc-edit.test.json +29 -29
  160. package/skills/tests/rdc-fixit.test.json +36 -36
  161. package/skills/tests/rdc-fs-mcp.test.json +36 -36
  162. package/skills/tests/rdc-handoff.test.json +28 -28
  163. package/skills/tests/rdc-help.test.json +29 -29
  164. package/skills/tests/rdc-housekeeping.test.json +28 -32
  165. package/skills/tests/rdc-lifeai-brochure-author.test.json +35 -35
  166. package/skills/tests/rdc-overnight.test.json +37 -37
  167. package/skills/tests/rdc-plan.test.json +27 -27
  168. package/skills/tests/rdc-preplan.test.json +31 -31
  169. package/skills/tests/rdc-prototype.test.json +28 -28
  170. package/skills/tests/rdc-rdc-brochurify.test.json +23 -23
  171. package/skills/tests/rdc-rdc-extract-verifier-rules.test.json +34 -34
  172. package/skills/tests/rdc-regen-media.test.json +29 -0
  173. package/skills/tests/rdc-release.test.json +29 -29
  174. package/skills/tests/rdc-report.test.json +28 -28
  175. package/skills/tests/rdc-review.test.json +29 -29
  176. package/skills/tests/rdc-rpms-filemap.test.json +28 -28
  177. package/skills/tests/rdc-self-test.test.json +24 -24
  178. package/skills/tests/rdc-status.test.json +29 -29
  179. package/skills/tests/rdc-terminal-config.test.json +29 -29
  180. package/skills/tests/rdc-watch.test.json +24 -24
  181. package/skills/tests/rdc-workitems.test.json +27 -27
  182. package/skills/watch/SKILL.md +97 -97
  183. package/skills/workitems/SKILL.md +151 -151
  184. package/tests/acceptance.test.mjs +59 -59
  185. package/tests/channel-formatter.contract.test.mjs +251 -251
  186. package/tests/curl-surface.test.mjs +289 -289
  187. package/tests/harness-gates.test.mjs +325 -325
  188. package/tests/help-surface.test.mjs +61 -61
  189. package/tests/install-rdc-skills.test.mjs +49 -49
  190. package/tests/manifest-contract-fields.test.mjs +78 -78
  191. package/tests/mcp.test.mjs +271 -271
  192. package/tests/rdc-brochure.test.mjs +125 -125
  193. package/tests/require-work-item-on-commit.test.mjs +162 -162
  194. package/tests/run-evidence-gate.test.mjs +82 -82
  195. package/tests/skill-test-matrix.test.mjs +66 -66
  196. package/tests/validate-skills.js +27 -27
  197. package/tests/work-item-exit-gate-l2.test.mjs +368 -368
  198. package/tests/work-item-exit-gate-l3.test.mjs +197 -197
  199. package/RELEASE.md +0 -42
  200. package/tests/housekeeping-lessons-triage.test.mjs +0 -49
  201. package/tests/lessons-pipeline-contract.test.mjs +0 -27
  202. package/tests/release-contract.test.mjs +0 -16
@@ -1,31 +1,31 @@
1
- #!/usr/bin/env node
2
- // Hard blocks session if CWD is not the expected project directory.
3
- // Exits with code 1 to fail the hook + outputs a blocking systemMessage.
4
-
5
- const { execSync } = require('child_process');
6
- const hookLog = require('./hook-logger');
7
- const cwd = process.cwd().replace(/\\/g, '/');
8
-
9
- let expected = process.env.RDC_PROJECT_SCOPE || 'regen-root';
10
- try {
11
- expected = execSync('git rev-parse --show-toplevel', { encoding: 'utf8', stdio: 'pipe' })
12
- .trim().replace(/\\/g, '/');
13
- } catch (_) {}
14
-
15
- if (!cwd.endsWith(expected)) {
16
- hookLog('check-cwd', 'SessionStart', 'block', { cwd, expected });
17
- process.stdout.write(JSON.stringify({
18
- systemMessage:
19
- `🚫 HARD BLOCK — Wrong working directory.\n\n` +
20
- `Launched from: "${cwd}"\n` +
21
- `Required: "${expected}"\n\n` +
22
- `DO NOT proceed with any task. DO NOT read files, run commands, or help with anything.\n\n` +
23
- `Tell the user:\n` +
24
- `"Session is blocked. Claude Code must be launched from ${expected}.\n` +
25
- ` Close this session and relaunch from the correct directory."\n\n` +
26
- `Then stop.`
27
- }));
28
- process.exit(1);
29
- }
30
-
31
- hookLog('check-cwd', 'SessionStart', 'pass', { cwd });
1
+ #!/usr/bin/env node
2
+ // Hard blocks session if CWD is not the expected project directory.
3
+ // Exits with code 1 to fail the hook + outputs a blocking systemMessage.
4
+
5
+ const { execSync } = require('child_process');
6
+ const hookLog = require('./hook-logger');
7
+ const cwd = process.cwd().replace(/\\/g, '/');
8
+
9
+ let expected = process.env.RDC_PROJECT_SCOPE || 'regen-root';
10
+ try {
11
+ expected = execSync('git rev-parse --show-toplevel', { encoding: 'utf8', stdio: 'pipe' })
12
+ .trim().replace(/\\/g, '/');
13
+ } catch (_) {}
14
+
15
+ if (!cwd.endsWith(expected)) {
16
+ hookLog('check-cwd', 'SessionStart', 'block', { cwd, expected });
17
+ process.stdout.write(JSON.stringify({
18
+ systemMessage:
19
+ `🚫 HARD BLOCK — Wrong working directory.\n\n` +
20
+ `Launched from: "${cwd}"\n` +
21
+ `Required: "${expected}"\n\n` +
22
+ `DO NOT proceed with any task. DO NOT read files, run commands, or help with anything.\n\n` +
23
+ `Tell the user:\n` +
24
+ `"Session is blocked. Claude Code must be launched from ${expected}.\n` +
25
+ ` Close this session and relaunch from the correct directory."\n\n` +
26
+ `Then stop.`
27
+ }));
28
+ process.exit(1);
29
+ }
30
+
31
+ hookLog('check-cwd', 'SessionStart', 'pass', { cwd });
@@ -1,164 +1,164 @@
1
- #!/usr/bin/env node
2
- /**
3
- * SessionStart hook — hard gate for the RDC skills runtime.
4
- *
5
- * This hook repairs the approved install path when it can do so safely:
6
- * npm install -g @lifeaitools/rdc-skills@latest
7
- * rdc-skills-install --profile lifeai --project-root <repo> --write-startup-blocks
8
- *
9
- * It then verifies that the local MCP server answers /health and sees a real
10
- * skills catalog. If repair fails, startup is blocked before agents trust stale
11
- * copied skill files.
12
- */
13
- 'use strict';
14
-
15
- const fs = require('fs');
16
- const os = require('os');
17
- const path = require('path');
18
- const { execFileSync, execSync } = require('child_process');
19
- const hookLog = require('./hook-logger');
20
-
21
- const MIN_SKILLS = 20;
22
- const MCP_HEALTH = 'http://127.0.0.1:3110/health';
23
- const PACKAGE = '@lifeaitools/rdc-skills';
24
- const stampPath = path.join(os.tmpdir(), 'rdc-skills-environment-last-repair.json');
25
-
26
- function q(value) {
27
- return `"${String(value).replace(/"/g, '\\"')}"`;
28
- }
29
-
30
- function run(command, args, opts = {}) {
31
- return execFileSync(command, args, {
32
- encoding: 'utf8',
33
- stdio: ['ignore', 'pipe', 'pipe'],
34
- timeout: opts.timeout || 30000,
35
- cwd: opts.cwd || process.cwd(),
36
- env: { ...process.env, ...(opts.env || {}) },
37
- }).trim();
38
- }
39
-
40
- function shell(command, opts = {}) {
41
- return execSync(command, {
42
- encoding: 'utf8',
43
- stdio: ['ignore', 'pipe', 'pipe'],
44
- timeout: opts.timeout || 30000,
45
- cwd: opts.cwd || process.cwd(),
46
- env: { ...process.env, ...(opts.env || {}) },
47
- }).trim();
48
- }
49
-
50
- function commandExists(name) {
51
- try {
52
- if (process.platform === 'win32') run('where.exe', [name], { timeout: 5000 });
53
- else run('which', [name], { timeout: 5000 });
54
- return true;
55
- } catch {
56
- return false;
57
- }
58
- }
59
-
60
- function projectRoot() {
61
- try {
62
- return shell('git rev-parse --show-toplevel', { timeout: 5000 });
63
- } catch {
64
- return process.cwd();
65
- }
66
- }
67
-
68
- function globalPackageJson() {
69
- try {
70
- const root = shell('npm root -g', { timeout: 10000 });
71
- const pkg = path.join(root, '@lifeaitools', 'rdc-skills', 'package.json');
72
- if (!fs.existsSync(pkg)) return null;
73
- return JSON.parse(fs.readFileSync(pkg, 'utf8'));
74
- } catch {
75
- return null;
76
- }
77
- }
78
-
79
- async function health() {
80
- try {
81
- const res = await fetch(MCP_HEALTH, { signal: AbortSignal.timeout(4000) });
82
- if (!res.ok) return null;
83
- return await res.json();
84
- } catch {
85
- return null;
86
- }
87
- }
88
-
89
- function recentlyRepaired() {
90
- try {
91
- const data = JSON.parse(fs.readFileSync(stampPath, 'utf8'));
92
- return Date.now() - Date.parse(data.ts) < 10 * 60 * 1000;
93
- } catch {
94
- return false;
95
- }
96
- }
97
-
98
- function markRepaired(reason) {
99
- try {
100
- fs.writeFileSync(stampPath, JSON.stringify({ ts: new Date().toISOString(), reason }, null, 2));
101
- } catch {
102
- /* best effort */
103
- }
104
- }
105
-
106
- function repair(reason) {
107
- hookLog('check-rdc-environment', 'SessionStart', 'repair', { reason });
108
- shell(`npm install -g ${q(`${PACKAGE}@latest`)}`, { timeout: 120000 });
109
- shell(`rdc-skills-install --profile lifeai --project-root ${q(projectRoot())} --write-startup-blocks`, { timeout: 180000 });
110
- markRepaired(reason);
111
- }
112
-
113
- function block(message, details = {}) {
114
- hookLog('check-rdc-environment', 'SessionStart', 'block', { message, ...details });
115
- process.stdout.write(JSON.stringify({
116
- systemMessage:
117
- `HARD BLOCK — RDC skills environment is not healthy.\n\n` +
118
- `${message}\n\n` +
119
- `Do not proceed with RDC work until the approved install path is repaired:\n` +
120
- `npm install -g @lifeaitools/rdc-skills@latest\n` +
121
- `rdc-skills-install --profile lifeai --project-root ${projectRoot()} --write-startup-blocks`
122
- }));
123
- process.exit(1);
124
- }
125
-
126
- async function main() {
127
- const initialPkg = globalPackageJson();
128
- const initialHealth = await health();
129
- const reasons = [];
130
-
131
- if (!initialPkg) reasons.push('global package missing');
132
- if (!commandExists('rdc-skills-install')) reasons.push('installer command missing');
133
- if (!initialHealth || initialHealth.status !== 'ok' || Number(initialHealth.skills || 0) < MIN_SKILLS) {
134
- reasons.push('local MCP health/catalog invalid');
135
- }
136
-
137
- if (reasons.length) {
138
- if (recentlyRepaired()) {
139
- block(`RDC skills still unhealthy after a recent repair attempt: ${reasons.join(', ')}`);
140
- }
141
- try {
142
- repair(reasons.join(', '));
143
- } catch (err) {
144
- block(`Automatic RDC skills repair failed: ${err.message}`, { reasons });
145
- }
146
- }
147
-
148
- const finalPkg = globalPackageJson();
149
- const finalHealth = await health();
150
- if (!finalPkg) block('Global @lifeaitools/rdc-skills package is missing after repair.');
151
- if (!commandExists('rdc-skills-install')) block('rdc-skills-install is missing after repair.');
152
- if (!finalHealth || finalHealth.status !== 'ok' || Number(finalHealth.skills || 0) < MIN_SKILLS) {
153
- block(`rdc-skills MCP is unhealthy after repair. Health: ${JSON.stringify(finalHealth)}`);
154
- }
155
-
156
- hookLog('check-rdc-environment', 'SessionStart', 'pass', {
157
- version: finalPkg.version,
158
- mcpVersion: finalHealth.version,
159
- skills: finalHealth.skills,
160
- });
161
- process.exit(0);
162
- }
163
-
164
- main().catch((err) => block(`RDC skills environment check crashed: ${err.message}`));
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SessionStart hook — hard gate for the RDC skills runtime.
4
+ *
5
+ * This hook repairs the approved install path when it can do so safely:
6
+ * npm install -g @lifeaitools/rdc-skills@latest
7
+ * rdc-skills-install --profile lifeai --project-root <repo> --write-startup-blocks
8
+ *
9
+ * It then verifies that the local MCP server answers /health and sees a real
10
+ * skills catalog. If repair fails, startup is blocked before agents trust stale
11
+ * copied skill files.
12
+ */
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const os = require('os');
17
+ const path = require('path');
18
+ const { execFileSync, execSync } = require('child_process');
19
+ const hookLog = require('./hook-logger');
20
+
21
+ const MIN_SKILLS = 20;
22
+ const MCP_HEALTH = 'http://127.0.0.1:3110/health';
23
+ const PACKAGE = '@lifeaitools/rdc-skills';
24
+ const stampPath = path.join(os.tmpdir(), 'rdc-skills-environment-last-repair.json');
25
+
26
+ function q(value) {
27
+ return `"${String(value).replace(/"/g, '\\"')}"`;
28
+ }
29
+
30
+ function run(command, args, opts = {}) {
31
+ return execFileSync(command, args, {
32
+ encoding: 'utf8',
33
+ stdio: ['ignore', 'pipe', 'pipe'],
34
+ timeout: opts.timeout || 30000,
35
+ cwd: opts.cwd || process.cwd(),
36
+ env: { ...process.env, ...(opts.env || {}) },
37
+ }).trim();
38
+ }
39
+
40
+ function shell(command, opts = {}) {
41
+ return execSync(command, {
42
+ encoding: 'utf8',
43
+ stdio: ['ignore', 'pipe', 'pipe'],
44
+ timeout: opts.timeout || 30000,
45
+ cwd: opts.cwd || process.cwd(),
46
+ env: { ...process.env, ...(opts.env || {}) },
47
+ }).trim();
48
+ }
49
+
50
+ function commandExists(name) {
51
+ try {
52
+ if (process.platform === 'win32') run('where.exe', [name], { timeout: 5000 });
53
+ else run('which', [name], { timeout: 5000 });
54
+ return true;
55
+ } catch {
56
+ return false;
57
+ }
58
+ }
59
+
60
+ function projectRoot() {
61
+ try {
62
+ return shell('git rev-parse --show-toplevel', { timeout: 5000 });
63
+ } catch {
64
+ return process.cwd();
65
+ }
66
+ }
67
+
68
+ function globalPackageJson() {
69
+ try {
70
+ const root = shell('npm root -g', { timeout: 10000 });
71
+ const pkg = path.join(root, '@lifeaitools', 'rdc-skills', 'package.json');
72
+ if (!fs.existsSync(pkg)) return null;
73
+ return JSON.parse(fs.readFileSync(pkg, 'utf8'));
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+
79
+ async function health() {
80
+ try {
81
+ const res = await fetch(MCP_HEALTH, { signal: AbortSignal.timeout(4000) });
82
+ if (!res.ok) return null;
83
+ return await res.json();
84
+ } catch {
85
+ return null;
86
+ }
87
+ }
88
+
89
+ function recentlyRepaired() {
90
+ try {
91
+ const data = JSON.parse(fs.readFileSync(stampPath, 'utf8'));
92
+ return Date.now() - Date.parse(data.ts) < 10 * 60 * 1000;
93
+ } catch {
94
+ return false;
95
+ }
96
+ }
97
+
98
+ function markRepaired(reason) {
99
+ try {
100
+ fs.writeFileSync(stampPath, JSON.stringify({ ts: new Date().toISOString(), reason }, null, 2));
101
+ } catch {
102
+ /* best effort */
103
+ }
104
+ }
105
+
106
+ function repair(reason) {
107
+ hookLog('check-rdc-environment', 'SessionStart', 'repair', { reason });
108
+ shell(`npm install -g ${q(`${PACKAGE}@latest`)}`, { timeout: 120000 });
109
+ shell(`rdc-skills-install --profile lifeai --project-root ${q(projectRoot())} --write-startup-blocks`, { timeout: 180000 });
110
+ markRepaired(reason);
111
+ }
112
+
113
+ function block(message, details = {}) {
114
+ hookLog('check-rdc-environment', 'SessionStart', 'block', { message, ...details });
115
+ process.stdout.write(JSON.stringify({
116
+ systemMessage:
117
+ `HARD BLOCK — RDC skills environment is not healthy.\n\n` +
118
+ `${message}\n\n` +
119
+ `Do not proceed with RDC work until the approved install path is repaired:\n` +
120
+ `npm install -g @lifeaitools/rdc-skills@latest\n` +
121
+ `rdc-skills-install --profile lifeai --project-root ${projectRoot()} --write-startup-blocks`
122
+ }));
123
+ process.exit(1);
124
+ }
125
+
126
+ async function main() {
127
+ const initialPkg = globalPackageJson();
128
+ const initialHealth = await health();
129
+ const reasons = [];
130
+
131
+ if (!initialPkg) reasons.push('global package missing');
132
+ if (!commandExists('rdc-skills-install')) reasons.push('installer command missing');
133
+ if (!initialHealth || initialHealth.status !== 'ok' || Number(initialHealth.skills || 0) < MIN_SKILLS) {
134
+ reasons.push('local MCP health/catalog invalid');
135
+ }
136
+
137
+ if (reasons.length) {
138
+ if (recentlyRepaired()) {
139
+ block(`RDC skills still unhealthy after a recent repair attempt: ${reasons.join(', ')}`);
140
+ }
141
+ try {
142
+ repair(reasons.join(', '));
143
+ } catch (err) {
144
+ block(`Automatic RDC skills repair failed: ${err.message}`, { reasons });
145
+ }
146
+ }
147
+
148
+ const finalPkg = globalPackageJson();
149
+ const finalHealth = await health();
150
+ if (!finalPkg) block('Global @lifeaitools/rdc-skills package is missing after repair.');
151
+ if (!commandExists('rdc-skills-install')) block('rdc-skills-install is missing after repair.');
152
+ if (!finalHealth || finalHealth.status !== 'ok' || Number(finalHealth.skills || 0) < MIN_SKILLS) {
153
+ block(`rdc-skills MCP is unhealthy after repair. Health: ${JSON.stringify(finalHealth)}`);
154
+ }
155
+
156
+ hookLog('check-rdc-environment', 'SessionStart', 'pass', {
157
+ version: finalPkg.version,
158
+ mcpVersion: finalHealth.version,
159
+ skills: finalHealth.skills,
160
+ });
161
+ process.exit(0);
162
+ }
163
+
164
+ main().catch((err) => block(`RDC skills environment check crashed: ${err.message}`));
@@ -1,6 +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); });
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); });
@@ -1,19 +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
- });
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
+ });