@wundam/orchex 1.0.0-rc.2 → 1.0.0-rc.21

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 (98) hide show
  1. package/README.md +59 -18
  2. package/dist/cloud-executor.d.ts +71 -0
  3. package/dist/cloud-executor.js +335 -0
  4. package/dist/cloud-sync.d.ts +8 -0
  5. package/dist/cloud-sync.js +52 -0
  6. package/dist/config.d.ts +30 -4
  7. package/dist/config.js +61 -2
  8. package/dist/context-builder.d.ts +2 -0
  9. package/dist/context-builder.js +11 -3
  10. package/dist/cost.js +1 -1
  11. package/dist/entitlements/jwt.d.ts +7 -0
  12. package/dist/entitlements/jwt.js +78 -0
  13. package/dist/entitlements/resolve.d.ts +17 -0
  14. package/dist/entitlements/resolve.js +49 -0
  15. package/dist/entitlements/types.d.ts +21 -0
  16. package/dist/entitlements/types.js +4 -0
  17. package/dist/executors/base.d.ts +1 -1
  18. package/dist/executors/bedrock-executor.d.ts +39 -0
  19. package/dist/executors/bedrock-executor.js +197 -0
  20. package/dist/executors/index.d.ts +1 -0
  21. package/dist/executors/index.js +24 -1
  22. package/dist/index.js +468 -23
  23. package/dist/intelligence/index.d.ts +44 -0
  24. package/dist/intelligence/index.js +160 -0
  25. package/dist/key-cache.d.ts +31 -0
  26. package/dist/key-cache.js +84 -0
  27. package/dist/login-helpers.d.ts +25 -0
  28. package/dist/login-helpers.js +54 -0
  29. package/dist/manifest.js +18 -1
  30. package/dist/mcp-instructions.d.ts +1 -0
  31. package/dist/mcp-instructions.js +84 -0
  32. package/dist/mcp-resources.d.ts +8 -0
  33. package/dist/mcp-resources.js +420 -0
  34. package/dist/model-cache.d.ts +18 -0
  35. package/dist/model-cache.js +62 -0
  36. package/dist/model-validator.d.ts +20 -0
  37. package/dist/model-validator.js +125 -0
  38. package/dist/orchestrator.d.ts +14 -0
  39. package/dist/orchestrator.js +191 -32
  40. package/dist/setup/ide-registry.d.ts +13 -0
  41. package/dist/setup/ide-registry.js +51 -0
  42. package/dist/setup/index.d.ts +1 -0
  43. package/dist/setup/index.js +111 -0
  44. package/dist/tier-gating.js +0 -16
  45. package/dist/tiers.d.ts +35 -5
  46. package/dist/tiers.js +39 -3
  47. package/dist/tools.d.ts +6 -1
  48. package/dist/tools.js +852 -95
  49. package/dist/types.d.ts +71 -60
  50. package/dist/types.js +3 -0
  51. package/dist/waves.d.ts +1 -1
  52. package/dist/waves.js +29 -2
  53. package/package.json +41 -5
  54. package/src/entitlements/public-key.pem +9 -0
  55. package/dist/intelligence/anti-pattern-detector.d.ts +0 -117
  56. package/dist/intelligence/anti-pattern-detector.js +0 -327
  57. package/dist/intelligence/budget-enforcer.d.ts +0 -119
  58. package/dist/intelligence/budget-enforcer.js +0 -226
  59. package/dist/intelligence/context-optimizer.d.ts +0 -111
  60. package/dist/intelligence/context-optimizer.js +0 -282
  61. package/dist/intelligence/cost-tracker.d.ts +0 -114
  62. package/dist/intelligence/cost-tracker.js +0 -183
  63. package/dist/intelligence/deliverable-extractor.d.ts +0 -134
  64. package/dist/intelligence/deliverable-extractor.js +0 -909
  65. package/dist/intelligence/dependency-inferrer.d.ts +0 -87
  66. package/dist/intelligence/dependency-inferrer.js +0 -403
  67. package/dist/intelligence/diagnostics.d.ts +0 -33
  68. package/dist/intelligence/diagnostics.js +0 -64
  69. package/dist/intelligence/error-analyzer.d.ts +0 -7
  70. package/dist/intelligence/error-analyzer.js +0 -76
  71. package/dist/intelligence/file-chunker.d.ts +0 -15
  72. package/dist/intelligence/file-chunker.js +0 -64
  73. package/dist/intelligence/fix-stream-manager.d.ts +0 -59
  74. package/dist/intelligence/fix-stream-manager.js +0 -212
  75. package/dist/intelligence/heuristics.d.ts +0 -23
  76. package/dist/intelligence/heuristics.js +0 -124
  77. package/dist/intelligence/learning-engine.d.ts +0 -157
  78. package/dist/intelligence/learning-engine.js +0 -433
  79. package/dist/intelligence/learning-feedback.d.ts +0 -96
  80. package/dist/intelligence/learning-feedback.js +0 -202
  81. package/dist/intelligence/pattern-analyzer.d.ts +0 -35
  82. package/dist/intelligence/pattern-analyzer.js +0 -189
  83. package/dist/intelligence/plan-parser.d.ts +0 -124
  84. package/dist/intelligence/plan-parser.js +0 -498
  85. package/dist/intelligence/planner.d.ts +0 -29
  86. package/dist/intelligence/planner.js +0 -86
  87. package/dist/intelligence/self-healer.d.ts +0 -16
  88. package/dist/intelligence/self-healer.js +0 -84
  89. package/dist/intelligence/slicing-metrics.d.ts +0 -62
  90. package/dist/intelligence/slicing-metrics.js +0 -202
  91. package/dist/intelligence/slicing-templates.d.ts +0 -81
  92. package/dist/intelligence/slicing-templates.js +0 -420
  93. package/dist/intelligence/split-suggester.d.ts +0 -69
  94. package/dist/intelligence/split-suggester.js +0 -176
  95. package/dist/intelligence/stream-generator.d.ts +0 -90
  96. package/dist/intelligence/stream-generator.js +0 -452
  97. package/dist/telemetry/telemetry-types.d.ts +0 -85
  98. package/dist/telemetry/telemetry-types.js +0 -1
