create-claude-cabinet 0.6.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 (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +196 -0
  3. package/bin/create-claude-cabinet.js +8 -0
  4. package/lib/cli.js +624 -0
  5. package/lib/copy.js +152 -0
  6. package/lib/db-setup.js +51 -0
  7. package/lib/metadata.js +42 -0
  8. package/lib/reset.js +193 -0
  9. package/lib/settings-merge.js +93 -0
  10. package/package.json +29 -0
  11. package/templates/EXTENSIONS.md +311 -0
  12. package/templates/README.md +485 -0
  13. package/templates/briefing/_briefing-api-template.md +21 -0
  14. package/templates/briefing/_briefing-architecture-template.md +16 -0
  15. package/templates/briefing/_briefing-cabinet-template.md +20 -0
  16. package/templates/briefing/_briefing-identity-template.md +18 -0
  17. package/templates/briefing/_briefing-scopes-template.md +39 -0
  18. package/templates/briefing/_briefing-template.md +148 -0
  19. package/templates/briefing/_briefing-work-tracking-template.md +18 -0
  20. package/templates/cabinet/committees-template.yaml +49 -0
  21. package/templates/cabinet/composition-patterns.md +240 -0
  22. package/templates/cabinet/eval-protocol.md +208 -0
  23. package/templates/cabinet/lifecycle.md +93 -0
  24. package/templates/cabinet/output-contract.md +148 -0
  25. package/templates/cabinet/prompt-guide.md +266 -0
  26. package/templates/hooks/cor-upstream-guard.sh +79 -0
  27. package/templates/hooks/git-guardrails.sh +67 -0
  28. package/templates/hooks/skill-telemetry.sh +66 -0
  29. package/templates/hooks/skill-tool-telemetry.sh +54 -0
  30. package/templates/hooks/stop-hook.md +56 -0
  31. package/templates/memory/patterns/_pattern-template.md +119 -0
  32. package/templates/memory/patterns/pattern-intelligence-first.md +41 -0
  33. package/templates/rules/enforcement-pipeline.md +151 -0
  34. package/templates/scripts/cor-drift-check.cjs +84 -0
  35. package/templates/scripts/finding-schema.json +94 -0
  36. package/templates/scripts/load-triage-history.js +151 -0
  37. package/templates/scripts/merge-findings.js +126 -0
  38. package/templates/scripts/pib-db-schema.sql +68 -0
  39. package/templates/scripts/pib-db.js +365 -0
  40. package/templates/scripts/triage-server.mjs +98 -0
  41. package/templates/scripts/triage-ui.html +536 -0
  42. package/templates/skills/audit/SKILL.md +273 -0
  43. package/templates/skills/audit/phases/finding-output.md +56 -0
  44. package/templates/skills/audit/phases/member-execution.md +83 -0
  45. package/templates/skills/audit/phases/member-selection.md +44 -0
  46. package/templates/skills/audit/phases/structural-checks.md +54 -0
  47. package/templates/skills/audit/phases/triage-history.md +45 -0
  48. package/templates/skills/cabinet-accessibility/SKILL.md +180 -0
  49. package/templates/skills/cabinet-anti-confirmation/SKILL.md +172 -0
  50. package/templates/skills/cabinet-architecture/SKILL.md +279 -0
  51. package/templates/skills/cabinet-boundary-man/SKILL.md +265 -0
  52. package/templates/skills/cabinet-cor-health/SKILL.md +342 -0
  53. package/templates/skills/cabinet-data-integrity/SKILL.md +157 -0
  54. package/templates/skills/cabinet-debugger/SKILL.md +221 -0
  55. package/templates/skills/cabinet-historian/SKILL.md +253 -0
  56. package/templates/skills/cabinet-organized-mind/SKILL.md +338 -0
  57. package/templates/skills/cabinet-process-therapist/SKILL.md +261 -0
  58. package/templates/skills/cabinet-qa/SKILL.md +205 -0
  59. package/templates/skills/cabinet-record-keeper/SKILL.md +168 -0
  60. package/templates/skills/cabinet-roster-check/SKILL.md +297 -0
  61. package/templates/skills/cabinet-security/SKILL.md +181 -0
  62. package/templates/skills/cabinet-small-screen/SKILL.md +154 -0
  63. package/templates/skills/cabinet-speed-freak/SKILL.md +169 -0
  64. package/templates/skills/cabinet-system-advocate/SKILL.md +194 -0
  65. package/templates/skills/cabinet-technical-debt/SKILL.md +115 -0
  66. package/templates/skills/cabinet-usability/SKILL.md +189 -0
  67. package/templates/skills/cabinet-workflow-cop/SKILL.md +238 -0
  68. package/templates/skills/cor-upgrade/SKILL.md +302 -0
  69. package/templates/skills/debrief/SKILL.md +409 -0
  70. package/templates/skills/debrief/phases/auto-maintenance.md +48 -0
  71. package/templates/skills/debrief/phases/close-work.md +88 -0
  72. package/templates/skills/debrief/phases/health-checks.md +54 -0
  73. package/templates/skills/debrief/phases/inventory.md +40 -0
  74. package/templates/skills/debrief/phases/loose-ends.md +52 -0
  75. package/templates/skills/debrief/phases/record-lessons.md +67 -0
  76. package/templates/skills/debrief/phases/report.md +59 -0
  77. package/templates/skills/debrief/phases/update-state.md +48 -0
  78. package/templates/skills/debrief/phases/upstream-feedback.md +129 -0
  79. package/templates/skills/debrief-quick/SKILL.md +12 -0
  80. package/templates/skills/execute/SKILL.md +293 -0
  81. package/templates/skills/execute/phases/cabinet.md +49 -0
  82. package/templates/skills/execute/phases/commit-and-deploy.md +66 -0
  83. package/templates/skills/execute/phases/load-plan.md +49 -0
  84. package/templates/skills/execute/phases/validators.md +50 -0
  85. package/templates/skills/execute/phases/verification-tools.md +67 -0
  86. package/templates/skills/extract/SKILL.md +168 -0
  87. package/templates/skills/investigate/SKILL.md +160 -0
  88. package/templates/skills/link/SKILL.md +52 -0
  89. package/templates/skills/menu/SKILL.md +61 -0
  90. package/templates/skills/onboard/SKILL.md +356 -0
  91. package/templates/skills/onboard/phases/detect-state.md +79 -0
  92. package/templates/skills/onboard/phases/generate-briefing.md +127 -0
  93. package/templates/skills/onboard/phases/generate-session-loop.md +87 -0
  94. package/templates/skills/onboard/phases/interview.md +233 -0
  95. package/templates/skills/onboard/phases/modularity-menu.md +162 -0
  96. package/templates/skills/onboard/phases/options.md +98 -0
  97. package/templates/skills/onboard/phases/post-onboard-audit.md +121 -0
  98. package/templates/skills/onboard/phases/summary.md +122 -0
  99. package/templates/skills/onboard/phases/work-tracking.md +231 -0
  100. package/templates/skills/orient/SKILL.md +251 -0
  101. package/templates/skills/orient/phases/auto-maintenance.md +48 -0
  102. package/templates/skills/orient/phases/briefing.md +53 -0
  103. package/templates/skills/orient/phases/cabinet.md +46 -0
  104. package/templates/skills/orient/phases/context.md +63 -0
  105. package/templates/skills/orient/phases/data-sync.md +35 -0
  106. package/templates/skills/orient/phases/health-checks.md +50 -0
  107. package/templates/skills/orient/phases/work-scan.md +69 -0
  108. package/templates/skills/orient-quick/SKILL.md +12 -0
  109. package/templates/skills/plan/SKILL.md +358 -0
  110. package/templates/skills/plan/phases/cabinet-critique.md +47 -0
  111. package/templates/skills/plan/phases/calibration-examples.md +75 -0
  112. package/templates/skills/plan/phases/completeness-check.md +44 -0
  113. package/templates/skills/plan/phases/composition-check.md +36 -0
  114. package/templates/skills/plan/phases/overlap-check.md +62 -0
  115. package/templates/skills/plan/phases/plan-template.md +69 -0
  116. package/templates/skills/plan/phases/present.md +60 -0
  117. package/templates/skills/plan/phases/research.md +43 -0
  118. package/templates/skills/plan/phases/work-tracker.md +95 -0
  119. package/templates/skills/publish/SKILL.md +74 -0
  120. package/templates/skills/pulse/SKILL.md +242 -0
  121. package/templates/skills/pulse/phases/auto-fix-scope.md +40 -0
  122. package/templates/skills/pulse/phases/checks.md +58 -0
  123. package/templates/skills/pulse/phases/output.md +54 -0
  124. package/templates/skills/seed/SKILL.md +257 -0
  125. package/templates/skills/seed/phases/build-member.md +93 -0
  126. package/templates/skills/seed/phases/evaluate-existing.md +61 -0
  127. package/templates/skills/seed/phases/maintain.md +92 -0
  128. package/templates/skills/seed/phases/scan-signals.md +86 -0
  129. package/templates/skills/triage-audit/SKILL.md +251 -0
  130. package/templates/skills/triage-audit/phases/apply-verdicts.md +90 -0
  131. package/templates/skills/triage-audit/phases/load-findings.md +38 -0
  132. package/templates/skills/triage-audit/phases/triage-ui.md +66 -0
  133. package/templates/skills/unlink/SKILL.md +35 -0
  134. package/templates/skills/validate/SKILL.md +116 -0
  135. package/templates/skills/validate/phases/validators.md +53 -0
@@ -0,0 +1,365 @@
1
+ #!/usr/bin/env node
2
+ // Process-in-a-Box reference data layer
3
+ //
4
+ // Local SQLite database for work tracking and audit findings.
5
+ // This is the default persistence layer — projects that outgrow it
6
+ // override via phase files (pointing to their own API, DB, or service).
7
+ //
8
+ // Usage:
9
+ // node scripts/pib-db.js init # Create/migrate DB
10
+ // node scripts/pib-db.js query "SELECT * FROM ..." # Run a query
11
+ // node scripts/pib-db.js create-action "Do the thing" --area dev
12
+ // node scripts/pib-db.js list-actions [--status X] # Open actions (or filtered)
13
+ // node scripts/pib-db.js update-action act:abc --status in-progress
14
+ // node scripts/pib-db.js complete-action act:abc123
15
+ // node scripts/pib-db.js create-project "My Project" --area dev
16
+ // node scripts/pib-db.js list-projects # Active projects
17
+ // node scripts/pib-db.js ingest-findings <run-dir> # Ingest audit findings
18
+ // node scripts/pib-db.js triage <finding-id> <status> [notes]
19
+ // node scripts/pib-db.js triage-history # Suppression list JSON
20
+ //
21
+ // Environment:
22
+ // PIB_DB_PATH — path to SQLite file (default: ./pib.db)
23
+
24
+ import { existsSync, readFileSync } from 'node:fs';
25
+ import { join, dirname } from 'node:path';
26
+ import { fileURLToPath } from 'node:url';
27
+ import { createRequire } from 'node:module';
28
+ import { randomUUID } from 'node:crypto';
29
+
30
+ const __dirname = dirname(fileURLToPath(import.meta.url));
31
+ const DB_PATH = process.env.PIB_DB_PATH || join(process.cwd(), 'pib.db');
32
+
33
+ // ---------------------------------------------------------------------------
34
+ // SQLite setup — try better-sqlite3, fall back to node:sqlite if available
35
+ // ---------------------------------------------------------------------------
36
+ let db;
37
+
38
+ function getDb() {
39
+ if (db) return db;
40
+ try {
41
+ const require = createRequire(import.meta.url);
42
+ const Database = require('better-sqlite3');
43
+ db = new Database(DB_PATH);
44
+ db.pragma('journal_mode = WAL');
45
+ db.pragma('foreign_keys = ON');
46
+ return db;
47
+ } catch {
48
+ console.error('Error: better-sqlite3 not found. Install it:');
49
+ console.error(' npm install better-sqlite3');
50
+ process.exit(1);
51
+ }
52
+ }
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // Init — create tables from schema
56
+ // ---------------------------------------------------------------------------
57
+ function init() {
58
+ const d = getDb();
59
+ const schemaPath = join(__dirname, 'pib-db-schema.sql');
60
+ const schema = readFileSync(schemaPath, 'utf-8');
61
+ d.exec(schema);
62
+
63
+ // Migrate existing DBs — add columns that may not exist yet
64
+ const migrations = [
65
+ { table: 'actions', column: 'status', sql: "ALTER TABLE actions ADD COLUMN status TEXT NOT NULL DEFAULT 'open' CHECK(status IN ('open','in-progress','blocked','deferred','done'))" },
66
+ { table: 'actions', column: 'tags', sql: "ALTER TABLE actions ADD COLUMN tags TEXT NOT NULL DEFAULT ''" },
67
+ ];
68
+ for (const m of migrations) {
69
+ const cols = d.prepare(`PRAGMA table_info(${m.table})`).all();
70
+ if (!cols.some(c => c.name === m.column)) {
71
+ try { d.exec(m.sql); } catch { /* column may already exist */ }
72
+ }
73
+ }
74
+
75
+ console.log(`Database initialized at ${DB_PATH}`);
76
+ }
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Query — run arbitrary SQL
80
+ // ---------------------------------------------------------------------------
81
+ function query(sql) {
82
+ const d = getDb();
83
+ if (sql.trim().toUpperCase().startsWith('SELECT')) {
84
+ const rows = d.prepare(sql).all();
85
+ console.log(JSON.stringify(rows, null, 2));
86
+ } else {
87
+ const result = d.exec(sql);
88
+ console.log('Done.');
89
+ }
90
+ }
91
+
92
+ // ---------------------------------------------------------------------------
93
+ // Actions
94
+ // ---------------------------------------------------------------------------
95
+ function generateFid(prefix) {
96
+ return `${prefix}:${randomUUID().replace(/-/g, '').slice(0, 8)}`;
97
+ }
98
+
99
+ function today() {
100
+ return new Date().toISOString().slice(0, 10);
101
+ }
102
+
103
+ function createAction(text, { area, projectFid, due, notes } = {}) {
104
+ const d = getDb();
105
+ const fid = generateFid('act');
106
+ d.prepare(`
107
+ INSERT INTO actions (fid, text, area, project_fid, due, notes, created)
108
+ VALUES (?, ?, ?, ?, ?, ?, ?)
109
+ `).run(fid, text, area || null, projectFid || null, due || null, notes || '', today());
110
+ console.log(`Created action ${fid}: ${text}`);
111
+ return fid;
112
+ }
113
+
114
+ function listActions({ status, project } = {}) {
115
+ const d = getDb();
116
+ const conditions = ['a.deleted_at IS NULL'];
117
+ const params = [];
118
+
119
+ if (status) {
120
+ conditions.push('a.status = ?');
121
+ params.push(status);
122
+ } else {
123
+ conditions.push('a.completed = 0');
124
+ }
125
+ if (project) {
126
+ conditions.push('a.project_fid = ?');
127
+ params.push(project);
128
+ }
129
+
130
+ const rows = d.prepare(`
131
+ SELECT a.fid, a.text, a.area, a.due, a.flagged, a.status, a.tags, p.name as project
132
+ FROM actions a
133
+ LEFT JOIN projects p ON a.project_fid = p.fid
134
+ WHERE ${conditions.join(' AND ')}
135
+ ORDER BY
136
+ CASE WHEN a.due IS NOT NULL AND a.due <= date('now') THEN 0 ELSE 1 END,
137
+ a.due,
138
+ a.flagged DESC,
139
+ a.created DESC
140
+ `).all(...params);
141
+ console.log(JSON.stringify(rows, null, 2));
142
+ return rows;
143
+ }
144
+
145
+ function updateAction(fid, { status, text, tags, notes, due, flagged } = {}) {
146
+ const d = getDb();
147
+ const sets = [];
148
+ const params = [];
149
+
150
+ if (status !== undefined) { sets.push('status = ?'); params.push(status); }
151
+ if (text !== undefined) { sets.push('text = ?'); params.push(text); }
152
+ if (tags !== undefined) { sets.push('tags = ?'); params.push(tags); }
153
+ if (notes !== undefined) { sets.push('notes = ?'); params.push(notes); }
154
+ if (due !== undefined) { sets.push('due = ?'); params.push(due); }
155
+ if (flagged !== undefined) { sets.push('flagged = ?'); params.push(flagged === 'true' || flagged === '1' ? 1 : 0); }
156
+
157
+ // If marking done, also set completed fields
158
+ if (status === 'done') {
159
+ sets.push('completed = 1', 'completed_at = ?');
160
+ params.push(new Date().toISOString());
161
+ }
162
+
163
+ if (sets.length === 0) {
164
+ console.error('No fields to update. Use --status, --text, --tags, --notes, --due, or --flagged');
165
+ process.exit(1);
166
+ }
167
+
168
+ params.push(fid);
169
+ d.prepare(`UPDATE actions SET ${sets.join(', ')} WHERE fid = ?`).run(...params);
170
+ console.log(`Updated ${fid}`);
171
+ }
172
+
173
+ function completeAction(fid) {
174
+ const d = getDb();
175
+ d.prepare(`
176
+ UPDATE actions SET completed = 1, completed_at = ?, status = 'done' WHERE fid = ?
177
+ `).run(new Date().toISOString(), fid);
178
+ console.log(`Completed ${fid}`);
179
+ }
180
+
181
+ // ---------------------------------------------------------------------------
182
+ // Projects
183
+ // ---------------------------------------------------------------------------
184
+ function createProject(name, { area, notes, due } = {}) {
185
+ const d = getDb();
186
+ const fid = generateFid('prj');
187
+ d.prepare(`
188
+ INSERT INTO projects (fid, name, area, notes, due, created)
189
+ VALUES (?, ?, ?, ?, ?, ?)
190
+ `).run(fid, name, area || null, notes || '', due || null, today());
191
+ console.log(`Created project ${fid}: ${name}`);
192
+ return fid;
193
+ }
194
+
195
+ function listProjects() {
196
+ const d = getDb();
197
+ const rows = d.prepare(`
198
+ SELECT p.fid, p.name, p.area, p.status, p.due,
199
+ (SELECT COUNT(*) FROM actions a WHERE a.project_fid = p.fid AND a.completed = 0 AND a.deleted_at IS NULL) as open_actions
200
+ FROM projects p
201
+ WHERE p.status = 'active' AND p.deleted_at IS NULL
202
+ ORDER BY p.created DESC
203
+ `).all();
204
+ console.log(JSON.stringify(rows, null, 2));
205
+ return rows;
206
+ }
207
+
208
+ // ---------------------------------------------------------------------------
209
+ // Audit — ingest findings from a run directory
210
+ // ---------------------------------------------------------------------------
211
+ function ingestFindings(runDir) {
212
+ const d = getDb();
213
+ const summaryPath = join(runDir, 'run-summary.json');
214
+ if (!existsSync(summaryPath)) {
215
+ console.error(`No run-summary.json found in ${runDir}`);
216
+ process.exit(1);
217
+ }
218
+ const data = JSON.parse(readFileSync(summaryPath, 'utf-8'));
219
+ const runId = data.meta?.runId || `run-${Date.now()}`;
220
+ const timestamp = data.meta?.timestamp || new Date().toISOString();
221
+ const dateStr = timestamp.slice(0, 10);
222
+
223
+ d.prepare(`
224
+ INSERT OR REPLACE INTO audit_runs (id, date, timestamp, trigger, finding_count)
225
+ VALUES (?, ?, ?, ?, ?)
226
+ `).run(runId, dateStr, timestamp, data.meta?.trigger || 'manual', data.findings?.length || 0);
227
+
228
+ const insert = d.prepare(`
229
+ INSERT OR REPLACE INTO audit_findings
230
+ (id, run_id, perspective, severity, title, description, assumption,
231
+ evidence, question, file, line, suggested_fix, auto_fixable, type)
232
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
233
+ `);
234
+
235
+ let count = 0;
236
+ for (const f of (data.findings || [])) {
237
+ insert.run(
238
+ f.id, runId, f.perspective, f.severity, f.title,
239
+ f.description || null, f.assumption || null, f.evidence || null,
240
+ f.question || null, f.file || null, f.line || null,
241
+ f.suggestedFix || null, f.autoFixable ? 1 : 0, f.type || 'finding'
242
+ );
243
+ count++;
244
+ }
245
+ console.log(`Ingested ${count} findings from ${runDir} (run: ${runId})`);
246
+ }
247
+
248
+ // ---------------------------------------------------------------------------
249
+ // Triage
250
+ // ---------------------------------------------------------------------------
251
+ function triageFinding(findingId, status, notes) {
252
+ const d = getDb();
253
+ d.prepare(`
254
+ UPDATE audit_findings
255
+ SET triage_status = ?, triage_notes = ?, triaged_at = ?
256
+ WHERE id = ?
257
+ `).run(status, notes || null, new Date().toISOString(), findingId);
258
+ console.log(`Triaged ${findingId} → ${status}`);
259
+ }
260
+
261
+ function triageHistory() {
262
+ const d = getDb();
263
+
264
+ const rejected = d.prepare(`
265
+ SELECT id, perspective, title FROM audit_findings
266
+ WHERE triage_status = 'rejected'
267
+ `).all();
268
+
269
+ const deferred = d.prepare(`
270
+ SELECT id, perspective, title FROM audit_findings
271
+ WHERE triage_status = 'deferred'
272
+ `).all();
273
+
274
+ const result = {
275
+ rejectedIds: rejected.map(r => r.id),
276
+ rejectedFingerprints: rejected.map(r => ({ perspective: r.perspective, title: r.title })),
277
+ deferredIds: deferred.map(r => r.id),
278
+ deferredFingerprints: deferred.map(r => ({ perspective: r.perspective, title: r.title })),
279
+ };
280
+ console.log(JSON.stringify(result, null, 2));
281
+ return result;
282
+ }
283
+
284
+ // ---------------------------------------------------------------------------
285
+ // CLI
286
+ // ---------------------------------------------------------------------------
287
+ const [,, command, ...args] = process.argv;
288
+
289
+ function parseFlags(args) {
290
+ const flags = {};
291
+ const positional = [];
292
+ for (let i = 0; i < args.length; i++) {
293
+ if (args[i].startsWith('--')) {
294
+ const key = args[i].slice(2);
295
+ flags[key] = args[i + 1] || true;
296
+ i++;
297
+ } else {
298
+ positional.push(args[i]);
299
+ }
300
+ }
301
+ return { flags, positional };
302
+ }
303
+
304
+ switch (command) {
305
+ case 'init':
306
+ init();
307
+ break;
308
+ case 'query':
309
+ query(args.join(' '));
310
+ break;
311
+ case 'create-action': {
312
+ const { flags, positional } = parseFlags(args);
313
+ createAction(positional[0], flags);
314
+ break;
315
+ }
316
+ case 'list-actions': {
317
+ const { flags } = parseFlags(args);
318
+ listActions(flags);
319
+ break;
320
+ }
321
+ case 'update-action': {
322
+ const { flags, positional } = parseFlags(args);
323
+ updateAction(positional[0], flags);
324
+ break;
325
+ }
326
+ case 'complete-action':
327
+ completeAction(args[0]);
328
+ break;
329
+ case 'create-project': {
330
+ const { flags, positional } = parseFlags(args);
331
+ createProject(positional[0], flags);
332
+ break;
333
+ }
334
+ case 'list-projects':
335
+ listProjects();
336
+ break;
337
+ case 'ingest-findings':
338
+ ingestFindings(args[0]);
339
+ break;
340
+ case 'triage':
341
+ triageFinding(args[0], args[1], args.slice(2).join(' ') || undefined);
342
+ break;
343
+ case 'triage-history':
344
+ triageHistory();
345
+ break;
346
+ default:
347
+ console.log(`Usage: pib-db.js <command>
348
+
349
+ Commands:
350
+ init Create/migrate the database
351
+ query "SQL" Run a SQL query
352
+ create-action "text" [--area X] Create an action
353
+ list-actions [--status X] [--project X] List actions (default: open)
354
+ update-action <fid> [--status X] [--text X] [--tags X] [--notes X]
355
+ complete-action <fid> Mark action complete (status=done)
356
+ create-project "name" [--area X] Create a project
357
+ list-projects List active projects
358
+ ingest-findings <run-dir> Ingest audit findings from a run directory
359
+ triage <finding-id> <status> Triage a finding (approved/rejected/deferred/fixed)
360
+ triage-history Output suppression list as JSON
361
+
362
+ Environment:
363
+ PIB_DB_PATH Path to SQLite file (default: ./pib.db)`);
364
+ if (command) process.exit(1);
365
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Minimal triage server — serves the triage UI and holds findings/verdicts in memory.
3
+ *
4
+ * Claude POSTs findings, user triages in browser, Claude GETs verdicts.
5
+ *
6
+ * Usage: node triage-server.mjs [--port 3457]
7
+ */
8
+
9
+ import { createServer } from 'node:http';
10
+ import { readFile } from 'node:fs/promises';
11
+ import { fileURLToPath } from 'node:url';
12
+ import { dirname, join } from 'node:path';
13
+
14
+ const __dirname = dirname(fileURLToPath(import.meta.url));
15
+ const PORT = parseInt(process.argv.find((_, i, a) => a[i - 1] === '--port') || '3457');
16
+
17
+ let currentFindings = [];
18
+ let currentVerdicts = null;
19
+
20
+ function json(res, data, status = 200) {
21
+ res.writeHead(status, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
22
+ res.end(JSON.stringify(data));
23
+ }
24
+
25
+ const server = createServer(async (req, res) => {
26
+ const url = new URL(req.url, `http://localhost:${PORT}`);
27
+
28
+ // CORS preflight
29
+ if (req.method === 'OPTIONS') {
30
+ res.writeHead(204, {
31
+ 'Access-Control-Allow-Origin': '*',
32
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
33
+ 'Access-Control-Allow-Headers': 'Content-Type',
34
+ });
35
+ return res.end();
36
+ }
37
+
38
+ // Serve HTML
39
+ if (req.method === 'GET' && url.pathname === '/') {
40
+ try {
41
+ const html = await readFile(join(__dirname, 'triage-ui.html'), 'utf-8');
42
+ res.writeHead(200, { 'Content-Type': 'text/html' });
43
+ return res.end(html);
44
+ } catch (err) {
45
+ res.writeHead(500);
46
+ return res.end('Failed to read triage-ui.html');
47
+ }
48
+ }
49
+
50
+ // POST /api/findings — Claude sends findings
51
+ if (req.method === 'POST' && url.pathname === '/api/findings') {
52
+ let body = '';
53
+ for await (const chunk of req) body += chunk;
54
+ try {
55
+ const data = JSON.parse(body);
56
+ currentFindings = data.findings || data;
57
+ currentVerdicts = null; // Reset verdicts for new batch
58
+ console.log(`Loaded ${currentFindings.length} findings`);
59
+ return json(res, { ok: true, count: currentFindings.length });
60
+ } catch (err) {
61
+ return json(res, { error: 'Invalid JSON' }, 400);
62
+ }
63
+ }
64
+
65
+ // GET /api/findings — UI fetches findings
66
+ if (req.method === 'GET' && url.pathname === '/api/findings') {
67
+ return json(res, { findings: currentFindings });
68
+ }
69
+
70
+ // POST /api/verdicts — UI submits verdicts
71
+ if (req.method === 'POST' && url.pathname === '/api/verdicts') {
72
+ let body = '';
73
+ for await (const chunk of req) body += chunk;
74
+ try {
75
+ currentVerdicts = JSON.parse(body);
76
+ console.log(`Received ${currentVerdicts.triaged}/${currentVerdicts.total} verdicts`);
77
+ return json(res, { ok: true });
78
+ } catch (err) {
79
+ return json(res, { error: 'Invalid JSON' }, 400);
80
+ }
81
+ }
82
+
83
+ // GET /api/verdicts — Claude reads verdicts
84
+ if (req.method === 'GET' && url.pathname === '/api/verdicts') {
85
+ if (!currentVerdicts) {
86
+ return json(res, { submitted: false, message: 'No verdicts submitted yet' });
87
+ }
88
+ return json(res, currentVerdicts);
89
+ }
90
+
91
+ // 404
92
+ res.writeHead(404);
93
+ res.end('Not found');
94
+ });
95
+
96
+ server.listen(PORT, () => {
97
+ console.log(`Triage server running at http://localhost:${PORT}`);
98
+ });