@compilr-dev/cli 0.6.1 → 0.6.3

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 (64) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +12 -0
  3. package/dist/commands-v2/handlers/core.js +2 -2
  4. package/dist/commands-v2/handlers/index.d.ts +1 -0
  5. package/dist/commands-v2/handlers/index.js +5 -2
  6. package/dist/commands-v2/handlers/perf.d.ts +9 -0
  7. package/dist/commands-v2/handlers/perf.js +66 -0
  8. package/dist/commands-v2/handlers/project.js +2 -3
  9. package/dist/compilr-diff-companion.vsix +0 -0
  10. package/dist/index.js +14 -8
  11. package/dist/repl-v2.js +5 -1
  12. package/dist/session/project-session-manager.js +1 -1
  13. package/dist/slash-autocomplete.js +18 -16
  14. package/dist/tabbed-menu.js +8 -7
  15. package/dist/ui/base/overlay-base.js +2 -1
  16. package/dist/ui/overlay/impl/artifact-detail-overlay-v2.js +12 -7
  17. package/dist/ui/overlay/impl/config-overlay-v2.js +2 -2
  18. package/dist/ui/overlay/impl/document-detail-overlay-v2.js +12 -8
  19. package/dist/ui/overlay/impl/pending-overlay-v2.js +4 -1
  20. package/dist/ui/overlay-manager.js +7 -6
  21. package/dist/ui/terminal-render-item.js +2 -1
  22. package/dist/ui/terminal-renderer.js +1 -2
  23. package/dist/ui/terminal-ui.js +6 -4
  24. package/dist/ui/terminal.d.ts +9 -0
  25. package/dist/ui/terminal.js +28 -15
  26. package/dist/utils/update-checker.d.ts +6 -1
  27. package/dist/utils/update-checker.js +16 -1
  28. package/package.json +5 -4
  29. package/dist/.tsbuildinfo.app +0 -1
  30. package/dist/.tsbuildinfo.data +0 -1
  31. package/dist/.tsbuildinfo.domain +0 -1
  32. package/dist/.tsbuildinfo.foundation +0 -1
  33. package/dist/guide/guide-content.d.ts +0 -23
  34. package/dist/guide/guide-content.js +0 -196
  35. package/dist/multi-agent/activity.d.ts +0 -21
  36. package/dist/multi-agent/activity.js +0 -34
  37. package/dist/multi-agent/agent-selection.d.ts +0 -55
  38. package/dist/multi-agent/agent-selection.js +0 -90
  39. package/dist/multi-agent/artifacts.d.ts +0 -197
  40. package/dist/multi-agent/artifacts.js +0 -379
  41. package/dist/multi-agent/collision-utils.d.ts +0 -16
  42. package/dist/multi-agent/collision-utils.js +0 -28
  43. package/dist/multi-agent/context-resolver.d.ts +0 -97
  44. package/dist/multi-agent/context-resolver.js +0 -316
  45. package/dist/multi-agent/mention-parser.d.ts +0 -64
  46. package/dist/multi-agent/mention-parser.js +0 -146
  47. package/dist/multi-agent/shared-context.d.ts +0 -293
  48. package/dist/multi-agent/shared-context.js +0 -671
  49. package/dist/multi-agent/skill-requirements.d.ts +0 -66
  50. package/dist/multi-agent/skill-requirements.js +0 -178
  51. package/dist/multi-agent/task-assignment.d.ts +0 -69
  52. package/dist/multi-agent/task-assignment.js +0 -123
  53. package/dist/multi-agent/task-suggestion.d.ts +0 -31
  54. package/dist/multi-agent/task-suggestion.js +0 -72
  55. package/dist/multi-agent/team-agent.d.ts +0 -201
  56. package/dist/multi-agent/team-agent.js +0 -488
  57. package/dist/multi-agent/team.d.ts +0 -286
  58. package/dist/multi-agent/team.js +0 -610
  59. package/dist/multi-agent/tool-config.d.ts +0 -110
  60. package/dist/multi-agent/tool-config.js +0 -661
  61. package/dist/multi-agent/types.d.ts +0 -211
  62. package/dist/multi-agent/types.js +0 -617
  63. package/dist/tools/guide-tool.d.ts +0 -12
  64. package/dist/tools/guide-tool.js +0 -59
