@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.
- package/README.md +59 -18
- package/dist/cloud-executor.d.ts +71 -0
- package/dist/cloud-executor.js +335 -0
- package/dist/cloud-sync.d.ts +8 -0
- package/dist/cloud-sync.js +52 -0
- package/dist/config.d.ts +30 -4
- package/dist/config.js +61 -2
- package/dist/context-builder.d.ts +2 -0
- package/dist/context-builder.js +11 -3
- package/dist/cost.js +1 -1
- package/dist/entitlements/jwt.d.ts +7 -0
- package/dist/entitlements/jwt.js +78 -0
- package/dist/entitlements/resolve.d.ts +17 -0
- package/dist/entitlements/resolve.js +49 -0
- package/dist/entitlements/types.d.ts +21 -0
- package/dist/entitlements/types.js +4 -0
- package/dist/executors/base.d.ts +1 -1
- package/dist/executors/bedrock-executor.d.ts +39 -0
- package/dist/executors/bedrock-executor.js +197 -0
- package/dist/executors/index.d.ts +1 -0
- package/dist/executors/index.js +24 -1
- package/dist/index.js +468 -23
- package/dist/intelligence/index.d.ts +44 -0
- package/dist/intelligence/index.js +160 -0
- package/dist/key-cache.d.ts +31 -0
- package/dist/key-cache.js +84 -0
- package/dist/login-helpers.d.ts +25 -0
- package/dist/login-helpers.js +54 -0
- package/dist/manifest.js +18 -1
- package/dist/mcp-instructions.d.ts +1 -0
- package/dist/mcp-instructions.js +84 -0
- package/dist/mcp-resources.d.ts +8 -0
- package/dist/mcp-resources.js +420 -0
- package/dist/model-cache.d.ts +18 -0
- package/dist/model-cache.js +62 -0
- package/dist/model-validator.d.ts +20 -0
- package/dist/model-validator.js +125 -0
- package/dist/orchestrator.d.ts +14 -0
- package/dist/orchestrator.js +191 -32
- package/dist/setup/ide-registry.d.ts +13 -0
- package/dist/setup/ide-registry.js +51 -0
- package/dist/setup/index.d.ts +1 -0
- package/dist/setup/index.js +111 -0
- package/dist/tier-gating.js +0 -16
- package/dist/tiers.d.ts +35 -5
- package/dist/tiers.js +39 -3
- package/dist/tools.d.ts +6 -1
- package/dist/tools.js +852 -95
- package/dist/types.d.ts +71 -60
- package/dist/types.js +3 -0
- package/dist/waves.d.ts +1 -1
- package/dist/waves.js +29 -2
- package/package.json +41 -5
- package/src/entitlements/public-key.pem +9 -0
- package/dist/intelligence/anti-pattern-detector.d.ts +0 -117
- package/dist/intelligence/anti-pattern-detector.js +0 -327
- package/dist/intelligence/budget-enforcer.d.ts +0 -119
- package/dist/intelligence/budget-enforcer.js +0 -226
- package/dist/intelligence/context-optimizer.d.ts +0 -111
- package/dist/intelligence/context-optimizer.js +0 -282
- package/dist/intelligence/cost-tracker.d.ts +0 -114
- package/dist/intelligence/cost-tracker.js +0 -183
- package/dist/intelligence/deliverable-extractor.d.ts +0 -134
- package/dist/intelligence/deliverable-extractor.js +0 -909
- package/dist/intelligence/dependency-inferrer.d.ts +0 -87
- package/dist/intelligence/dependency-inferrer.js +0 -403
- package/dist/intelligence/diagnostics.d.ts +0 -33
- package/dist/intelligence/diagnostics.js +0 -64
- package/dist/intelligence/error-analyzer.d.ts +0 -7
- package/dist/intelligence/error-analyzer.js +0 -76
- package/dist/intelligence/file-chunker.d.ts +0 -15
- package/dist/intelligence/file-chunker.js +0 -64
- package/dist/intelligence/fix-stream-manager.d.ts +0 -59
- package/dist/intelligence/fix-stream-manager.js +0 -212
- package/dist/intelligence/heuristics.d.ts +0 -23
- package/dist/intelligence/heuristics.js +0 -124
- package/dist/intelligence/learning-engine.d.ts +0 -157
- package/dist/intelligence/learning-engine.js +0 -433
- package/dist/intelligence/learning-feedback.d.ts +0 -96
- package/dist/intelligence/learning-feedback.js +0 -202
- package/dist/intelligence/pattern-analyzer.d.ts +0 -35
- package/dist/intelligence/pattern-analyzer.js +0 -189
- package/dist/intelligence/plan-parser.d.ts +0 -124
- package/dist/intelligence/plan-parser.js +0 -498
- package/dist/intelligence/planner.d.ts +0 -29
- package/dist/intelligence/planner.js +0 -86
- package/dist/intelligence/self-healer.d.ts +0 -16
- package/dist/intelligence/self-healer.js +0 -84
- package/dist/intelligence/slicing-metrics.d.ts +0 -62
- package/dist/intelligence/slicing-metrics.js +0 -202
- package/dist/intelligence/slicing-templates.d.ts +0 -81
- package/dist/intelligence/slicing-templates.js +0 -420
- package/dist/intelligence/split-suggester.d.ts +0 -69
- package/dist/intelligence/split-suggester.js +0 -176
- package/dist/intelligence/stream-generator.d.ts +0 -90
- package/dist/intelligence/stream-generator.js +0 -452
- package/dist/telemetry/telemetry-types.d.ts +0 -85
- 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;
|