devmind 1.1.1 → 1.1.2

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 (45) hide show
  1. package/README.md +174 -169
  2. package/dist/cli/handlers.js +24 -4
  3. package/dist/cli/handlers.js.map +1 -1
  4. package/dist/cli/register-analysis.js +11 -0
  5. package/dist/cli/register-analysis.js.map +1 -1
  6. package/dist/codebase/index.js +3 -2
  7. package/dist/codebase/index.js.map +1 -1
  8. package/dist/codebase/parsers/typescript.js +122 -72
  9. package/dist/codebase/parsers/typescript.js.map +1 -1
  10. package/dist/codebase/scanners/filesystem.d.ts +2 -2
  11. package/dist/codebase/scanners/filesystem.js +31 -6
  12. package/dist/codebase/scanners/filesystem.js.map +1 -1
  13. package/dist/commands/analyze.js +4 -3
  14. package/dist/commands/analyze.js.map +1 -1
  15. package/dist/commands/audit-design.js +74 -0
  16. package/dist/commands/audit-design.js.map +1 -1
  17. package/dist/commands/audit.js +4 -3
  18. package/dist/commands/audit.js.map +1 -1
  19. package/dist/commands/autosave.d.ts +14 -0
  20. package/dist/commands/autosave.js +121 -0
  21. package/dist/commands/autosave.js.map +1 -1
  22. package/dist/commands/design-system.js +8 -0
  23. package/dist/commands/design-system.js.map +1 -1
  24. package/dist/commands/extract.js +4 -3
  25. package/dist/commands/extract.js.map +1 -1
  26. package/dist/commands/retrieve.d.ts +3 -0
  27. package/dist/commands/retrieve.js +593 -5
  28. package/dist/commands/retrieve.js.map +1 -1
  29. package/dist/commands/status.js +4 -3
  30. package/dist/commands/status.js.map +1 -1
  31. package/dist/core/devmind-ignore.d.ts +2 -0
  32. package/dist/core/devmind-ignore.js +99 -0
  33. package/dist/core/devmind-ignore.js.map +1 -0
  34. package/dist/core/errors.js +32 -0
  35. package/dist/core/errors.js.map +1 -1
  36. package/dist/core/index.d.ts +2 -0
  37. package/dist/core/index.js +2 -0
  38. package/dist/core/index.js.map +1 -1
  39. package/dist/core/tool-output.d.ts +18 -0
  40. package/dist/core/tool-output.js +95 -0
  41. package/dist/core/tool-output.js.map +1 -0
  42. package/dist/generators/unified.d.ts +1 -1
  43. package/dist/generators/unified.js +301 -72
  44. package/dist/generators/unified.js.map +1 -1
  45. package/package.json +86 -86
@@ -26,6 +26,410 @@ function tokenize(input) {
26
26
  .map((token) => token.trim())
27
27
  .filter((token) => token.length >= 2);
28
28
  }
