@ginkoai/cli 1.4.13 → 1.6.1

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 (73) hide show
  1. package/dist/commands/graph/api-client.d.ts +45 -1
  2. package/dist/commands/graph/api-client.d.ts.map +1 -1
  3. package/dist/commands/graph/api-client.js +86 -4
  4. package/dist/commands/graph/api-client.js.map +1 -1
  5. package/dist/commands/graph/health.d.ts +22 -0
  6. package/dist/commands/graph/health.d.ts.map +1 -0
  7. package/dist/commands/graph/health.js +37 -0
  8. package/dist/commands/graph/health.js.map +1 -0
  9. package/dist/commands/graph/index.d.ts.map +1 -1
  10. package/dist/commands/graph/index.js +8 -0
  11. package/dist/commands/graph/index.js.map +1 -1
  12. package/dist/commands/handoff.d.ts +7 -1
  13. package/dist/commands/handoff.d.ts.map +1 -1
  14. package/dist/commands/handoff.js +8 -0
  15. package/dist/commands/handoff.js.map +1 -1
  16. package/dist/commands/log.d.ts +5 -4
  17. package/dist/commands/log.d.ts.map +1 -1
  18. package/dist/commands/log.js +197 -216
  19. package/dist/commands/log.js.map +1 -1
  20. package/dist/commands/start/start-reflection.d.ts +22 -0
  21. package/dist/commands/start/start-reflection.d.ts.map +1 -1
  22. package/dist/commands/start/start-reflection.js +295 -47
  23. package/dist/commands/start/start-reflection.js.map +1 -1
  24. package/dist/index.js +7 -7
  25. package/dist/index.js.map +1 -1
  26. package/dist/lib/charter-loader.d.ts +74 -0
  27. package/dist/lib/charter-loader.d.ts.map +1 -0
  28. package/dist/lib/charter-loader.js +328 -0
  29. package/dist/lib/charter-loader.js.map +1 -0
  30. package/dist/lib/context-loader-events.d.ts +49 -0
  31. package/dist/lib/context-loader-events.d.ts.map +1 -1
  32. package/dist/lib/context-loader-events.js +289 -36
  33. package/dist/lib/context-loader-events.js.map +1 -1
  34. package/dist/lib/event-logger.d.ts +8 -5
  35. package/dist/lib/event-logger.d.ts.map +1 -1
  36. package/dist/lib/event-logger.js +54 -14
  37. package/dist/lib/event-logger.js.map +1 -1
  38. package/dist/lib/event-task-linker.d.ts +81 -0
  39. package/dist/lib/event-task-linker.d.ts.map +1 -0
  40. package/dist/lib/event-task-linker.js +132 -0
  41. package/dist/lib/event-task-linker.js.map +1 -0
  42. package/dist/lib/output-formatter.d.ts +182 -0
  43. package/dist/lib/output-formatter.d.ts.map +1 -0
  44. package/dist/lib/output-formatter.js +263 -0
  45. package/dist/lib/output-formatter.js.map +1 -0
  46. package/dist/lib/session-cursor.d.ts +12 -5
  47. package/dist/lib/session-cursor.d.ts.map +1 -1
  48. package/dist/lib/session-cursor.js +12 -5
  49. package/dist/lib/session-cursor.js.map +1 -1
  50. package/dist/lib/sprint-loader.d.ts +94 -0
  51. package/dist/lib/sprint-loader.d.ts.map +1 -0
  52. package/dist/lib/sprint-loader.js +330 -0
  53. package/dist/lib/sprint-loader.js.map +1 -0
  54. package/dist/lib/sprint-parser.d.ts +79 -0
  55. package/dist/lib/sprint-parser.d.ts.map +1 -0
  56. package/dist/lib/sprint-parser.js +346 -0
  57. package/dist/lib/sprint-parser.js.map +1 -0
  58. package/dist/lib/write-dispatcher/adapters/local-adapter.d.ts.map +1 -1
  59. package/dist/lib/write-dispatcher/adapters/local-adapter.js +8 -2
  60. package/dist/lib/write-dispatcher/adapters/local-adapter.js.map +1 -1
  61. package/dist/utils/command-helpers.d.ts +76 -0
  62. package/dist/utils/command-helpers.d.ts.map +1 -0
  63. package/dist/utils/command-helpers.js +314 -0
  64. package/dist/utils/command-helpers.js.map +1 -0
  65. package/dist/utils/graph-health-monitor.d.ts +75 -0
  66. package/dist/utils/graph-health-monitor.d.ts.map +1 -0
  67. package/dist/utils/graph-health-monitor.js +108 -0
  68. package/dist/utils/graph-health-monitor.js.map +1 -0
  69. package/dist/utils/synthesis.d.ts +9 -0
  70. package/dist/utils/synthesis.d.ts.map +1 -1
  71. package/dist/utils/synthesis.js +47 -40
  72. package/dist/utils/synthesis.js.map +1 -1
  73. package/package.json +1 -1
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @fileType: utility
3
+ * @status: current
4
+ * @updated: 2025-11-21
5
+ * @tags: [event-linking, task-detection, graph-relationships, epic-001]
6
+ * @related: [event-logger.ts, sprint-parser.ts, event-queue.ts]
7
+ * @priority: high
8
+ * @complexity: low
9
+ * @dependencies: []
10
+ */
11
+ /**
12
+ * Event-Task Linker (EPIC-001 TASK-4)
13
+ *
14
+ * Extracts task mentions (TASK-XXX patterns) from event descriptions
15
+ * to enable hot/cold task detection via RECENT_ACTIVITY relationships.
16
+ *
17
+ * Usage in graph sync:
18
+ * 1. Extract task IDs from event description
19
+ * 2. Create (Task)<-[:RECENT_ACTIVITY]-(Event) relationships
20
+ * 3. Query recent activity to determine task "hotness"
21
+ *
22
+ * Enables momentum awareness:
23
+ * - Hot tasks: Many recent events (actively being worked on)
24
+ * - Cold tasks: No recent events (stale, needs attention)
25
+ * - Priority surfacing: Show hot tasks first in sprint views
26
+ */
27
+ /**
28
+ * Extract task IDs from text
29
+ *
30
+ * Matches patterns like:
31
+ * - "TASK-1"
32
+ * - "TASK-123"
33
+ * - "task-5" (case insensitive)
34
+ * - "Completed TASK-4, started TASK-5"
35
+ *
36
+ * @param text - Event description or any text content
37
+ * @returns Array of unique task IDs (normalized to uppercase)
38
+ *
39
+ * @example
40
+ * extractTaskMentions("Completed TASK-4, started TASK-5")
41
+ * // Returns: ["TASK-4", "TASK-5"]
42
+ */
43
+ export declare function extractTaskMentions(text: string): string[];
44
+ /**
45
+ * Check if text mentions any tasks
46
+ *
47
+ * @param text - Event description or any text content
48
+ * @returns True if text contains at least one task mention
49
+ */
50
+ export declare function hasTasksMentions(text: string): boolean;
51
+ /**
52
+ * Calculate task hotness score based on recent activity
53
+ *
54
+ * Hotness formula:
55
+ * - Events in last 4h: Weight 3x
56
+ * - Events in last 24h: Weight 2x
57
+ * - Events in last 7d: Weight 1x
58
+ *
59
+ * @param events - Array of event timestamps (ISO format)
60
+ * @param now - Current timestamp (for testing)
61
+ * @returns Hotness score (0-100)
62
+ *
63
+ * @example
64
+ * calculateHotness([
65
+ * "2025-11-21T10:00:00Z", // 2h ago
66
+ * "2025-11-21T08:00:00Z", // 4h ago
67
+ * "2025-11-20T10:00:00Z" // 1d ago
68
+ * ])
69
+ * // Returns: ~85 (high hotness)
70
+ */
71
+ export declare function calculateHotness(events: Array<{
72
+ timestamp: string;
73
+ }>, now?: Date): number;
74
+ /**
75
+ * Classify hotness level for UI display
76
+ *
77
+ * @param hotness - Hotness score (0-100)
78
+ * @returns Level classification
79
+ */
80
+ export declare function getHotnessLevel(hotness: number): 'cold' | 'warm' | 'hot' | 'blazing';
81
+ //# sourceMappingURL=event-task-linker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-task-linker.d.ts","sourceRoot":"","sources":["../../src/lib/event-task-linker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAiB1D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,KAAK,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,EACpC,GAAG,GAAE,IAAiB,GACrB,MAAM,CAiCR;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAKpF"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * @fileType: utility
3
+ * @status: current
4
+ * @updated: 2025-11-21
5
+ * @tags: [event-linking, task-detection, graph-relationships, epic-001]
6
+ * @related: [event-logger.ts, sprint-parser.ts, event-queue.ts]
7
+ * @priority: high
8
+ * @complexity: low
9
+ * @dependencies: []
10
+ */
11
+ /**
12
+ * Event-Task Linker (EPIC-001 TASK-4)
13
+ *
14
+ * Extracts task mentions (TASK-XXX patterns) from event descriptions
15
+ * to enable hot/cold task detection via RECENT_ACTIVITY relationships.
16
+ *
17
+ * Usage in graph sync:
18
+ * 1. Extract task IDs from event description
19
+ * 2. Create (Task)<-[:RECENT_ACTIVITY]-(Event) relationships
20
+ * 3. Query recent activity to determine task "hotness"
21
+ *
22
+ * Enables momentum awareness:
23
+ * - Hot tasks: Many recent events (actively being worked on)
24
+ * - Cold tasks: No recent events (stale, needs attention)
25
+ * - Priority surfacing: Show hot tasks first in sprint views
26
+ */
27
+ /**
28
+ * Extract task IDs from text
29
+ *
30
+ * Matches patterns like:
31
+ * - "TASK-1"
32
+ * - "TASK-123"
33
+ * - "task-5" (case insensitive)
34
+ * - "Completed TASK-4, started TASK-5"
35
+ *
36
+ * @param text - Event description or any text content
37
+ * @returns Array of unique task IDs (normalized to uppercase)
38
+ *
39
+ * @example
40
+ * extractTaskMentions("Completed TASK-4, started TASK-5")
41
+ * // Returns: ["TASK-4", "TASK-5"]
42
+ */
43
+ export function extractTaskMentions(text) {
44
+ if (!text || typeof text !== 'string') {
45
+ return [];
46
+ }
47
+ // Match TASK-XXX pattern (case insensitive)
48
+ const taskPattern = /TASK-(\d+)/gi;
49
+ const matches = text.matchAll(taskPattern);
50
+ // Extract unique task IDs, normalize to uppercase
51
+ const taskIds = new Set();
52
+ for (const match of matches) {
53
+ const taskId = `TASK-${match[1]}`;
54
+ taskIds.add(taskId);
55
+ }
56
+ return Array.from(taskIds);
57
+ }
58
+ /**
59
+ * Check if text mentions any tasks
60
+ *
61
+ * @param text - Event description or any text content
62
+ * @returns True if text contains at least one task mention
63
+ */
64
+ export function hasTasksMentions(text) {
65
+ return extractTaskMentions(text).length > 0;
66
+ }
67
+ /**
68
+ * Calculate task hotness score based on recent activity
69
+ *
70
+ * Hotness formula:
71
+ * - Events in last 4h: Weight 3x
72
+ * - Events in last 24h: Weight 2x
73
+ * - Events in last 7d: Weight 1x
74
+ *
75
+ * @param events - Array of event timestamps (ISO format)
76
+ * @param now - Current timestamp (for testing)
77
+ * @returns Hotness score (0-100)
78
+ *
79
+ * @example
80
+ * calculateHotness([
81
+ * "2025-11-21T10:00:00Z", // 2h ago
82
+ * "2025-11-21T08:00:00Z", // 4h ago
83
+ * "2025-11-20T10:00:00Z" // 1d ago
84
+ * ])
85
+ * // Returns: ~85 (high hotness)
86
+ */
87
+ export function calculateHotness(events, now = new Date()) {
88
+ if (!events || events.length === 0) {
89
+ return 0;
90
+ }
91
+ const nowMs = now.getTime();
92
+ const hour4Ms = 4 * 60 * 60 * 1000;
93
+ const hour24Ms = 24 * 60 * 60 * 1000;
94
+ const day7Ms = 7 * 24 * 60 * 60 * 1000;
95
+ let score = 0;
96
+ for (const event of events) {
97
+ const eventTime = new Date(event.timestamp).getTime();
98
+ const ageMs = nowMs - eventTime;
99
+ if (ageMs < 0) {
100
+ // Future event (clock skew), ignore
101
+ continue;
102
+ }
103
+ if (ageMs <= hour4Ms) {
104
+ score += 30; // Very recent: 3x weight
105
+ }
106
+ else if (ageMs <= hour24Ms) {
107
+ score += 20; // Recent: 2x weight
108
+ }
109
+ else if (ageMs <= day7Ms) {
110
+ score += 10; // This week: 1x weight
111
+ }
112
+ // Events older than 7 days don't contribute
113
+ }
114
+ // Cap at 100
115
+ return Math.min(score, 100);
116
+ }
117
+ /**
118
+ * Classify hotness level for UI display
119
+ *
120
+ * @param hotness - Hotness score (0-100)
121
+ * @returns Level classification
122
+ */
123
+ export function getHotnessLevel(hotness) {
124
+ if (hotness === 0)
125
+ return 'cold';
126
+ if (hotness < 30)
127
+ return 'warm';
128
+ if (hotness < 70)
129
+ return 'hot';
130
+ return 'blazing';
131
+ }
132
+ //# sourceMappingURL=event-task-linker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-task-linker.js","sourceRoot":"","sources":["../../src/lib/event-task-linker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,4CAA4C;IAC5C,MAAM,WAAW,GAAG,cAAc,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAE3C,kDAAkD;IAClD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAoC,EACpC,MAAY,IAAI,IAAI,EAAE;IAEtB,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACnC,MAAM,QAAQ,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACrC,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAEvC,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC;QAEhC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,oCAAoC;YACpC,SAAS;QACX,CAAC;QAED,IAAI,KAAK,IAAI,OAAO,EAAE,CAAC;YACrB,KAAK,IAAI,EAAE,CAAC,CAAC,yBAAyB;QACxC,CAAC;aAAM,IAAI,KAAK,IAAI,QAAQ,EAAE,CAAC;YAC7B,KAAK,IAAI,EAAE,CAAC,CAAC,oBAAoB;QACnC,CAAC;aAAM,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,KAAK,IAAI,EAAE,CAAC,CAAC,uBAAuB;QACtC,CAAC;QACD,4CAA4C;IAC9C,CAAC;IAED,aAAa;IACb,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,IAAI,OAAO,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IACjC,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,MAAM,CAAC;IAChC,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,KAAK,CAAC;IAC/B,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,182 @@
1
+ /**
2
+ * @fileType: utility
3
+ * @status: current
4
+ * @updated: 2025-11-24
5
+ * @tags: [output, formatting, human-ux, ai-ux, task-11, dual-output]
6
+ * @related: [start-reflection.ts, context-loader-events.ts]
7
+ * @priority: high
8
+ * @complexity: medium
9
+ * @dependencies: [chalk]
10
+ */
11
+ /**
12
+ * Session output containing both human and AI formats
13
+ */
14
+ export interface SessionOutput {
15
+ /** Concise console display for humans (6-8 lines) */
16
+ humanOutput: string;
17
+ /** Rich structured data for AI context */
18
+ aiContext: AISessionContext;
19
+ }
20
+ /**
21
+ * Full AI context structure - everything the AI needs to understand the session
22
+ */
23
+ export interface AISessionContext {
24
+ /** Session metadata */
25
+ session: {
26
+ id: string;
27
+ branch: string;
28
+ startedAt: string;
29
+ flowScore: number;
30
+ flowState: string;
31
+ workMode: string;
32
+ };
33
+ /** Project charter (mission, goals, success criteria) */
34
+ charter?: {
35
+ purpose: string;
36
+ goals: string[];
37
+ successCriteria: string[];
38
+ scope?: {
39
+ inScope: string[];
40
+ outOfScope: string[];
41
+ tbd: string[];
42
+ };
43
+ };
44
+ /** Team activity from last 7 days */
45
+ teamActivity?: {
46
+ decisions: Array<{
47
+ user: string;
48
+ description: string;
49
+ timestamp: string;
50
+ impact?: string;
51
+ }>;
52
+ achievements: Array<{
53
+ user: string;
54
+ description: string;
55
+ timestamp: string;
56
+ }>;
57
+ };
58
+ /** Relevant patterns and gotchas */
59
+ patterns?: Array<{
60
+ title: string;
61
+ content: string;
62
+ tags: string[];
63
+ category?: string;
64
+ }>;
65
+ /** Current sprint context */
66
+ sprint?: {
67
+ id: string;
68
+ name: string;
69
+ goal: string;
70
+ progress: number;
71
+ currentTask?: {
72
+ id: string;
73
+ title: string;
74
+ status: string;
75
+ files: string[];
76
+ priority: string;
77
+ /** ADR constraints this task must follow (EPIC-002 Phase 1) */
78
+ constraints?: Array<{
79
+ adr: {
80
+ id: string;
81
+ title: string;
82
+ summary?: string;
83
+ };
84
+ source: string;
85
+ }>;
86
+ };
87
+ tasks: Array<{
88
+ id: string;
89
+ title: string;
90
+ status: 'pending' | 'in_progress' | 'completed';
91
+ }>;
92
+ };
93
+ /** Recent work synthesis */
94
+ synthesis?: {
95
+ completedWork: string[];
96
+ inProgressWork: string[];
97
+ blockedItems: string[];
98
+ keyDecisions: string[];
99
+ gotchas: string[];
100
+ resumePoint?: string;
101
+ nextAction?: string;
102
+ suggestedCommand?: string;
103
+ };
104
+ /** Git status */
105
+ git: {
106
+ branch: string;
107
+ commitsAhead: number;
108
+ uncommittedChanges: {
109
+ modified: string[];
110
+ created: string[];
111
+ untracked: string[];
112
+ };
113
+ warnings: string[];
114
+ };
115
+ /** Context loading metrics */
116
+ metrics: {
117
+ eventsLoaded: number;
118
+ documentsLoaded: number;
119
+ tokenEstimate: number;
120
+ tokenReduction?: string;
121
+ loadTimeMs: number;
122
+ };
123
+ }
124
+ /**
125
+ * Configuration for output formatting
126
+ */
127
+ export interface OutputConfig {
128
+ /** Show verbose AI context in console */
129
+ verbose?: boolean;
130
+ /** Minimal output for quick starts */
131
+ minimal?: boolean;
132
+ /** Disable colors (for piping) */
133
+ noColor?: boolean;
134
+ /** Work mode affects output verbosity */
135
+ workMode?: 'hack-ship' | 'think-build' | 'full-planning';
136
+ }
137
+ /**
138
+ * Format session output for both human and AI consumers
139
+ *
140
+ * Human output is concise (6-8 lines), AI context is rich (full JSON)
141
+ */
142
+ export declare function formatSessionOutput(context: AISessionContext, config?: OutputConfig): SessionOutput;
143
+ /**
144
+ * Format concise human-readable output (≤20 lines, typically 6-12)
145
+ *
146
+ * Focuses on: Flow state, resume point, sprint, branch, warnings, next action
147
+ * Format matches CLAUDE.md specification (TASK-P2)
148
+ */
149
+ export declare function formatHumanOutput(context: AISessionContext, config?: OutputConfig): string;
150
+ /**
151
+ * Format verbose output showing AI context (for debugging)
152
+ */
153
+ export declare function formatVerboseOutput(context: AISessionContext): string;
154
+ /**
155
+ * Format AI context as JSON for file storage or MCP
156
+ */
157
+ export declare function formatAIContextJSON(context: AISessionContext): string;
158
+ /**
159
+ * Format AI context as compact JSONL (for append-only logs)
160
+ */
161
+ export declare function formatAIContextJSONL(context: AISessionContext): string;
162
+ /**
163
+ * Format list of items with optional limit
164
+ */
165
+ export declare function formatList(items: string[], options?: {
166
+ limit?: number;
167
+ prefix?: string;
168
+ emptyMessage?: string;
169
+ }): string;
170
+ /**
171
+ * Format success message
172
+ */
173
+ export declare function formatSuccess(message: string, details?: string): string;
174
+ /**
175
+ * Format error message
176
+ */
177
+ export declare function formatError(message: string, details?: string): string;
178
+ /**
179
+ * Format warning message
180
+ */
181
+ export declare function formatWarning(message: string): string;
182
+ //# sourceMappingURL=output-formatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-formatter.d.ts","sourceRoot":"","sources":["../../src/lib/output-formatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,263 @@
1
+ /**
2
+ * @fileType: utility
3
+ * @status: current
4
+ * @updated: 2025-11-24
5
+ * @tags: [output, formatting, human-ux, ai-ux, task-11, dual-output]
6
+ * @related: [start-reflection.ts, context-loader-events.ts]
7
+ * @priority: high
8
+ * @complexity: medium
9
+ * @dependencies: [chalk]
10
+ */
11
+ /**
12
+ * Dual Output Formatter (TASK-11)
13
+ *
14
+ * Separates human-readable console output from AI-optimized context data.
15
+ *
16
+ * Key Principles (AI-UX):
17
+ * 1. Human output: Concise, scannable, action-oriented (6-8 lines max)
18
+ * 2. AI context: Rich, structured, complete (JSON with full details)
19
+ * 3. Both outputs reflect the SAME underlying data
20
+ * 4. --verbose flag shows AI context in console for debugging
21
+ */
22
+ import chalk from 'chalk';
23
+ /**
24
+ * Format session output for both human and AI consumers
25
+ *
26
+ * Human output is concise (6-8 lines), AI context is rich (full JSON)
27
+ */
28
+ export function formatSessionOutput(context, config = {}) {
29
+ const humanOutput = formatHumanOutput(context, config);
30
+ const aiContext = context;
31
+ return { humanOutput, aiContext };
32
+ }
33
+ /**
34
+ * Format concise human-readable output (≤20 lines, typically 6-12)
35
+ *
36
+ * Focuses on: Flow state, resume point, sprint, branch, warnings, next action
37
+ * Format matches CLAUDE.md specification (TASK-P2)
38
+ */
39
+ export function formatHumanOutput(context, config = {}) {
40
+ const lines = [];
41
+ const { minimal = false } = config;
42
+ // Line 1: Ready | Flow State | Work Mode
43
+ const flowState = getFlowStateLabel(context.session.flowScore);
44
+ lines.push(chalk.green('Ready') + chalk.dim(' | ') +
45
+ chalk.cyan(`${flowState} (${context.session.flowScore}/10)`) + chalk.dim(' | ') +
46
+ chalk.white(`${context.session.workMode} mode`));
47
+ // Line 2: Resume point (most important!)
48
+ if (context.synthesis?.resumePoint) {
49
+ const resumeShort = truncate(context.synthesis.resumePoint, 80);
50
+ lines.push(chalk.white('Resume: ') + chalk.dim(resumeShort));
51
+ }
52
+ // Empty line for spacing
53
+ lines.push('');
54
+ // Line 3-4: Sprint + Branch context
55
+ if (context.sprint && !minimal) {
56
+ const progress = typeof context.sprint.progress === 'number' ? context.sprint.progress : 0;
57
+ const sprintName = context.sprint.name || 'Active Sprint';
58
+ const progressDisplay = progress >= 100
59
+ ? chalk.green('Completed (100%)')
60
+ : chalk.dim(`(${progress}%)`);
61
+ lines.push(chalk.white('Sprint: ') + chalk.cyan(sprintName) + ' ' + progressDisplay);
62
+ // Show current task if available
63
+ if (context.sprint.currentTask) {
64
+ lines.push(chalk.dim(' [@] ') + chalk.yellow(`${context.sprint.currentTask.id}: ${context.sprint.currentTask.title}`));
65
+ // Show ADR constraints for current task (EPIC-002 Phase 1)
66
+ if (context.sprint.currentTask.constraints && context.sprint.currentTask.constraints.length > 0) {
67
+ const adrList = context.sprint.currentTask.constraints
68
+ .map(c => c.adr.id)
69
+ .join(', ');
70
+ lines.push(chalk.dim(' Follow: ') + chalk.magenta(adrList));
71
+ }
72
+ }
73
+ }
74
+ // Branch + uncommitted files count
75
+ const totalChanges = context.git.uncommittedChanges.modified.length +
76
+ context.git.uncommittedChanges.created.length +
77
+ context.git.uncommittedChanges.untracked.length;
78
+ if (totalChanges > 0) {
79
+ lines.push(chalk.white('Branch: ') +
80
+ chalk.cyan(context.git.branch) +
81
+ chalk.dim(` (${totalChanges} uncommitted files)`));
82
+ }
83
+ else {
84
+ lines.push(chalk.white('Branch: ') + chalk.cyan(context.git.branch) + chalk.dim(' (clean)'));
85
+ }
86
+ // Warnings section (1-2 key warnings)
87
+ const warnings = [];
88
+ // Priority 1: Blockers
89
+ if (context.synthesis?.blockedItems && context.synthesis.blockedItems.length > 0) {
90
+ warnings.push(chalk.red(`āš ļø Blocked: ${truncate(context.synthesis.blockedItems[0], 70)}`));
91
+ }
92
+ // Priority 2: Git warnings
93
+ if (context.git.warnings.length > 0 && warnings.length < 2) {
94
+ warnings.push(chalk.yellow(`āš ļø ${context.git.warnings[0]}`));
95
+ }
96
+ // Show warnings with spacing if any
97
+ if (warnings.length > 0) {
98
+ lines.push('');
99
+ warnings.forEach(w => lines.push(w));
100
+ }
101
+ // Next action / ready message
102
+ lines.push('');
103
+ if (context.synthesis?.nextAction) {
104
+ lines.push(chalk.white('Next: ') + chalk.dim(context.synthesis.nextAction));
105
+ }
106
+ else {
107
+ lines.push(chalk.white('Next: ') + chalk.dim('What would you like to work on?'));
108
+ }
109
+ return lines.join('\n');
110
+ }
111
+ /**
112
+ * Format verbose output showing AI context (for debugging)
113
+ */
114
+ export function formatVerboseOutput(context) {
115
+ const lines = [];
116
+ lines.push(chalk.cyan.bold('\nšŸ“Š AI Context (--verbose mode)\n'));
117
+ // Charter
118
+ if (context.charter) {
119
+ lines.push(chalk.yellow('šŸ“œ Charter:'));
120
+ lines.push(` Purpose: ${truncate(context.charter.purpose, 100)}`);
121
+ lines.push(` Goals: ${context.charter.goals.length} defined`);
122
+ lines.push('');
123
+ }
124
+ // Team Activity
125
+ if (context.teamActivity) {
126
+ const decisions = context.teamActivity.decisions?.length || 0;
127
+ const achievements = context.teamActivity.achievements?.length || 0;
128
+ lines.push(chalk.yellow('šŸ‘„ Team Activity (7d):'));
129
+ lines.push(` Decisions: ${decisions}, Achievements: ${achievements}`);
130
+ lines.push('');
131
+ }
132
+ // Patterns
133
+ if (context.patterns && context.patterns.length > 0) {
134
+ lines.push(chalk.yellow('🧠 Patterns:'));
135
+ for (const pattern of context.patterns.slice(0, 3)) {
136
+ lines.push(` - ${pattern.title} [${pattern.tags.join(', ')}]`);
137
+ }
138
+ lines.push('');
139
+ }
140
+ // Synthesis Details
141
+ if (context.synthesis) {
142
+ lines.push(chalk.yellow('šŸ“ Synthesis:'));
143
+ lines.push(` Completed: ${context.synthesis.completedWork?.length || 0} items`);
144
+ lines.push(` In Progress: ${context.synthesis.inProgressWork?.length || 0} items`);
145
+ lines.push(` Decisions: ${context.synthesis.keyDecisions?.length || 0}`);
146
+ lines.push(` Gotchas: ${context.synthesis.gotchas?.length || 0}`);
147
+ lines.push('');
148
+ }
149
+ // Metrics
150
+ lines.push(chalk.yellow('šŸ“ˆ Metrics:'));
151
+ lines.push(` Events: ${context.metrics.eventsLoaded}`);
152
+ lines.push(` Documents: ${context.metrics.documentsLoaded}`);
153
+ lines.push(` Tokens: ${context.metrics.tokenEstimate.toLocaleString()}`);
154
+ if (context.metrics.tokenReduction) {
155
+ lines.push(` Reduction: ${context.metrics.tokenReduction}`);
156
+ }
157
+ lines.push(` Load time: ${context.metrics.loadTimeMs}ms`);
158
+ return lines.join('\n');
159
+ }
160
+ /**
161
+ * Format AI context as JSON for file storage or MCP
162
+ */
163
+ export function formatAIContextJSON(context) {
164
+ return JSON.stringify(context, null, 2);
165
+ }
166
+ /**
167
+ * Format AI context as compact JSONL (for append-only logs)
168
+ */
169
+ export function formatAIContextJSONL(context) {
170
+ return JSON.stringify({
171
+ timestamp: new Date().toISOString(),
172
+ type: 'session-context',
173
+ ...context,
174
+ });
175
+ }
176
+ // ============================================================================
177
+ // Helper Functions
178
+ // ============================================================================
179
+ /**
180
+ * Get flow state emoji based on score
181
+ */
182
+ function getFlowEmoji(score) {
183
+ if (score >= 8)
184
+ return 'šŸ”„'; // Hot
185
+ if (score >= 6)
186
+ return '⚔'; // Energized
187
+ if (score >= 4)
188
+ return '🌊'; // Flowing
189
+ if (score >= 2)
190
+ return '🌱'; // Growing
191
+ return 'ā„ļø'; // Cold
192
+ }
193
+ /**
194
+ * Get flow state label based on score (for concise output)
195
+ */
196
+ function getFlowStateLabel(score) {
197
+ if (score >= 8)
198
+ return 'Hot';
199
+ if (score >= 6)
200
+ return 'Warm';
201
+ if (score >= 4)
202
+ return 'Flowing';
203
+ if (score >= 2)
204
+ return 'Warming Up';
205
+ return 'Cold';
206
+ }
207
+ /**
208
+ * Format a progress bar
209
+ */
210
+ function formatProgressBar(progress, width = 20) {
211
+ const filled = Math.round((progress / 100) * width);
212
+ const empty = width - filled;
213
+ return chalk.green('ā–ˆ'.repeat(filled)) + chalk.dim('ā–‘'.repeat(empty));
214
+ }
215
+ /**
216
+ * Truncate string with ellipsis
217
+ */
218
+ function truncate(str, maxLength) {
219
+ if (str.length <= maxLength)
220
+ return str;
221
+ return str.substring(0, maxLength - 3) + '...';
222
+ }
223
+ /**
224
+ * Format list of items with optional limit
225
+ */
226
+ export function formatList(items, options = {}) {
227
+ const { limit = 5, prefix = ' - ', emptyMessage = '(none)' } = options;
228
+ if (items.length === 0)
229
+ return emptyMessage;
230
+ const displayed = items.slice(0, limit);
231
+ const lines = displayed.map((item) => `${prefix}${item}`);
232
+ if (items.length > limit) {
233
+ lines.push(chalk.dim(`${prefix}... and ${items.length - limit} more`));
234
+ }
235
+ return lines.join('\n');
236
+ }
237
+ /**
238
+ * Format success message
239
+ */
240
+ export function formatSuccess(message, details) {
241
+ let output = chalk.green(`āœ“ ${message}`);
242
+ if (details) {
243
+ output += '\n' + chalk.dim(` ${details}`);
244
+ }
245
+ return output;
246
+ }
247
+ /**
248
+ * Format error message
249
+ */
250
+ export function formatError(message, details) {
251
+ let output = chalk.red(`āœ— ${message}`);
252
+ if (details) {
253
+ output += '\n' + chalk.dim(` ${details}`);
254
+ }
255
+ return output;
256
+ }
257
+ /**
258
+ * Format warning message
259
+ */
260
+ export function formatWarning(message) {
261
+ return chalk.yellow(`āš ļø ${message}`);
262
+ }
263
+ //# sourceMappingURL=output-formatter.js.map