@lifeaitools/rdc-skills 0.25.9 → 0.26.0

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 (77) hide show
  1. package/.claude-plugin/plugin.json +1608 -1551
  2. package/.github/workflows/self-test.yml +34 -34
  3. package/CHANGELOG.md +332 -319
  4. package/MANIFEST.md +224 -224
  5. package/README.md +368 -367
  6. package/RELEASE.md +50 -45
  7. package/bin/rdc-skills-mcp.mjs +1 -1
  8. package/commands/build.md +181 -181
  9. package/commands/collab.md +180 -180
  10. package/commands/deploy.md +173 -148
  11. package/commands/fixit.md +150 -150
  12. package/commands/handoff.md +173 -173
  13. package/commands/overnight.md +220 -220
  14. package/commands/plan.md +158 -158
  15. package/commands/preplan.md +131 -131
  16. package/commands/prototype.md +145 -145
  17. package/commands/report.md +99 -99
  18. package/commands/review.md +120 -120
  19. package/commands/status.md +86 -86
  20. package/commands/workitems.md +127 -127
  21. package/deploy/install-systemd.sh +16 -0
  22. package/deploy/systemd/rdc-skills-mcp.service +19 -0
  23. package/git-sha.json +1 -1
  24. package/guides/agent-bootstrap.md +195 -195
  25. package/guides/agents/backend.md +102 -102
  26. package/guides/agents/content.md +94 -94
  27. package/guides/agents/cs2.md +56 -56
  28. package/guides/agents/data.md +86 -86
  29. package/guides/agents/design.md +77 -77
  30. package/guides/agents/frontend.md +91 -91
  31. package/guides/agents/infrastructure.md +81 -81
  32. package/guides/agents/setup.md +272 -272
  33. package/guides/agents/verify.md +119 -119
  34. package/guides/agents/viz.md +106 -106
  35. package/guides/orchestration-epic.md +17 -0
  36. package/hooks/check-rdc-environment.js +172 -318
  37. package/hooks/lib/box-lock.js +28 -8
  38. package/package.json +1 -2
  39. package/scripts/install-rdc-skills.js +1244 -1474
  40. package/scripts/probe-installed-hooks.mjs +2 -2
  41. package/scripts/self-test.mjs +1459 -1459
  42. package/scripts/validate-publish-manifests.js +502 -502
  43. package/skills/build/SKILL.md +662 -578
  44. package/skills/channel-formatter/SKILL.md +538 -538
  45. package/skills/collab/SKILL.md +239 -239
  46. package/skills/convert/SKILL.md +138 -138
  47. package/skills/deploy/SKILL.md +583 -541
  48. package/skills/design/SKILL.md +205 -205
  49. package/skills/env/SKILL.md +146 -139
  50. package/skills/fixit/SKILL.md +203 -203
  51. package/skills/handoff/SKILL.md +236 -236
  52. package/skills/housekeeping/SKILL.md +189 -189
  53. package/skills/onramp/SKILL.md +1459 -1459
  54. package/skills/overnight/SKILL.md +251 -251
  55. package/skills/plan/SKILL.md +345 -345
  56. package/skills/preplan/SKILL.md +90 -90
  57. package/skills/prototype/SKILL.md +150 -150
  58. package/skills/refactor/SKILL.md +51 -0
  59. package/skills/regen-media/SKILL.md +94 -94
  60. package/skills/release/SKILL.md +140 -140
  61. package/skills/report/SKILL.md +100 -100
  62. package/skills/review/SKILL.md +151 -151
  63. package/skills/self-test/SKILL.md +108 -108
  64. package/skills/status/SKILL.md +99 -99
  65. package/skills/tests/MATRIX.md +59 -55
  66. package/skills/tests/README.md +1 -1
  67. package/skills/tests/onramp.test.json +101 -87
  68. package/skills/tests/rdc-env.test.json +12 -0
  69. package/skills/tests/rdc-new-model.test.json +12 -0
  70. package/skills/tests/rdc-refactor.test.json +29 -0
  71. package/skills/tests/rdc-regen-media.test.json +29 -29
  72. package/skills/watch/SKILL.md +84 -84
  73. package/skills/workitems/SKILL.md +151 -151
  74. package/tests/no-local-pm2.test.mjs +45 -0
  75. package/scripts/local-install-with-stop.sh +0 -41
  76. package/scripts/probe-lock-holders.mjs +0 -36
  77. package/scripts/rebuild-mcp.mjs +0 -107