29
+ function hasUiTokenSignal(tokens) {
30
+ const hasToken = tokens.includes('token') || tokens.includes('tokens');
31
+ if (!hasToken)
32
+ return false;
33
+ return (tokens.includes('design') ||
34
+ tokens.includes('theme') ||
35
+ tokens.includes('spacing') ||
36
+ tokens.includes('typography') ||
37
+ tokens.includes('color') ||
38
+ tokens.includes('colors') ||
39
+ tokens.includes('component') ||
40
+ tokens.includes('components'));
41
+ }
42
+ function hasAuthTokenSignal(tokens) {
43
+ const hasToken = tokens.includes('token') || tokens.includes('tokens');
44
+ return hasToken && !hasUiTokenSignal(tokens);
45
+ }
46
+ function detectRoutes(tokens) {
47
+ const routes = [];
48
+ const hasAny = (values) => values.some((value) => tokens.includes(value));
49
+ const authSignal = hasAny(['auth', 'authentication', 'authorize', 'authorization', 'login']) ||
50
+ hasAuthTokenSignal(tokens);
51
+ if (authSignal) {
52
+ routes.push('auth');
53
+ }
54
+ if (hasAny(['db', 'database', 'schema', 'sql', 'query', 'migration', 'postgres', 'mysql'])) {
55
+ routes.push('db');
56
+ }
57
+ const uiSignal = hasAny([
58
+ 'ui',
59
+ 'ux',
60
+ 'frontend',
61
+ 'component',
62
+ 'layout',
63
+ 'design',
64
+ 'theme',
65
+ 'hydration',
66
+ 'ssr',
67
+ 'csr',
68
+ 'a11y',
69
+ 'accessibility',
70
+ 'form',
71
+ 'client',
72
+ 'server-component',
73
+ 'animation',
74
+ 'animations',
75
+ 'motion',
76
+ 'framer',
77
+ 'gsap',
78
+ 'lottie',
79
+ 'keyframe',
80
+ 'keyframes',
81
+ ]) || hasUiTokenSignal(tokens);
82
+ if (uiSignal) {
83
+ routes.push('ui');
84
+ }
85
+ return routes;
86
+ }
87
+ function detectContractTargets(tokens) {
88
+ const targets = [];
89
+ const hasAny = (values) => values.some((value) => tokens.includes(value));
90
+ if (hasAny([
91
+ 'econnrefused',
92
+ 'eaddrinuse',
93
+ 'port',
94
+ 'listen',
95
+ 'upstream',
96
+ 'proxy',
97
+ 'http',
98
+ 'gateway',
99
+ 'basepath',
100
+ 'header',
101
+ ])) {
102
+ targets.push('http');
103
+ }
104
+ if (hasAny([
105
+ 'middleware',
106
+ 'helper',
107
+ 'helpers',
108
+ 'signature',
109
+ 'next',
110
+ 'ctx',
111
+ 'req',
112
+ 'res',
113
+ 'wrapper',
114
+ ])) {
115
+ targets.push('middleware');
116
+ }
117
+ const authContractSignal = hasAny([
118
+ 'auth',
119
+ 'jwt',
120
+ 'session',
121
+ 'claim',
122
+ 'claims',
123
+ 'sub',
124
+ 'aud',
125
+ 'iss',
126
+ 'scope',
127
+ 'roles',
128
+ 'permissions',
129
+ ]) || hasAuthTokenSignal(tokens);
130
+ if (authContractSignal) {
131
+ targets.push('auth');
132
+ }
133
+ if (hasAny([
134
+ 'ui',
135
+ 'ux',
136
+ 'frontend',
137
+ 'component',
138
+ 'layout',
139
+ 'design',
140
+ 'theme',
141
+ 'hydration',
142
+ 'ssr',
143
+ 'csr',
144
+ 'a11y',
145
+ 'accessibility',
146
+ 'form',
147
+ 'validation',
148
+ 'server-component',
149
+ 'client-component',
150
+ ])
151
+ || hasUiTokenSignal(tokens)) {
152
+ targets.push('ui');
153
+ }
154
+ if (hasAny([
155
+ 'animation',
156
+ 'animations',
157
+ 'motion',
158
+ 'framer',
159
+ 'framer-motion',
160
+ 'gsap',
161
+ 'lottie',
162
+ 'keyframe',
163
+ 'keyframes',
164
+ 'spring',
165
+ 'tween',
166
+ 'timeline',
167
+ 'reduced-motion',
168
+ 'prefers-reduced-motion',
169
+ ])) {
170
+ targets.push('motion');
171
+ }
172
+ if (hasAny(['go', 'golang', 'goroutine', 'gin', 'fiber', 'echo'])) {
173
+ targets.push('go');
174
+ }
175
+ if (hasAny(['python', 'fastapi', 'django', 'flask', 'uvicorn', 'pydantic'])) {
176
+ targets.push('python');
177
+ }
178
+ if (hasAny(['next', 'nextjs', 'next.js', 'app-router', 'route-handler', 'server-component'])) {
179
+ targets.push('next');
180
+ }
181
+ if (hasAny(['php', 'php-fpm', 'composer'])) {
182
+ targets.push('php');
183
+ }
184
+ if (hasAny(['laravel', 'eloquent', 'artisan', 'sanctum', 'passport'])) {
185
+ targets.push('laravel');
186
+ }
187
+ return [...new Set(targets)];
188
+ }
189
+ function parseRouteOption(raw) {
190
+ if (!raw)
191
+ return [];
192
+ const entries = raw
193
+ .split(',')
194
+ .map((value) => value.trim().toLowerCase())
195
+ .filter(Boolean);
196
+ const routes = [];
197
+ for (const entry of entries) {
198
+ if (entry === 'auth' || entry === 'db' || entry === 'ui') {
199
+ routes.push(entry);
200
+ }
201
+ }
202
+ return [...new Set(routes)];
203
+ }
204
+ function detectEscalationLevel(tokens, override) {
205
+ if (override !== undefined) {
206
+ const parsed = Number(override);
207
+ if (parsed === 1 || parsed === 2 || parsed === 3)
208
+ return parsed;
209
+ }
210
+ const level3Hints = [
211
+ 'refactor',
212
+ 'cross-module',
213
+ 'crossmodule',
214
+ 'migration',
215
+ 'migrate',
216
+ 'incident',
217
+ 'debug',
218
+ 'outage',
219
+ 'hotfix',
220
+ ];
221
+ if (level3Hints.some((hint) => tokens.includes(hint)))
222
+ return 3;
223
+ const level2Hints = [
224
+ 'modify',
225
+ 'change',
226
+ 'behavior',
227
+ 'invariant',
228
+ 'contract',
229
+ 'breaking',
230
+ 'semantics',
231
+ ];
232
+ if (level2Hints.some((hint) => tokens.includes(hint)))
233
+ return 2;
234
+ return 1;
235
+ }
236
+ function routeCandidatesForLevel(level) {
237
+ const candidates = [{ level: 1, file: 'summary.md' }];
238
+ if (level >= 2)
239
+ candidates.push({ level: 2, file: 'details.md' });
240
+ if (level >= 3)
241
+ candidates.push({ level: 3, file: 'deep-dive.md' });
242
+ return candidates;
243
+ }
244
+ function shouldIncludeState(tokens, explicit) {
245
+ if (explicit === true)
246
+ return true;
247
+ const stateHints = [
248
+ 'decision',
249
+ 'decisions',
250
+ 'hypothesis',
251
+ 'hypotheses',
252
+ 'assumption',
253
+ 'assumptions',
254
+ 'ruled-out',
255
+ 'ruled',
256
+ 'confirmed',
257
+ 'state',
258
+ 'context',
259
+ ];
260
+ return stateHints.some((hint) => tokens.includes(hint));
261
+ }
262
+ function maxStateEntriesForLevel(level) {
263
+ if (level >= 3)
264
+ return 10;
265
+ if (level === 2)
266
+ return 6;
267
+ return 3;
268
+ }
269
+ function parseStateLogLines(content, kind, maxEntries) {
270
+ const lines = content
271
+ .split(/\r?\n/)
272
+ .map((line) => line.trim())
273
+ .filter(Boolean);
274
+ const parsed = [];
275
+ for (let i = lines.length - 1; i >= 0 && parsed.length < maxEntries; i -= 1) {
276
+ try {
277
+ const raw = JSON.parse(lines[i]);
278
+ const timestamp = typeof raw.timestamp === 'string' ? raw.timestamp : new Date(0).toISOString();
279
+ const textField = kind === 'decision'
280
+ ? typeof raw.decision === 'string'
281
+ ? raw.decision
282
+ : ''
283
+ : typeof raw.hypothesis === 'string'
284
+ ? raw.hypothesis
285
+ : '';
286
+ if (!textField)
287
+ continue;
288
+ parsed.push({
289
+ kind,
290
+ timestamp,
291
+ source: typeof raw.source === 'string' ? raw.source : undefined,
292
+ note: typeof raw.note === 'string' ? raw.note : null,
293
+ text: textField,
294
+ status: kind === 'hypothesis' &&
295
+ (raw.status === 'open' || raw.status === 'ruled-out' || raw.status === 'confirmed')
296
+ ? raw.status
297
+ : undefined,
298
+ });
299
+ }
300
+ catch {
301
+ // Skip malformed line.
302
+ }
303
+ }
304
+ return parsed.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
305
+ }
306
+ async function loadStateEntries(outputDir, escalationLevel) {
307
+ const maxEntries = maxStateEntriesForLevel(escalationLevel);
308
+ const entries = [];
309
+ try {
310
+ const decisionContent = await readFileSafe(path.join(outputDir, 'context', 'DECISIONS.jsonl'));
311
+ entries.push(...parseStateLogLines(decisionContent, 'decision', maxEntries));
312
+ }
313
+ catch {
314
+ // Optional file
315
+ }
316
+ try {
317
+ const hypothesisContent = await readFileSafe(path.join(outputDir, 'context', 'HYPOTHESES.jsonl'));
318
+ entries.push(...parseStateLogLines(hypothesisContent, 'hypothesis', maxEntries));
319
+ }
320
+ catch {
321
+ // Optional file
322
+ }
323
+ return entries.sort((a, b) => b.timestamp.localeCompare(a.timestamp)).slice(0, maxEntries * 2);
324
+ }
325
+ async function loadContractContext(outputDir, contracts) {
326
+ const chunks = [];
327
+ for (const contract of contracts) {
328
+ const relSource = `context/contracts/${contract}.md`;
329
+ const fullPath = path.join(outputDir, relSource);
330
+ try {
331
+ const content = await readFileSafe(fullPath);
332
+ if (!content.trim())
333
+ continue;
334
+ chunks.push({
335
+ contract,
336
+ source: relSource,
337
+ title: `${contract.toUpperCase()} Contract`,
338
+ content: content.trim(),
339
+ });
340
+ }
341
+ catch {
342
+ // Optional file
343
+ }
344
+ }
345
+ return chunks;
346
+ }
347
+ function shouldIncludeDesignSystem(tokens, routes) {
348
+ if (routes.includes('ui'))
349
+ return true;
350
+ const uiHints = [
351
+ 'ui',
352
+ 'ux',
353
+ 'design-system',
354
+ 'component',
355
+ 'a11y',
356
+ 'accessibility',
357
+ 'hydration',
358
+ 'ssr',
359
+ 'csr',
360
+ ];
361
+ return uiHints.some((hint) => tokens.includes(hint)) || hasUiTokenSignal(tokens);
362
+ }
363
+ async function loadDesignSystemContext(outputDir) {
364
+ const source = 'design-system.json';
365
+ const fullPath = path.join(outputDir, source);
366
+ let raw = '';
367
+ try {
368
+ raw = await readFileSafe(fullPath);
369
+ }
370
+ catch {
371
+ return null;
372
+ }
373
+ if (!raw.trim())
374
+ return null;
375
+ try {
376
+ const parsed = JSON.parse(raw);
377
+ const lines = [
378
+ `Design system: ${parsed.name || 'unnamed'} (v${parsed.version || 'n/a'})`,
379
+ `Allowed component imports: ${(parsed.allowedComponentImports || []).join(', ') || '(none)'}`,
380
+ `Token sources: ${(parsed.tokenSources || []).join(', ') || '(none)'}`,
381
+ `Required wrappers: ${(parsed.requiredWrappers || []).join(', ') || '(none)'}`,
382
+ `Banned rules: ${(parsed.bannedRegexRules || [])
383
+ .map((rule) => `${rule.id || 'rule'}${rule.message ? ` (${rule.message})` : ''}`)
384
+ .join('; ') || '(none)'}`,
385
+ `Motion config: reducedMotionRequired=${parsed.motion?.reducedMotionRequired !== false}, maxDurationMs=${parsed.motion?.maxDurationMs || 900}, forbidInfiniteAnimations=${parsed.motion?.forbidInfiniteAnimations !== false}`,
386
+ ];
387
+ return {
388
+ source,
389
+ title: 'Design System Context',
390
+ content: lines.join('\n'),
391
+ };
392
+ }
393
+ catch {
394
+ const snippet = raw.split(/\r?\n/).slice(0, 40).join('\n').trim();
395
+ return {
396
+ source,
397
+ title: 'Design System Context',
398
+ content: snippet || '(unreadable design-system content)',
399
+ };
400
+ }
401
+ }
402
+ function shouldLoadRefactorLedger(tokens, includeState) {
403
+ if (includeState)
404
+ return true;
405
+ return tokens.includes('refactor') || tokens.includes('rewrite') || tokens.includes('migration');
406
+ }
407
+ async function loadRoutedContext(outputDir, routes, escalationLevel) {
408
+ const chunks = [];
409
+ for (const route of routes) {
410
+ const candidates = routeCandidatesForLevel(escalationLevel);
411
+ for (const candidate of candidates) {
412
+ const relSource = `context/${route}/${candidate.file}`;
413
+ const fullPath = path.join(outputDir, relSource);
414
+ try {
415
+ const content = await readFileSafe(fullPath);
416
+ if (!content.trim())
417
+ continue;
418
+ chunks.push({
419
+ route,
420
+ level: candidate.level,
421
+ source: relSource,
422
+ title: `${route.toUpperCase()} Context (level-${candidate.level})`,
423
+ content: content.trim(),
424
+ });
425
+ }
426
+ catch {
427
+ // Optional routed context files; skip missing/unreadable paths.
428
+ }
429
+ }
430
+ }
431
+ return chunks;
432
+ }
29
433
  function hashContent(content) {
30
434
  return createHash('sha256').update(content, 'utf8').digest('hex').slice(0, 16);
31
435
  }
