maskweaver 0.10.1 → 0.11.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.
@@ -0,0 +1,87 @@
1
+ import type { ContextIndex } from './types.js';
2
+ export declare function getWikiDir(basePath: string): string;
3
+ export declare function getBuildDir(basePath: string, changeId: string, buildId: string): string;
4
+ export declare function getTasksDir(basePath: string, changeId: string, buildId: string): string;
5
+ export declare function getTaskDir(basePath: string, changeId: string, buildId: string, taskId: string): string;
6
+ export declare function getWavesDir(basePath: string, changeId: string, buildId: string): string;
7
+ export declare function getSnapshotsDir(basePath: string, changeId: string, buildId: string): string;
8
+ export declare function writeBrief(taskDir: string, data: {
9
+ taskId: string;
10
+ buildId: string;
11
+ wave: number;
12
+ dependsOn: string[];
13
+ allowedPaths: string[];
14
+ forbiddenPaths: string[];
15
+ contextPaths: string[];
16
+ acceptanceRefs: string[];
17
+ verifyCommands: string[];
18
+ goal: string;
19
+ requiredOutcome: string[];
20
+ criticalWarnings: string[];
21
+ }): Promise<string>;
22
+ export declare function writeContext(taskDir: string, data: {
23
+ taskId: string;
24
+ sourceHashes: Record<string, string>;
25
+ investigation: string;
26
+ constraints: string[];
27
+ reuseCandidates: string[];
28
+ knownRisks: string[];
29
+ }): Promise<string>;
30
+ export declare function writeResult(taskDir: string, data: {
31
+ taskId: string;
32
+ status: 'succeeded' | 'failed';
33
+ changedFiles: string[];
34
+ createdSymbols: string[];
35
+ errorSummary?: string;
36
+ downstreamExports: Array<{
37
+ kind: string;
38
+ path: string;
39
+ summary: string;
40
+ }>;
41
+ notes: string;
42
+ }): Promise<string>;
43
+ export declare function writeVerify(taskDir: string, data: {
44
+ taskId: string;
45
+ status: 'passed' | 'failed';
46
+ commands: Array<{
47
+ command: string;
48
+ status: string;
49
+ output?: string;
50
+ }>;
51
+ }): Promise<string>;
52
+ export declare function writeWavePlan(wavesDir: string, data: {
53
+ buildId: string;
54
+ waveIndex: number;
55
+ tasks: Array<{
56
+ taskId: string;
57
+ phaseId: string;
58
+ allowedPaths: string[];
59
+ dependsOn: string[];
60
+ }>;
61
+ parallelSafe: boolean;
62
+ }): Promise<string>;
63
+ export declare function updateWaveResults(wavesDir: string, waveIndex: number, results: {
64
+ tasks: Array<{
65
+ taskId: string;
66
+ status: string;
67
+ changedFiles: string[];
68
+ verification: string;
69
+ }>;
70
+ contextUpdates: string[];
71
+ }): Promise<void>;
72
+ export declare function ensureWikiDir(basePath: string): Promise<string>;
73
+ export declare function writeWikiPage(basePath: string, page: string, content: string): Promise<void>;
74
+ export declare function readWikiPage(basePath: string, page: string): Promise<string | null>;
75
+ export declare function appendChangesLog(basePath: string, entry: {
76
+ ts: string;
77
+ buildId: string;
78
+ wave: number;
79
+ taskId: string;
80
+ status: string;
81
+ changedFiles: string[];
82
+ exports: string[];
83
+ verify: string;
84
+ }): Promise<void>;
85
+ export declare function writeContextIndex(buildDir: string, index: ContextIndex): Promise<void>;
86
+ export declare function readContextIndex(buildDir: string): Promise<ContextIndex | null>;
87
+ export declare function snapshotWiki(basePath: string, snapshotDir: string): Promise<void>;
@@ -0,0 +1,366 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { mkdir, readFile, writeFile, readdir, copyFile } from 'node:fs/promises';
4
+ import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
5
+ export function getWikiDir(basePath) {
6
+ return path.join(basePath, 'wiki');
7
+ }
8
+ export function getBuildDir(basePath, changeId, buildId) {
9
+ return path.join(basePath, 'changes', changeId, 'builds', buildId);
10
+ }
11
+ export function getTasksDir(basePath, changeId, buildId) {
12
+ return path.join(getBuildDir(basePath, changeId, buildId), 'tasks');
13
+ }
14
+ export function getTaskDir(basePath, changeId, buildId, taskId) {
15
+ return path.join(getTasksDir(basePath, changeId, buildId), taskId);
16
+ }
17
+ export function getWavesDir(basePath, changeId, buildId) {
18
+ return path.join(getBuildDir(basePath, changeId, buildId), 'waves');
19
+ }
20
+ export function getSnapshotsDir(basePath, changeId, buildId) {
21
+ return path.join(getBuildDir(basePath, changeId, buildId), 'snapshots');
22
+ }
23
+ export async function writeBrief(taskDir, data) {
24
+ await mkdir(taskDir, { recursive: true });
25
+ const frontmatter = {
26
+ task_id: data.taskId,
27
+ build_id: data.buildId,
28
+ wave: data.wave,
29
+ status: 'ready',
30
+ depends_on: data.dependsOn,
31
+ allowed_paths: data.allowedPaths,
32
+ forbidden_paths: data.forbiddenPaths,
33
+ context_files: data.contextPaths,
34
+ acceptance_refs: data.acceptanceRefs,
35
+ verify_commands: data.verifyCommands,
36
+ };
37
+ const lines = [
38
+ '---',
39
+ stringifyYaml(frontmatter).trim(),
40
+ '---',
41
+ '',
42
+ `# Task: ${data.taskId}`,
43
+ '',
44
+ data.goal,
45
+ '',
46
+ '# Required Outcome',
47
+ '',
48
+ ];
49
+ for (const item of data.requiredOutcome) {
50
+ lines.push(`- ${item}`);
51
+ }
52
+ lines.push('', '# Critical Context', '');
53
+ lines.push('Read `context.md` before editing.', '');
54
+ lines.push('# Completion Report', '');
55
+ lines.push('Write `result.md` with: files changed, exported API, verification commands run, blockers.');
56
+ if (data.criticalWarnings.length > 0) {
57
+ lines.push('', '# Critical Warnings', '');
58
+ for (const w of data.criticalWarnings) {
59
+ lines.push(`- **WARNING:** ${w}`);
60
+ }
61
+ }
62
+ lines.push('');
63
+ const content = lines.join('\n');
64
+ const filePath = path.join(taskDir, 'brief.md');
65
+ await writeFile(filePath, content, 'utf-8');
66
+ return filePath;
67
+ }
68
+ export async function writeContext(taskDir, data) {
69
+ await mkdir(taskDir, { recursive: true });
70
+ const frontmatter = {
71
+ task_id: data.taskId,
72
+ source_hashes: data.sourceHashes,
73
+ };
74
+ const lines = [
75
+ '---',
76
+ stringifyYaml(frontmatter).trim(),
77
+ '---',
78
+ '',
79
+ `# Context: ${data.taskId}`,
80
+ '',
81
+ data.investigation,
82
+ '',
83
+ ];
84
+ if (data.constraints.length > 0) {
85
+ lines.push('# Constraints', '');
86
+ for (const c of data.constraints) {
87
+ lines.push(`- ${c}`);
88
+ }
89
+ lines.push('');
90
+ }
91
+ if (data.reuseCandidates.length > 0) {
92
+ lines.push('# Reuse Candidates', '');
93
+ for (const r of data.reuseCandidates) {
94
+ lines.push(`- ${r}`);
95
+ }
96
+ lines.push('');
97
+ }
98
+ if (data.knownRisks.length > 0) {
99
+ lines.push('# Known Risks', '');
100
+ for (const r of data.knownRisks) {
101
+ lines.push(`- ${r}`);
102
+ }
103
+ lines.push('');
104
+ }
105
+ const content = lines.join('\n');
106
+ const filePath = path.join(taskDir, 'context.md');
107
+ await writeFile(filePath, content, 'utf-8');
108
+ return filePath;
109
+ }
110
+ export async function writeResult(taskDir, data) {
111
+ await mkdir(taskDir, { recursive: true });
112
+ const frontmatter = {
113
+ task_id: data.taskId,
114
+ status: data.status,
115
+ changed_files: data.changedFiles,
116
+ created_symbols: data.createdSymbols,
117
+ };
118
+ if (data.errorSummary) {
119
+ frontmatter.error_summary = data.errorSummary;
120
+ }
121
+ const lines = [
122
+ '---',
123
+ stringifyYaml(frontmatter).trim(),
124
+ '---',
125
+ '',
126
+ `# Result: ${data.taskId}`,
127
+ '',
128
+ `**Status:** ${data.status}`,
129
+ '',
130
+ ];
131
+ if (data.changedFiles.length > 0) {
132
+ lines.push('# Changed Files', '');
133
+ for (const f of data.changedFiles) {
134
+ lines.push(`- ${f}`);
135
+ }
136
+ lines.push('');
137
+ }
138
+ if (data.createdSymbols.length > 0) {
139
+ lines.push('# Created Symbols', '');
140
+ for (const s of data.createdSymbols) {
141
+ lines.push(`- ${s}`);
142
+ }
143
+ lines.push('');
144
+ }
145
+ if (data.downstreamExports.length > 0) {
146
+ lines.push('# Downstream Exports', '');
147
+ for (const e of data.downstreamExports) {
148
+ lines.push(`- **${e.kind}:** \`${e.path}\` — ${e.summary}`);
149
+ }
150
+ lines.push('');
151
+ }
152
+ if (data.errorSummary) {
153
+ lines.push('# Error Summary', '', data.errorSummary, '');
154
+ }
155
+ lines.push('# Notes', '', data.notes, '');
156
+ const content = lines.join('\n');
157
+ const filePath = path.join(taskDir, 'result.md');
158
+ await writeFile(filePath, content, 'utf-8');
159
+ return filePath;
160
+ }
161
+ export async function writeVerify(taskDir, data) {
162
+ await mkdir(taskDir, { recursive: true });
163
+ const frontmatter = {
164
+ task_id: data.taskId,
165
+ status: data.status,
166
+ };
167
+ const lines = [
168
+ '---',
169
+ stringifyYaml(frontmatter).trim(),
170
+ '---',
171
+ '',
172
+ `# Verification: ${data.taskId}`,
173
+ '',
174
+ `**Status:** ${data.status}`,
175
+ '',
176
+ ];
177
+ for (const cmd of data.commands) {
178
+ lines.push(`## Command: \`${cmd.command}\``);
179
+ lines.push('');
180
+ lines.push(`**Status:** ${cmd.status}`);
181
+ if (cmd.output !== undefined) {
182
+ lines.push('');
183
+ lines.push('```');
184
+ lines.push(cmd.output);
185
+ lines.push('```');
186
+ }
187
+ lines.push('');
188
+ }
189
+ const content = lines.join('\n');
190
+ const filePath = path.join(taskDir, 'verify.md');
191
+ await writeFile(filePath, content, 'utf-8');
192
+ return filePath;
193
+ }
194
+ export async function writeWavePlan(wavesDir, data) {
195
+ await mkdir(wavesDir, { recursive: true });
196
+ const frontmatter = {
197
+ build_id: data.buildId,
198
+ wave_index: data.waveIndex,
199
+ parallel_safe: data.parallelSafe,
200
+ task_count: data.tasks.length,
201
+ };
202
+ const lines = [
203
+ '---',
204
+ stringifyYaml(frontmatter).trim(),
205
+ '---',
206
+ '',
207
+ `# Wave ${data.waveIndex}`,
208
+ '',
209
+ `Build: ${data.buildId} `,
210
+ `Parallel Safe: ${data.parallelSafe}`,
211
+ '',
212
+ '## Tasks',
213
+ '',
214
+ ];
215
+ for (const t of data.tasks) {
216
+ lines.push(`### ${t.taskId} (${t.phaseId})`);
217
+ lines.push('');
218
+ if (t.dependsOn.length > 0) {
219
+ lines.push(`Depends on: ${t.dependsOn.join(', ')}`);
220
+ }
221
+ if (t.allowedPaths.length > 0) {
222
+ lines.push('');
223
+ lines.push('Allowed paths:');
224
+ for (const p of t.allowedPaths) {
225
+ lines.push(`- ${p}`);
226
+ }
227
+ }
228
+ lines.push('');
229
+ }
230
+ const content = lines.join('\n');
231
+ const filePath = path.join(wavesDir, `wave-${String(data.waveIndex).padStart(3, '0')}.md`);
232
+ await writeFile(filePath, content, 'utf-8');
233
+ return filePath;
234
+ }
235
+ export async function updateWaveResults(wavesDir, waveIndex, results) {
236
+ const waveFile = path.join(wavesDir, `wave-${String(waveIndex).padStart(3, '0')}.md`);
237
+ let existing = '';
238
+ if (fs.existsSync(waveFile)) {
239
+ existing = await readFile(waveFile, 'utf-8');
240
+ }
241
+ const lines = existing ? [existing.trimEnd()] : [
242
+ `# Wave ${waveIndex}`,
243
+ '',
244
+ ];
245
+ lines.push('', '---', '', '## Results', '');
246
+ for (const t of results.tasks) {
247
+ lines.push(`### ${t.taskId}`);
248
+ lines.push('');
249
+ lines.push(`**Status:** ${t.status}`);
250
+ if (t.changedFiles.length > 0) {
251
+ lines.push('');
252
+ lines.push('Changed files:');
253
+ for (const f of t.changedFiles) {
254
+ lines.push(`- ${f}`);
255
+ }
256
+ }
257
+ if (t.verification) {
258
+ lines.push('');
259
+ lines.push(`Verification: ${t.verification}`);
260
+ }
261
+ lines.push('');
262
+ }
263
+ if (results.contextUpdates.length > 0) {
264
+ lines.push('### Context Updates', '');
265
+ for (const u of results.contextUpdates) {
266
+ lines.push(`- ${u}`);
267
+ }
268
+ lines.push('');
269
+ }
270
+ lines.push('');
271
+ await writeFile(waveFile, lines.join('\n'), 'utf-8');
272
+ }
273
+ export async function ensureWikiDir(basePath) {
274
+ const wikiDir = getWikiDir(basePath);
275
+ await mkdir(wikiDir, { recursive: true });
276
+ const defaultPages = [
277
+ 'architecture.md',
278
+ 'conventions.md',
279
+ 'dependency-map.md',
280
+ 'changes-log.md',
281
+ 'changes-log.jsonl',
282
+ ];
283
+ for (const page of defaultPages) {
284
+ const pagePath = path.join(wikiDir, page);
285
+ if (!fs.existsSync(pagePath)) {
286
+ if (page.endsWith('.jsonl')) {
287
+ await writeFile(pagePath, '', 'utf-8');
288
+ }
289
+ else {
290
+ const title = page.replace(/\.md$/, '').replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
291
+ await writeFile(pagePath, `# ${title}\n\n`, 'utf-8');
292
+ }
293
+ }
294
+ }
295
+ return wikiDir;
296
+ }
297
+ export async function writeWikiPage(basePath, page, content) {
298
+ const wikiDir = getWikiDir(basePath);
299
+ await mkdir(wikiDir, { recursive: true });
300
+ const pagePath = path.join(wikiDir, page);
301
+ await writeFile(pagePath, content, 'utf-8');
302
+ }
303
+ export async function readWikiPage(basePath, page) {
304
+ const wikiDir = getWikiDir(basePath);
305
+ const pagePath = path.join(wikiDir, page);
306
+ if (!fs.existsSync(pagePath)) {
307
+ return null;
308
+ }
309
+ return await readFile(pagePath, 'utf-8');
310
+ }
311
+ export async function appendChangesLog(basePath, entry) {
312
+ const wikiDir = getWikiDir(basePath);
313
+ await mkdir(wikiDir, { recursive: true });
314
+ const jsonlPath = path.join(wikiDir, 'changes-log.jsonl');
315
+ const jsonlLine = JSON.stringify(entry) + '\n';
316
+ await writeFile(jsonlPath, jsonlLine, { encoding: 'utf-8', flag: 'a' });
317
+ const mdPath = path.join(wikiDir, 'changes-log.md');
318
+ const timestamp = entry.ts.replace(/[TZ]/g, ' ').trim();
319
+ const lines = [
320
+ '',
321
+ `## ${timestamp} — ${entry.taskId}`,
322
+ '',
323
+ `- **Build:** ${entry.buildId} `,
324
+ `- **Wave:** ${entry.wave} `,
325
+ `- **Status:** ${entry.status} `,
326
+ ];
327
+ if (entry.changedFiles.length > 0) {
328
+ lines.push(`- **Files:** ${entry.changedFiles.join(', ')} `);
329
+ }
330
+ if (entry.exports.length > 0) {
331
+ lines.push(`- **Exports:** ${entry.exports.join(', ')} `);
332
+ }
333
+ lines.push(`- **Verify:** ${entry.verify} `);
334
+ lines.push('');
335
+ await writeFile(mdPath, lines.join('\n'), { encoding: 'utf-8', flag: 'a' });
336
+ }
337
+ export async function writeContextIndex(buildDir, index) {
338
+ await mkdir(buildDir, { recursive: true });
339
+ const indexPath = path.join(buildDir, 'context-index.yaml');
340
+ const content = stringifyYaml(index, { indent: 2 });
341
+ await writeFile(indexPath, content, 'utf-8');
342
+ }
343
+ export async function readContextIndex(buildDir) {
344
+ const indexPath = path.join(buildDir, 'context-index.yaml');
345
+ if (!fs.existsSync(indexPath)) {
346
+ return null;
347
+ }
348
+ const content = await readFile(indexPath, 'utf-8');
349
+ const parsed = parseYaml(content);
350
+ return parsed;
351
+ }
352
+ export async function snapshotWiki(basePath, snapshotDir) {
353
+ const wikiDir = getWikiDir(basePath);
354
+ if (!fs.existsSync(wikiDir)) {
355
+ return;
356
+ }
357
+ await mkdir(snapshotDir, { recursive: true });
358
+ const entries = await readdir(wikiDir, { withFileTypes: true });
359
+ for (const entry of entries) {
360
+ if (entry.isFile()) {
361
+ const srcPath = path.join(wikiDir, entry.name);
362
+ const destPath = path.join(snapshotDir, entry.name);
363
+ await copyFile(srcPath, destPath);
364
+ }
365
+ }
366
+ }
@@ -330,6 +330,99 @@ export interface EnvironmentAnalysis {
330
330
  summary: string;
331
331
  analyzedAt: string;
332
332
  }
