agent-step-gate 0.3.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 (68) hide show
  1. package/ARCHITECTURE.md +393 -0
  2. package/README.md +662 -0
  3. package/SKILL.md +190 -0
  4. package/Weaver.md +140 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +573 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/core/errors.d.ts +16 -0
  9. package/dist/core/errors.js +32 -0
  10. package/dist/core/errors.js.map +1 -0
  11. package/dist/core/gate.d.ts +20 -0
  12. package/dist/core/gate.js +82 -0
  13. package/dist/core/gate.js.map +1 -0
  14. package/dist/core/keys.d.ts +18 -0
  15. package/dist/core/keys.js +37 -0
  16. package/dist/core/keys.js.map +1 -0
  17. package/dist/core/plan.d.ts +2 -0
  18. package/dist/core/plan.js +135 -0
  19. package/dist/core/plan.js.map +1 -0
  20. package/dist/core/program.d.ts +69 -0
  21. package/dist/core/program.js +191 -0
  22. package/dist/core/program.js.map +1 -0
  23. package/dist/core/reconcile.d.ts +37 -0
  24. package/dist/core/reconcile.js +198 -0
  25. package/dist/core/reconcile.js.map +1 -0
  26. package/dist/core/session.d.ts +25 -0
  27. package/dist/core/session.js +88 -0
  28. package/dist/core/session.js.map +1 -0
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.js +29 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/storage/db.d.ts +3 -0
  33. package/dist/storage/db.js +117 -0
  34. package/dist/storage/db.js.map +1 -0
  35. package/dist/storage/repository.d.ts +24 -0
  36. package/dist/storage/repository.js +449 -0
  37. package/dist/storage/repository.js.map +1 -0
  38. package/dist/tools/activeTask.d.ts +2 -0
  39. package/dist/tools/activeTask.js +41 -0
  40. package/dist/tools/activeTask.js.map +1 -0
  41. package/dist/tools/cancelTask.d.ts +2 -0
  42. package/dist/tools/cancelTask.js +39 -0
  43. package/dist/tools/cancelTask.js.map +1 -0
  44. package/dist/tools/checkpoint.d.ts +2 -0
  45. package/dist/tools/checkpoint.js +71 -0
  46. package/dist/tools/checkpoint.js.map +1 -0
  47. package/dist/tools/current.d.ts +2 -0
  48. package/dist/tools/current.js +64 -0
  49. package/dist/tools/current.js.map +1 -0
  50. package/dist/tools/finalize.d.ts +2 -0
  51. package/dist/tools/finalize.js +95 -0
  52. package/dist/tools/finalize.js.map +1 -0
  53. package/dist/tools/index.d.ts +6 -0
  54. package/dist/tools/index.js +7 -0
  55. package/dist/tools/index.js.map +1 -0
  56. package/dist/tools/startPlan.d.ts +2 -0
  57. package/dist/tools/startPlan.js +124 -0
  58. package/dist/tools/startPlan.js.map +1 -0
  59. package/dist/types/index.d.ts +142 -0
  60. package/dist/types/index.js +6 -0
  61. package/dist/types/index.js.map +1 -0
  62. package/package.json +48 -0
  63. package/scripts/interactive-demo.ts +394 -0
  64. package/scripts/mcp-call.mjs +56 -0
  65. package/scripts/prompt-check-hook.sh +27 -0
  66. package/scripts/session-start-hook.sh +47 -0
  67. package/scripts/stop-hook.mjs +83 -0
  68. package/scripts/stop-hook.sh +75 -0
