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.
- package/LICENSE +21 -0
- package/README.md +196 -0
- package/bin/create-claude-cabinet.js +8 -0
- package/lib/cli.js +624 -0
- package/lib/copy.js +152 -0
- package/lib/db-setup.js +51 -0
- package/lib/metadata.js +42 -0
- package/lib/reset.js +193 -0
- package/lib/settings-merge.js +93 -0
- package/package.json +29 -0
- package/templates/EXTENSIONS.md +311 -0
- package/templates/README.md +485 -0
- package/templates/briefing/_briefing-api-template.md +21 -0
- package/templates/briefing/_briefing-architecture-template.md +16 -0
- package/templates/briefing/_briefing-cabinet-template.md +20 -0
- package/templates/briefing/_briefing-identity-template.md +18 -0
- package/templates/briefing/_briefing-scopes-template.md +39 -0
- package/templates/briefing/_briefing-template.md +148 -0
- package/templates/briefing/_briefing-work-tracking-template.md +18 -0
- package/templates/cabinet/committees-template.yaml +49 -0
- package/templates/cabinet/composition-patterns.md +240 -0
- package/templates/cabinet/eval-protocol.md +208 -0
- package/templates/cabinet/lifecycle.md +93 -0
- package/templates/cabinet/output-contract.md +148 -0
- package/templates/cabinet/prompt-guide.md +266 -0
- package/templates/hooks/cor-upstream-guard.sh +79 -0
- package/templates/hooks/git-guardrails.sh +67 -0
- package/templates/hooks/skill-telemetry.sh +66 -0
- package/templates/hooks/skill-tool-telemetry.sh +54 -0
- package/templates/hooks/stop-hook.md +56 -0
- package/templates/memory/patterns/_pattern-template.md +119 -0
- package/templates/memory/patterns/pattern-intelligence-first.md +41 -0
- package/templates/rules/enforcement-pipeline.md +151 -0
- package/templates/scripts/cor-drift-check.cjs +84 -0
- package/templates/scripts/finding-schema.json +94 -0
- package/templates/scripts/load-triage-history.js +151 -0
- package/templates/scripts/merge-findings.js +126 -0
- package/templates/scripts/pib-db-schema.sql +68 -0
- package/templates/scripts/pib-db.js +365 -0
- package/templates/scripts/triage-server.mjs +98 -0
- package/templates/scripts/triage-ui.html +536 -0
- package/templates/skills/audit/SKILL.md +273 -0
- package/templates/skills/audit/phases/finding-output.md +56 -0
- package/templates/skills/audit/phases/member-execution.md +83 -0
- package/templates/skills/audit/phases/member-selection.md +44 -0
- package/templates/skills/audit/phases/structural-checks.md +54 -0
- package/templates/skills/audit/phases/triage-history.md +45 -0
- package/templates/skills/cabinet-accessibility/SKILL.md +180 -0
- package/templates/skills/cabinet-anti-confirmation/SKILL.md +172 -0
- package/templates/skills/cabinet-architecture/SKILL.md +279 -0
- package/templates/skills/cabinet-boundary-man/SKILL.md +265 -0
- package/templates/skills/cabinet-cor-health/SKILL.md +342 -0
- package/templates/skills/cabinet-data-integrity/SKILL.md +157 -0
- package/templates/skills/cabinet-debugger/SKILL.md +221 -0
- package/templates/skills/cabinet-historian/SKILL.md +253 -0
- package/templates/skills/cabinet-organized-mind/SKILL.md +338 -0
- package/templates/skills/cabinet-process-therapist/SKILL.md +261 -0
- package/templates/skills/cabinet-qa/SKILL.md +205 -0
- package/templates/skills/cabinet-record-keeper/SKILL.md +168 -0
- package/templates/skills/cabinet-roster-check/SKILL.md +297 -0
- package/templates/skills/cabinet-security/SKILL.md +181 -0
- package/templates/skills/cabinet-small-screen/SKILL.md +154 -0
- package/templates/skills/cabinet-speed-freak/SKILL.md +169 -0
- package/templates/skills/cabinet-system-advocate/SKILL.md +194 -0
- package/templates/skills/cabinet-technical-debt/SKILL.md +115 -0
- package/templates/skills/cabinet-usability/SKILL.md +189 -0
- package/templates/skills/cabinet-workflow-cop/SKILL.md +238 -0
- package/templates/skills/cor-upgrade/SKILL.md +302 -0
- package/templates/skills/debrief/SKILL.md +409 -0
- package/templates/skills/debrief/phases/auto-maintenance.md +48 -0
- package/templates/skills/debrief/phases/close-work.md +88 -0
- package/templates/skills/debrief/phases/health-checks.md +54 -0
- package/templates/skills/debrief/phases/inventory.md +40 -0
- package/templates/skills/debrief/phases/loose-ends.md +52 -0
- package/templates/skills/debrief/phases/record-lessons.md +67 -0
- package/templates/skills/debrief/phases/report.md +59 -0
- package/templates/skills/debrief/phases/update-state.md +48 -0
- package/templates/skills/debrief/phases/upstream-feedback.md +129 -0
- package/templates/skills/debrief-quick/SKILL.md +12 -0
- package/templates/skills/execute/SKILL.md +293 -0
- package/templates/skills/execute/phases/cabinet.md +49 -0
- package/templates/skills/execute/phases/commit-and-deploy.md +66 -0
- package/templates/skills/execute/phases/load-plan.md +49 -0
- package/templates/skills/execute/phases/validators.md +50 -0
- package/templates/skills/execute/phases/verification-tools.md +67 -0
- package/templates/skills/extract/SKILL.md +168 -0
- package/templates/skills/investigate/SKILL.md +160 -0
- package/templates/skills/link/SKILL.md +52 -0
- package/templates/skills/menu/SKILL.md +61 -0
- package/templates/skills/onboard/SKILL.md +356 -0
- package/templates/skills/onboard/phases/detect-state.md +79 -0
- package/templates/skills/onboard/phases/generate-briefing.md +127 -0
- package/templates/skills/onboard/phases/generate-session-loop.md +87 -0
- package/templates/skills/onboard/phases/interview.md +233 -0
- package/templates/skills/onboard/phases/modularity-menu.md +162 -0
- package/templates/skills/onboard/phases/options.md +98 -0
- package/templates/skills/onboard/phases/post-onboard-audit.md +121 -0
- package/templates/skills/onboard/phases/summary.md +122 -0
- package/templates/skills/onboard/phases/work-tracking.md +231 -0
- package/templates/skills/orient/SKILL.md +251 -0
- package/templates/skills/orient/phases/auto-maintenance.md +48 -0
- package/templates/skills/orient/phases/briefing.md +53 -0
- package/templates/skills/orient/phases/cabinet.md +46 -0
- package/templates/skills/orient/phases/context.md +63 -0
- package/templates/skills/orient/phases/data-sync.md +35 -0
- package/templates/skills/orient/phases/health-checks.md +50 -0
- package/templates/skills/orient/phases/work-scan.md +69 -0
- package/templates/skills/orient-quick/SKILL.md +12 -0
- package/templates/skills/plan/SKILL.md +358 -0
- package/templates/skills/plan/phases/cabinet-critique.md +47 -0
- package/templates/skills/plan/phases/calibration-examples.md +75 -0
- package/templates/skills/plan/phases/completeness-check.md +44 -0
- package/templates/skills/plan/phases/composition-check.md +36 -0
- package/templates/skills/plan/phases/overlap-check.md +62 -0
- package/templates/skills/plan/phases/plan-template.md +69 -0
- package/templates/skills/plan/phases/present.md +60 -0
- package/templates/skills/plan/phases/research.md +43 -0
- package/templates/skills/plan/phases/work-tracker.md +95 -0
- package/templates/skills/publish/SKILL.md +74 -0
- package/templates/skills/pulse/SKILL.md +242 -0
- package/templates/skills/pulse/phases/auto-fix-scope.md +40 -0
- package/templates/skills/pulse/phases/checks.md +58 -0
- package/templates/skills/pulse/phases/output.md +54 -0
- package/templates/skills/seed/SKILL.md +257 -0
- package/templates/skills/seed/phases/build-member.md +93 -0
- package/templates/skills/seed/phases/evaluate-existing.md +61 -0
- package/templates/skills/seed/phases/maintain.md +92 -0
- package/templates/skills/seed/phases/scan-signals.md +86 -0
- package/templates/skills/triage-audit/SKILL.md +251 -0
- package/templates/skills/triage-audit/phases/apply-verdicts.md +90 -0
- package/templates/skills/triage-audit/phases/load-findings.md +38 -0
- package/templates/skills/triage-audit/phases/triage-ui.md +66 -0
- package/templates/skills/unlink/SKILL.md +35 -0
- package/templates/skills/validate/SKILL.md +116 -0
- 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
|
+
});
|