@@ -1,671 +0,0 @@
1
- /**
2
- * SharedContextManager - Shared context across team agents
3
- *
4
- * Provides a lightweight, token-budgeted context layer that's injected
5
- * into all agent system prompts. Enables cross-agent knowledge sharing.
6
- *
7
- * Token Budget: ~4000 tokens (2% of 200K context)
8
- */
9
- // =============================================================================
10
- // Constants
11
- // =============================================================================
12
- const DEFAULT_TOKEN_BUDGET = 4000;
13
- const MAX_DECISIONS = 10;
14
- const MAX_DECISION_LENGTH = 100;
15
- const MAX_MEMORY_SUMMARY_LENGTH = 2000;
16
- const MAX_ACTIVITY_ITEMS = 5;
17
- // Token counting via tiktoken
18
- import { estimateTokens } from '../utils/token-tracker.js';
19
- // =============================================================================
20
- // SharedContextManager
21
- // =============================================================================
22
- /**
23
- * Manages shared context across all team agents
24
- */
25
- export class SharedContextManager {
26
- project;
27
- team;
28
- teamRoster = []; // Full roster for team awareness
29
- recentActivity = []; // Activity feed for team awareness
30
- decisions = [];
31
- artifactIndex = [];
32
- tokenBudget;
33
- updatedAt = new Date();
34
- constructor(options) {
35
- this.project = {
36
- id: options?.project?.id ?? null,
37
- name: options?.project?.name ?? 'Unknown Project',
38
- path: options?.project?.path ?? process.cwd(),
39
- memorySummary: options?.project?.memorySummary ?? '',
40
- };
41
- this.team = {
42
- agents: ['default'],
43
- activeAgent: 'default',
44
- };
45
- this.tokenBudget = {
46
- max: options?.maxTokens ?? DEFAULT_TOKEN_BUDGET,
47
- current: 0,
48
- };
49
- this.updateTokenCount();
50
- }
51
- // ---------------------------------------------------------------------------
52
- // Project Management
53
- // ---------------------------------------------------------------------------
54
- /**
55
- * Update project information
56
- */
57
- setProject(project) {
58
- if (project.id !== undefined)
59
- this.project.id = project.id;
60
- if (project.name !== undefined)
61
- this.project.name = project.name;
62
- if (project.path !== undefined)
63
- this.project.path = project.path;
64
- if (project.memorySummary !== undefined) {
65
- // Truncate if too long
66
- this.project.memorySummary = project.memorySummary.slice(0, MAX_MEMORY_SUMMARY_LENGTH);
67
- }
68
- this.touch();
69
- }
70
- /**
71
- * Get project information
72
- */
73
- getProject() {
74
- return { ...this.project };
75
- }
76
- // ---------------------------------------------------------------------------
77
- // Team Management
78
- // ---------------------------------------------------------------------------
79
- /**
80
- * Update team roster
81
- */
82
- setTeam(agents, activeAgent) {
83
- this.team.agents = [...agents];
84
- if (activeAgent && agents.includes(activeAgent)) {
85
- this.team.activeAgent = activeAgent;
86
- }
87
- this.touch();
88
- }
89
- /**
90
- * Set active agent
91
- */
92
- setActiveAgent(agentId) {
93
- if (this.team.agents.includes(agentId)) {
94
- this.team.activeAgent = agentId;
95
- this.touch();
96
- }
97
- }
98
- /**
99
- * Add an agent to the team
100
- */
101
- addAgent(agentId) {
102
- if (!this.team.agents.includes(agentId)) {
103
- this.team.agents.push(agentId);
104
- this.touch();
105
- }
106
- }
107
- /**
108
- * Remove an agent from the team
109
- */
110
- removeAgent(agentId) {
111
- const index = this.team.agents.indexOf(agentId);
112
- if (index !== -1) {
113
- this.team.agents.splice(index, 1);
114
- // If active agent was removed, switch to default
115
- if (this.team.activeAgent === agentId) {
116
- this.team.activeAgent = 'default';
117
- }
118
- this.touch();
119
- }
120
- }
121
- /**
122
- * Get team roster
123
- */
124
- getTeam() {
125
- return { ...this.team, agents: [...this.team.agents] };
126
- }
127
- // ---------------------------------------------------------------------------
128
- // Team Roster (for Team Awareness)
129
- // ---------------------------------------------------------------------------
130
- /**
131
- * Update the full team roster with agent details
132
- * Called when team composition changes
133
- */
134
- updateTeamRoster(entries) {
135
- this.teamRoster = entries.map(entry => ({ ...entry }));
136
- this.touch();
137
- }
138
- /**
139
- * Set active agent in the roster
140
- * Called on agent switch
141
- */
142
- setRosterActiveAgent(agentId) {
143
- for (const entry of this.teamRoster) {
144
- entry.isActive = entry.id === agentId;
145
- }
146
- // Also update the simple team info
147
- if (this.team.agents.includes(agentId)) {
148
- this.team.activeAgent = agentId;
149
- }
150
- this.touch();
151
- }
152
- /**
153
- * Get the full team roster
154
- */
155
- getTeamRoster() {
156
- return this.teamRoster.map(entry => ({ ...entry }));
157
- }
158
- /**
159
- * Check if team roster is available (more than just default agent)
160
- */
161
- hasTeamRoster() {
162
- return this.teamRoster.length > 1;
163
- }
164
- /**
165
- * Update task counts for an agent
166
- * Called by REPL when todo list or work items change
167
- */
168
- updateAgentTaskCounts(agentId, todoCount, workItemCount) {
169
- const entry = this.teamRoster.find(e => e.id === agentId);
170
- if (entry) {
171
- entry.todoCount = todoCount;
172
- entry.workItemCount = workItemCount;
173
- this.touch();
174
- }
175
- }
176
- /**
177
- * Update task counts for all agents
178
- * Called with a map of agentId -> { todoCount, workItemCount }
179
- */
180
- updateAllAgentTaskCounts(counts) {
181
- for (const entry of this.teamRoster) {
182
- if (entry.id in counts) {
183
- const agentCounts = counts[entry.id];
184
- entry.todoCount = agentCounts.todoCount;
185
- entry.workItemCount = agentCounts.workItemCount;
186
- }
187
- else {
188
- entry.todoCount = 0;
189
- entry.workItemCount = 0;
190
- }
191
- }
192
- this.touch();
193
- }
194
- // ---------------------------------------------------------------------------
195
- // Activity Feed (for Team Awareness)
196
- // ---------------------------------------------------------------------------
197
- /**
198
- * Record a team activity
199
- * Called when significant actions occur (artifact created, task completed, etc.)
200
- */
201
- recordActivity(activity) {
202
- const newActivity = {
203
- ...activity,
204
- timestamp: new Date(),
205
- };
206
- this.recentActivity.unshift(newActivity);
207
- // Keep only MAX_ACTIVITY_ITEMS
208
- if (this.recentActivity.length > MAX_ACTIVITY_ITEMS) {
209
- this.recentActivity = this.recentActivity.slice(0, MAX_ACTIVITY_ITEMS);
210
- }
211
- this.touch();
212
- }
213
- /**
214
- * Get recent activity
215
- */
216
- getRecentActivity() {
217
- return this.recentActivity.map(a => ({ ...a }));
218
- }
219
- /**
220
- * Check if there is recent activity to display
221
- */
222
- hasRecentActivity() {
223
- return this.recentActivity.length > 0;
224
- }
225
- /**
226
- * Clear activity feed
227
- */
228
- clearActivity() {
229
- this.recentActivity = [];
230
- this.touch();
231
- }
232
- // ---------------------------------------------------------------------------
233
- // Decision Management
234
- // ---------------------------------------------------------------------------
235
- /**
236
- * Add a decision to the shared context
237
- */
238
- addDecision(decision) {
239
- const id = `dec-${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
240
- // Truncate summary if too long
241
- const summary = decision.summary.slice(0, MAX_DECISION_LENGTH);
242
- const newDecision = {
243
- id,
244
- agent: decision.agent,
245
- summary,
246
- reasoning: decision.reasoning,
247
- timestamp: new Date(),
248
- };
249
- this.decisions.unshift(newDecision);
250
- // Keep only MAX_DECISIONS
251
- if (this.decisions.length > MAX_DECISIONS) {
252
- this.decisions = this.decisions.slice(0, MAX_DECISIONS);
253
- }
254
- this.touch();
255
- return id;
256
- }
257
- /**
258
- * Update/supersede a decision
259
- */
260
- supersededDecision(oldId, newDecision) {
261
- // Mark old decision as superseded
262
- const old = this.decisions.find(d => d.id === oldId);
263
- const newId = this.addDecision(newDecision);
264
- if (old) {
265
- old.supersededBy = newId;
266
- }
267
- return newId;
268
- }
269
- /**
270
- * Get all decisions (active only by default)
271
- */
272
- getDecisions(includeSuperseded = false) {
273
- if (includeSuperseded) {
274
- return [...this.decisions];
275
- }
276
- return this.decisions.filter(d => !d.supersededBy);
277
- }
278
- /**
279
- * Clear all decisions
280
- */
281
- clearDecisions() {
282
- this.decisions = [];
283
- this.touch();
284
- }
285
- // ---------------------------------------------------------------------------
286
- // Artifact Index Management
287
- // ---------------------------------------------------------------------------
288
- /**
289
- * Update the artifact index (called by ArtifactStore)
290
- */
291
- setArtifactIndex(index) {
292
- this.artifactIndex = [...index];
293
- this.touch();
294
- }
295
- /**
296
- * Add an artifact to the index
297
- */
298
- addArtifactToIndex(artifact) {
299
- // Remove existing with same ID
300
- this.artifactIndex = this.artifactIndex.filter(a => a.id !== artifact.id);
301
- this.artifactIndex.unshift(artifact);
302
- this.touch();
303
- }
304
- /**
305
- * Remove an artifact from the index
306
- */
307
- removeArtifactFromIndex(artifactId) {
308
- this.artifactIndex = this.artifactIndex.filter(a => a.id !== artifactId);
309
- this.touch();
310
- }
311
- /**
312
- * Get artifact index
313
- */
314
- getArtifactIndex() {
315
- return [...this.artifactIndex];
316
- }
317
- // ---------------------------------------------------------------------------
318
- // Token Budget Management
319
- // ---------------------------------------------------------------------------
320
- /**
321
- * Update token count based on current content
322
- */
323
- updateTokenCount() {
324
- let tokens = 0;
325
- // Project info
326
- tokens += estimateTokens(this.project.name);
327
- tokens += estimateTokens(this.project.path);
328
- tokens += estimateTokens(this.project.memorySummary);
329
- // Team info
330
- tokens += estimateTokens(this.team.agents.join(', '));
331
- // Decisions
332
- for (const decision of this.decisions) {
333
- tokens += estimateTokens(decision.summary);
334
- if (decision.reasoning) {
335
- tokens += estimateTokens(decision.reasoning);
336
- }
337
- }
338
- // Artifact index
339
- for (const artifact of this.artifactIndex) {
340
- tokens += estimateTokens(artifact.name);
341
- tokens += estimateTokens(artifact.summary);
342
- }
343
- this.tokenBudget.current = tokens;
344
- }
345
- /**
346
- * Check if adding content would exceed budget
347
- */
348
- wouldExceedBudget(additionalTokens) {
349
- return (this.tokenBudget.current + additionalTokens) > this.tokenBudget.max;
350
- }
351
- /**
352
- * Get token budget status
353
- */
354
- getTokenBudget() {
355
- return { ...this.tokenBudget };
356
- }
357
- /**
358
- * Get utilization percentage
359
- */
360
- getUtilization() {
361
- return Math.round((this.tokenBudget.current / this.tokenBudget.max) * 100);
362
- }
363
- // ---------------------------------------------------------------------------
364
- // Context Formatting
365
- // ---------------------------------------------------------------------------
366
- /**
367
- * Format shared context for injection into system prompt.
368
- * @param options.excludeRoster - If true, omit the team roster (it will be injected via anchor instead)
369
- */
370
- format(options) {
371
- const lines = [];
372
- lines.push('## SHARED TEAM CONTEXT');
373
- lines.push('');
374
- // Project info
375
- lines.push(`**Project:** ${this.project.name}`);
376
- lines.push(`**Path:** ${this.project.path}`);
377
- if (this.project.memorySummary) {
378
- lines.push(`**Summary:** ${this.project.memorySummary}`);
379
- }
380
- lines.push('');
381
- // Team roster — skip if excluded (will be injected via anchor for live updates)
382
- if (!options?.excludeRoster) {
383
- if (this.hasTeamRoster()) {
384
- lines.push(this.formatTeamRoster());
385
- lines.push('');
386
- }
387
- else {
388
- // Fallback to simple team info
389
- lines.push(`**Team:** ${this.team.agents.map(a => `$${a}`).join(', ')}`);
390
- lines.push(`**Active:** $${this.team.activeAgent}`);
391
- lines.push('');
392
- // Team collaboration instructions (only if multiple agents)
393
- if (this.team.agents.length > 1) {
394
- lines.push('**Team Collaboration:**');
395
- lines.push('- You are part of a multi-agent team. Each agent has a specialized role.');
396
- lines.push('- If a question or task seems better suited for another team member, suggest asking them.');
397
- lines.push('- Example: "This is more of an architecture question - you might want to ask $arch about this."');
398
- lines.push('- Reference artifacts created by other agents when relevant using $mention syntax.');
399
- lines.push('');
400
- }
401
- }
402
- }
403
- // Recent team activity (if any)
404
- if (this.hasRecentActivity()) {
405
- lines.push(this.formatRecentActivity());
406
- lines.push('');
407
- }
408
- // Decisions
409
- const activeDecisions = this.getDecisions(false);
410
- if (activeDecisions.length > 0) {
411
- lines.push('**Recent Decisions:**');
412
- for (const decision of activeDecisions.slice(0, 5)) {
413
- lines.push(`- [$${decision.agent}] ${decision.summary}`);
414
- }
415
- lines.push('');
416
- }
417
- // Artifacts
418
- if (this.artifactIndex.length > 0) {
419
- lines.push('**Available Artifacts:**');
420
- for (const artifact of this.artifactIndex.slice(0, 10)) {
421
- lines.push(`- "${artifact.name}" by $${artifact.agent} (${artifact.type})`);
422
- }
423
- lines.push('');
424
- }
425
- return lines.join('\n');
426
- }
427
- /**
428
- * Format team roster as a table for team awareness
429
- */
430
- formatTeamRoster() {
431
- const lines = [];
432
- // Find the active agent for personalized guidance
433
- const activeEntry = this.teamRoster.find(e => e.isActive);
434
- // Check if any agent has task counts to show
435
- const showTaskCounts = this.teamRoster.some(e => (e.todoCount ?? 0) > 0 || (e.workItemCount ?? 0) > 0);
436
- lines.push('### Your Team');
437
- lines.push('');
438
- if (showTaskCounts) {
439
- lines.push('| Agent | Role | Tasks | Expertise |');
440
- lines.push('|-------|------|-------|-----------|');
441
- }
442
- else {
443
- lines.push('| Agent | Role | Expertise |');
444
- lines.push('|-------|------|-----------|');
445
- }
446
- for (const entry of this.teamRoster) {
447
- const marker = entry.isActive ? '→ ' : ' ';
448
- const youMarker = entry.isActive ? ' ← YOU' : '';
449
- // Show first 3 expertise keywords to keep it concise
450
- const expertise = entry.expertise.slice(0, 3).join(', ');
451
- if (showTaskCounts) {
452
- // Format task counts
453
- const todoCount = entry.todoCount ?? 0;
454
- const workItemCount = entry.workItemCount ?? 0;
455
- const taskParts = [];
456
- if (todoCount > 0)
457
- taskParts.push(`${String(todoCount)} todo${todoCount > 1 ? 's' : ''}`);
458
- if (workItemCount > 0)
459
- taskParts.push(`${String(workItemCount)} item${workItemCount > 1 ? 's' : ''}`);
460
- const taskStr = taskParts.length > 0 ? taskParts.join(', ') : '-';
461
- lines.push(`| ${marker}$${entry.id} ${entry.mascot} | ${entry.displayName} | ${taskStr} | ${expertise}${youMarker} |`);
462
- }
463
- else {
464
- lines.push(`| ${marker}$${entry.id} ${entry.mascot} | ${entry.displayName} | ${expertise}${youMarker} |`);
465
- }
466
- }
467
- lines.push('');
468
- // Handoff guidance - dynamic based on active agent and team
469
- if (activeEntry) {
470
- const yourExpertise = activeEntry.expertise.slice(0, 3).join(', ');
471
- lines.push(`**Your expertise:** ${yourExpertise}`);
472
- lines.push('');
473
- }
474
- lines.push('**Collaboration Guidelines:**');
475
- lines.push('- If a task falls outside your expertise, suggest the appropriate specialist');
476
- lines.push('- Example: "This would be better handled by $arch for architecture decisions"');
477
- lines.push('- Do NOT attempt tasks far outside your expertise - it\'s better to suggest a handoff');
478
- // Add specific handoff suggestions based on teammates
479
- const handoffSuggestions = this.buildHandoffSuggestions(activeEntry?.id);
480
- if (handoffSuggestions.length > 0) {
481
- lines.push('');
482
- lines.push('**When to involve teammates:**');
483
- for (const suggestion of handoffSuggestions) {
484
- lines.push(`- ${suggestion}`);
485
- }
486
- }
487
- return lines.join('\n');
488
- }
489
- /**
490
- * Build handoff suggestions based on team composition
491
- */
492
- buildHandoffSuggestions(activeAgentId) {
493
- const suggestions = [];
494
- for (const entry of this.teamRoster) {
495
- // Skip the active agent
496
- if (entry.id === activeAgentId)
497
- continue;
498
- // Build suggestion based on role
499
- const role = entry.role;
500
- switch (role) {
501
- case 'arch':
502
- suggestions.push(`Architecture/design questions → "Let me get $${entry.id}'s input on the design"`);
503
- break;
504
- case 'pm':
505
- suggestions.push(`Planning/prioritization → "$${entry.id} can help with task breakdown"`);
506
- break;
507
- case 'qa':
508
- suggestions.push(`Testing/quality concerns → "$${entry.id} should review the test strategy"`);
509
- break;
510
- case 'dev':
511
- suggestions.push(`Implementation details → "$${entry.id} can handle the coding"`);
512
- break;
513
- case 'ops':
514
- suggestions.push(`Deployment/infrastructure → "$${entry.id} can advise on CI/CD"`);
515
- break;
516
- case 'docs':
517
- suggestions.push(`Documentation needs → "$${entry.id} can write the user docs"`);
518
- break;
519
- case 'ba':
520
- suggestions.push(`Requirements clarity → "$${entry.id} can help clarify the business need"`);
521
- break;
522
- default:
523
- // For default or custom agents, use their display name
524
- if (entry.expertise.length > 0) {
525
- suggestions.push(`${entry.expertise[0]} → "$${entry.id} specializes in this"`);
526
- }
527
- }
528
- }
529
- // Limit to 5 suggestions to keep it concise
530
- return suggestions.slice(0, 5);
531
- }
532
- /**
533
- * Format recent activity feed for team awareness
534
- */
535
- formatRecentActivity() {
536
- const lines = [];
537
- lines.push('### Recent Team Activity');
538
- lines.push('');
539
- for (const activity of this.recentActivity) {
540
- const ago = this.formatTimeAgo(activity.timestamp);
541
- lines.push(`- [${ago}] $${activity.agentId} ${activity.summary}`);
542
- }
543
- return lines.join('\n');
544
- }
545
- /**
546
- * Format a timestamp as "Xm ago", "Xh ago", etc.
547
- */
548
- formatTimeAgo(timestamp) {
549
- const now = Date.now();
550
- const diff = now - timestamp.getTime();
551
- const minutes = Math.floor(diff / 60000);
552
- const hours = Math.floor(diff / 3600000);
553
- if (minutes < 1) {
554
- return 'just now';
555
- }
556
- else if (minutes < 60) {
557
- return `${String(minutes)}m ago`;
558
- }
559
- else if (hours < 24) {
560
- return `${String(hours)}h ago`;
561
- }
562
- else {
563
- const days = Math.floor(hours / 24);
564
- return `${String(days)}d ago`;
565
- }
566
- }
567
- /**
568
- * Format a compact version (for token-constrained situations)
569
- */
570
- formatCompact() {
571
- const parts = [];
572
- parts.push(`Project: ${this.project.name}`);
573
- parts.push(`Team: ${this.team.agents.map(a => `$${a}`).join(', ')}`);
574
- const activeDecisions = this.getDecisions(false);
575
- if (activeDecisions.length > 0) {
576
- parts.push(`Decisions: ${String(activeDecisions.length)}`);
577
- }
578
- if (this.artifactIndex.length > 0) {
579
- parts.push(`Artifacts: ${this.artifactIndex.map(a => `"${a.name}"`).join(', ')}`);
580
- }
581
- return parts.join(' | ');
582
- }
583
- // ---------------------------------------------------------------------------
584
- // Persistence
585
- // ---------------------------------------------------------------------------
586
- /**
587
- * Mark as updated
588
- */
589
- touch() {
590
- this.updatedAt = new Date();
591
- this.updateTokenCount();
592
- }
593
- /**
594
- * Serialize for persistence
595
- */
596
- serialize() {
597
- return {
598
- version: 1,
599
- project: {
600
- id: this.project.id,
601
- name: this.project.name,
602
- path: this.project.path,
603
- memorySummary: this.project.memorySummary,
604
- },
605
- decisions: this.decisions.map(d => ({
606
- id: d.id,
607
- agent: d.agent,
608
- summary: d.summary,
609
- reasoning: d.reasoning,
610
- timestamp: d.timestamp.toISOString(),
611
- supersededBy: d.supersededBy,
612
- })),
613
- artifactIndex: this.artifactIndex.map(a => ({
614
- id: a.id,
615
- name: a.name,
616
- agent: a.agent,
617
- type: a.type,
618
- summary: a.summary,
619
- updatedAt: a.updatedAt.toISOString(),
620
- })),
621
- updatedAt: this.updatedAt.toISOString(),
622
- };
623
- }
624
- /**
625
- * Restore from serialized data
626
- */
627
- static fromSerialized(data) {
628
- const manager = new SharedContextManager({
629
- project: {
630
- id: data.project.id,
631
- name: data.project.name,
632
- path: data.project.path,
633
- memorySummary: data.project.memorySummary,
634
- },
635
- });
636
- // Restore decisions
637
- manager.decisions = data.decisions.map(d => ({
638
- id: d.id,
639
- agent: d.agent,
640
- summary: d.summary,
641
- reasoning: d.reasoning,
642
- timestamp: new Date(d.timestamp),
643
- supersededBy: d.supersededBy,
644
- }));
645
- // Restore artifact index
646
- manager.artifactIndex = data.artifactIndex.map(a => ({
647
- id: a.id,
648
- name: a.name,
649
- agent: a.agent,
650
- type: a.type,
651
- summary: a.summary,
652
- updatedAt: new Date(a.updatedAt),
653
- }));
654
- manager.updatedAt = new Date(data.updatedAt);
655
- manager.updateTokenCount();
656
- return manager;
657
- }
658
- /**
659
- * Get the full context object
660
- */
661
- getContext() {
662
- return {
663
- project: this.getProject(),
664
- team: this.getTeam(),
665
- decisions: this.getDecisions(),
666
- artifactIndex: this.getArtifactIndex(),
667
- tokenBudget: this.getTokenBudget(),
668
- updatedAt: this.updatedAt,
669
- };
670
- }
671
- }