@@ -0,0 +1,198 @@
1
+ import db from '../storage/db.js';
2
+ function now() { return new Date().toISOString(); }
3
+ export function reconcile(programId) {
4
+ const report = {
5
+ timestamp: now(),
6
+ scope: { programId },
7
+ summary: { totalPrograms: 0, totalNodes: 0, totalTasks: 0, totalSteps: 0, healthy: 0, stale: 0, orphan: 0, drift: 0 },
8
+ orphans: [],
9
+ staleTasks: [],
10
+ drifts: [],
11
+ suggestions: [],
12
+ };
13
+ // ---- 1. Count totals ----
14
+ const progFilter = programId ? 'WHERE program_id = ?' : '';
15
+ const progParams = programId ? [programId] : [];
16
+ report.summary.totalPrograms = db.prepare(`SELECT COUNT(*) as c FROM programs ${progFilter}`).get(...progParams).c;
17
+ report.summary.totalNodes = db.prepare(`SELECT COUNT(*) as c FROM program_nodes ${progFilter}`).get(...progParams).c;
18
+ // For tasks/steps, scope by program's sessions if programId given
19
+ let taskFilter = '';
20
+ let taskParams = [];
21
+ if (programId) {
22
+ const sessions = db.prepare('SELECT session_id FROM sessions WHERE program_id = ?').all(programId);
23
+ const sids = sessions.map((s) => s.session_id);
24
+ if (sids.length > 0) {
25
+ const ph = sids.map(() => '?').join(',');
26
+ taskFilter = `WHERE session_id IN (${ph})`;
27
+ taskParams = sids;
28
+ }
29
+ else {
30
+ taskFilter = "WHERE 1=0";
31
+ }
32
+ }
33
+ report.summary.totalTasks = db.prepare(`SELECT COUNT(*) as c FROM tasks ${taskFilter}`).get(...taskParams).c;
34
+ if (report.summary.totalTasks > 0) {
35
+ const steps = db.prepare(`SELECT COUNT(*) as c FROM steps WHERE task_id IN (SELECT id FROM tasks ${taskFilter})`).get(...taskParams);
36
+ report.summary.totalSteps = steps.c;
37
+ }
38
+ // ---- 2. Find orphan/stale steps ----
39
+ // Step whose task is cancelled or doesn't exist
40
+ const orphanSteps = db.prepare(`
41
+ SELECT s.id, s.task_id, s.path, s.status, s.completed_at
42
+ FROM steps s
43
+ LEFT JOIN tasks t ON s.task_id = t.id
44
+ WHERE t.id IS NULL OR t.status = 'cancelled'
45
+ `).all();
46
+ for (const s of orphanSteps) {
47
+ report.orphans.push({
48
+ type: 'step_orphan',
49
+ stepId: s.id,
50
+ taskId: s.task_id,
51
+ path: s.path,
52
+ detail: s.status === 'completed'
53
+ ? `Step completed but task is gone/cancelled. Can serve as skipKey source.`
54
+ : `Step is ${s.status} but its task no longer exists.`,
55
+ });
56
+ report.summary.orphan++;
57
+ }
58
+ // Steps with status 'current' but updated > 30 min ago (stale)
59
+ const staleThreshold = new Date(Date.now() - 30 * 60 * 1000).toISOString();
60
+ const staleSteps = db.prepare(`
61
+ SELECT s.id, s.task_id, s.path, t.updated_at
62
+ FROM steps s
63
+ JOIN tasks t ON s.task_id = t.id
64
+ WHERE s.status = 'current' AND t.updated_at < ?
65
+ ORDER BY t.updated_at ASC
66
+ `).all(staleThreshold);
67
+ for (const s of staleSteps) {
68
+ report.orphans.push({
69
+ type: 'step_stale',
70
+ stepId: s.id,
71
+ taskId: s.task_id,
72
+ path: s.path,
73
+ detail: `Step stuck in 'current' since ${s.updated_at}.`,
74
+ });
75
+ }
76
+ // ---- 3. Find stale active tasks ----
77
+ const staleTasks = db.prepare(`
78
+ SELECT id, title, status, session_id, created_at, updated_at
79
+ FROM tasks
80
+ WHERE status = 'active' AND updated_at < ?
81
+ ORDER BY updated_at ASC
82
+ `).all(staleThreshold);
83
+ for (const t of staleTasks) {
84
+ report.staleTasks.push({
85
+ taskId: t.id,
86
+ title: t.title,
87
+ status: t.status,
88
+ sessionId: t.session_id,
89
+ createdAt: t.created_at,
90
+ updatedAt: t.updated_at,
91
+ });
92
+ report.summary.stale++;
93
+ }
94
+ // ---- 4. Completed steps in incomplete tasks ----
95
+ const danglingCompletes = db.prepare(`
96
+ SELECT s.id, s.task_id, s.path, t.status as task_status
97
+ FROM steps s
98
+ JOIN tasks t ON s.task_id = t.id
99
+ WHERE s.status = 'completed' AND t.status = 'active'
100
+ `).all();
101
+ if (danglingCompletes.length > 0) {
102
+ report.suggestions.push(`${danglingCompletes.length} completed step(s) exist in active tasks. Consider finalizing.`);
103
+ }
104
+ // ---- 5. Task completed but Node not refreshed ----
105
+ const nodeDrifts = db.prepare(`
106
+ SELECT pn.node_id, pn.program_id, pn.title, pn.status as node_status
107
+ FROM program_nodes pn
108
+ WHERE pn.status = 'in_progress'
109
+ `).all();
110
+ for (const nd of nodeDrifts) {
111
+ // Check if all tasks under this node are completed
112
+ const sessions = db.prepare('SELECT session_id FROM sessions WHERE program_node_id = ?').all(nd.node_id);
113
+ if (sessions.length === 0)
114
+ continue;
115
+ const sids = sessions.map((s) => s.session_id);
116
+ const ph = sids.map(() => '?').join(',');
117
+ const allDone = db.prepare(`
118
+ SELECT COUNT(*) as c FROM tasks
119
+ WHERE session_id IN (${ph}) AND status != 'completed'
120
+ `).get(...sids);
121
+ const anyActive = db.prepare(`
122
+ SELECT COUNT(*) as c FROM tasks
123
+ WHERE session_id IN (${ph}) AND status = 'active'
124
+ `).get(...sids);
125
+ if (allDone.c === 0) {
126
+ // All tasks done but node still in_progress — drift!
127
+ report.drifts.push({
128
+ type: 'task_done_node_pending',
129
+ detail: `Node "${nd.title}" (${nd.node_id}): all tasks completed but node status is still '${nd.node_status}'. Run finalize on the last task to trigger auto-propagation.`,
130
+ });
131
+ report.summary.drift++;
132
+ }
133
+ if (anyActive.c === 0 && allDone.c === 0) {
134
+ // No active, all completed, node not refreshed
135
+ report.drifts.push({
136
+ type: 'task_done_node_pending',
137
+ detail: `Node "${nd.title}" (${nd.node_id}): all tasks done, node not refreshed.`,
138
+ });
139
+ if (report.summary.drift === 0)
140
+ report.summary.drift++;
141
+ }
142
+ }
143
+ // Also: task active but node already completed
144
+ const revDrifts = db.prepare(`
145
+ SELECT t.id as task_id, t.title as task_title, pn.node_id, pn.title as node_title
146
+ FROM tasks t
147
+ JOIN sessions s ON t.session_id = s.session_id
148
+ JOIN program_nodes pn ON s.program_node_id = pn.node_id
149
+ WHERE t.status = 'active' AND pn.status = 'completed'
150
+ `).all();
151
+ for (const rd of revDrifts) {
152
+ report.drifts.push({
153
+ type: 'task_active_node_done',
154
+ detail: `Task "${rd.task_title}" (${rd.task_id}) is active but its Node "${rd.node_title}" is already completed.`,
155
+ });
156
+ report.summary.drift++;
157
+ }
158
+ // ---- 6. Recalculate node/program status ----
159
+ const allNodes = programId
160
+ ? db.prepare('SELECT * FROM program_nodes WHERE program_id = ?').all(programId)
161
+ : db.prepare('SELECT * FROM program_nodes').all();
162
+ // Detect drift: node that's in_progress but all tasks done — report only, do not mutate
163
+ for (const node of allNodes) {
164
+ if (node.status !== 'in_progress')
165
+ continue;
166
+ const sessions = db.prepare('SELECT session_id FROM sessions WHERE program_node_id = ?').all(node.node_id);
167
+ if (sessions.length === 0)
168
+ continue;
169
+ const sids = sessions.map((s) => s.session_id);
170
+ const ph = sids.map(() => '?').join(',');
171
+ const pending = db.prepare(`SELECT COUNT(*) as c FROM tasks WHERE session_id IN (${ph}) AND status != 'completed'`).get(...sids);
172
+ if (pending.c === 0) {
173
+ report.drifts.push({
174
+ type: 'task_done_node_pending',
175
+ detail: `Node "${node.title}" (${node.node_id}): all tasks done but node is in_progress. Run finalize on the last task to trigger auto-propagation.`,
176
+ });
177
+ report.summary.drift++;
178
+ report.suggestions.push(`Node "${node.title}" (${node.node_id}) has all tasks complete but status is stuck. Re-run finalize on the last task to fix.`);
179
+ }
180
+ }
181
+ // ---- 7. Build suggestions ----
182
+ const activeCount = db.prepare("SELECT COUNT(*) as c FROM tasks WHERE status = 'active'").get().c;
183
+ if (activeCount > 3) {
184
+ report.suggestions.push(`${activeCount} active tasks. Consider reviewing for stale/inactive tasks with 'program rebuild --dry-run'.`);
185
+ }
186
+ if (report.orphans.length > 0) {
187
+ report.suggestions.push(`${report.orphans.length} orphan/stale step(s) found. They can serve as skipKey sources or be cleaned up.`);
188
+ }
189
+ if (report.drifts.length > 0) {
190
+ report.suggestions.push(`${report.drifts.length} state drift(s) found. Run finalize on affected tasks to propagate.`);
191
+ }
192
+ if (report.summary.totalTasks === 0) {
193
+ report.suggestions.push('No tasks found. Create a plan with: node dist/cli.js start-plan ...');
194
+ }
195
+ report.summary.healthy = report.summary.totalTasks - report.summary.stale - report.summary.orphan;
196
+ return report;
197
+ }
198
+ //# sourceMappingURL=reconcile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../src/core/reconcile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ export declare function getCurrentSessionId(): string | null;
2
+ export interface SessionInfo {
3
+ sessionId: string;
4
+ sessionSecret: string;
5
+ recoveryToken: string;
6
+ cliInstanceId: string;
7
+ workspace: string;
8
+ }
9
+ /** Create a new session and write the session file. */
10
+ export declare function createSession(workspace: string): SessionInfo;
11
+ /** Verify session_secret against stored hash. */
12
+ export declare function verifySessionSecret(sessionId: string, secret: string): boolean;
13
+ /** Verify recovery_token against stored hash. */
14
+ export declare function verifyRecoveryToken(sessionId: string, token: string): boolean;
15
+ /** Check if a session is still active. */
16
+ export declare function isSessionActive(sessionId: string): boolean;
17
+ /** Write session credentials to local file for CLI/Hook use. */
18
+ export declare function writeSessionFile(info: SessionInfo): void;
19
+ /** Write binding file — deterministic pointer for Stop Hook.
20
+ * Only one binding per workspace at a time (last-write-wins per MCP process). */
21
+ export declare function writeBindingFile(info: {
22
+ sessionId: string;
23
+ cliInstanceId: string;
24
+ workspace: string;
25
+ }): void;
@@ -0,0 +1,88 @@
1
+ import crypto from 'node:crypto';
2
+ import { mkdirSync, writeFileSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ import db from '../storage/db.js';
5
+ import { randomCode } from './keys.js';
6
+ const SESSION_DIR = '.step-gate/sessions';
7
+ const BINDING_DIR = '.step-gate/bindings';
8
+ // Shared state: the single session for this MCP process.
9
+ // Created lazily on first gate_start_plan call, then shared by all tools.
10
+ let currentSessionId = null;
11
+ export function getCurrentSessionId() { return currentSessionId; }
12
+ function sha256(input) {
13
+ return crypto.createHash('sha256').update(input).digest('hex');
14
+ }
15
+ function now() {
16
+ return new Date().toISOString();
17
+ }
18
+ /** Create a new session and write the session file. */
19
+ export function createSession(workspace) {
20
+ const sessionId = `ses_${randomCode(6)}`;
21
+ const sessionSecret = randomCode(6);
22
+ const recoveryToken = randomCode(6);
23
+ const cliInstanceId = `cli_${randomCode(6)}`;
24
+ const ts = now();
25
+ db.prepare(`
26
+ INSERT INTO sessions (session_id, session_secret_hash, recovery_token_hash, workspace, created_by_cli, created_at, updated_at)
27
+ VALUES (?, ?, ?, ?, ?, ?, ?)
28
+ `).run(sessionId, sha256(sessionSecret), sha256(recoveryToken), workspace, cliInstanceId, ts, ts);
29
+ db.prepare(`
30
+ INSERT INTO cli_instances (cli_instance_id, session_id, workspace, pid, created_at, last_seen_at)
31
+ VALUES (?, ?, ?, ?, ?, ?)
32
+ `).run(cliInstanceId, sessionId, workspace, process.pid, ts, ts);
33
+ currentSessionId = sessionId;
34
+ writeSessionFile({ sessionId, sessionSecret, recoveryToken, cliInstanceId, workspace });
35
+ writeBindingFile({ sessionId, cliInstanceId, workspace });
36
+ return { sessionId, sessionSecret, recoveryToken, cliInstanceId, workspace };
37
+ }
38
+ /** Verify session_secret against stored hash. */
39
+ export function verifySessionSecret(sessionId, secret) {
40
+ const row = db.prepare('SELECT session_secret_hash FROM sessions WHERE session_id = ?')
41
+ .get(sessionId);
42
+ if (!row)
43
+ return false;
44
+ return sha256(secret) === row.session_secret_hash;
45
+ }
46
+ /** Verify recovery_token against stored hash. */
47
+ export function verifyRecoveryToken(sessionId, token) {
48
+ const row = db.prepare('SELECT recovery_token_hash FROM sessions WHERE session_id = ?')
49
+ .get(sessionId);
50
+ if (!row)
51
+ return false;
52
+ return sha256(token) === row.recovery_token_hash;
53
+ }
54
+ /** Check if a session is still active. */
55
+ export function isSessionActive(sessionId) {
56
+ const row = db.prepare("SELECT status FROM sessions WHERE session_id = ?")
57
+ .get(sessionId);
58
+ return row?.status === 'active';
59
+ }
60
+ /** Write session credentials to local file for CLI/Hook use. */
61
+ export function writeSessionFile(info) {
62
+ const dir = resolve(info.workspace || process.cwd(), SESSION_DIR);
63
+ mkdirSync(dir, { recursive: true });
64
+ writeFileSync(resolve(dir, `${info.sessionId}.json`), JSON.stringify({
65
+ session_id: info.sessionId,
66
+ session_secret: info.sessionSecret,
67
+ recovery_token: info.recoveryToken,
68
+ cli_instance_id: info.cliInstanceId,
69
+ workspace: info.workspace || process.cwd(),
70
+ created_at: now(),
71
+ }, null, 2));
72
+ }
73
+ /** Write binding file — deterministic pointer for Stop Hook.
74
+ * Only one binding per workspace at a time (last-write-wins per MCP process). */
75
+ export function writeBindingFile(info) {
76
+ const dir = resolve(info.workspace || process.cwd(), BINDING_DIR);
77
+ mkdirSync(dir, { recursive: true });
78
+ const bindId = `bind_${info.cliInstanceId}`;
79
+ writeFileSync(resolve(dir, `${bindId}.json`), JSON.stringify({
80
+ binding_id: bindId,
81
+ session_id: info.sessionId,
82
+ session_file: `${SESSION_DIR}/${info.sessionId}.json`,
83
+ cli_instance_id: info.cliInstanceId,
84
+ workspace: info.workspace || process.cwd(),
85
+ created_at: now(),
86
+ }, null, 2));
87
+ }
88
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { registerStartPlan } from './tools/startPlan.js';
4
+ import { registerCurrent } from './tools/current.js';
5
+ import { registerCheckpoint } from './tools/checkpoint.js';
6
+ import { registerFinalize } from './tools/finalize.js';
7
+ import { registerActiveTask } from './tools/activeTask.js';
8
+ import { registerCancelTask } from './tools/cancelTask.js';
9
+ import { getCurrentSessionId } from './core/session.js';
10
+ // Session: created lazily on first gate_start_plan call.
11
+ // All tools share the same session via getCurrentSessionId().
12
+ // This ensures gate_current and gate_checkpoint see the same session
13
+ // as gate_start_plan.
14
+ const server = new McpServer({
15
+ name: 'agent-step-gate',
16
+ version: '0.2.0',
17
+ });
18
+ registerStartPlan(server);
19
+ registerCurrent(server, () => getCurrentSessionId());
20
+ registerCheckpoint(server);
21
+ registerFinalize(server);
22
+ registerActiveTask(server, () => getCurrentSessionId());
23
+ registerCancelTask(server, () => getCurrentSessionId());
24
+ async function main() {
25
+ const transport = new StdioServerTransport();
26
+ await server.connect(transport);
27
+ }
28
+ main().catch(console.error);
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,yDAAyD;AACzD,8DAA8D;AAC9D,qEAAqE;AACrE,sBAAsB;AAEtB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,iBAAiB;IACvB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAC1B,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,mBAAmB,EAAE,CAAC,CAAC;AACrD,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAC3B,gBAAgB,CAAC,MAAM,CAAC,CAAC;AACzB,kBAAkB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,mBAAmB,EAAE,CAAC,CAAC;AACxD,kBAAkB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,mBAAmB,EAAE,CAAC,CAAC;AAExD,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import Database from 'better-sqlite3';
2
+ declare const db: Database.Database;
3
+ export default db;
@@ -0,0 +1,117 @@
1
+ import Database from 'better-sqlite3';
2
+ import { mkdirSync } from 'node:fs';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const DB_PATH = resolve(__dirname, '..', '..', 'data', 'gate.db');
7
+ mkdirSync(dirname(DB_PATH), { recursive: true });
8
+ const db = new Database(DB_PATH);
9
+ db.pragma('journal_mode = WAL');
10
+ db.pragma('synchronous = NORMAL');
11
+ db.pragma('busy_timeout = 5000');
12
+ db.pragma('foreign_keys = ON');
13
+ db.exec(`
14
+ CREATE TABLE IF NOT EXISTS programs (
15
+ program_id TEXT PRIMARY KEY,
16
+ title TEXT NOT NULL,
17
+ status TEXT NOT NULL DEFAULT 'active',
18
+ total_nodes INTEGER NOT NULL DEFAULT 0,
19
+ created_at TEXT NOT NULL,
20
+ updated_at TEXT NOT NULL
21
+ );
22
+
23
+ CREATE TABLE IF NOT EXISTS program_nodes (
24
+ node_id TEXT PRIMARY KEY,
25
+ program_id TEXT NOT NULL,
26
+ title TEXT NOT NULL,
27
+ description TEXT,
28
+ order_index INTEGER NOT NULL DEFAULT 0,
29
+ status TEXT NOT NULL DEFAULT 'pending',
30
+ session_id TEXT,
31
+ completed_at TEXT,
32
+ created_at TEXT NOT NULL,
33
+ FOREIGN KEY(program_id) REFERENCES programs(program_id)
34
+ );
35
+
36
+ CREATE TABLE IF NOT EXISTS sessions (
37
+ session_id TEXT PRIMARY KEY,
38
+ session_secret_hash TEXT NOT NULL,
39
+ recovery_token_hash TEXT NOT NULL,
40
+ title TEXT,
41
+ workspace TEXT,
42
+ program_id TEXT,
43
+ program_node_id TEXT,
44
+ status TEXT NOT NULL DEFAULT 'active',
45
+ created_by_cli TEXT,
46
+ created_at TEXT NOT NULL,
47
+ updated_at TEXT NOT NULL,
48
+ FOREIGN KEY(program_id) REFERENCES programs(program_id),
49
+ FOREIGN KEY(program_node_id) REFERENCES program_nodes(node_id)
50
+ );
51
+
52
+ CREATE TABLE IF NOT EXISTS cli_instances (
53
+ cli_instance_id TEXT PRIMARY KEY,
54
+ session_id TEXT NOT NULL,
55
+ hostname TEXT,
56
+ pid INTEGER,
57
+ workspace TEXT,
58
+ status TEXT NOT NULL DEFAULT 'active',
59
+ created_at TEXT NOT NULL,
60
+ last_seen_at TEXT NOT NULL,
61
+ FOREIGN KEY(session_id) REFERENCES sessions(session_id)
62
+ );
63
+
64
+ CREATE TABLE IF NOT EXISTS tasks (
65
+ id TEXT PRIMARY KEY,
66
+ title TEXT NOT NULL,
67
+ status TEXT NOT NULL DEFAULT 'active',
68
+ current_index INTEGER NOT NULL DEFAULT 0,
69
+ total_steps INTEGER NOT NULL,
70
+ final_key_hash TEXT,
71
+ session_id TEXT,
72
+ created_at TEXT NOT NULL,
73
+ updated_at TEXT NOT NULL,
74
+ FOREIGN KEY(session_id) REFERENCES sessions(session_id)
75
+ );
76
+
77
+ CREATE TABLE IF NOT EXISTS steps (
78
+ id TEXT PRIMARY KEY,
79
+ task_id TEXT NOT NULL,
80
+ parent_path TEXT,
81
+ title TEXT NOT NULL,
82
+ path TEXT NOT NULL,
83
+ order_index INTEGER NOT NULL,
84
+ depends_on TEXT,
85
+ status TEXT NOT NULL DEFAULT 'pending',
86
+ step_key_hash TEXT,
87
+ completed_at TEXT,
88
+ created_at TEXT NOT NULL,
89
+ FOREIGN KEY(task_id) REFERENCES tasks(id)
90
+ );
91
+
92
+ CREATE TABLE IF NOT EXISTS events (
93
+ id TEXT PRIMARY KEY,
94
+ task_id TEXT NOT NULL,
95
+ step_id TEXT,
96
+ event_type TEXT NOT NULL,
97
+ payload TEXT,
98
+ created_at TEXT NOT NULL,
99
+ FOREIGN KEY(task_id) REFERENCES tasks(id)
100
+ );
101
+ `);
102
+ // Safe migrations for existing databases
103
+ for (const sql of [
104
+ "ALTER TABLE tasks ADD COLUMN session_id TEXT",
105
+ "ALTER TABLE sessions ADD COLUMN created_by_cli TEXT",
106
+ "ALTER TABLE sessions ADD COLUMN workspace TEXT",
107
+ "ALTER TABLE sessions ADD COLUMN program_id TEXT",
108
+ "ALTER TABLE sessions ADD COLUMN program_node_id TEXT",
109
+ "ALTER TABLE program_nodes ADD COLUMN node_key_hash TEXT",
110
+ ]) {
111
+ try {
112
+ db.exec(sql);
113
+ }
114
+ catch { /* column exists */ }
115
+ }
116
+ export default db;
117
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/storage/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAElE,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAEjD,MAAM,EAAE,GAAsB,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;AAEpD,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;AAChC,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;AAClC,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;AACjC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;AAE/B,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwFP,CAAC,CAAC;AAEH,yCAAyC;AACzC,KAAK,MAAM,GAAG,IAAI;IAChB,8CAA8C;IAC9C,qDAAqD;IACrD,gDAAgD;IAChD,iDAAiD;IACjD,sDAAsD;IACtD,yDAAyD;CAC1D,EAAE,CAAC;IACF,IAAI,CAAC;QAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC;AACrD,CAAC;AAED,eAAe,EAAE,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { TaskRow, StepRow, EventRow } from '../types/index.js';
2
+ export declare function createTask(task: TaskRow, steps: StepRow[]): void;
3
+ export declare function getTask(taskId: string): TaskRow | undefined;
4
+ export declare function getCurrentSteps(taskId: string): StepRow[];
5
+ export declare function getCurrentStep(taskId: string): StepRow | undefined;
6
+ export declare function getStep(stepId: string): StepRow | undefined;
7
+ export declare function getTaskSteps(taskId: string): StepRow[];
8
+ export declare function completeStep(stepId: string): void;
9
+ export declare function setCurrentStep(stepId: string, keyHash: string): void;
10
+ export declare function updateTaskStatus(taskId: string, status: string): void;
11
+ export declare function setFinalKeyHash(taskId: string, hash: string): void;
12
+ export declare function verifyStepKey(taskId: string, stepId: string, keyPlaintext: string): boolean;
13
+ export declare function verifyTaskKey(taskId: string, keyPlaintext: string): boolean;
14
+ /** @deprecated use verifyTaskKey */
15
+ export declare const verifyFinalKey: typeof verifyTaskKey;
16
+ export declare function getActiveTasks(sessionId?: string): TaskRow[];
17
+ export declare function getActiveTask(): TaskRow | undefined;
18
+ export declare function cancelTask(taskId: string, sessionId: string): void;
19
+ export declare function addEvent(taskId: string, stepId: string | null, eventType: string, payload?: string): void;
20
+ export declare function completeAndAdvance(completedStepId: string, nextStepIds: string[], nextKeyHashes: string[], taskId: string, finalKeyHash: string | null): void;
21
+ export declare function verifySkipKey(oldTaskId: string, stepId: string, oldKey: string): boolean;
22
+ /** Record that a skipKey has been consumed — prevents unlimited reuse (B1 fix) */
23
+ export declare function recordSkipConsumed(taskId: string, stepId: string): void;
24
+ export declare function getEvents(taskId: string): EventRow[];