wogiflow 1.0.13 → 1.0.15
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/lib/installer.js +8 -2
- package/lib/upgrader.js +1 -1
- package/package.json +1 -1
- package/scripts/flow +3 -7
- package/scripts/flow-context-orchestrator.js +475 -0
- package/scripts/flow-init +40 -242
- package/scripts/flow-long-input-chunking.js +6 -6
- package/scripts/flow-long-input-parsing.js +6 -6
- package/scripts/flow-long-input-stories.js +7 -7
- package/scripts/flow-long-input.js +13 -15
- package/scripts/flow-onboard +38 -1242
- package/scripts/flow-product-scanner.js +466 -0
- package/scripts/flow-section-index.js +174 -1
- package/scripts/flow-story.js +39 -0
- package/scripts/hooks/core/index.js +6 -1
- package/scripts/hooks/core/session-context.js +40 -26
- package/scripts/hooks/core/setup-check.js +153 -0
- package/scripts/postinstall.js +73 -23
- package/templates/context/product-placeholder.md +72 -0
- package/templates/context/product.md +68 -0
- package/lib/unified-wizard.js +0 -1465
package/lib/installer.js
CHANGED
|
@@ -477,8 +477,14 @@ async function init(args) {
|
|
|
477
477
|
console.log('\n✅ Wogi Flow initialized successfully!\n');
|
|
478
478
|
console.log('Next steps:');
|
|
479
479
|
console.log(' 1. Review .workflow/config.json');
|
|
480
|
-
console.log(' 2. Run
|
|
481
|
-
console.log(' 3. Create your first task with
|
|
480
|
+
console.log(' 2. Run `/wogi-status` to see project status');
|
|
481
|
+
console.log(' 3. Create your first task with `/wogi-story "Task title"`');
|
|
482
|
+
console.log('');
|
|
483
|
+
console.log('Available commands:');
|
|
484
|
+
console.log(' /wogi-ready - View available tasks');
|
|
485
|
+
console.log(' /wogi-status - Project overview');
|
|
486
|
+
console.log(' /wogi-health - Check workflow health');
|
|
487
|
+
console.log(' /wogi-story - Create a new story');
|
|
482
488
|
console.log('');
|
|
483
489
|
}
|
|
484
490
|
|
package/lib/upgrader.js
CHANGED
|
@@ -393,7 +393,7 @@ async function upgrade(args) {
|
|
|
393
393
|
console.log('\n✅ Upgrade complete!\n');
|
|
394
394
|
console.log('Next steps:');
|
|
395
395
|
console.log(' 1. Review changes in .workflow/');
|
|
396
|
-
console.log(' 2. Run
|
|
396
|
+
console.log(' 2. Run `/wogi-health` to verify installation');
|
|
397
397
|
console.log(' 3. Commit the upgraded files');
|
|
398
398
|
}
|
|
399
399
|
}
|
package/package.json
CHANGED
package/scripts/flow
CHANGED
|
@@ -37,9 +37,8 @@ show_help() {
|
|
|
37
37
|
echo "Usage: flow <command> [options]"
|
|
38
38
|
echo ""
|
|
39
39
|
echo "Setup & Updates:"
|
|
40
|
-
echo "
|
|
41
|
-
echo "
|
|
42
|
-
echo " onboard Analyze existing project & set up context"
|
|
40
|
+
echo " init Setup guide (directs to AI assistant)"
|
|
41
|
+
echo " onboard Project onboarding (directs to AI assistant)"
|
|
43
42
|
echo " migrate Migrate to universal CLI-agnostic structure"
|
|
44
43
|
echo " migrate --dry-run Preview migration without changes"
|
|
45
44
|
echo " update Update to latest version (preserves data)"
|
|
@@ -383,12 +382,9 @@ case "${1:-}" in
|
|
|
383
382
|
session-end)
|
|
384
383
|
node "$SCRIPT_DIR/flow-session-end.js" "${@:2}"
|
|
385
384
|
;;
|
|
386
|
-
init)
|
|
385
|
+
init|install)
|
|
387
386
|
"$SCRIPT_DIR/flow-init" "${@:2}"
|
|
388
387
|
;;
|
|
389
|
-
install)
|
|
390
|
-
"$SCRIPT_DIR/flow-install" "${@:2}"
|
|
391
|
-
;;
|
|
392
388
|
onboard)
|
|
393
389
|
"$SCRIPT_DIR/flow-onboard" "${@:2}"
|
|
394
390
|
;;
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Context Orchestrator
|
|
5
|
+
*
|
|
6
|
+
* Enables targeted context loading for tasks using the PIN system.
|
|
7
|
+
* Supports orchestrator pattern where cheaper models (Haiku) gather
|
|
8
|
+
* relevant context for expensive models (Opus).
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - PIN-based section lookup
|
|
12
|
+
* - Task description to relevant sections mapping
|
|
13
|
+
* - Token-aware context truncation
|
|
14
|
+
* - Product context integration
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* const { getTargetedContext } = require('./flow-context-orchestrator');
|
|
18
|
+
* const context = await getTargetedContext({ task: "Add user auth" });
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const {
|
|
23
|
+
PATHS,
|
|
24
|
+
fileExists,
|
|
25
|
+
readFile,
|
|
26
|
+
parseFlags,
|
|
27
|
+
outputJson,
|
|
28
|
+
info,
|
|
29
|
+
warn
|
|
30
|
+
} = require('./flow-utils');
|
|
31
|
+
|
|
32
|
+
const {
|
|
33
|
+
getSectionsForTask,
|
|
34
|
+
getSectionsByPins,
|
|
35
|
+
formatSectionsAsContext,
|
|
36
|
+
formatSectionsAsReferences
|
|
37
|
+
} = require('./flow-section-resolver');
|
|
38
|
+
|
|
39
|
+
// ============================================================
|
|
40
|
+
// Configuration
|
|
41
|
+
// ============================================================
|
|
42
|
+
|
|
43
|
+
// Approximate tokens per character (conservative estimate)
|
|
44
|
+
const CHARS_PER_TOKEN = 4;
|
|
45
|
+
|
|
46
|
+
// Default limits
|
|
47
|
+
const DEFAULT_MAX_TOKENS = 8000;
|
|
48
|
+
const DEFAULT_SECTION_LIMIT = 10;
|
|
49
|
+
|
|
50
|
+
// ============================================================
|
|
51
|
+
// Token Estimation
|
|
52
|
+
// ============================================================
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Estimate token count for a string
|
|
56
|
+
* @param {string} text - Text to estimate
|
|
57
|
+
* @returns {number} - Estimated token count
|
|
58
|
+
*/
|
|
59
|
+
function estimateTokens(text) {
|
|
60
|
+
if (!text) return 0;
|
|
61
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Truncate sections to fit within token limit
|
|
66
|
+
* @param {Object[]} sections - Sections to truncate
|
|
67
|
+
* @param {number} maxTokens - Max tokens
|
|
68
|
+
* @returns {Object[]} - Truncated sections
|
|
69
|
+
*/
|
|
70
|
+
function truncateToTokenLimit(sections, maxTokens) {
|
|
71
|
+
const result = [];
|
|
72
|
+
let currentTokens = 0;
|
|
73
|
+
|
|
74
|
+
for (const section of sections) {
|
|
75
|
+
const sectionTokens = estimateTokens(section.content || '');
|
|
76
|
+
|
|
77
|
+
if (currentTokens + sectionTokens <= maxTokens) {
|
|
78
|
+
result.push(section);
|
|
79
|
+
currentTokens += sectionTokens;
|
|
80
|
+
} else if (result.length === 0) {
|
|
81
|
+
// Always include at least one section, even if truncated
|
|
82
|
+
const availableChars = (maxTokens - currentTokens) * CHARS_PER_TOKEN;
|
|
83
|
+
result.push({
|
|
84
|
+
...section,
|
|
85
|
+
content: section.content?.substring(0, availableChars) + '...[truncated]',
|
|
86
|
+
truncated: true
|
|
87
|
+
});
|
|
88
|
+
break;
|
|
89
|
+
} else {
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ============================================================
|
|
98
|
+
// Section Merging
|
|
99
|
+
// ============================================================
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Merge and deduplicate sections from multiple sources
|
|
103
|
+
* @param {Object[][]} sectionArrays - Arrays of sections to merge
|
|
104
|
+
* @returns {Object[]} - Merged and deduplicated sections
|
|
105
|
+
*/
|
|
106
|
+
function mergeSections(...sectionArrays) {
|
|
107
|
+
const seen = new Map();
|
|
108
|
+
|
|
109
|
+
for (const sections of sectionArrays) {
|
|
110
|
+
for (const section of sections) {
|
|
111
|
+
if (!seen.has(section.id)) {
|
|
112
|
+
seen.set(section.id, section);
|
|
113
|
+
} else {
|
|
114
|
+
// Keep the one with higher score
|
|
115
|
+
const existing = seen.get(section.id);
|
|
116
|
+
const existingScore = existing.score || existing.matchScore || 0;
|
|
117
|
+
const newScore = section.score || section.matchScore || 0;
|
|
118
|
+
if (newScore > existingScore) {
|
|
119
|
+
seen.set(section.id, section);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return Array.from(seen.values());
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ============================================================
|
|
129
|
+
// Product Context
|
|
130
|
+
// ============================================================
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get product context from product.md
|
|
134
|
+
* @param {Object} options - { format: 'full' | 'summary' }
|
|
135
|
+
* @returns {Object|null} - Product context or null
|
|
136
|
+
*/
|
|
137
|
+
async function getProductContext(options = {}) {
|
|
138
|
+
const { format = 'summary' } = options;
|
|
139
|
+
const productPath = path.join(PATHS.specs, 'product.md');
|
|
140
|
+
|
|
141
|
+
if (!fileExists(productPath)) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
// Get product sections via PINs
|
|
147
|
+
const productPins = ['product-name', 'target-users', 'value-prop', 'core-features'];
|
|
148
|
+
const sections = await getSectionsByPins(productPins, { limit: 5 });
|
|
149
|
+
|
|
150
|
+
if (sections.length === 0) {
|
|
151
|
+
// Fall back to reading the file directly
|
|
152
|
+
const content = readFile(productPath);
|
|
153
|
+
return {
|
|
154
|
+
context: content,
|
|
155
|
+
source: 'file',
|
|
156
|
+
tokenEstimate: estimateTokens(content)
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const context = formatSectionsAsContext(sections, { format });
|
|
161
|
+
return {
|
|
162
|
+
context,
|
|
163
|
+
sections: sections.map(s => s.id),
|
|
164
|
+
source: 'pins',
|
|
165
|
+
tokenEstimate: estimateTokens(context)
|
|
166
|
+
};
|
|
167
|
+
} catch (err) {
|
|
168
|
+
warn(`Error loading product context: ${err.message}`);
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get product overview (name, tagline, type only)
|
|
175
|
+
* @returns {Object|null} - Brief product info
|
|
176
|
+
*/
|
|
177
|
+
function getProductOverview() {
|
|
178
|
+
const productPath = path.join(PATHS.specs, 'product.md');
|
|
179
|
+
|
|
180
|
+
if (!fileExists(productPath)) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const content = readFile(productPath);
|
|
186
|
+
|
|
187
|
+
// Extract key fields using regex
|
|
188
|
+
const nameMatch = content.match(/\*\*Name\*\*:\s*(.+)/);
|
|
189
|
+
const taglineMatch = content.match(/\*\*Tagline\*\*:\s*(.+)/);
|
|
190
|
+
const typeMatch = content.match(/\*\*Type\*\*:\s*(.+)/);
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
name: nameMatch ? nameMatch[1].trim() : null,
|
|
194
|
+
tagline: taglineMatch ? taglineMatch[1].trim() : null,
|
|
195
|
+
type: typeMatch ? typeMatch[1].trim() : null
|
|
196
|
+
};
|
|
197
|
+
} catch (err) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ============================================================
|
|
203
|
+
// Main Context Gathering
|
|
204
|
+
// ============================================================
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get targeted context for a task
|
|
208
|
+
* @param {Object} options
|
|
209
|
+
* @param {string} options.task - Task description
|
|
210
|
+
* @param {string[]} options.pins - Explicit pins to include
|
|
211
|
+
* @param {number} options.maxTokens - Max tokens for context
|
|
212
|
+
* @param {string} options.format - 'full' | 'summary' | 'reference'
|
|
213
|
+
* @param {boolean} options.includeProduct - Include product context
|
|
214
|
+
* @returns {Object} - { context, sections, tokenEstimate }
|
|
215
|
+
*/
|
|
216
|
+
async function getTargetedContext(options = {}) {
|
|
217
|
+
const {
|
|
218
|
+
task = '',
|
|
219
|
+
pins = [],
|
|
220
|
+
maxTokens = DEFAULT_MAX_TOKENS,
|
|
221
|
+
format = 'full',
|
|
222
|
+
includeProduct = true
|
|
223
|
+
} = options;
|
|
224
|
+
|
|
225
|
+
const allSections = [];
|
|
226
|
+
|
|
227
|
+
// Get sections by task description
|
|
228
|
+
if (task) {
|
|
229
|
+
const taskSections = await getSectionsForTask(task, {
|
|
230
|
+
limit: DEFAULT_SECTION_LIMIT
|
|
231
|
+
});
|
|
232
|
+
allSections.push(...taskSections);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Get sections by explicit pins
|
|
236
|
+
if (pins.length > 0) {
|
|
237
|
+
const pinSections = await getSectionsByPins(pins, {
|
|
238
|
+
limit: Math.floor(DEFAULT_SECTION_LIMIT / 2)
|
|
239
|
+
});
|
|
240
|
+
allSections.push(...pinSections);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Merge and deduplicate
|
|
244
|
+
const mergedSections = mergeSections(allSections);
|
|
245
|
+
|
|
246
|
+
// Calculate available tokens for sections
|
|
247
|
+
let availableTokens = maxTokens;
|
|
248
|
+
let productContextResult = null;
|
|
249
|
+
|
|
250
|
+
// Include product context if requested
|
|
251
|
+
if (includeProduct) {
|
|
252
|
+
productContextResult = await getProductContext({ format: 'summary' });
|
|
253
|
+
if (productContextResult) {
|
|
254
|
+
availableTokens -= productContextResult.tokenEstimate;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Truncate sections to fit
|
|
259
|
+
const truncatedSections = truncateToTokenLimit(mergedSections, availableTokens);
|
|
260
|
+
|
|
261
|
+
// Format sections
|
|
262
|
+
const sectionsContext = formatSectionsAsContext(truncatedSections, { format });
|
|
263
|
+
|
|
264
|
+
// Combine contexts
|
|
265
|
+
let fullContext = '';
|
|
266
|
+
if (productContextResult) {
|
|
267
|
+
fullContext += '## Product Context\n\n' + productContextResult.context + '\n\n';
|
|
268
|
+
}
|
|
269
|
+
if (sectionsContext) {
|
|
270
|
+
fullContext += sectionsContext;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
context: fullContext.trim(),
|
|
275
|
+
sections: truncatedSections.map(s => ({
|
|
276
|
+
id: s.id,
|
|
277
|
+
score: s.score || s.matchScore || 0,
|
|
278
|
+
truncated: s.truncated || false
|
|
279
|
+
})),
|
|
280
|
+
productIncluded: !!productContextResult,
|
|
281
|
+
tokenEstimate: estimateTokens(fullContext)
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get context for a specific task ID
|
|
287
|
+
* Loads task details and gathers relevant context
|
|
288
|
+
* @param {string} taskId - Task ID (e.g., "wf-abc123")
|
|
289
|
+
* @returns {Object} - Task context
|
|
290
|
+
*/
|
|
291
|
+
async function getContextForTaskId(taskId) {
|
|
292
|
+
// Try to find task in ready.json
|
|
293
|
+
const readyPath = path.join(PATHS.state, 'ready.json');
|
|
294
|
+
if (!fileExists(readyPath)) {
|
|
295
|
+
return getTargetedContext({ task: taskId });
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
try {
|
|
299
|
+
const ready = JSON.parse(readFile(readyPath));
|
|
300
|
+
const allTasks = [
|
|
301
|
+
...(ready.ready || []),
|
|
302
|
+
...(ready.inProgress || []),
|
|
303
|
+
...(ready.blocked || [])
|
|
304
|
+
];
|
|
305
|
+
|
|
306
|
+
const task = allTasks.find(t =>
|
|
307
|
+
(typeof t === 'string' && t === taskId) ||
|
|
308
|
+
(typeof t === 'object' && t.id === taskId)
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
if (task && typeof task === 'object' && task.title) {
|
|
312
|
+
return getTargetedContext({
|
|
313
|
+
task: `${task.title} ${task.description || ''}`,
|
|
314
|
+
pins: task.tags || []
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
} catch (err) {
|
|
318
|
+
// Fall back to using taskId as description
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return getTargetedContext({ task: taskId });
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Get minimal context references (for orchestrator hints)
|
|
326
|
+
* @param {string} task - Task description
|
|
327
|
+
* @returns {string} - Reference string
|
|
328
|
+
*/
|
|
329
|
+
async function getContextReferences(task) {
|
|
330
|
+
const sections = await getSectionsForTask(task, { limit: 5 });
|
|
331
|
+
return formatSectionsAsReferences(sections);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// ============================================================
|
|
335
|
+
// CLI
|
|
336
|
+
// ============================================================
|
|
337
|
+
|
|
338
|
+
async function main() {
|
|
339
|
+
const { args, flags } = parseFlags(process.argv.slice(2));
|
|
340
|
+
|
|
341
|
+
if (flags.help) {
|
|
342
|
+
console.log(`
|
|
343
|
+
Usage: node scripts/flow-context-orchestrator.js [command] [options]
|
|
344
|
+
|
|
345
|
+
Get targeted context for tasks using the PIN system.
|
|
346
|
+
|
|
347
|
+
Commands:
|
|
348
|
+
task "<description>" Get context for a task description
|
|
349
|
+
taskid <id> Get context for a task ID
|
|
350
|
+
product Get product context only
|
|
351
|
+
refs "<description>" Get section references only
|
|
352
|
+
|
|
353
|
+
Options:
|
|
354
|
+
--max-tokens <n> Max tokens for context (default: 8000)
|
|
355
|
+
--format <type> Output format: full, summary, reference
|
|
356
|
+
--no-product Exclude product context
|
|
357
|
+
--json Output as JSON
|
|
358
|
+
--help Show this help
|
|
359
|
+
|
|
360
|
+
Examples:
|
|
361
|
+
node scripts/flow-context-orchestrator.js task "Add user authentication"
|
|
362
|
+
node scripts/flow-context-orchestrator.js taskid wf-abc123 --json
|
|
363
|
+
node scripts/flow-context-orchestrator.js product --format summary
|
|
364
|
+
`);
|
|
365
|
+
process.exit(0);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const command = args[0];
|
|
369
|
+
const maxTokens = parseInt(flags['max-tokens']) || DEFAULT_MAX_TOKENS;
|
|
370
|
+
const format = flags.format || 'full';
|
|
371
|
+
const includeProduct = flags.product !== false;
|
|
372
|
+
|
|
373
|
+
switch (command) {
|
|
374
|
+
case 'task': {
|
|
375
|
+
const task = args.slice(1).join(' ');
|
|
376
|
+
if (!task) {
|
|
377
|
+
console.error('Usage: flow-context-orchestrator task "<description>"');
|
|
378
|
+
process.exit(1);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const result = await getTargetedContext({
|
|
382
|
+
task,
|
|
383
|
+
maxTokens,
|
|
384
|
+
format,
|
|
385
|
+
includeProduct
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
if (flags.json) {
|
|
389
|
+
outputJson(result);
|
|
390
|
+
} else {
|
|
391
|
+
console.log(result.context);
|
|
392
|
+
console.log(`\n--- ${result.sections.length} sections, ~${result.tokenEstimate} tokens ---`);
|
|
393
|
+
}
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
case 'taskid': {
|
|
398
|
+
const taskId = args[1];
|
|
399
|
+
if (!taskId) {
|
|
400
|
+
console.error('Usage: flow-context-orchestrator taskid <id>');
|
|
401
|
+
process.exit(1);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const result = await getContextForTaskId(taskId);
|
|
405
|
+
|
|
406
|
+
if (flags.json) {
|
|
407
|
+
outputJson(result);
|
|
408
|
+
} else {
|
|
409
|
+
console.log(result.context);
|
|
410
|
+
console.log(`\n--- ${result.sections.length} sections, ~${result.tokenEstimate} tokens ---`);
|
|
411
|
+
}
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
case 'product': {
|
|
416
|
+
const result = await getProductContext({ format });
|
|
417
|
+
|
|
418
|
+
if (!result) {
|
|
419
|
+
console.log('No product.md found');
|
|
420
|
+
process.exit(1);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (flags.json) {
|
|
424
|
+
outputJson(result);
|
|
425
|
+
} else {
|
|
426
|
+
console.log(result.context);
|
|
427
|
+
}
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
case 'refs': {
|
|
432
|
+
const task = args.slice(1).join(' ');
|
|
433
|
+
if (!task) {
|
|
434
|
+
console.error('Usage: flow-context-orchestrator refs "<description>"');
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const refs = await getContextReferences(task);
|
|
439
|
+
console.log(refs || 'No relevant sections found');
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
default:
|
|
444
|
+
console.error('Unknown command. Use --help for usage.');
|
|
445
|
+
process.exit(1);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// ============================================================
|
|
450
|
+
// Exports
|
|
451
|
+
// ============================================================
|
|
452
|
+
|
|
453
|
+
module.exports = {
|
|
454
|
+
// Main functions
|
|
455
|
+
getTargetedContext,
|
|
456
|
+
getContextForTaskId,
|
|
457
|
+
getContextReferences,
|
|
458
|
+
|
|
459
|
+
// Product context
|
|
460
|
+
getProductContext,
|
|
461
|
+
getProductOverview,
|
|
462
|
+
|
|
463
|
+
// Utilities
|
|
464
|
+
estimateTokens,
|
|
465
|
+
truncateToTokenLimit,
|
|
466
|
+
mergeSections
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// Run if called directly
|
|
470
|
+
if (require.main === module) {
|
|
471
|
+
main().catch(err => {
|
|
472
|
+
console.error(`Error: ${err.message}`);
|
|
473
|
+
process.exit(1);
|
|
474
|
+
});
|
|
475
|
+
}
|