@@ -38,6 +442,40 @@ function metadataScore(section, tokens) {
38
442
  }
39
443
  return score;
40
444
  }
445
+ function criticalityScore(section, content, tokens) {
446
+ const criticalHints = [
447
+ 'invariant',
448
+ 'invariants',
449
+ 'constraint',
450
+ 'constraints',
451
+ 'edge-case',
452
+ 'edge-cases',
453
+ 'edge case',
454
+ 'migration',
455
+ 'migrations',
456
+ 'contract',
457
+ 'contracts',
458
+ 'decision',
459
+ 'decisions',
460
+ 'rollback',
461
+ ];
462
+ const sectionSignal = `${section.title} ${section.tags.join(' ')} ${section.source} ${section.type}`.toLowerCase();
463
+ const contentSignal = content.toLowerCase();
464
+ let score = 0;
465
+ for (const hint of criticalHints) {
466
+ if (sectionSignal.includes(hint))
467
+ score += 4;
468
+ else if (contentSignal.includes(hint))
469
+ score += 2;
470
+ if (tokens.includes(hint.replace(/\s+/g, '-')) || tokens.includes(hint.replace(/\s+/g, ''))) {
471
+ if (sectionSignal.includes(hint) || contentSignal.includes(hint))
472
+ score += 2;
473
+ }
474
+ }
475
+ if (section.priority === 'high')
476
+ score += 1;
477
+ return score;
478
+ }
41
479
  function contentScore(content, tokens) {
42
480
  const lc = content.toLowerCase();
43
481
  let score = 0;
@@ -80,6 +518,12 @@ export async function retrieve(options) {
80
518
  const outputDir = options.output || '.devmind';
81
519
  const query = options.query || '';
82
520
  const tokens = tokenize(query);
521
+ const routeOverride = parseRouteOption(options.route);
522
+ const routedTargets = routeOverride.length > 0 ? routeOverride : detectRoutes(tokens);
523
+ const contractTargets = detectContractTargets(tokens);
524
+ const escalationLevel = detectEscalationLevel(tokens, options.level);
525
+ const includeState = shouldIncludeState(tokens, options.state);
526
+ const includeLedger = shouldLoadRefactorLedger(tokens, includeState);
83
527
  const typeFilter = options.type?.toLowerCase();
84
528
  const tagFilter = parseTags(options.tags);
85
529
  const limit = clampInt(options.limit, 6);
@@ -88,11 +532,27 @@ export async function retrieve(options) {
88
532
  const agentsPath = path.join(outputDir, 'AGENTS.md');
89
533
  let indexSections = [];
90
534
  let agentsContent = '';
535
+ let routedChunks = [];
536
+ let contractChunks = [];
537
+ let designSystemChunk = null;
538
+ let stateEntries = [];
539
+ let refactorLedger = '';
91
540
  try {
92
541
  indexSections = parseIndexSections(await profiler.section('retrieve.loadIndex', async () => readFileSafe(indexPath)));
93
542
  agentsContent = await profiler.section('retrieve.loadAgents', async () => readFileSafe(agentsPath));
543
+ routedChunks = await profiler.section('retrieve.loadRoutedContext', async () => loadRoutedContext(outputDir, routedTargets, escalationLevel));
544
+ contractChunks = await profiler.section('retrieve.loadContractContext', async () => loadContractContext(outputDir, contractTargets));
545
+ designSystemChunk = shouldIncludeDesignSystem(tokens, routedTargets)
546
+ ? await profiler.section('retrieve.loadDesignSystemContext', async () => loadDesignSystemContext(outputDir))
547
+ : null;
548
+ stateEntries = includeState
549
+ ? await profiler.section('retrieve.loadStateEntries', async () => loadStateEntries(outputDir, escalationLevel))
550
+ : [];
551
+ refactorLedger = includeLedger
552
+ ? await profiler.section('retrieve.loadRefactorLedger', async () => readFileSafe(path.join(outputDir, 'context', 'refactor-ledger.md')).catch(() => ''))
553
+ : '';
94
554
  }
95
- catch (error) {
555
+ catch {
96
556
  const message = `Failed to load retrieval files in ${outputDir}. Run "devmind scan" or "devmind generate --all".`;
97
557
  if (jsonMode) {
98
558
  jsonFail(message);
@@ -125,6 +585,16 @@ export async function retrieve(options) {
125
585
  success: true,
126
586
  query,
127
587
  outputDir,
588
+ routing: {
589
+ routes: routedTargets,
590
+ contracts: contractTargets,
591
+ escalationLevel,
592
+ },
593
+ contracts: contractChunks,
594
+ designSystem: designSystemChunk,
595
+ routed: routedChunks,
596
+ state: stateEntries,
597
+ ledger: refactorLedger,
128
598
  selected: [],
129
599
  message: 'No sections matched the requested filters.',
130
600
  timestamp: new Date().toISOString(),
@@ -134,7 +604,6 @@ export async function retrieve(options) {
134
604
  logger.warn('No sections matched the requested filters.');
135
605
  return;
136
606
  }
137
- // Stage 1: metadata filter + ranking
138
607
  filtered = filtered
139
608
  .map((section) => ({ section, stage1Score: metadataScore(section, tokens) }))
140
609
  .sort((a, b) => b.stage1Score - a.stage1Score ||
@@ -142,7 +611,6 @@ export async function retrieve(options) {
142
611
  a.section.startLine - b.section.startLine)
143
612
  .slice(0, Math.min(24, filtered.length))
144
613
  .map((item) => item.section);
145
- // Stage 2: content ranking
146
614
  const ranked = filtered.map((section) => {
147
615
  const content = sliceByLines(agentsContent, section.startLine, section.endLine);
148
616
  const hash = hashContent(content);
@@ -151,22 +619,67 @@ export async function retrieve(options) {
151
619
  logger.warn(`Section hash mismatch for ${section.id}; consider regenerating context.`);
152
620
  }
153
621
  }
154
- const score = metadataScore(section, tokens) * 2 + contentScore(content, tokens);
622
+ const criticalScore = criticalityScore(section, content, tokens);
623
+ const score = metadataScore(section, tokens) * 2 + contentScore(content, tokens) + criticalScore;
155
624
  return {
156
625
  section,
157
626
  stage1Score: metadataScore(section, tokens),
158
627
  content,
159
628
  score,
629
+ criticalityScore: criticalScore,
160
630
  };
161
631
  });
162
632
  ranked.sort((a, b) => b.score - a.score ||
633
+ b.criticalityScore - a.criticalityScore ||
163
634
  b.stage1Score - a.stage1Score ||
164
635
  a.section.id.localeCompare(b.section.id) ||
165
636
  a.section.startLine - b.section.startLine);
166
637
  const selected = ranked.slice(0, limit);
167
- const picked = [];
638
+ const routedPicked = [];
168
639
  let wordBudget = 0;
640
+ const contractPicked = [];
641
+ for (const chunk of contractChunks) {
642
+ const words = chunk.content.split(/\s+/).filter(Boolean).length;
643
+ if (wordBudget + words > maxWords && contractPicked.length > 0)
644
+ continue;
645
+ contractPicked.push(chunk);
646
+ wordBudget += words;
647
+ }
648
+ for (const chunk of routedChunks) {
649
+ const words = chunk.content.split(/\s+/).filter(Boolean).length;
650
+ if (wordBudget + words > maxWords && routedPicked.length > 0)
651
+ continue;
652
+ routedPicked.push(chunk);
653
+ wordBudget += words;
654
+ }
655
+ const includeDesignSystem = !!designSystemChunk &&
656
+ (wordBudget +
657
+ designSystemChunk.content
658
+ .split(/\s+/)
659
+ .filter(Boolean).length <=
660
+ maxWords ||
661
+ (wordBudget === 0 && !!designSystemChunk));
662
+ if (includeDesignSystem && designSystemChunk) {
663
+ wordBudget += designSystemChunk.content.split(/\s+/).filter(Boolean).length;
664
+ }
665
+ const statePicked = [];
666
+ for (const entry of stateEntries) {
667
+ const words = entry.text.split(/\s+/).filter(Boolean).length;
668
+ if (wordBudget + words > maxWords && statePicked.length > 0)
669
+ continue;
670
+ statePicked.push(entry);
671
+ wordBudget += words;
672
+ }
673
+ const ledgerWords = refactorLedger.split(/\s+/).filter(Boolean).length;
674
+ const includeLedgerInOutput = !!refactorLedger && (wordBudget + ledgerWords <= maxWords);
675
+ if (includeLedgerInOutput) {
676
+ wordBudget += ledgerWords;
677
+ }
678
+ const picked = [];
679
+ const sectionLimit = Math.max(0, limit - routedPicked.length - contractPicked.length);
169
680
  for (const item of selected) {
681
+ if (picked.length >= sectionLimit)
682
+ break;
170
683
  const words = item.content.split(/\s+/).filter(Boolean).length;
171
684
  if (wordBudget + words > maxWords && picked.length > 0)
172
685
  continue;
@@ -179,6 +692,16 @@ export async function retrieve(options) {
179
692
  ? {
180
693
  query,
181
694
  outputDir,
695
+ routing: {
696
+ routes: routedTargets,
697
+ contracts: contractTargets,
698
+ escalationLevel,
699
+ },
700
+ contracts: contractPicked,
701
+ designSystem: includeDesignSystem ? designSystemChunk : null,
702
+ routed: routedPicked,
703
+ state: statePicked,
704
+ ledger: includeLedgerInOutput ? refactorLedger : '',
182
705
  selected: picked.map((item) => ({
183
706
  id: item.section.id,
184
707
  title: item.section.title,
@@ -188,6 +711,7 @@ export async function retrieve(options) {
188
711
  startLine: item.section.startLine,
189
712
  endLine: item.section.endLine,
190
713
  score: item.score,
714
+ criticalityScore: item.criticalityScore,
191
715
  content: item.content,
192
716
  })),
193
717
  profile,
@@ -195,6 +719,16 @@ export async function retrieve(options) {
195
719
  : {
196
720
  query,
197
721
  outputDir,
722
+ routing: {
723
+ routes: routedTargets,
724
+ contracts: contractTargets,
725
+ escalationLevel,
726
+ },
727
+ contracts: contractPicked,
728
+ designSystem: includeDesignSystem ? designSystemChunk : null,
729
+ routed: routedPicked,
730
+ state: statePicked,
731
+ ledger: includeLedgerInOutput ? refactorLedger : '',
198
732
  selected: picked.map((item) => ({
199
733
  id: item.section.id,
200
734
  title: item.section.title,
@@ -204,6 +738,7 @@ export async function retrieve(options) {
204
738
  startLine: item.section.startLine,
205
739
  endLine: item.section.endLine,
206
740
  score: item.score,
741
+ criticalityScore: item.criticalityScore,
207
742
  content: item.content,
208
743
  })),
209
744
  }, null, 2));
@@ -211,7 +746,60 @@ export async function retrieve(options) {
211
746
  }
212
747
  let output = '# Retrieved Context\n\n';
213
748
  output += `Query: ${query || '(none)'}\n`;
749
+ if (routedTargets.length > 0) {
750
+ output += `Routing: ${routedTargets.map((target) => `\`${target}\``).join(', ')}\n`;
751
+ }
752
+ if (contractTargets.length > 0) {
753
+ output += `Contracts: ${contractTargets.map((target) => `\`${target}\``).join(', ')}\n`;
754
+ }
755
+ if (includeDesignSystem && designSystemChunk) {
756
+ output += 'Design system included: yes\n';
757
+ }
758
+ output += `Escalation level: ${escalationLevel}\n`;
759
+ output += `State logs included: ${statePicked.length}\n`;
214
760
  output += `Selected sections: ${picked.length}\n\n`;
761
+ if (routedPicked.length > 0) {
762
+ output += '## Routed Context\n\n';
763
+ for (const chunk of routedPicked) {
764
+ output += `### ${chunk.title}\n\n`;
765
+ output += `- Route: \`${chunk.route}\`\n`;
766
+ output += `- Source: \`${chunk.source}\`\n\n`;
767
+ output += `${chunk.content}\n\n`;
768
+ }
769
+ }
770
+ if (contractPicked.length > 0) {
771
+ output += '## Contract Context\n\n';
772
+ for (const chunk of contractPicked) {
773
+ output += `### ${chunk.title}\n\n`;
774
+ output += `- Contract: \`${chunk.contract}\`\n`;
775
+ output += `- Source: \`${chunk.source}\`\n\n`;
776
+ output += `${chunk.content}\n\n`;
777
+ }
778
+ }
779
+ if (includeDesignSystem && designSystemChunk) {
780
+ output += '## Design System Context\n\n';
781
+ output += `- Source: \`${designSystemChunk.source}\`\n\n`;
782
+ output += `${designSystemChunk.content}\n\n`;
783
+ }
784
+ if (statePicked.length > 0) {
785
+ output += '## State Log\n\n';
786
+ for (const entry of statePicked) {
787
+ output += `- [${entry.kind}] ${entry.timestamp}`;
788
+ if (entry.status)
789
+ output += ` | status=\`${entry.status}\``;
790
+ if (entry.source)
791
+ output += ` | source=\`${entry.source}\``;
792
+ output += `\n`;
793
+ output += ` ${entry.text}\n`;
794
+ if (entry.note)
795
+ output += ` note: ${entry.note}\n`;
796
+ output += '\n';
797
+ }
798
+ }
799
+ if (includeLedgerInOutput) {
800
+ output += '## Refactor Ledger\n\n';
801
+ output += `${refactorLedger}\n\n`;
802
+ }
215
803
  for (const item of picked) {
216
804
  output += `## ${item.section.title}\n\n`;
217
805
  output += `- ID: \`${item.section.id}\`\n`;