@@ -1,498 +0,0 @@
1
- /**
2
- * Markdown plan document parser.
3
- * Extracts structure from planning documents for stream generation.
4
- */
5
- import { createLogger } from '../logging.js';
6
- const logger = createLogger('plan-parser');
7
- /**
8
- * Strip code blocks from text so file references inside code examples
9
- * (e.g., import paths with .js extensions) aren't treated as real file refs.
10
- */
11
- function stripCodeBlocks(text) {
12
- return text.replace(/^```[\s\S]*?^```/gm, '');
13
- }
14
- /**
15
- * Extract file references from text.
16
- * Matches patterns like: `src/foo.ts`, "src/bar.js", src/baz.ts
17
- *
18
- * Code blocks are stripped first to avoid extracting import paths
19
- * (e.g., `from './foo.js'`) as real file references.
20
- *
21
- * Post-processing: `.js` files are dropped when a corresponding `.ts` exists,
22
- * since TypeScript projects use .js in imports but .ts as source files.
23
- */
24
- export function extractFileReferences(text) {
25
- // Strip code blocks to avoid matching import paths as file references
26
- const proseOnly = stripCodeBlocks(text);
27
- const patterns = [
28
- // Backtick-quoted paths
29
- /`([^`]+\.(ts|js|tsx|jsx|md|sql|json|yaml|yml))`/g,
30
- // Double-quoted paths
31
- /"([^"]+\.(ts|js|tsx|jsx|md|sql|json|yaml|yml))"/g,
32
- // Square bracket paths (markdown links)
33
- /\[([^\]]+\.(ts|js|tsx|jsx|md|sql|json|yaml|yml))\]/g,
34
- // Unquoted src/ paths
35
- /\b(src\/[^\s,)]+\.(ts|js|tsx|jsx))/g,
36
- // Unquoted tests/ paths
37
- /\b(tests?\/[^\s,)]+\.(ts|js|tsx|jsx))/g,
38
- // Unquoted docs/ paths
39
- /\b(docs\/[^\s,)]+\.md)/g,
40
- ];
41
- const refs = new Set();
42
- for (const pattern of patterns) {
43
- // Reset regex state
44
- pattern.lastIndex = 0;
45
- let match;
46
- while ((match = pattern.exec(proseOnly)) !== null) {
47
- // Clean up the path
48
- const filePath = match[1].trim();
49
- if (filePath && !filePath.includes(' ')) {
50
- refs.add(filePath);
51
- }
52
- }
53
- }
54
- // Drop .js files when a corresponding .ts exists (TypeScript ESM convention)
55
- const result = [...refs];
56
- return result.filter(ref => {
57
- if (ref.endsWith('.js')) {
58
- const tsEquivalent = ref.replace(/\.js$/, '.ts');
59
- if (refs.has(tsEquivalent))
60
- return false;
61
- }
62
- if (ref.endsWith('.jsx')) {
63
- const tsxEquivalent = ref.replace(/\.jsx$/, '.tsx');
64
- if (refs.has(tsxEquivalent))
65
- return false;
66
- }
67
- return true;
68
- });
69
- }
70
- /**
71
- * Extract explicit dependency mentions from text.
72
- * Matches patterns like: "depends on X", "requires Y", "after Z"
73
- */
74
- export function extractExplicitDeps(text) {
75
- const patterns = [
76
- /depends?\s+on\s+[`"]?([^`",.]+)[`"]?/gi,
77
- /requires?\s+[`"]?([^`",.]+)[`"]?/gi,
78
- /after\s+[`"]?([^`",.]+)[`"]?\s+(?:is\s+)?(?:complete|done|finished)/gi,
79
- /\bdeps?:\s*\[([^\]]+)\]/gi,
80
- ];
81
- const deps = new Set();
82
- for (const pattern of patterns) {
83
- // Reset regex state
84
- pattern.lastIndex = 0;
85
- let match;
86
- while ((match = pattern.exec(text)) !== null) {
87
- // Handle array format: deps: [a, b, c]
88
- if (pattern.source.includes('deps')) {
89
- const items = match[1].split(',').map(s => s.trim().replace(/[`"']/g, ''));
90
- for (const item of items) {
91
- if (item && item.length > 2 && item.length < 50) {
92
- deps.add(item);
93
- }
94
- }
95
- }
96
- else {
97
- // Clean up the match - remove quotes and trailing "is" (from "X is complete" pattern)
98
- const dep = match[1].trim().replace(/\s+is$/i, '').replace(/[`"]/g, '');
99
- if (dep && dep.length > 2 && dep.length < 50) {
100
- deps.add(dep);
101
- }
102
- }
103
- }
104
- }
105
- return [...deps];
106
- }
107
- /**
108
- * Extract code blocks from markdown.
109
- * Only matches fences at column 0 (start of line) to avoid treating
110
- * indented code examples inside YAML plan fields as separate blocks.
111
- */
112
- export function extractCodeBlocks(markdown) {
113
- const blocks = [];
114
- // H1: Normalize CRLF to LF for cross-platform compatibility
115
- const normalized = markdown.replace(/\r\n/g, '\n');
116
- // Anchor opening and closing fences to start of line with multiline flag.
117
- // This prevents indented ``` inside plan fields from being treated as delimiters.
118
- const pattern = /^```(\w+)?[ \t]*\n(?:\/\/\s*(\S+)\n)?([\s\S]*?)^```[ \t]*$/gm;
119
- let match;
120
- while ((match = pattern.exec(normalized)) !== null) {
121
- blocks.push({
122
- language: match[1] || 'text',
123
- filename: match[2],
124
- code: match[3].trim(),
125
- });
126
- }
127
- return blocks;
128
- }
129
- /**
130
- * Parse markdown into hierarchical sections.
131
- */
132
- export function parseMarkdownSections(markdown) {
133
- const lines = markdown.split('\n');
134
- const sections = [];
135
- const stack = [];
136
- let currentContent = [];
137
- let inCodeBlock = false;
138
- for (const line of lines) {
139
- // Track code blocks to avoid parsing headers inside them
140
- if (line.startsWith('```')) {
141
- inCodeBlock = !inCodeBlock;
142
- currentContent.push(line);
143
- continue;
144
- }
145
- if (inCodeBlock) {
146
- currentContent.push(line);
147
- continue;
148
- }
149
- const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
150
- if (headerMatch) {
151
- // Save previous content
152
- if (stack.length > 0) {
153
- stack[stack.length - 1].content = currentContent.join('\n').trim();
154
- }
155
- currentContent = [];
156
- const level = headerMatch[1].length;
157
- const title = headerMatch[2].trim();
158
- const section = {
159
- level,
160
- title,
161
- content: '',
162
- codeBlocks: [],
163
- fileReferences: [],
164
- explicitDeps: [],
165
- children: [],
166
- };
167
- // Pop stack until we find a parent with lower level
168
- while (stack.length > 0 && stack[stack.length - 1].level >= level) {
169
- stack.pop();
170
- }
171
- if (stack.length > 0) {
172
- stack[stack.length - 1].children.push(section);
173
- }
174
- else {
175
- sections.push(section);
176
- }
177
- stack.push(section);
178
- }
179
- else {
180
- currentContent.push(line);
181
- }
182
- }
183
- // Save final content
184
- if (stack.length > 0) {
185
- stack[stack.length - 1].content = currentContent.join('\n').trim();
186
- }
187
- // Post-process sections to extract metadata
188
- function processSection(section) {
189
- section.codeBlocks = extractCodeBlocks(section.content);
190
- section.fileReferences = extractFileReferences(section.content);
191
- section.explicitDeps = extractExplicitDeps(section.content);
192
- for (const child of section.children) {
193
- processSection(child);
194
- }
195
- }
196
- for (const section of sections) {
197
- processSection(section);
198
- }
199
- return sections;
200
- }
201
- /**
202
- * Parse a markdown planning document.
203
- */
204
- export function parsePlanDocument(markdown) {
205
- const sections = parseMarkdownSections(markdown);
206
- // Extract title from first H1
207
- const title = sections.find(s => s.level === 1)?.title ?? 'Untitled Plan';
208
- // Extract description from content before first H2
209
- const firstH1 = sections.find(s => s.level === 1);
210
- const description = firstH1?.content.split('\n').slice(0, 3).join('\n') ?? '';
211
- // Collect all file references and code blocks
212
- const allFileReferences = new Set();
213
- const allCodeBlocks = [];
214
- function collectFromSection(section) {
215
- for (const ref of section.fileReferences) {
216
- allFileReferences.add(ref);
217
- }
218
- allCodeBlocks.push(...section.codeBlocks);
219
- for (const child of section.children) {
220
- collectFromSection(child);
221
- }
222
- }
223
- for (const section of sections) {
224
- collectFromSection(section);
225
- }
226
- return {
227
- title,
228
- description,
229
- sections,
230
- allFileReferences: [...allFileReferences],
231
- allCodeBlocks,
232
- };
233
- }
234
- /**
235
- * Get sections at a specific level (typically H2 for major deliverables).
236
- */
237
- export function getSectionsAtLevel(sections, level) {
238
- const result = [];
239
- function collect(section) {
240
- if (section.level === level) {
241
- result.push(section);
242
- }
243
- for (const child of section.children) {
244
- collect(child);
245
- }
246
- }
247
- for (const section of sections) {
248
- collect(section);
249
- }
250
- return result;
251
- }
252
- /**
253
- * Flatten all sections into a single array.
254
- */
255
- export function flattenSections(sections) {
256
- const result = [];
257
- function collect(section) {
258
- result.push(section);
259
- for (const child of section.children) {
260
- collect(child);
261
- }
262
- }
263
- for (const section of sections) {
264
- collect(section);
265
- }
266
- return result;
267
- }
268
- /**
269
- * Strip YAML-style trailing comments from a value.
270
- * A comment starts with ` #` (space + hash) followed by space or end-of-string.
271
- * Quoted strings (single or double) are preserved entirely.
272
- */
273
- function stripYamlComment(value) {
274
- const trimmed = value.trim();
275
- // Quoted strings: preserve entirely (comment is inside quotes)
276
- if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
277
- (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
278
- return trimmed;
279
- }
280
- // Strip trailing comment: space(s) + # + (space or end)
281
- return trimmed.replace(/\s+#(?=\s|$).*/, '').trim();
282
- }
283
- /**
284
- * Parse a simple YAML string into key-value pairs.
285
- * Handles basic YAML with string values, arrays, and multi-line strings.
286
- */
287
- function parseSimpleYaml(yaml) {
288
- const result = {};
289
- const lines = yaml.split('\n');
290
- let currentKey = null;
291
- let currentValue = [];
292
- let inMultiLine = false;
293
- let inArray = false;
294
- let arrayItems = [];
295
- for (const line of lines) {
296
- // Multi-line string continuation (indented under |)
297
- if (inMultiLine && (line.startsWith(' ') || line.startsWith('\t') || line.trim() === '')) {
298
- currentValue.push(line.replace(/^ /, '').replace(/^\t/, ''));
299
- continue;
300
- }
301
- else if (inMultiLine) {
302
- // End of multi-line, save and reset
303
- if (currentKey) {
304
- result[currentKey] = currentValue.join('\n').trim();
305
- }
306
- inMultiLine = false;
307
- currentKey = null;
308
- currentValue = [];
309
- }
310
- // Array item (- value)
311
- if (inArray && line.match(/^\s+-\s+/)) {
312
- const item = line.replace(/^\s+-\s+/, '').trim();
313
- arrayItems.push(stripYamlComment(item));
314
- continue;
315
- }
316
- else if (inArray && !line.match(/^\s+-\s+/) && line.trim() !== '') {
317
- // End of array, save and reset
318
- if (currentKey) {
319
- result[currentKey] = arrayItems;
320
- }
321
- inArray = false;
322
- currentKey = null;
323
- arrayItems = [];
324
- }
325
- // Key-value pair
326
- const kvMatch = line.match(/^([\w-]+):\s*(.*)$/);
327
- if (kvMatch) {
328
- const [, key, value] = kvMatch;
329
- // Inline array: [a, b, c]
330
- if (value.startsWith('[') && value.endsWith(']')) {
331
- const items = value.slice(1, -1).split(',').map(s => stripYamlComment(s).replace(/['"]/g, ''));
332
- result[key] = items.filter(i => i.length > 0);
333
- continue;
334
- }
335
- // Multi-line string starts with |
336
- if (value === '|' || value === '|-') {
337
- currentKey = key;
338
- currentValue = [];
339
- inMultiLine = true;
340
- continue;
341
- }
342
- // Array starts on next line
343
- if (value === '') {
344
- currentKey = key;
345
- arrayItems = [];
346
- inArray = true;
347
- continue;
348
- }
349
- // Simple string value
350
- result[key] = stripYamlComment(value).replace(/^["']|["']$/g, '');
351
- }
352
- }
353
- // Handle final multi-line or array
354
- if (inMultiLine && currentKey) {
355
- result[currentKey] = currentValue.join('\n').trim();
356
- }
357
- if (inArray && currentKey) {
358
- result[currentKey] = arrayItems;
359
- }
360
- return result;
361
- }
362
- /**
363
- * Extract individual stream blocks from a nested `streams:` YAML structure.
364
- * Handles the common pattern where multiple streams are defined under a single wrapper:
365
- *
366
- * streams:
367
- * stream-id:
368
- * name: "..."
369
- * owns: [...]
370
- */
371
- function extractNestedStreamBlocks(yaml) {
372
- const lines = yaml.split('\n');
373
- const blocks = [];
374
- // Must start with `streams:` top-level key
375
- const streamsLineIdx = lines.findIndex(l => /^streams:\s*$/.test(l));
376
- if (streamsLineIdx === -1)
377
- return [];
378
- let currentId = null;
379
- let currentLines = [];
380
- for (let i = streamsLineIdx + 1; i < lines.length; i++) {
381
- const line = lines[i];
382
- // Stream ID line: exactly 2-space indent, key with alphanumeric/hyphens/underscores, ends with :
383
- const streamIdMatch = line.match(/^ ([\w-]+):\s*$/);
384
- if (streamIdMatch) {
385
- // Save previous stream (H2: skip empty streams)
386
- if (currentId && currentLines.some(l => l.trim())) {
387
- blocks.push({ id: currentId, block: currentLines.join('\n') });
388
- }
389
- currentId = streamIdMatch[1];
390
- currentLines = [];
391
- continue;
392
- }
393
- // Lines belonging to current stream (4+ spaces indent) or blank lines
394
- if (currentId && (/^ /.test(line) || line.trim() === '')) {
395
- // Strip 4 spaces of indentation to make it parseable as flat YAML
396
- currentLines.push(line.replace(/^ /, ''));
397
- continue;
398
- }
399
- // Non-indented non-empty line — we've left the streams: block
400
- if (line.trim() !== '' && !/^\s/.test(line)) {
401
- break;
402
- }
403
- }
404
- // Save last stream (H2: skip empty streams)
405
- if (currentId && currentLines.some(l => l.trim())) {
406
- blocks.push({ id: currentId, block: currentLines.join('\n') });
407
- }
408
- return blocks;
409
- }
410
- /**
411
- * Parse a flat YAML block into a YamlStreamDefinition, applying optional id override.
412
- */
413
- function parseFlatYamlDef(yaml, idOverride) {
414
- const parsed = parseSimpleYaml(yaml);
415
- const id = idOverride ?? parsed.id ?? '';
416
- const name = parsed.name ?? '';
417
- // Must have at least id or name
418
- if (!id && !name) {
419
- logger.warn({ block: yaml.slice(0, 100) }, 'Malformed YAML block skipped — no id or name found');
420
- return undefined;
421
- }
422
- const def = { id, name };
423
- if (parsed.deps) {
424
- def.deps = Array.isArray(parsed.deps) ? parsed.deps : [parsed.deps];
425
- }
426
- if (parsed.owns) {
427
- def.owns = Array.isArray(parsed.owns) ? parsed.owns : [parsed.owns];
428
- }
429
- if (parsed.reads) {
430
- def.reads = Array.isArray(parsed.reads) ? parsed.reads : [parsed.reads];
431
- }
432
- if (parsed.plan) {
433
- def.plan = parsed.plan;
434
- }
435
- if (parsed.verify) {
436
- def.verify = Array.isArray(parsed.verify) ? parsed.verify : [parsed.verify];
437
- }
438
- if (parsed.setup) {
439
- def.setup = Array.isArray(parsed.setup) ? parsed.setup : [parsed.setup];
440
- }
441
- return def;
442
- }
443
- /**
444
- * Extract YAML stream definitions from code blocks in a section.
445
- * Supports both flat format (one block per stream) and nested `streams:` wrapper format.
446
- */
447
- export function extractYamlStreamDefinitions(section) {
448
- const definitions = [];
449
- for (const block of section.codeBlocks) {
450
- if (block.language !== 'yaml' && block.language !== 'yml') {
451
- continue;
452
- }
453
- // Try nested streams: wrapper format first
454
- const nestedBlocks = extractNestedStreamBlocks(block.code);
455
- if (nestedBlocks.length > 0) {
456
- for (const { id, block: streamBlock } of nestedBlocks) {
457
- const def = parseFlatYamlDef(streamBlock, id);
458
- if (def)
459
- definitions.push(def);
460
- }
461
- continue;
462
- }
463
- // Fall back to flat format (id: at top level)
464
- if (!block.code.includes('id:') && !block.code.includes('name:')) {
465
- continue;
466
- }
467
- const def = parseFlatYamlDef(block.code);
468
- if (def)
469
- definitions.push(def);
470
- }
471
- return definitions;
472
- }
473
- /**
474
- * Find all sections that contain YAML stream definitions.
475
- */
476
- export function findYamlStreamSections(sections) {
477
- const result = [];
478
- function collect(section) {
479
- const defs = extractYamlStreamDefinitions(section);
480
- if (defs.length > 0) {
481
- result.push(section);
482
- }
483
- for (const child of section.children) {
484
- collect(child);
485
- }
486
- }
487
- for (const section of sections) {
488
- collect(section);
489
- }
490
- return result;
491
- }
492
- /**
493
- * Detect whether content is an unpopulated init-plan template.
494
- * Checks for the ORCHEX PLAN TEMPLATE marker in HTML comments.
495
- */
496
- export function isUnpopulatedTemplate(content) {
497
- return content.includes('ORCHEX PLAN TEMPLATE');
498
- }
@@ -1,29 +0,0 @@
1
- import type { StreamDefinition } from '../types.js';
2
- import type { PatternAnalysis } from './pattern-analyzer.js';
3
- import { type ContextOptimization } from './context-optimizer.js';
4
- export interface StreamSuggestion {
5
- id: string;
6
- stream: Omit<StreamDefinition, 'status' | 'attempts' | 'error'>;
7
- complexity: {
8
- score: number;
9
- factors: string[];
10
- };
11
- confidence: 'high' | 'medium' | 'low';
12
- }
13
- export interface PlanSuggestion {
14
- suggestions: StreamSuggestion[];
15
- waveEstimate: number;
16
- notes: string[];
17
- contextOptimization?: ContextOptimization;
18
- retryGuidance?: PatternAnalysis;
19
- }
20
- /**
21
- * Suggest streams from a feature description and project file contents.
22
- * This is advisory — the user/AI reviews before accepting.
23
- */
24
- export declare function suggestStreams(featureDescription: string, fileContents: Record<string, string>): PlanSuggestion;
25
- /**
26
- * Enhance plan suggestion with pattern analysis and context optimization.
27
- * Requires telemetry data for pattern analysis.
28
- */
29
- export declare function enhancePlanSuggestion(plan: PlanSuggestion, fileContents: Record<string, string>, patterns?: PatternAnalysis): PlanSuggestion;
@@ -1,86 +0,0 @@
1
- import { detectFileCoChanges, estimateComplexity } from './heuristics.js';
2
- import { optimizeContext } from './context-optimizer.js';
3
- /**
4
- * Suggest streams from a feature description and project file contents.
5
- * This is advisory — the user/AI reviews before accepting.
6
- */
7
- export function suggestStreams(featureDescription, fileContents) {
8
- const suggestions = [];
9
- const notes = [];
10
- // 1. Detect file groups from co-change analysis
11
- const groups = detectFileCoChanges(fileContents);
12
- if (groups.length > 0) {
13
- notes.push(`Found ${groups.length} tightly coupled file group(s) — consider grouping in same stream.`);
14
- }
15
- // 2. Identify test files that likely need updating
16
- const testFiles = Object.keys(fileContents).filter((f) => f.includes('.test.') || f.includes('.spec.') || f.startsWith('tests/'));
17
- const sourceFiles = Object.keys(fileContents).filter((f) => !testFiles.includes(f) && (f.endsWith('.ts') || f.endsWith('.js')));
18
- // 3. Generate stream suggestions per file group
19
- for (const group of groups) {
20
- const id = group.files[0]
21
- .replace(/^src\//, '')
22
- .replace(/\.ts$/, '')
23
- .replace(/\//g, '-');
24
- const streamDef = {
25
- name: `Update ${group.files.join(', ')}`,
26
- owns: group.files,
27
- reads: [],
28
- deps: [],
29
- plan: `Modify tightly coupled files: ${group.files.join(', ')}. ${group.reason}`,
30
- setup: [],
31
- verify: ['npm test'],
32
- };
33
- const complexity = estimateComplexity(streamDef);
34
- suggestions.push({
35
- id,
36
- stream: streamDef,
37
- complexity,
38
- confidence: 'medium',
39
- });
40
- }
41
- // 4. Suggest a test stream if test files exist
42
- if (testFiles.length > 0) {
43
- const testStream = {
44
- name: 'Update tests',
45
- owns: testFiles,
46
- reads: sourceFiles.slice(0, 5), // read at most 5 source files for context
47
- deps: suggestions.map((s) => s.id),
48
- plan: `Update tests after source changes. Test files: ${testFiles.join(', ')}`,
49
- setup: [],
50
- verify: ['npm test'],
51
- };
52
- suggestions.push({
53
- id: 'update-tests',
54
- stream: testStream,
55
- complexity: estimateComplexity(testStream),
56
- confidence: 'low',
57
- });
58
- }
59
- // 5. Estimate waves
60
- const waveEstimate = Math.max(1, suggestions.filter((s) => s.stream.deps?.length === 0).length > 0 ? 2 : 1);
61
- return { suggestions, waveEstimate, notes };
62
- }
63
- /**
64
- * Enhance plan suggestion with pattern analysis and context optimization.
65
- * Requires telemetry data for pattern analysis.
66
- */
67
- export function enhancePlanSuggestion(plan, fileContents, patterns) {
68
- // Add context optimization for each stream
69
- for (const suggestion of plan.suggestions) {
70
- const optimization = optimizeContext(suggestion.stream.owns ?? [], suggestion.stream.reads ?? [], fileContents, suggestion.stream.deps?.length ?? 0);
71
- if (optimization.estimatedSavings > 10) {
72
- plan.notes.push(`Stream "${suggestion.id}": Context optimization could save ~${optimization.estimatedSavings}% tokens by pruning ${optimization.excludedFiles.length} unused files.`);
73
- }
74
- }
75
- // Add retry guidance from patterns
76
- if (patterns && patterns.retryPatterns.length > 0) {
77
- plan.retryGuidance = patterns;
78
- const highRetryCategories = patterns.retryPatterns
79
- .filter(p => p.recommendation === 'always_retry')
80
- .map(p => p.category);
81
- if (highRetryCategories.length > 0) {
82
- plan.notes.push(`Retry recommendation: Always retry for ${highRetryCategories.join(', ')} errors (high success rate).`);
83
- }
84
- }
85
- return plan;
86
- }
@@ -1,16 +0,0 @@
1
- import type { StreamDefinition, Manifest } from '../types.js';
2
- export interface FixStreamResult {
3
- fixStreamId: string;
4
- fixStream: StreamDefinition;
5
- analysis: {
6
- category: string;
7
- suggestion: string;
8
- };
9
- }
10
- /**
11
- * Generate a fix stream for a failed stream.
12
- * Returns null if the error is non-retryable or max attempts exceeded.
13
- *
14
- * Tracks fix chain depth via parentStreamId to prevent infinite fix-fix-fix chains.
15
- */
16
- export declare function generateFixStream(manifest: Manifest, failedStreamId: string): FixStreamResult | null;