333
+ export type WaveTaskStatus = 'pending' | 'dispatched' | 'succeeded' | 'failed' | 'blocked' | 'verified';
334
+ export type FailureKind = 'prompt_failure' | 'implementation_failure' | 'validation_failure' | 'conflict_failure' | 'dependency_failure' | 'environment_failure';
335
+ export interface WavePlan {
336
+ waveIndex: number;
337
+ tasks: WaveTaskEntry[];
338
+ parallelSafe: boolean;
339
+ wikiSnapshotDir?: string;
340
+ startedAt?: string;
341
+ completedAt?: string;
342
+ }
343
+ export interface WaveTaskEntry {
344
+ taskId: string;
345
+ phaseId: string;
346
+ status: WaveTaskStatus;
347
+ agentTier: AgentTier;
348
+ mask: string | null;
349
+ allowedPaths: string[];
350
+ forbiddenPaths: string[];
351
+ dependsOn: string[];
352
+ }
353
+ export interface TaskDelegationContract {
354
+ buildId: string;
355
+ phaseId: string;
356
+ taskId: string;
357
+ waveIndex: number;
358
+ subagentType: string;
359
+ mask: string | null;
360
+ prompt: string;
361
+ briefPath: string;
362
+ contextPath: string;
363
+ allowedPaths: string[];
364
+ forbiddenPaths: string[];
365
+ verifyCommands: string[];
366
+ resumeCommand: string;
367
+ }
368
+ export interface TaskResult {
369
+ taskId: string;
370
+ phaseId: string;
371
+ status: 'succeeded' | 'failed';
372
+ changedFiles: string[];
373
+ createdSymbols: string[];
374
+ errorSummary?: string;
375
+ failureKind?: FailureKind;
376
+ downstreamExports: Array<{
377
+ kind: string;
378
+ path: string;
379
+ summary: string;
380
+ }>;
381
+ }
382
+ export interface WaveDelta {
383
+ waveIndex: number;
384
+ completed: string[];
385
+ failed: string[];
386
+ changedFiles: string[];
387
+ newSymbols: string[];
388
+ downstreamExports: Array<{
389
+ kind: string;
390
+ path: string;
391
+ summary: string;
392
+ }>;
393
+ }
394
+ export interface ContextIndex {
395
+ buildId: string;
396
+ lastWaveIndex: number;
397
+ tasks: Record<string, {
398
+ context: string[];
399
+ upstream: Array<{
400
+ taskId: string;
401
+ result: string;
402
+ verified: boolean;
403
+ exports: string[];
404
+ }>;
405
+ }>;
406
+ }
407
+ export type BuildDecision = {
408
+ kind: 'dispatch_wave';
409
+ wave: WavePlan;
410
+ contracts: TaskDelegationContract[];
411
+ } | {
412
+ kind: 'verify';
413
+ waveIndex: number;
414
+ } | {
415
+ kind: 'repair';
416
+ contract: TaskDelegationContract;
417
+ failureKind: FailureKind;
418
+ } | {
419
+ kind: 'blocked';
420
+ reason: string;
421
+ failedTasks: string[];
422
+ } | {
423
+ kind: 'complete';
424
+ summary: string;
425
+ };
333
426
  export type WeaveEvent = {
334
427
  type: 'phase_started';
335
428
  phaseId: string;
@@ -0,0 +1,11 @@
1
+ import type { WeavePlan, BuildDecision, TaskResult } from './types.js';
2
+ export declare function advanceBuildStep(options: {
3
+ plan: WeavePlan;
4
+ basePath: string;
5
+ buildId: string;
6
+ changeId: string;
7
+ lastResults?: TaskResult[];
8
+ maxRetries?: number;
9
+ phaseIds?: string[];
10
+ savePlan?: (plan: WeavePlan) => Promise<void>;
11
+ }): Promise<BuildDecision>;