mindforge-sdk 10.7.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/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # @mindforge/sdk
2
+
3
+ TypeScript SDK for embedding MindForge in tools, dashboards, and CI pipelines.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @mindforge/sdk
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```typescript
14
+ import { MindForgeClient } from '@mindforge/sdk';
15
+
16
+ const client = new MindForgeClient({
17
+ projectRoot: '/path/to/project',
18
+ apiKey: process.env.ANTHROPIC_API_KEY,
19
+ });
20
+
21
+ // Health check
22
+ const health = await client.health();
23
+ console.log(health.overallStatus); // 'healthy' | 'warning' | 'error'
24
+
25
+ // Read audit log
26
+ const findings = client.readAuditLog({ event: 'security_finding' });
27
+ console.log(findings);
28
+
29
+ // Read metrics
30
+ const metrics = client.readSessionMetrics(5);
31
+ console.log(metrics);
32
+ ```
33
+
34
+ ## Real-time event streaming
35
+
36
+ ```typescript
37
+ import { MindForgeEventStream } from '@mindforge/sdk';
38
+
39
+ const stream = new MindForgeEventStream();
40
+ await stream.start(7337);
41
+ stream.watchAuditLog('/path/to/project');
42
+
43
+ // Subscribe from browser or tool:
44
+ const es = new EventSource('http://localhost:7337/events');
45
+ es.addEventListener('audit_entry', (e) => {
46
+ const entry = JSON.parse(e.data);
47
+ if (entry.event === 'task_completed') {
48
+ console.log('Task done:', entry.task_name);
49
+ }
50
+ });
51
+ ```
52
+
53
+ ## Config validation
54
+
55
+ ```typescript
56
+ const { valid, errors } = client.validateConfig();
57
+ if (!valid) console.error(errors);
58
+ ```
59
+
60
+ ## Security notes
61
+
62
+ - `HANDOFF.json` may contain sensitive project state. Do not expose it to untrusted clients
63
+ or log its contents in external systems.
64
+ - The SDK operates on local files and provides no network authentication. Do not expose SDK
65
+ endpoints to the public internet.
66
+
67
+ ## New in v9.0.0
68
+
69
+ ### Additional exports
70
+
71
+ ```typescript
72
+ import {
73
+ MindForgeClient,
74
+ MindForgeEventStream,
75
+ VERSION, // '9.0.0'
76
+ } from '@mindforge/sdk';
77
+
78
+ import type {
79
+ WaveExecutionResult, // Result type returned by wave execution operations
80
+ MigrationResult, // Result type returned by schema migration operations
81
+ } from '@mindforge/sdk';
82
+ ```
83
+
84
+ ### New `MindForgeClient` methods
85
+
86
+ ```typescript
87
+ const client = new MindForgeClient({ projectRoot: '/path/to/project' });
88
+
89
+ // Read the current auto-state from auto-state.json
90
+ const state = client.readAutoState();
91
+ console.log(state.status); // 'idle' | 'running' | 'awaiting_regeneration'
92
+
93
+ // Check whether the local MindForge database has been initialized
94
+ const ready = client.isDatabaseInitialized();
95
+ if (!ready) {
96
+ console.warn('Run mindforge:init-project first');
97
+ }
98
+ ```
99
+
100
+ ## TypeScript support
101
+
102
+ Full type definitions included. No `@types/` package needed.
@@ -0,0 +1,28 @@
1
+ /**
2
+ * MindForge SDK — Main Client
3
+ */
4
+ import { EventEmitter } from 'events';
5
+ import type { MindForgeConfig, HealthReport, AuditLogEntry } from './types';
6
+ export declare class MindForgeClient extends EventEmitter {
7
+ private config;
8
+ private projectRoot;
9
+ constructor(config?: MindForgeConfig);
10
+ isInitialised(): boolean;
11
+ readState(): Record<string, unknown> | null;
12
+ readHandoff(): Record<string, unknown> | null;
13
+ health(): Promise<HealthReport>;
14
+ readAuditLog(filter?: {
15
+ event?: string;
16
+ phase?: number;
17
+ since?: Date;
18
+ }): AuditLogEntry[];
19
+ readSessionMetrics(limit?: number): unknown[];
20
+ validateConfig(): {
21
+ valid: boolean;
22
+ errors: string[];
23
+ warnings: string[];
24
+ };
25
+ readAutoState(): Record<string, unknown> | null;
26
+ getDbPath(): string;
27
+ isDatabaseInitialized(): boolean;
28
+ }
package/dist/client.js ADDED
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ /**
3
+ * MindForge SDK — Main Client
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.MindForgeClient = void 0;
40
+ const events_1 = require("events");
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ class MindForgeClient extends events_1.EventEmitter {
44
+ constructor(config = {}) {
45
+ super();
46
+ this.projectRoot = config.projectRoot ?? process.cwd();
47
+ this.config = {
48
+ projectRoot: this.projectRoot,
49
+ apiKey: config.apiKey ?? process.env.ANTHROPIC_API_KEY ?? '',
50
+ ciMode: config.ciMode ?? (process.env.CI === 'true'),
51
+ outputFormat: config.outputFormat ?? 'json',
52
+ taskTimeoutMs: config.taskTimeoutMs ?? 600000,
53
+ };
54
+ }
55
+ // ── Project state ──────────────────────────────────────────────────────────
56
+ isInitialised() {
57
+ return fs.existsSync(path.join(this.projectRoot, '.planning', 'PROJECT.md'));
58
+ }
59
+ readState() {
60
+ const statePath = path.join(this.projectRoot, '.planning', 'STATE.md');
61
+ if (!fs.existsSync(statePath))
62
+ return null;
63
+ return { raw: fs.readFileSync(statePath, 'utf8') };
64
+ }
65
+ readHandoff() {
66
+ const handoffPath = path.join(this.projectRoot, '.planning', 'HANDOFF.json');
67
+ if (!fs.existsSync(handoffPath))
68
+ return null;
69
+ try {
70
+ return JSON.parse(fs.readFileSync(handoffPath, 'utf8'));
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ // ── Health check ───────────────────────────────────────────────────────────
77
+ async health() {
78
+ const errors = [];
79
+ const warnings = [];
80
+ const info = [];
81
+ const requiredFiles = [
82
+ '.planning/STATE.md',
83
+ '.planning/HANDOFF.json',
84
+ '.planning/AUDIT.jsonl',
85
+ '.mindforge/org/CONVENTIONS.md',
86
+ ];
87
+ for (const file of requiredFiles) {
88
+ const fullPath = path.join(this.projectRoot, file);
89
+ if (!fs.existsSync(fullPath)) {
90
+ warnings.push({ category: 'installation', message: `Missing: ${file}`, autoRepairable: false });
91
+ }
92
+ }
93
+ // Check HANDOFF.json validity
94
+ const handoff = this.readHandoff();
95
+ if (handoff && !handoff.schema_version) {
96
+ errors.push({ category: 'state', message: 'HANDOFF.json missing schema_version field', autoRepairable: false });
97
+ }
98
+ // Check AUDIT.jsonl
99
+ const auditPath = path.join(this.projectRoot, '.planning', 'AUDIT.jsonl');
100
+ if (fs.existsSync(auditPath)) {
101
+ const lineCount = fs.readFileSync(auditPath, 'utf8').split('\n').filter(Boolean).length;
102
+ if (lineCount > 9000) {
103
+ warnings.push({ category: 'audit', message: `AUDIT.jsonl has ${lineCount} lines — archive soon`, autoRepairable: true });
104
+ }
105
+ info.push({ category: 'audit', message: `AUDIT.jsonl: ${lineCount} entries`, autoRepairable: false });
106
+ }
107
+ return {
108
+ overallStatus: errors.length > 0 ? 'error' : warnings.length > 0 ? 'warning' : 'healthy',
109
+ errors,
110
+ warnings,
111
+ informational: info,
112
+ timestamp: new Date().toISOString(),
113
+ };
114
+ }
115
+ // ── Audit log reading ──────────────────────────────────────────────────────
116
+ readAuditLog(filter) {
117
+ const auditPath = path.join(this.projectRoot, '.planning', 'AUDIT.jsonl');
118
+ if (!fs.existsSync(auditPath))
119
+ return [];
120
+ return fs.readFileSync(auditPath, 'utf8')
121
+ .split('\n')
122
+ .filter(Boolean)
123
+ .map((line) => { try {
124
+ return JSON.parse(line);
125
+ }
126
+ catch {
127
+ return null;
128
+ } })
129
+ .filter((entry) => !!entry)
130
+ .filter(entry => {
131
+ if (filter?.event && entry.event !== filter.event)
132
+ return false;
133
+ if (filter?.phase !== undefined && entry.phase !== filter.phase)
134
+ return false;
135
+ if (filter?.since && new Date(entry.timestamp) < filter.since)
136
+ return false;
137
+ return true;
138
+ });
139
+ }
140
+ // ── Metrics reading ────────────────────────────────────────────────────────
141
+ readSessionMetrics(limit = 10) {
142
+ const metricsPath = path.join(this.projectRoot, '.mindforge', 'metrics', 'session-quality.jsonl');
143
+ if (!fs.existsSync(metricsPath))
144
+ return [];
145
+ return fs.readFileSync(metricsPath, 'utf8')
146
+ .split('\n')
147
+ .filter(Boolean)
148
+ .slice(-limit)
149
+ .map((line) => { try {
150
+ return JSON.parse(line);
151
+ }
152
+ catch {
153
+ return null;
154
+ } })
155
+ .filter(Boolean);
156
+ }
157
+ // ── Config validation ──────────────────────────────────────────────────────
158
+ validateConfig() {
159
+ const errors = [];
160
+ const warnings = [];
161
+ const configPath = path.join(this.projectRoot, 'MINDFORGE.md');
162
+ if (!fs.existsSync(configPath)) {
163
+ return { valid: false, errors: ['MINDFORGE.md not found at project root'], warnings: [] };
164
+ }
165
+ const content = fs.readFileSync(configPath, 'utf8');
166
+ // Required fields that must be present in a valid MINDFORGE.md
167
+ const requiredFields = [
168
+ { key: 'VERSION', pattern: /\[VERSION\]\s*=\s*.+/ },
169
+ { key: 'REACTIVE_MODE', pattern: /\[REACTIVE_MODE\]\s*=\s*.+/ },
170
+ { key: 'PLANNER', pattern: /\[PLANNER\]\s*=\s*.+/ },
171
+ { key: 'EXECUTOR', pattern: /\[EXECUTOR\]\s*=\s*.+/ },
172
+ { key: 'MIN_SOUL_SCORE', pattern: /\[MIN_SOUL_SCORE\]\s*=\s*.+/ },
173
+ ];
174
+ for (const field of requiredFields) {
175
+ if (!field.pattern.test(content)) {
176
+ errors.push(`Missing required field: [${field.key}]`);
177
+ }
178
+ }
179
+ // Recommended fields — warn if missing
180
+ const recommendedFields = [
181
+ { key: 'COST_WARN_USD', pattern: /\[COST_WARN_USD\]\s*=\s*.+/ },
182
+ { key: 'COST_HARD_LIMIT_USD', pattern: /\[COST_HARD_LIMIT_USD\]\s*=\s*.+/ },
183
+ { key: 'BLOCK_ON_SECURITY', pattern: /\[BLOCK_ON_SECURITY\]\s*=\s*.+/ },
184
+ ];
185
+ for (const field of recommendedFields) {
186
+ if (!field.pattern.test(content)) {
187
+ warnings.push(`Recommended field missing: [${field.key}]`);
188
+ }
189
+ }
190
+ // Schema-based validation if schema file exists
191
+ const schemaPath = path.join(this.projectRoot, '.mindforge', 'MINDFORGE-SCHEMA.json');
192
+ if (fs.existsSync(schemaPath)) {
193
+ try {
194
+ const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
195
+ const nonOverridableKeys = Object.entries(schema.properties ?? {})
196
+ .filter(([, def]) => def.nonOverridable === true)
197
+ .map(([key]) => key);
198
+ for (const key of nonOverridableKeys) {
199
+ // Non-overridable fields must not be set to false
200
+ const disabledPattern = new RegExp(`\\[${key}\\]\\s*=\\s*false`, 'i');
201
+ if (disabledPattern.test(content)) {
202
+ errors.push(`Non-overridable field [${key}] cannot be disabled`);
203
+ }
204
+ }
205
+ }
206
+ catch {
207
+ warnings.push('MINDFORGE-SCHEMA.json exists but could not be parsed');
208
+ }
209
+ }
210
+ return { valid: errors.length === 0, errors, warnings };
211
+ }
212
+ // ── v9 Pillar XXIV: Wave execution status ─────────────────────────────────
213
+ readAutoState() {
214
+ const statePath = path.join(this.projectRoot, '.planning', 'auto-state.json');
215
+ if (!fs.existsSync(statePath))
216
+ return null;
217
+ try {
218
+ return JSON.parse(fs.readFileSync(statePath, 'utf8'));
219
+ }
220
+ catch {
221
+ return null;
222
+ }
223
+ }
224
+ // ── v9 Pillar XXVI: Knowledge query ───────────────────────────────────────
225
+ getDbPath() {
226
+ return path.join(this.projectRoot, '.mindforge', 'celestial.db');
227
+ }
228
+ isDatabaseInitialized() {
229
+ return fs.existsSync(this.getDbPath());
230
+ }
231
+ }
232
+ exports.MindForgeClient = MindForgeClient;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * MindForge SDK — Command Builders
3
+ * Builds the command strings that can be sent to Claude Code / Antigravity
4
+ * via their programmatic APIs.
5
+ */
6
+ export interface CommandOptions {
7
+ flags?: string[];
8
+ args?: string[];
9
+ }
10
+ export declare const commands: {
11
+ /**
12
+ * Build a /mindforge:health command string
13
+ */
14
+ health(opts?: CommandOptions): string;
15
+ /**
16
+ * Build a /mindforge:plan-phase command string
17
+ */
18
+ planPhase(phase: number, opts?: CommandOptions): string;
19
+ /**
20
+ * Build a /mindforge:execute-phase command string
21
+ */
22
+ executePhase(phase: number, opts?: CommandOptions): string;
23
+ /**
24
+ * Build a /mindforge:security-scan command string
25
+ */
26
+ securityScan(path?: string, opts?: CommandOptions): string;
27
+ /**
28
+ * Build a /mindforge:audit command string with filters
29
+ */
30
+ audit(filter?: {
31
+ phase?: number;
32
+ event?: string;
33
+ since?: string;
34
+ }): string;
35
+ /**
36
+ * Build a /mindforge:pr-review command string
37
+ */
38
+ prReview(opts?: CommandOptions): string;
39
+ };
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ /**
3
+ * MindForge SDK — Command Builders
4
+ * Builds the command strings that can be sent to Claude Code / Antigravity
5
+ * via their programmatic APIs.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.commands = void 0;
9
+ exports.commands = {
10
+ /**
11
+ * Build a /mindforge:health command string
12
+ */
13
+ health(opts = {}) {
14
+ const flags = opts.flags?.join(' ') ?? '';
15
+ return `/mindforge:health ${flags}`.trim();
16
+ },
17
+ /**
18
+ * Build a /mindforge:plan-phase command string
19
+ */
20
+ planPhase(phase, opts = {}) {
21
+ const flags = opts.flags?.join(' ') ?? '';
22
+ return `/mindforge:plan-phase ${phase} ${flags}`.trim();
23
+ },
24
+ /**
25
+ * Build a /mindforge:execute-phase command string
26
+ */
27
+ executePhase(phase, opts = {}) {
28
+ const flags = opts.flags?.join(' ') ?? '';
29
+ return `/mindforge:execute-phase ${phase} ${flags}`.trim();
30
+ },
31
+ /**
32
+ * Build a /mindforge:security-scan command string
33
+ */
34
+ securityScan(path, opts = {}) {
35
+ const flags = opts.flags?.join(' ') ?? '';
36
+ return `/mindforge:security-scan ${path ?? ''} ${flags}`.trim();
37
+ },
38
+ /**
39
+ * Build a /mindforge:audit command string with filters
40
+ */
41
+ audit(filter = {}) {
42
+ const parts = ['/mindforge:audit'];
43
+ if (filter.phase)
44
+ parts.push(`--phase ${filter.phase}`);
45
+ if (filter.event)
46
+ parts.push(`--event ${filter.event}`);
47
+ if (filter.since)
48
+ parts.push(`--since ${filter.since}`);
49
+ return parts.join(' ');
50
+ },
51
+ /**
52
+ * Build a /mindforge:pr-review command string
53
+ */
54
+ prReview(opts = {}) {
55
+ const flags = opts.flags?.join(' ') ?? '';
56
+ return `/mindforge:pr-review ${flags}`.trim();
57
+ },
58
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * MindForge SDK — Server-Sent Events (SSE) stream for real-time progress
3
+ * Enables external tools to subscribe to MindForge execution progress.
4
+ */
5
+ export declare class MindForgeEventStream {
6
+ private clients;
7
+ private server;
8
+ private auditWatcher;
9
+ private lastAuditLine;
10
+ /**
11
+ * Start the SSE server on the given port
12
+ */
13
+ start(port?: number): Promise<void>;
14
+ /**
15
+ * Watch AUDIT.jsonl for new entries and broadcast as SSE events
16
+ */
17
+ watchAuditLog(projectRoot: string): void;
18
+ /**
19
+ * Broadcast an event to all connected clients
20
+ */
21
+ broadcast(eventType: string, data: unknown): void;
22
+ private sendEvent;
23
+ /**
24
+ * Stop the event stream server
25
+ */
26
+ stop(): void;
27
+ }
package/dist/events.js ADDED
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ /**
3
+ * MindForge SDK — Server-Sent Events (SSE) stream for real-time progress
4
+ * Enables external tools to subscribe to MindForge execution progress.
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.MindForgeEventStream = void 0;
41
+ const http = __importStar(require("http"));
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ class MindForgeEventStream {
45
+ constructor() {
46
+ this.clients = [];
47
+ this.server = null;
48
+ this.auditWatcher = null;
49
+ this.lastAuditLine = 0;
50
+ }
51
+ /**
52
+ * Start the SSE server on the given port
53
+ */
54
+ start(port = 7337) {
55
+ return new Promise((resolve, reject) => {
56
+ this.server = http.createServer((req, res) => {
57
+ if (req.url !== '/events') {
58
+ res.writeHead(404);
59
+ res.end();
60
+ return;
61
+ }
62
+ // SECURITY: Only allow connections from localhost
63
+ const clientIp = req.socket.remoteAddress;
64
+ const isLocalhost = clientIp === '127.0.0.1' ||
65
+ clientIp === '::1' ||
66
+ clientIp === '::ffff:127.0.0.1';
67
+ if (!isLocalhost) {
68
+ res.writeHead(403);
69
+ res.end('Forbidden: MindForge event stream is localhost-only');
70
+ return;
71
+ }
72
+ // CORS: Only allow localhost origins (exact match, not wildcard)
73
+ const origin = req.headers.origin;
74
+ const allowedOriginPattern = /^https?:\/\/localhost(:\d+)?$/;
75
+ const corsOrigin = origin && allowedOriginPattern.test(origin)
76
+ ? origin
77
+ : 'http://localhost';
78
+ res.writeHead(200, {
79
+ 'Content-Type': 'text/event-stream',
80
+ 'Cache-Control': 'no-cache',
81
+ 'Connection': 'keep-alive',
82
+ 'Access-Control-Allow-Origin': corsOrigin,
83
+ 'X-Content-Type-Options': 'nosniff',
84
+ });
85
+ const clientId = Date.now().toString();
86
+ this.clients.push({ id: clientId, response: res });
87
+ // Send initial connection event
88
+ this.sendEvent(res, 'connected', { clientId, timestamp: new Date().toISOString() });
89
+ // Clean up on disconnect
90
+ req.on('close', () => {
91
+ this.clients = this.clients.filter(c => c.id !== clientId);
92
+ });
93
+ });
94
+ // SECURITY: Bind to localhost ONLY — not all interfaces
95
+ this.server.listen(port, '127.0.0.1', () => {
96
+ console.log(`MindForge event stream: http://localhost:${port}/events (localhost only)`);
97
+ resolve();
98
+ });
99
+ this.server.on('error', (err) => {
100
+ if (err.code === 'EADDRINUSE') {
101
+ reject(new Error(`Port ${port} is already in use. Use: new MindForgeEventStream().start(${port + 1})`));
102
+ }
103
+ else {
104
+ reject(err);
105
+ }
106
+ });
107
+ });
108
+ }
109
+ /**
110
+ * Watch AUDIT.jsonl for new entries and broadcast as SSE events
111
+ */
112
+ watchAuditLog(projectRoot) {
113
+ const auditPath = path.join(projectRoot, '.planning', 'AUDIT.jsonl');
114
+ if (!fs.existsSync(auditPath)) {
115
+ // Create the file if it doesn't exist yet
116
+ fs.writeFileSync(auditPath, '');
117
+ }
118
+ // Set initial line count
119
+ const content = fs.readFileSync(auditPath, 'utf8');
120
+ this.lastAuditLine = content.split('\n').filter(Boolean).length;
121
+ try {
122
+ this.auditWatcher = fs.watch(auditPath, () => {
123
+ const lines = fs.readFileSync(auditPath, 'utf8')
124
+ .split('\n')
125
+ .filter(Boolean);
126
+ // Broadcast new lines
127
+ for (let i = this.lastAuditLine; i < lines.length; i++) {
128
+ try {
129
+ const entry = JSON.parse(lines[i]);
130
+ this.broadcast('audit_entry', entry);
131
+ }
132
+ catch {
133
+ // Ignore parse errors for incomplete lines
134
+ }
135
+ }
136
+ this.lastAuditLine = lines.length;
137
+ });
138
+ }
139
+ catch (err) {
140
+ if (err.code === 'ENOSPC') {
141
+ // Linux inotify limit reached — fall back to polling
142
+ console.warn('MindForge: inotify limit reached, falling back to 2s polling');
143
+ setInterval(() => {
144
+ const lines = fs.readFileSync(auditPath, 'utf8').split('\n').filter(Boolean);
145
+ for (let i = this.lastAuditLine; i < lines.length; i++) {
146
+ try {
147
+ this.broadcast('audit_entry', JSON.parse(lines[i]));
148
+ }
149
+ catch { /* ignore */ }
150
+ }
151
+ this.lastAuditLine = lines.length;
152
+ }, 2000);
153
+ }
154
+ else {
155
+ throw err;
156
+ }
157
+ }
158
+ }
159
+ /**
160
+ * Broadcast an event to all connected clients
161
+ */
162
+ broadcast(eventType, data) {
163
+ this.clients.forEach(client => {
164
+ this.sendEvent(client.response, eventType, data);
165
+ });
166
+ }
167
+ sendEvent(res, type, data) {
168
+ try {
169
+ res.write(`event: ${type}\n`);
170
+ res.write(`data: ${JSON.stringify(data)}\n\n`);
171
+ }
172
+ catch {
173
+ // Client disconnected
174
+ }
175
+ }
176
+ /**
177
+ * Stop the event stream server
178
+ */
179
+ stop() {
180
+ this.auditWatcher?.close();
181
+ this.server?.close();
182
+ this.clients.forEach(c => c.response.end());
183
+ this.clients = [];
184
+ }
185
+ }
186
+ exports.MindForgeEventStream = MindForgeEventStream;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * MindForge SDK — Public API
3
+ * @module @mindforge/sdk
4
+ */
5
+ export { MindForgeClient } from './client';
6
+ export { MindForgeEventStream } from './events';
7
+ export { commands } from './commands';
8
+ export { MindForgeMemory } from './memory';
9
+ export type { CommandOptions } from './commands';
10
+ export type { MindForgeConfig, PhaseResult, TaskResult, SecurityFinding, GateResult, HealthReport, HealthIssue, MindForgeEvent, AuditLogEntry, WaveExecutionResult, MigrationResult, } from './types';
11
+ export declare const VERSION = "10.7.0";