@@ -1,151 +1,151 @@
1
- ---
2
- name: rdc:workitems
3
- description: "Usage `rdc:workitems [add|done|status|list|query]` — Manual work item CRUD outside the normal build flow: add to backlog, mark done, update status, list open epics, query by label."
4
- ---
5
-
6
- > **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
7
- > Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
8
- > One checklist upfront, updated in place, shown again at end with a 1-line verdict.
9
-
10
- > If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md`).
11
-
12
- > **Sandbox contract:** This skill honors `RDC_TEST=1` per `guides/agent-bootstrap.md` § RDC_TEST Sandbox Contract. Destructive external calls short-circuit under the flag. Supabase work item writes (`insert_work_item`, `update_work_item_status`, `bump_epic_version`) are skipped under `RDC_TEST=1`.
13
-
14
-
15
- # rdc:workitems — Work Item Management
16
-
17
- ## When to Use
18
- - Project lead says "create a ticket", "add to backlog", "log this bug", "update status"
19
- - An agent needs to create or update epics/tasks during a build session
20
- - Checking what work is open before starting a new epic
21
-
22
- ## Arguments
23
-
24
- - `rdc:workitems epics` — list open epics
25
- - `rdc:workitems add <title> --epic <id>` — create a task under an epic
26
- - `rdc:workitems update <id> --status <status>` — update a work item's status
27
- - `rdc:workitems done <id>` — mark a task complete
28
- - `rdc:workitems list <epic-id>` — list tasks in an epic
29
-
30
- ## Rules
31
-
32
- 1. **Epic first, always.** Never create a task without a parent epic.
33
- 2. **Label everything.** Minimum one system label per item.
34
- 3. **Check first.** `get_open_epics()` before creating anything new.
35
- 4. **Link design docs.** Put `.rdc/plans/` or `.rdc/research/` paths in descriptions (fallback: `.rdc/plans/` / `.rdc/research/`).
36
-
37
- ## Read Epics
38
-
39
- ```sql
40
- SELECT get_open_epics();
41
- SELECT get_open_epics('urgent');
42
- SELECT get_open_epics(p_label_filter := 'custom-label');
43
- ```
44
-
45
- ## Create Epic
46
-
47
- ```sql
48
- SELECT insert_work_item(
49
- p_title := 'EPIC: Clear descriptive title',
50
- p_description := 'What and why. Reference .rdc/plans/<n>.md if applicable.',
51
- p_item_type := 'epic',
52
- p_priority := 'high',
53
- p_labels := ARRAY['system-label'],
54
- p_source := 'planning'
55
- );
56
- ```
57
-
58
- ## Create Task
59
-
60
- ```sql
61
- SELECT insert_work_item(
62
- p_title := 'Specific actionable task title',
63
- p_description := 'What: <deliverable>
64
- Where: <files to create/modify>
65
- Agent type: frontend | backend | data | design | infra | content | cs2
66
- Guide: .rdc/guides/<type>.md (fallback: .rdc/guides/<type>.md)
67
- Design doc: .rdc/plans/<n>.md (fallback: .rdc/plans/<n>.md, if exists)
68
- Depends on: <other task title if sequential>
69
- Est: <hours>',
70
- p_parent_id := '<epic-uuid>'::uuid,
71
- p_item_type := 'task',
72
- p_priority := 'high',
73
- p_labels := ARRAY['system-label'],
74
- p_estimated_hours := 2,
75
- p_source := 'planning'
76
- );
77
- ```
78
-
79
- ## Update Status
80
-
81
- ```sql
82
- SELECT update_work_item_status('<uuid>'::uuid, 'in_progress');
83
- SELECT update_work_item_status('<uuid>'::uuid, 'review', '["Implementation complete; ready for validator"]'::jsonb, '<agent-session-id>', 'agent');
84
- SELECT update_work_item_status('<uuid>'::uuid, 'done', '["Validator verified implementation report, CodeFlow post, and checklist evidence"]'::jsonb, '<validator-session-id>', 'validator');
85
- SELECT update_work_item_status('<uuid>'::uuid, 'blocked', '["Why it is blocked"]'::jsonb);
86
- ```
87
-
88
- Non-epic `done` is a validator-only close and requires an existing
89
- `implementation_report.codeflow_post`, checked required checklist evidence, and
90
- originating-agent tick audit.
91
-
92
- ## Read Tasks in an Epic
93
-
94
- ```sql
95
- SELECT get_work_items_by_epic('<epic-uuid>'::uuid);
96
- SELECT get_work_items_by_epic('<epic-uuid>'::uuid, 'todo');
97
- ```
98
-
99
- ## Bump Epic Version
100
-
101
- ```sql
102
- SELECT bump_epic_version('<epic-uuid>'::uuid, '0.2.0', 'What changed', 'planning');
103
- ```
104
-
105
- ## Valid Values
106
-
107
- | Field | Values |
108
- |-------|--------|
109
- | status | `todo` `in_progress` `blocked` `review` `done` `archived` |
110
- | priority | `urgent` `high` `normal` `low` |
111
- | item_type | `epic` `task` `subtask` `bug` `spike` |
112
-
113
- ## System Labels
114
-
115
- | Label | When |
116
- |-------|------|
117
- | `project-a` | Your-app-specific work |
118
- | `project-b` | Another app work |
119
- | `cs2` | Core paradigm packages |
120
- | `hail` | Grammar, DSL compiler |
121
- | `pal` | Moment windows, memory |
122
- | `virtue` | Virtue engine, coherence |
123
- | `marketing` | Outreach, campaigns |
124
- | `media` | Assets, R2, image pipeline |
125
- | `infrastructure` | CI/CD, deployment |
126
- | `ui` | Component library |
127
- | `data` | Schema, migrations |
128
- | `content` | Copy, messaging |
129
- | `website` | Public-facing sites |
130
-
131
- ## Agent Type → Guide File Reference
132
-
133
- | Type | Guide | Use For |
134
- |------|-------|---------|
135
- | `frontend` | .rdc/guides/frontend.md | React, pages, UI, Tailwind |
136
- | `backend` | .rdc/guides/backend.md | API routes, database, auth |
137
- | `data` | .rdc/guides/data.md | Migrations, schema, RPC |
138
- | `design` | .rdc/guides/design.md | Brand, palette, OG images |
139
- | `infra` | .rdc/guides/infrastructure.md | CI/CD, deploy, DNS |
140
- | `content` | .rdc/guides/content.md | Copy, messaging, tone |
141
- | `cs2` | .rdc/guides/cs2.md | CS 2.0 paradigm |
142
- | `hail` | .rdc/guides/cs2.md + packages/hail/CLAUDE.md | Grammar, DSL |
143
- | `viz` | .rdc/guides/frontend.md + design.md | Custom visualizations |
144
-
145
- ## What NOT to Do
146
-
147
- - Never raw INSERT/UPDATE against work items — RPC functions only
148
- - Never create tasks without a parent epic
149
- - Never leave labels empty
150
- - Never write vague titles ("Fix stuff") — be specific
151
- - Never put design intention in the task — put it in `.rdc/research/` (fallback: `.rdc/research/`) and link
1
+ ---
2
+ name: rdc:workitems
3
+ description: "Usage `rdc:workitems [add|done|status|list|query]` — Manual work item CRUD outside the normal build flow: add to backlog, mark done, update status, list open epics, query by label."
4
+ ---
5
+
6
+ > **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
7
+ > Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
8
+ > One checklist upfront, updated in place, shown again at end with a 1-line verdict.
9
+
10
+ > If dispatching subagents or running as a subagent: read `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md` first (fallback: `{PROJECT_ROOT}/.rdc/guides/agent-bootstrap.md`).
11
+
12
+ > **Sandbox contract:** This skill honors `RDC_TEST=1` per `guides/agent-bootstrap.md` § RDC_TEST Sandbox Contract. Destructive external calls short-circuit under the flag. Supabase work item writes (`insert_work_item`, `update_work_item_status`, `bump_epic_version`) are skipped under `RDC_TEST=1`.
13
+
14
+
15
+ # rdc:workitems — Work Item Management
16
+
17
+ ## When to Use
18
+ - Project lead says "create a ticket", "add to backlog", "log this bug", "update status"
19
+ - An agent needs to create or update epics/tasks during a build session
20
+ - Checking what work is open before starting a new epic
21
+
22
+ ## Arguments
23
+
24
+ - `rdc:workitems epics` — list open epics
25
+ - `rdc:workitems add <title> --epic <id>` — create a task under an epic
26
+ - `rdc:workitems update <id> --status <status>` — update a work item's status
27
+ - `rdc:workitems done <id>` — mark a task complete
28
+ - `rdc:workitems list <epic-id>` — list tasks in an epic
29
+
30
+ ## Rules
31
+
32
+ 1. **Epic first, always.** Never create a task without a parent epic.
33
+ 2. **Label everything.** Minimum one system label per item.
34
+ 3. **Check first.** `get_open_epics()` before creating anything new.
35
+ 4. **Link design docs.** Put `.rdc/plans/` or `.rdc/research/` paths in descriptions (fallback: `.rdc/plans/` / `.rdc/research/`).
36
+
37
+ ## Read Epics
38
+
39
+ ```sql
40
+ SELECT get_open_epics();
41
+ SELECT get_open_epics('urgent');
42
+ SELECT get_open_epics(p_label_filter := 'custom-label');
43
+ ```
44
+
45
+ ## Create Epic
46
+
47
+ ```sql
48
+ SELECT insert_work_item(
49
+ p_title := 'EPIC: Clear descriptive title',
50
+ p_description := 'What and why. Reference .rdc/plans/<n>.md if applicable.',
51
+ p_item_type := 'epic',
52
+ p_priority := 'high',
53
+ p_labels := ARRAY['system-label'],
54
+ p_source := 'planning'
55
+ );
56
+ ```
57
+
58
+ ## Create Task
59
+
60
+ ```sql
61
+ SELECT insert_work_item(
62
+ p_title := 'Specific actionable task title',
63
+ p_description := 'What: <deliverable>
64
+ Where: <files to create/modify>
65
+ Agent type: frontend | backend | data | design | infra | content | cs2
66
+ Guide: .rdc/guides/<type>.md (fallback: .rdc/guides/<type>.md)
67
+ Design doc: .rdc/plans/<n>.md (fallback: .rdc/plans/<n>.md, if exists)
68
+ Depends on: <other task title if sequential>
69
+ Est: <hours>',
70
+ p_parent_id := '<epic-uuid>'::uuid,
71
+ p_item_type := 'task',
72
+ p_priority := 'high',
73
+ p_labels := ARRAY['system-label'],
74
+ p_estimated_hours := 2,
75
+ p_source := 'planning'
76
+ );
77
+ ```
78
+
79
+ ## Update Status
80
+
81
+ ```sql
82
+ SELECT update_work_item_status('<uuid>'::uuid, 'in_progress');
83
+ SELECT update_work_item_status('<uuid>'::uuid, 'review', '["Implementation complete; ready for validator"]'::jsonb, '<agent-session-id>', 'agent');
84
+ SELECT update_work_item_status('<uuid>'::uuid, 'done', '["Validator verified implementation report, CodeFlow post, and checklist evidence"]'::jsonb, '<validator-session-id>', 'validator');
85
+ SELECT update_work_item_status('<uuid>'::uuid, 'blocked', '["Why it is blocked"]'::jsonb);
86
+ ```
87
+
88
+ Non-epic `done` is a validator-only close and requires an existing
89
+ `implementation_report.codeflow_post`, checked required checklist evidence, and
90
+ originating-agent tick audit.
91
+
92
+ ## Read Tasks in an Epic
93
+
94
+ ```sql
95
+ SELECT get_work_items_by_epic('<epic-uuid>'::uuid);
96
+ SELECT get_work_items_by_epic('<epic-uuid>'::uuid, 'todo');
97
+ ```
98
+
99
+ ## Bump Epic Version
100
+
101
+ ```sql
102
+ SELECT bump_epic_version('<epic-uuid>'::uuid, '0.2.0', 'What changed', 'planning');
103
+ ```
104
+
105
+ ## Valid Values
106
+
107
+ | Field | Values |
108
+ |-------|--------|
109
+ | status | `todo` `in_progress` `blocked` `review` `done` `archived` |
110
+ | priority | `urgent` `high` `normal` `low` |
111
+ | item_type | `epic` `task` `subtask` `bug` `spike` |
112
+
113
+ ## System Labels
114
+
115
+ | Label | When |
116
+ |-------|------|
117
+ | `project-a` | Your-app-specific work |
118
+ | `project-b` | Another app work |
119
+ | `cs2` | Core paradigm packages |
120
+ | `hail` | Grammar, DSL compiler |
121
+ | `pal` | Moment windows, memory |
122
+ | `virtue` | Virtue engine, coherence |
123
+ | `marketing` | Outreach, campaigns |
124
+ | `media` | Assets, R2, image pipeline |
125
+ | `infrastructure` | CI/CD, deployment |
126
+ | `ui` | Component library |
127
+ | `data` | Schema, migrations |
128
+ | `content` | Copy, messaging |
129
+ | `website` | Public-facing sites |
130
+
131
+ ## Agent Type → Guide File Reference
132
+
133
+ | Type | Guide | Use For |
134
+ |------|-------|---------|
135
+ | `frontend` | .rdc/guides/frontend.md | React, pages, UI, Tailwind |
136
+ | `backend` | .rdc/guides/backend.md | API routes, database, auth |
137
+ | `data` | .rdc/guides/data.md | Migrations, schema, RPC |
138
+ | `design` | .rdc/guides/design.md | Brand, palette, OG images |
139
+ | `infra` | .rdc/guides/infrastructure.md | CI/CD, deploy, DNS |
140
+ | `content` | .rdc/guides/content.md | Copy, messaging, tone |
141
+ | `cs2` | .rdc/guides/cs2.md | CS 2.0 paradigm |
142
+ | `hail` | .rdc/guides/cs2.md + packages/hail/CLAUDE.md | Grammar, DSL |
143
+ | `viz` | .rdc/guides/frontend.md + design.md | Custom visualizations |
144
+
145
+ ## What NOT to Do
146
+
147
+ - Never raw INSERT/UPDATE against work items — RPC functions only
148
+ - Never create tasks without a parent epic
149
+ - Never leave labels empty
150
+ - Never write vague titles ("Fix stuff") — be specific
151
+ - Never put design intention in the task — put it in `.rdc/research/` (fallback: `.rdc/research/`) and link
@@ -0,0 +1,45 @@
1
+ import assert from 'node:assert/strict';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import test from 'node:test';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
8
+ const read = (relative) => fs.readFileSync(path.join(ROOT, relative), 'utf8');
9
+
10
+ test('SessionStart verifies package files without a local MCP or PM2', () => {
11
+ const hook = read('hooks/check-rdc-environment.js');
12
+ assert.doesNotMatch(hook, /\bpm2\b/i);
13
+ assert.doesNotMatch(hook, /127\.0\.0\.1:3110/);
14
+ assert.doesNotMatch(hook, /rdc-skills-mcp/);
15
+ assert.match(hook, /global package missing/);
16
+ assert.match(hook, /rdc-skills-install/);
17
+ });
18
+
19
+ test('installer registers the public MCP and never launches a process manager', () => {
20
+ const installer = read('scripts/install-rdc-skills.js');
21
+ assert.match(installer, /https:\/\/rdc-skills\.regendevcorp\.com\/mcp/);
22
+ assert.doesNotMatch(installer, /\bpm2\b/i);
23
+ assert.doesNotMatch(installer, /127\.0\.0\.1:3110/);
24
+ });
25
+
26
+ test('environment skill and release contract preserve the runtime boundary', () => {
27
+ for (const relative of ['skills/env/SKILL.md', 'RELEASE.md']) {
28
+ const content = read(relative);
29
+ assert.doesNotMatch(content, /\bpm2\s+(start|stop|restart|resurrect|jlist|list|ping)\b/i);
30
+ assert.match(content, /public.*rdc-skills MCP/is);
31
+ }
32
+ });
33
+
34
+ test('legacy local PM2 helpers are absent', () => {
35
+ for (const relative of [
36
+ 'scripts/local-install-with-stop.sh',
37
+ 'scripts/probe-lock-holders.mjs',
38
+ 'scripts/rebuild-mcp.mjs',
39
+ ]) {
40
+ assert.equal(fs.existsSync(path.join(ROOT, relative)), false, `${relative} still exists`);
41
+ }
42
+
43
+ const pkg = JSON.parse(read('package.json'));
44
+ assert.equal(pkg.scripts['rebuild-mcp'], undefined);
45
+ });
@@ -1,41 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Install rdc-skills globally FROM LOCAL SOURCE, using the stop-first sequence.
3
- #
4
- # This is the empirical test of the fix: the same `npm install -g` that produced
5
- # EBUSY should now succeed because the lock holder is stopped first. It is a LOCAL
6
- # install (a path, not the registry) — nothing is published.
7
- #
8
- # The restart is unconditional: leaving the MCP down after a failed install would
9
- # be worse than the failure being repaired.
10
- set -u
11
-
12
- HOLDER=rdc-skills-mcp
13
-
14
- echo "== before =="
15
- pm2 jlist | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{const p=JSON.parse(s).find(x=>x.name===process.argv[1]);console.log(' '+process.argv[1]+': '+(p?p.pm2_env.status:'absent'));})" "$HOLDER"
16
- npm ls -g '@lifeaitools/rdc-skills' --depth=0 2>/dev/null | tail -1
17
-
18
- echo "== stop the lock holder =="
19
- pm2 stop "$HOLDER" >/dev/null 2>&1 && echo " stopped $HOLDER" || echo " could not stop (may be absent)"
20
-
21
- echo "== npm install -g from local source =="
22
- set +e
23
- npm install -g "C:/Dev/rdc-skills" 2>&1 | tail -5
24
- INSTALL_RC=${PIPESTATUS[0]}
25
- set -e
26
- echo " install exit=$INSTALL_RC"
27
-
28
- echo "== restart (always) =="
29
- pm2 restart "$HOLDER" >/dev/null 2>&1 && echo " restarted $HOLDER" || echo " restart FAILED"
30
-
31
- echo "== after =="
32
- npm ls -g '@lifeaitools/rdc-skills' --depth=0 2>/dev/null | tail -1
33
- for i in 1 2 3 4 5 6 7 8; do
34
- code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 3 http://127.0.0.1:3110/health 2>/dev/null)
35
- [ "$code" = "200" ] && break
36
- sleep 1
37
- done
38
- echo " health 3110 -> HTTP ${code:-none}"
39
- curl -s --max-time 3 http://127.0.0.1:3110/health 2>/dev/null | head -c 200
40
- echo
41
- exit "$INSTALL_RC"
@@ -1,36 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Read-only probe: which PM2 processes hold the global rdc-skills package dir?
4
- *
5
- * Mirrors processesHoldingPackage() in hooks/check-rdc-environment.js so the
6
- * matcher can be verified WITHOUT running a real repair (which would stop the
7
- * MCP and reinstall the package).
8
- *
9
- * `npm install -g` upgrades by renaming that directory; Windows refuses to rename
10
- * a live process's cwd, which is the EBUSY that hard-blocked sessions.
11
- */
12
- import { execSync } from 'node:child_process';
13
- import path from 'node:path';
14
-
15
- const shell = (c) => execSync(c, { encoding: 'utf8', timeout: 15000 }).trim();
16
- const norm = (p) => String(p || '').toLowerCase().replace(/\\/g, '/');
17
-
18
- const root = shell('npm root -g');
19
- const pkgDir = norm(path.join(root, '@lifeaitools', 'rdc-skills'));
20
-
21
- const all = JSON.parse(shell('pm2 jlist'));
22
- const holders = all.filter((p) => {
23
- const cwd = norm(p?.pm2_env?.pm_cwd);
24
- return cwd && (cwd === pkgDir || cwd.startsWith(`${pkgDir}/`));
25
- });
26
-
27
- console.log(`package dir : ${pkgDir}`);
28
- console.log(`pm2 procs : ${all.length}`);
29
- for (const p of holders) {
30
- console.log(` HOLDS LOCK: ${p.name} status=${p.pm2_env.status} cwd=${norm(p.pm2_env.pm_cwd)}`);
31
- }
32
- console.log(
33
- holders.length
34
- ? `\nMATCHED ${holders.length} — these are stopped before npm -g, so EBUSY is avoided.`
35
- : '\nNO MATCH — nothing holds the dir; npm -g would have been safe anyway.',
36
- );
@@ -1,107 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * scripts/rebuild-mcp.mjs — "build hook" for the rdc-skills MCP server.
4
- *
5
- * The server reads skills live from disk, so a skill edit is usually picked up
6
- * without a restart. This script exists to (a) bounce the PM2 process so any
7
- * in-memory module cache is dropped, and (b) assert the local /health endpoint
8
- * reports the current on-disk skill count — a fast smoke that the server is up
9
- * and seeing the same catalog this script does.
10
- *
11
- * Tolerant of a missing PM2: if pm2 is not installed/registered, it prints how
12
- * to start the server and exits 0. It never hard-fails the caller.
13
- */
14
-
15
- import { execSync } from 'node:child_process';
16
- import fs from 'node:fs';
17
- import path from 'node:path';
18
- import { fileURLToPath } from 'node:url';
19
- import { listSkills } from '../lib/catalog.mjs';
20
-
21
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
22
- const REPO_ROOT = path.resolve(__dirname, '..');
23
- const PM2_NAME = 'rdc-skills-mcp';
24
- const PORT = parseInt(process.env.PORT || '3110', 10);
25
- const BIN = path.join(REPO_ROOT, 'bin', 'rdc-skills-mcp.mjs');
26
-
27
- const ok = (m) => console.log(` \x1b[32m✓\x1b[0m ${m}`);
28
- const info = (m) => console.log(` \x1b[36m→\x1b[0m ${m}`);
29
- const warn = (m) => console.log(` \x1b[33m⚠\x1b[0m ${m}`);
30
-
31
- function sh(cmd) {
32
- return execSync(cmd, { encoding: 'utf8', stdio: 'pipe' }).trim();
33
- }
34
-
35
- function pm2Available() {
36
- try {
37
- sh('pm2 -v');
38
- return true;
39
- } catch {
40
- return false;
41
- }
42
- }
43
-
44
- function pm2HasProcess() {
45
- try {
46
- const list = JSON.parse(sh('pm2 jlist'));
47
- return Array.isArray(list) && list.some((p) => p.name === PM2_NAME);
48
- } catch {
49
- return false;
50
- }
51
- }
52
-
53
- async function checkHealth(expectedSkills) {
54
- // Node 22 has global fetch.
55
- try {
56
- const res = await fetch(`http://127.0.0.1:${PORT}/health`, { signal: AbortSignal.timeout(4000) });
57
- if (!res.ok) {
58
- warn(`/health returned HTTP ${res.status}`);
59
- return false;
60
- }
61
- const json = await res.json();
62
- if (json.skills === expectedSkills) {
63
- ok(`/health OK — skills=${json.skills} (matches disk), version=${json.version}`);
64
- return true;
65
- }
66
- warn(`/health skills=${json.skills} but disk has ${expectedSkills} — server may be stale`);
67
- return false;
68
- } catch (err) {
69
- warn(`/health unreachable on port ${PORT}: ${err?.message || err}`);
70
- return false;
71
- }
72
- }
73
-
74
- async function main() {
75
- const diskSkills = listSkills().length;
76
- info(`On-disk catalog: ${diskSkills} skill(s)`);
77
-
78
- if (!pm2Available()) {
79
- warn('pm2 not found — cannot bounce the MCP process.');
80
- info(`Start it manually: PORT=${PORT} pm2 start ${BIN} --name ${PM2_NAME}`);
81
- info(`Or run directly: PORT=${PORT} node ${BIN}`);
82
- process.exit(0);
83
- }
84
-
85
- if (pm2HasProcess()) {
86
- try {
87
- sh(`pm2 restart ${PM2_NAME} --update-env`);
88
- ok(`pm2 restart ${PM2_NAME}`);
89
- } catch (err) {
90
- warn(`pm2 restart failed: ${err?.message || err}`);
91
- }
92
- } else {
93
- info(`pm2 process '${PM2_NAME}' not registered.`);
94
- info(`Start it: PORT=${PORT} pm2 start ${BIN} --name ${PM2_NAME}`);
95
- process.exit(0);
96
- }
97
-
98
- // Give the process a beat to bind, then probe /health (no long sleeps).
99
- await new Promise((r) => setTimeout(r, 1200));
100
- await checkHealth(diskSkills);
101
- process.exit(0);
102
- }
103
-
104
- main().catch((err) => {
105
- warn(`rebuild-mcp error (non-fatal): ${err?.message || err}`);
106
- process.exit(0);
107
- });