@ginkoai/cli 1.6.2 → 1.7.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 (224) hide show
  1. package/dist/commands/agent/agent-client.d.ts +150 -0
  2. package/dist/commands/agent/agent-client.d.ts.map +1 -0
  3. package/dist/commands/agent/agent-client.js +170 -0
  4. package/dist/commands/agent/agent-client.js.map +1 -0
  5. package/dist/commands/agent/index.d.ts +22 -0
  6. package/dist/commands/agent/index.d.ts.map +1 -0
  7. package/dist/commands/agent/index.js +121 -0
  8. package/dist/commands/agent/index.js.map +1 -0
  9. package/dist/commands/agent/list.d.ts +22 -0
  10. package/dist/commands/agent/list.d.ts.map +1 -0
  11. package/dist/commands/agent/list.js +119 -0
  12. package/dist/commands/agent/list.js.map +1 -0
  13. package/dist/commands/agent/register.d.ts +21 -0
  14. package/dist/commands/agent/register.d.ts.map +1 -0
  15. package/dist/commands/agent/register.js +97 -0
  16. package/dist/commands/agent/register.js.map +1 -0
  17. package/dist/commands/agent/status.d.ts +19 -0
  18. package/dist/commands/agent/status.d.ts.map +1 -0
  19. package/dist/commands/agent/status.js +271 -0
  20. package/dist/commands/agent/status.js.map +1 -0
  21. package/dist/commands/agent/work.d.ts +22 -0
  22. package/dist/commands/agent/work.d.ts.map +1 -0
  23. package/dist/commands/agent/work.js +459 -0
  24. package/dist/commands/agent/work.js.map +1 -0
  25. package/dist/commands/checkpoint/create.d.ts +27 -0
  26. package/dist/commands/checkpoint/create.d.ts.map +1 -0
  27. package/dist/commands/checkpoint/create.js +82 -0
  28. package/dist/commands/checkpoint/create.js.map +1 -0
  29. package/dist/commands/checkpoint/index.d.ts +23 -0
  30. package/dist/commands/checkpoint/index.d.ts.map +1 -0
  31. package/dist/commands/checkpoint/index.js +91 -0
  32. package/dist/commands/checkpoint/index.js.map +1 -0
  33. package/dist/commands/checkpoint/list.d.ts +27 -0
  34. package/dist/commands/checkpoint/list.d.ts.map +1 -0
  35. package/dist/commands/checkpoint/list.js +115 -0
  36. package/dist/commands/checkpoint/list.js.map +1 -0
  37. package/dist/commands/checkpoint/show.d.ts +23 -0
  38. package/dist/commands/checkpoint/show.d.ts.map +1 -0
  39. package/dist/commands/checkpoint/show.js +102 -0
  40. package/dist/commands/checkpoint/show.js.map +1 -0
  41. package/dist/commands/dlq.d.ts +24 -0
  42. package/dist/commands/dlq.d.ts.map +1 -0
  43. package/dist/commands/dlq.js +172 -0
  44. package/dist/commands/dlq.js.map +1 -0
  45. package/dist/commands/escalation/create.d.ts +22 -0
  46. package/dist/commands/escalation/create.d.ts.map +1 -0
  47. package/dist/commands/escalation/create.js +122 -0
  48. package/dist/commands/escalation/create.js.map +1 -0
  49. package/dist/commands/escalation/escalation-client.d.ts +101 -0
  50. package/dist/commands/escalation/escalation-client.d.ts.map +1 -0
  51. package/dist/commands/escalation/escalation-client.js +129 -0
  52. package/dist/commands/escalation/escalation-client.js.map +1 -0
  53. package/dist/commands/escalation/index.d.ts +22 -0
  54. package/dist/commands/escalation/index.d.ts.map +1 -0
  55. package/dist/commands/escalation/index.js +94 -0
  56. package/dist/commands/escalation/index.js.map +1 -0
  57. package/dist/commands/escalation/list.d.ts +24 -0
  58. package/dist/commands/escalation/list.d.ts.map +1 -0
  59. package/dist/commands/escalation/list.js +170 -0
  60. package/dist/commands/escalation/list.js.map +1 -0
  61. package/dist/commands/escalation/resolve.d.ts +20 -0
  62. package/dist/commands/escalation/resolve.d.ts.map +1 -0
  63. package/dist/commands/escalation/resolve.js +102 -0
  64. package/dist/commands/escalation/resolve.js.map +1 -0
  65. package/dist/commands/graph/api-client.d.ts +21 -1
  66. package/dist/commands/graph/api-client.d.ts.map +1 -1
  67. package/dist/commands/graph/api-client.js +23 -0
  68. package/dist/commands/graph/api-client.js.map +1 -1
  69. package/dist/commands/handoff.d.ts.map +1 -1
  70. package/dist/commands/handoff.js +9 -1
  71. package/dist/commands/handoff.js.map +1 -1
  72. package/dist/commands/log.d.ts +3 -0
  73. package/dist/commands/log.d.ts.map +1 -1
  74. package/dist/commands/log.js +73 -14
  75. package/dist/commands/log.js.map +1 -1
  76. package/dist/commands/notifications/history.d.ts +21 -0
  77. package/dist/commands/notifications/history.d.ts.map +1 -0
  78. package/dist/commands/notifications/history.js +160 -0
  79. package/dist/commands/notifications/history.js.map +1 -0
  80. package/dist/commands/notifications/index.d.ts +22 -0
  81. package/dist/commands/notifications/index.d.ts.map +1 -0
  82. package/dist/commands/notifications/index.js +87 -0
  83. package/dist/commands/notifications/index.js.map +1 -0
  84. package/dist/commands/notifications/list.d.ts +19 -0
  85. package/dist/commands/notifications/list.d.ts.map +1 -0
  86. package/dist/commands/notifications/list.js +132 -0
  87. package/dist/commands/notifications/list.js.map +1 -0
  88. package/dist/commands/notifications/test.d.ts +19 -0
  89. package/dist/commands/notifications/test.d.ts.map +1 -0
  90. package/dist/commands/notifications/test.js +217 -0
  91. package/dist/commands/notifications/test.js.map +1 -0
  92. package/dist/commands/orchestrate.d.ts +25 -0
  93. package/dist/commands/orchestrate.d.ts.map +1 -0
  94. package/dist/commands/orchestrate.js +858 -0
  95. package/dist/commands/orchestrate.js.map +1 -0
  96. package/dist/commands/sprint/deps.d.ts +29 -0
  97. package/dist/commands/sprint/deps.d.ts.map +1 -0
  98. package/dist/commands/sprint/deps.js +269 -0
  99. package/dist/commands/sprint/deps.js.map +1 -0
  100. package/dist/commands/sprint/index.d.ts +10 -5
  101. package/dist/commands/sprint/index.d.ts.map +1 -1
  102. package/dist/commands/sprint/index.js +26 -5
  103. package/dist/commands/sprint/index.js.map +1 -1
  104. package/dist/commands/start/index.d.ts.map +1 -1
  105. package/dist/commands/start/index.js +6 -0
  106. package/dist/commands/start/index.js.map +1 -1
  107. package/dist/commands/start/start-reflection.d.ts.map +1 -1
  108. package/dist/commands/start/start-reflection.js +8 -0
  109. package/dist/commands/start/start-reflection.js.map +1 -1
  110. package/dist/commands/verify.d.ts +17 -0
  111. package/dist/commands/verify.d.ts.map +1 -0
  112. package/dist/commands/verify.js +232 -0
  113. package/dist/commands/verify.js.map +1 -0
  114. package/dist/core/session-log-manager.d.ts +1 -1
  115. package/dist/core/session-log-manager.d.ts.map +1 -1
  116. package/dist/index.js +78 -1
  117. package/dist/index.js.map +1 -1
  118. package/dist/lib/__tests__/task-timeout.test.d.ts +12 -0
  119. package/dist/lib/__tests__/task-timeout.test.d.ts.map +1 -0
  120. package/dist/lib/__tests__/task-timeout.test.js +278 -0
  121. package/dist/lib/__tests__/task-timeout.test.js.map +1 -0
  122. package/dist/lib/agent-heartbeat.d.ts +68 -0
  123. package/dist/lib/agent-heartbeat.d.ts.map +1 -0
  124. package/dist/lib/agent-heartbeat.js +117 -0
  125. package/dist/lib/agent-heartbeat.js.map +1 -0
  126. package/dist/lib/checkpoint.d.ts +85 -0
  127. package/dist/lib/checkpoint.d.ts.map +1 -0
  128. package/dist/lib/checkpoint.js +323 -0
  129. package/dist/lib/checkpoint.js.map +1 -0
  130. package/dist/lib/context-metrics.d.ts +230 -0
  131. package/dist/lib/context-metrics.d.ts.map +1 -0
  132. package/dist/lib/context-metrics.js +372 -0
  133. package/dist/lib/context-metrics.js.map +1 -0
  134. package/dist/lib/dead-letter-queue.d.ts +108 -0
  135. package/dist/lib/dead-letter-queue.d.ts.map +1 -0
  136. package/dist/lib/dead-letter-queue.js +378 -0
  137. package/dist/lib/dead-letter-queue.js.map +1 -0
  138. package/dist/lib/event-logger.d.ts +9 -1
  139. package/dist/lib/event-logger.d.ts.map +1 -1
  140. package/dist/lib/event-logger.js +45 -3
  141. package/dist/lib/event-logger.js.map +1 -1
  142. package/dist/lib/event-queue.d.ts.map +1 -1
  143. package/dist/lib/event-queue.js +13 -2
  144. package/dist/lib/event-queue.js.map +1 -1
  145. package/dist/lib/examples/timeout-demo.d.ts +13 -0
  146. package/dist/lib/examples/timeout-demo.d.ts.map +1 -0
  147. package/dist/lib/examples/timeout-demo.js +102 -0
  148. package/dist/lib/examples/timeout-demo.js.map +1 -0
  149. package/dist/lib/examples/timeout-integration-example.d.ts +17 -0
  150. package/dist/lib/examples/timeout-integration-example.d.ts.map +1 -0
  151. package/dist/lib/examples/timeout-integration-example.js +223 -0
  152. package/dist/lib/examples/timeout-integration-example.js.map +1 -0
  153. package/dist/lib/notification-hooks.d.ts +103 -0
  154. package/dist/lib/notification-hooks.d.ts.map +1 -0
  155. package/dist/lib/notification-hooks.js +223 -0
  156. package/dist/lib/notification-hooks.js.map +1 -0
  157. package/dist/lib/notifications/discord.d.ts +20 -0
  158. package/dist/lib/notifications/discord.d.ts.map +1 -0
  159. package/dist/lib/notifications/discord.js +140 -0
  160. package/dist/lib/notifications/discord.js.map +1 -0
  161. package/dist/lib/notifications/index.d.ts +66 -0
  162. package/dist/lib/notifications/index.d.ts.map +1 -0
  163. package/dist/lib/notifications/index.js +120 -0
  164. package/dist/lib/notifications/index.js.map +1 -0
  165. package/dist/lib/notifications/slack.d.ts +20 -0
  166. package/dist/lib/notifications/slack.d.ts.map +1 -0
  167. package/dist/lib/notifications/slack.js +186 -0
  168. package/dist/lib/notifications/slack.js.map +1 -0
  169. package/dist/lib/notifications/teams.d.ts +20 -0
  170. package/dist/lib/notifications/teams.d.ts.map +1 -0
  171. package/dist/lib/notifications/teams.js +146 -0
  172. package/dist/lib/notifications/teams.js.map +1 -0
  173. package/dist/lib/notifications/webhook.d.ts +23 -0
  174. package/dist/lib/notifications/webhook.d.ts.map +1 -0
  175. package/dist/lib/notifications/webhook.js +65 -0
  176. package/dist/lib/notifications/webhook.js.map +1 -0
  177. package/dist/lib/orchestrator-state.d.ts +194 -0
  178. package/dist/lib/orchestrator-state.d.ts.map +1 -0
  179. package/dist/lib/orchestrator-state.js +332 -0
  180. package/dist/lib/orchestrator-state.js.map +1 -0
  181. package/dist/lib/realtime-cursor.d.ts +107 -0
  182. package/dist/lib/realtime-cursor.d.ts.map +1 -0
  183. package/dist/lib/realtime-cursor.js +260 -0
  184. package/dist/lib/realtime-cursor.js.map +1 -0
  185. package/dist/lib/rollback.d.ts +86 -0
  186. package/dist/lib/rollback.d.ts.map +1 -0
  187. package/dist/lib/rollback.js +405 -0
  188. package/dist/lib/rollback.js.map +1 -0
  189. package/dist/lib/sprint-loader.d.ts +39 -2
  190. package/dist/lib/sprint-loader.d.ts.map +1 -1
  191. package/dist/lib/sprint-loader.js +269 -5
  192. package/dist/lib/sprint-loader.js.map +1 -1
  193. package/dist/lib/stale-agent-detector.d.ts +102 -0
  194. package/dist/lib/stale-agent-detector.d.ts.map +1 -0
  195. package/dist/lib/stale-agent-detector.js +156 -0
  196. package/dist/lib/stale-agent-detector.js.map +1 -0
  197. package/dist/lib/task-dependencies.d.ts +143 -0
  198. package/dist/lib/task-dependencies.d.ts.map +1 -0
  199. package/dist/lib/task-dependencies.js +357 -0
  200. package/dist/lib/task-dependencies.js.map +1 -0
  201. package/dist/lib/task-timeout.d.ts +153 -0
  202. package/dist/lib/task-timeout.d.ts.map +1 -0
  203. package/dist/lib/task-timeout.js +505 -0
  204. package/dist/lib/task-timeout.js.map +1 -0
  205. package/dist/lib/verification/build-check.d.ts +55 -0
  206. package/dist/lib/verification/build-check.d.ts.map +1 -0
  207. package/dist/lib/verification/build-check.js +111 -0
  208. package/dist/lib/verification/build-check.js.map +1 -0
  209. package/dist/lib/verification/index.d.ts +19 -0
  210. package/dist/lib/verification/index.d.ts.map +1 -0
  211. package/dist/lib/verification/index.js +17 -0
  212. package/dist/lib/verification/index.js.map +1 -0
  213. package/dist/lib/verification/lint-check.d.ts +34 -0
  214. package/dist/lib/verification/lint-check.d.ts.map +1 -0
  215. package/dist/lib/verification/lint-check.js +215 -0
  216. package/dist/lib/verification/lint-check.js.map +1 -0
  217. package/dist/lib/verification/test-runner.d.ts +50 -0
  218. package/dist/lib/verification/test-runner.d.ts.map +1 -0
  219. package/dist/lib/verification/test-runner.js +225 -0
  220. package/dist/lib/verification/test-runner.js.map +1 -0
  221. package/dist/utils/command-helpers.d.ts.map +1 -1
  222. package/dist/utils/command-helpers.js +7 -0
  223. package/dist/utils/command-helpers.js.map +1 -1
  224. package/package.json +1 -1
@@ -0,0 +1,156 @@
1
+ /**
2
+ * @fileType: utility
3
+ * @status: current
4
+ * @updated: 2025-12-07
5
+ * @tags: [agent, stale-detection, epic-004, multi-agent, resilience]
6
+ * @related: [agent-heartbeat.ts, ../commands/graph/api-client.ts]
7
+ * @priority: high
8
+ * @complexity: medium
9
+ * @dependencies: []
10
+ */
11
+ /**
12
+ * Default configuration
13
+ */
14
+ const DEFAULT_CONFIG = {
15
+ gracePeriodMinutes: 5,
16
+ graphId: process.env.GINKO_GRAPH_ID || '',
17
+ };
18
+ /**
19
+ * Detect stale agents
20
+ *
21
+ * Identifies agents that haven't sent a heartbeat within the grace period
22
+ * and are not already marked as offline.
23
+ *
24
+ * @param config - Optional configuration overrides
25
+ * @returns Array of stale agents with their claimed tasks
26
+ */
27
+ export async function detectStaleAgents(config) {
28
+ const finalConfig = { ...DEFAULT_CONFIG, ...config };
29
+ if (!finalConfig.graphId) {
30
+ throw new Error('Graph ID not configured. Set GINKO_GRAPH_ID environment variable.');
31
+ }
32
+ try {
33
+ // Import graph API client lazily
34
+ const { GraphApiClient } = await import('../commands/graph/api-client.js');
35
+ const client = new GraphApiClient();
36
+ // Call stale detection API
37
+ const response = await client.request('GET', `/api/v1/agent/stale?graphId=${finalConfig.graphId}&gracePeriod=${finalConfig.gracePeriodMinutes}`);
38
+ // Convert to StaleAgent objects
39
+ return response.staleAgents.map((agent) => ({
40
+ agentId: agent.agentId,
41
+ lastHeartbeat: new Date(agent.lastHeartbeat),
42
+ staleSince: new Date(agent.staleSince),
43
+ claimedTasks: agent.claimedTasks,
44
+ }));
45
+ }
46
+ catch (error) {
47
+ const errorMessage = error instanceof Error ? error.message : String(error);
48
+ console.warn('[StaleAgentDetector] Failed to detect stale agents:', errorMessage);
49
+ throw error;
50
+ }
51
+ }
52
+ /**
53
+ * Release tasks claimed by a stale agent
54
+ *
55
+ * Marks agent as offline and releases all tasks it has claimed,
56
+ * making them available for other agents to claim.
57
+ *
58
+ * @param agentId - Agent ID to release tasks for
59
+ * @param config - Optional configuration overrides
60
+ * @returns Array of released tasks
61
+ */
62
+ export async function releaseStaleAgentTasks(agentId, config) {
63
+ const finalConfig = { ...DEFAULT_CONFIG, ...config };
64
+ if (!finalConfig.graphId) {
65
+ throw new Error('Graph ID not configured. Set GINKO_GRAPH_ID environment variable.');
66
+ }
67
+ try {
68
+ // Import graph API client lazily
69
+ const { GraphApiClient } = await import('../commands/graph/api-client.js');
70
+ const client = new GraphApiClient();
71
+ // Call release API
72
+ const response = await client.request('POST', `/api/v1/agent/stale/release?graphId=${finalConfig.graphId}`, { agentId });
73
+ if (!response.success) {
74
+ throw new Error(`Failed to release tasks for agent ${agentId}`);
75
+ }
76
+ // Convert to ReleasedTask objects
77
+ return response.releasedTasks.map((task) => ({
78
+ taskId: task.taskId,
79
+ previousAgent: task.previousAgent,
80
+ releasedAt: new Date(task.releasedAt),
81
+ }));
82
+ }
83
+ catch (error) {
84
+ const errorMessage = error instanceof Error ? error.message : String(error);
85
+ console.warn('[StaleAgentDetector] Failed to release stale agent tasks:', errorMessage);
86
+ throw error;
87
+ }
88
+ }
89
+ /**
90
+ * Get last heartbeat timestamp for an agent
91
+ *
92
+ * @param agentId - Agent ID to check
93
+ * @param config - Optional configuration overrides
94
+ * @returns Last heartbeat timestamp or null if not found
95
+ */
96
+ export async function getAgentLastHeartbeat(agentId, config) {
97
+ const finalConfig = { ...DEFAULT_CONFIG, ...config };
98
+ if (!finalConfig.graphId) {
99
+ throw new Error('Graph ID not configured. Set GINKO_GRAPH_ID environment variable.');
100
+ }
101
+ try {
102
+ // Import graph API client lazily
103
+ const { GraphApiClient } = await import('../commands/graph/api-client.js');
104
+ const client = new GraphApiClient();
105
+ // Call agent details API
106
+ const response = await client.request('GET', `/api/v1/agent/${agentId}?graphId=${finalConfig.graphId}`);
107
+ if (!response.agent.lastHeartbeat) {
108
+ return null;
109
+ }
110
+ return new Date(response.agent.lastHeartbeat);
111
+ }
112
+ catch (error) {
113
+ const errorMessage = error instanceof Error ? error.message : String(error);
114
+ console.warn('[StaleAgentDetector] Failed to get agent heartbeat:', errorMessage);
115
+ // If agent not found, return null instead of throwing
116
+ if (errorMessage.includes('not found') || errorMessage.includes('404')) {
117
+ return null;
118
+ }
119
+ throw error;
120
+ }
121
+ }
122
+ /**
123
+ * Detect and release all stale agents
124
+ *
125
+ * Convenience method that detects stale agents and releases their tasks.
126
+ * Typically used in periodic cleanup jobs.
127
+ *
128
+ * @param config - Optional configuration overrides
129
+ * @returns Summary of stale agents and released tasks
130
+ */
131
+ export async function detectAndReleaseStaleAgents(config) {
132
+ console.log('[StaleAgentDetector] Starting stale agent detection...');
133
+ const staleAgents = await detectStaleAgents(config);
134
+ if (staleAgents.length === 0) {
135
+ console.log('[StaleAgentDetector] No stale agents detected');
136
+ return { staleAgents: [], releasedTasks: [] };
137
+ }
138
+ console.log(`[StaleAgentDetector] Found ${staleAgents.length} stale agent(s)`);
139
+ const allReleasedTasks = [];
140
+ for (const agent of staleAgents) {
141
+ console.log(`[StaleAgentDetector] Releasing tasks for stale agent ${agent.agentId}...`);
142
+ try {
143
+ const releasedTasks = await releaseStaleAgentTasks(agent.agentId, config);
144
+ allReleasedTasks.push(...releasedTasks);
145
+ console.log(`[StaleAgentDetector] Released ${releasedTasks.length} task(s) from ${agent.agentId}`);
146
+ }
147
+ catch (error) {
148
+ const errorMessage = error instanceof Error ? error.message : String(error);
149
+ console.error(`[StaleAgentDetector] Failed to release tasks for ${agent.agentId}:`, errorMessage);
150
+ // Continue with other agents even if one fails
151
+ }
152
+ }
153
+ console.log(`[StaleAgentDetector] Complete: ${staleAgents.length} stale agent(s), ${allReleasedTasks.length} task(s) released`);
154
+ return { staleAgents, releasedTasks: allReleasedTasks };
155
+ }
156
+ //# sourceMappingURL=stale-agent-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stale-agent-detector.js","sourceRoot":"","sources":["../../src/lib/stale-agent-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * @fileType: utility
3
+ * @status: current
4
+ * @updated: 2025-12-07
5
+ * @tags: [task, dependencies, topological-sort, orchestration, epic-004, sprint-4]
6
+ * @related: [sprint-loader.ts, ../commands/orchestrate.ts, ../commands/sprint/deps.ts]
7
+ * @priority: high
8
+ * @complexity: medium
9
+ * @dependencies: []
10
+ */
11
+ /**
12
+ * Task Dependencies (EPIC-004 Sprint 4 TASK-6)
13
+ *
14
+ * Provides topological ordering and dependency management for tasks:
15
+ * - Compute execution order in waves (parallel execution within waves)
16
+ * - Detect circular dependencies
17
+ * - Find available tasks (dependencies satisfied)
18
+ * - Validate dependency graph
19
+ */
20
+ /**
21
+ * Task with dependency information
22
+ */
23
+ export interface Task {
24
+ id: string;
25
+ dependsOn: string[];
26
+ status?: 'pending' | 'in_progress' | 'complete' | 'blocked';
27
+ title?: string;
28
+ effort?: string;
29
+ priority?: number;
30
+ }
31
+ /**
32
+ * Group of tasks that can execute in parallel
33
+ */
34
+ export interface ExecutionWave {
35
+ wave: number;
36
+ tasks: Task[];
37
+ }
38
+ /**
39
+ * Dependency validation error
40
+ */
41
+ export interface DependencyError {
42
+ type: 'missing' | 'self_reference' | 'circular';
43
+ taskId: string;
44
+ details: string;
45
+ }
46
+ /**
47
+ * Dependency graph statistics
48
+ */
49
+ export interface DependencyStats {
50
+ totalTasks: number;
51
+ tasksWithDeps: number;
52
+ tasksWithoutDeps: number;
53
+ maxDepth: number;
54
+ avgDepsPerTask: number;
55
+ }
56
+ /**
57
+ * Detect circular dependencies using three-color DFS
58
+ *
59
+ * Colors:
60
+ * - white (0): Not visited
61
+ * - gray (1): Currently in recursion stack (visiting)
62
+ * - black (2): Fully processed
63
+ *
64
+ * A back edge to a gray node indicates a cycle.
65
+ *
66
+ * @param tasks - Array of tasks with dependencies
67
+ * @returns Array of cycles found, e.g., [['TASK-1', 'TASK-2', 'TASK-1']]
68
+ */
69
+ export declare function detectCircularDependencies(tasks: Task[]): string[][];
70
+ /**
71
+ * Validate dependencies for common errors
72
+ *
73
+ * Checks for:
74
+ * - Self-referential dependencies (TASK-1 depends on TASK-1)
75
+ * - Missing dependencies (depends on non-existent task)
76
+ *
77
+ * @param tasks - Array of tasks with dependencies
78
+ * @returns Array of validation errors
79
+ */
80
+ export declare function validateDependencies(tasks: Task[]): DependencyError[];
81
+ /**
82
+ * Compute execution order using modified Kahn's algorithm
83
+ *
84
+ * Groups tasks into waves where:
85
+ * - Wave 1: Tasks with no dependencies
86
+ * - Wave N: Tasks that depend only on tasks in waves 1..N-1
87
+ *
88
+ * Tasks within a wave can execute in parallel.
89
+ *
90
+ * @param tasks - Array of tasks with dependencies
91
+ * @returns Array of execution waves
92
+ * @throws Error if circular dependencies detected
93
+ */
94
+ export declare function getExecutionOrder(tasks: Task[]): ExecutionWave[];
95
+ /**
96
+ * Get tasks that are ready to execute
97
+ *
98
+ * A task is available if:
99
+ * - Status is 'pending' or undefined
100
+ * - All dependencies have status 'complete'
101
+ *
102
+ * @param tasks - Array of tasks with dependencies and status
103
+ * @returns Tasks ready for execution
104
+ */
105
+ export declare function getAvailableTasks(tasks: Task[]): Task[];
106
+ /**
107
+ * Get tasks that are blocked (have incomplete dependencies)
108
+ *
109
+ * @param tasks - Array of tasks with dependencies and status
110
+ * @returns Tasks that cannot start due to incomplete dependencies
111
+ */
112
+ export declare function getBlockedTasks(tasks: Task[]): Task[];
113
+ /**
114
+ * Compute dependency graph statistics
115
+ *
116
+ * @param tasks - Array of tasks with dependencies
117
+ * @returns Statistics about the dependency graph
118
+ */
119
+ export declare function getDependencyStats(tasks: Task[]): DependencyStats;
120
+ /**
121
+ * Build reverse dependency map (who depends on this task)
122
+ *
123
+ * @param tasks - Array of tasks with dependencies
124
+ * @returns Map from task ID to array of dependent task IDs
125
+ */
126
+ export declare function buildReverseDependencyMap(tasks: Task[]): Map<string, string[]>;
127
+ /**
128
+ * Get all downstream tasks (tasks that transitively depend on this task)
129
+ *
130
+ * @param taskId - Starting task ID
131
+ * @param tasks - Array of tasks with dependencies
132
+ * @returns Array of downstream task IDs
133
+ */
134
+ export declare function getDownstreamTasks(taskId: string, tasks: Task[]): string[];
135
+ /**
136
+ * Get all upstream tasks (tasks that this task transitively depends on)
137
+ *
138
+ * @param taskId - Starting task ID
139
+ * @param tasks - Array of tasks with dependencies
140
+ * @returns Array of upstream task IDs
141
+ */
142
+ export declare function getUpstreamTasks(taskId: string, tasks: Task[]): string[];
143
+ //# sourceMappingURL=task-dependencies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-dependencies.d.ts","sourceRoot":"","sources":["../../src/lib/task-dependencies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,SAAS,GAAG,aAAa,GAAG,UAAU,GAAG,SAAS,CAAC;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,GAAG,gBAAgB,GAAG,UAAU,CAAC;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,EAAE,CAiDpE;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,eAAe,EAAE,CAsCrE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,aAAa,EAAE,CA2EhE;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAuBvD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAsBrD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,eAAe,CA+BjE;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAmB9E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAkB1E;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAwBxE"}
@@ -0,0 +1,357 @@
1
+ /**
2
+ * @fileType: utility
3
+ * @status: current
4
+ * @updated: 2025-12-07
5
+ * @tags: [task, dependencies, topological-sort, orchestration, epic-004, sprint-4]
6
+ * @related: [sprint-loader.ts, ../commands/orchestrate.ts, ../commands/sprint/deps.ts]
7
+ * @priority: high
8
+ * @complexity: medium
9
+ * @dependencies: []
10
+ */
11
+ /**
12
+ * Detect circular dependencies using three-color DFS
13
+ *
14
+ * Colors:
15
+ * - white (0): Not visited
16
+ * - gray (1): Currently in recursion stack (visiting)
17
+ * - black (2): Fully processed
18
+ *
19
+ * A back edge to a gray node indicates a cycle.
20
+ *
21
+ * @param tasks - Array of tasks with dependencies
22
+ * @returns Array of cycles found, e.g., [['TASK-1', 'TASK-2', 'TASK-1']]
23
+ */
24
+ export function detectCircularDependencies(tasks) {
25
+ const cycles = [];
26
+ const taskMap = new Map();
27
+ const color = new Map(); // 0=white, 1=gray, 2=black
28
+ // Build task map
29
+ for (const task of tasks) {
30
+ taskMap.set(task.id, task);
31
+ color.set(task.id, 0); // white
32
+ }
33
+ function dfs(taskId, path) {
34
+ const task = taskMap.get(taskId);
35
+ if (!task)
36
+ return false;
37
+ color.set(taskId, 1); // gray - visiting
38
+ path.push(taskId);
39
+ for (const depId of task.dependsOn) {
40
+ const depColor = color.get(depId);
41
+ if (depColor === 1) {
42
+ // Back edge found - cycle detected
43
+ const cycleStart = path.indexOf(depId);
44
+ const cycle = [...path.slice(cycleStart), depId];
45
+ cycles.push(cycle);
46
+ return true;
47
+ }
48
+ if (depColor === 0) {
49
+ // White node - recurse
50
+ dfs(depId, path);
51
+ }
52
+ // Black nodes are fully processed, skip
53
+ }
54
+ path.pop();
55
+ color.set(taskId, 2); // black - done
56
+ return false;
57
+ }
58
+ // Run DFS from each unvisited node
59
+ for (const task of tasks) {
60
+ if (color.get(task.id) === 0) {
61
+ dfs(task.id, []);
62
+ }
63
+ }
64
+ return cycles;
65
+ }
66
+ /**
67
+ * Validate dependencies for common errors
68
+ *
69
+ * Checks for:
70
+ * - Self-referential dependencies (TASK-1 depends on TASK-1)
71
+ * - Missing dependencies (depends on non-existent task)
72
+ *
73
+ * @param tasks - Array of tasks with dependencies
74
+ * @returns Array of validation errors
75
+ */
76
+ export function validateDependencies(tasks) {
77
+ const errors = [];
78
+ const taskIds = new Set(tasks.map(t => t.id));
79
+ for (const task of tasks) {
80
+ for (const depId of task.dependsOn) {
81
+ // Self-reference check
82
+ if (depId === task.id) {
83
+ errors.push({
84
+ type: 'self_reference',
85
+ taskId: task.id,
86
+ details: `Task ${task.id} depends on itself`,
87
+ });
88
+ continue;
89
+ }
90
+ // Missing dependency check
91
+ if (!taskIds.has(depId)) {
92
+ errors.push({
93
+ type: 'missing',
94
+ taskId: task.id,
95
+ details: `Task ${task.id} depends on non-existent task ${depId}`,
96
+ });
97
+ }
98
+ }
99
+ }
100
+ // Also check for circular dependencies
101
+ const cycles = detectCircularDependencies(tasks);
102
+ for (const cycle of cycles) {
103
+ errors.push({
104
+ type: 'circular',
105
+ taskId: cycle[0],
106
+ details: `Circular dependency: ${cycle.join(' → ')}`,
107
+ });
108
+ }
109
+ return errors;
110
+ }
111
+ /**
112
+ * Compute execution order using modified Kahn's algorithm
113
+ *
114
+ * Groups tasks into waves where:
115
+ * - Wave 1: Tasks with no dependencies
116
+ * - Wave N: Tasks that depend only on tasks in waves 1..N-1
117
+ *
118
+ * Tasks within a wave can execute in parallel.
119
+ *
120
+ * @param tasks - Array of tasks with dependencies
121
+ * @returns Array of execution waves
122
+ * @throws Error if circular dependencies detected
123
+ */
124
+ export function getExecutionOrder(tasks) {
125
+ if (tasks.length === 0) {
126
+ return [];
127
+ }
128
+ // Validate first
129
+ const errors = validateDependencies(tasks);
130
+ const circularErrors = errors.filter(e => e.type === 'circular');
131
+ if (circularErrors.length > 0) {
132
+ throw new Error(`Cannot compute execution order: ${circularErrors[0].details}`);
133
+ }
134
+ const taskMap = new Map();
135
+ const inDegree = new Map();
136
+ const dependents = new Map(); // task -> tasks that depend on it
137
+ // Initialize
138
+ for (const task of tasks) {
139
+ taskMap.set(task.id, task);
140
+ inDegree.set(task.id, 0);
141
+ dependents.set(task.id, []);
142
+ }
143
+ // Build in-degree and dependents
144
+ for (const task of tasks) {
145
+ let validDeps = 0;
146
+ for (const depId of task.dependsOn) {
147
+ if (taskMap.has(depId)) {
148
+ validDeps++;
149
+ dependents.get(depId).push(task.id);
150
+ }
151
+ // Skip missing dependencies (already warned in validation)
152
+ }
153
+ inDegree.set(task.id, validDeps);
154
+ }
155
+ const waves = [];
156
+ const processed = new Set();
157
+ // Process waves until all tasks are assigned
158
+ while (processed.size < tasks.length) {
159
+ // Find all tasks with in-degree 0 that haven't been processed
160
+ const waveTasks = [];
161
+ for (const task of tasks) {
162
+ if (!processed.has(task.id) && inDegree.get(task.id) === 0) {
163
+ waveTasks.push(task);
164
+ }
165
+ }
166
+ if (waveTasks.length === 0) {
167
+ // This shouldn't happen if validation passed, but safety check
168
+ const remaining = tasks.filter(t => !processed.has(t.id));
169
+ throw new Error(`Cannot assign remaining tasks to waves: ${remaining.map(t => t.id).join(', ')}`);
170
+ }
171
+ // Add wave
172
+ waves.push({
173
+ wave: waves.length + 1,
174
+ tasks: waveTasks,
175
+ });
176
+ // Mark as processed and update in-degrees
177
+ for (const task of waveTasks) {
178
+ processed.add(task.id);
179
+ // Decrement in-degree of all dependents
180
+ for (const depId of dependents.get(task.id)) {
181
+ const currentDegree = inDegree.get(depId);
182
+ inDegree.set(depId, currentDegree - 1);
183
+ }
184
+ }
185
+ }
186
+ return waves;
187
+ }
188
+ /**
189
+ * Get tasks that are ready to execute
190
+ *
191
+ * A task is available if:
192
+ * - Status is 'pending' or undefined
193
+ * - All dependencies have status 'complete'
194
+ *
195
+ * @param tasks - Array of tasks with dependencies and status
196
+ * @returns Tasks ready for execution
197
+ */
198
+ export function getAvailableTasks(tasks) {
199
+ const taskMap = new Map();
200
+ for (const task of tasks) {
201
+ taskMap.set(task.id, task);
202
+ }
203
+ return tasks.filter(task => {
204
+ // Must be pending (not started, not complete, not blocked)
205
+ const status = task.status || 'pending';
206
+ if (status !== 'pending') {
207
+ return false;
208
+ }
209
+ // All dependencies must be complete
210
+ for (const depId of task.dependsOn) {
211
+ const dep = taskMap.get(depId);
212
+ if (!dep || dep.status !== 'complete') {
213
+ return false;
214
+ }
215
+ }
216
+ return true;
217
+ });
218
+ }
219
+ /**
220
+ * Get tasks that are blocked (have incomplete dependencies)
221
+ *
222
+ * @param tasks - Array of tasks with dependencies and status
223
+ * @returns Tasks that cannot start due to incomplete dependencies
224
+ */
225
+ export function getBlockedTasks(tasks) {
226
+ const taskMap = new Map();
227
+ for (const task of tasks) {
228
+ taskMap.set(task.id, task);
229
+ }
230
+ return tasks.filter(task => {
231
+ const status = task.status || 'pending';
232
+ if (status !== 'pending') {
233
+ return false;
234
+ }
235
+ // Check if any dependency is incomplete
236
+ for (const depId of task.dependsOn) {
237
+ const dep = taskMap.get(depId);
238
+ if (!dep || dep.status !== 'complete') {
239
+ return true; // Blocked
240
+ }
241
+ }
242
+ return false; // All deps complete, not blocked
243
+ });
244
+ }
245
+ /**
246
+ * Compute dependency graph statistics
247
+ *
248
+ * @param tasks - Array of tasks with dependencies
249
+ * @returns Statistics about the dependency graph
250
+ */
251
+ export function getDependencyStats(tasks) {
252
+ if (tasks.length === 0) {
253
+ return {
254
+ totalTasks: 0,
255
+ tasksWithDeps: 0,
256
+ tasksWithoutDeps: 0,
257
+ maxDepth: 0,
258
+ avgDepsPerTask: 0,
259
+ };
260
+ }
261
+ const tasksWithDeps = tasks.filter(t => t.dependsOn.length > 0).length;
262
+ const totalDeps = tasks.reduce((sum, t) => sum + t.dependsOn.length, 0);
263
+ // Compute max depth (longest path in DAG)
264
+ let maxDepth = 0;
265
+ try {
266
+ const waves = getExecutionOrder(tasks);
267
+ maxDepth = waves.length;
268
+ }
269
+ catch {
270
+ // Circular dependency - can't compute depth
271
+ maxDepth = -1;
272
+ }
273
+ return {
274
+ totalTasks: tasks.length,
275
+ tasksWithDeps,
276
+ tasksWithoutDeps: tasks.length - tasksWithDeps,
277
+ maxDepth,
278
+ avgDepsPerTask: totalDeps / tasks.length,
279
+ };
280
+ }
281
+ /**
282
+ * Build reverse dependency map (who depends on this task)
283
+ *
284
+ * @param tasks - Array of tasks with dependencies
285
+ * @returns Map from task ID to array of dependent task IDs
286
+ */
287
+ export function buildReverseDependencyMap(tasks) {
288
+ const reverseMap = new Map();
289
+ // Initialize all tasks
290
+ for (const task of tasks) {
291
+ reverseMap.set(task.id, []);
292
+ }
293
+ // Build reverse relationships
294
+ for (const task of tasks) {
295
+ for (const depId of task.dependsOn) {
296
+ const dependents = reverseMap.get(depId);
297
+ if (dependents) {
298
+ dependents.push(task.id);
299
+ }
300
+ }
301
+ }
302
+ return reverseMap;
303
+ }
304
+ /**
305
+ * Get all downstream tasks (tasks that transitively depend on this task)
306
+ *
307
+ * @param taskId - Starting task ID
308
+ * @param tasks - Array of tasks with dependencies
309
+ * @returns Array of downstream task IDs
310
+ */
311
+ export function getDownstreamTasks(taskId, tasks) {
312
+ const reverseMap = buildReverseDependencyMap(tasks);
313
+ const visited = new Set();
314
+ const result = [];
315
+ function dfs(id) {
316
+ const dependents = reverseMap.get(id) || [];
317
+ for (const depId of dependents) {
318
+ if (!visited.has(depId)) {
319
+ visited.add(depId);
320
+ result.push(depId);
321
+ dfs(depId);
322
+ }
323
+ }
324
+ }
325
+ dfs(taskId);
326
+ return result;
327
+ }
328
+ /**
329
+ * Get all upstream tasks (tasks that this task transitively depends on)
330
+ *
331
+ * @param taskId - Starting task ID
332
+ * @param tasks - Array of tasks with dependencies
333
+ * @returns Array of upstream task IDs
334
+ */
335
+ export function getUpstreamTasks(taskId, tasks) {
336
+ const taskMap = new Map();
337
+ for (const task of tasks) {
338
+ taskMap.set(task.id, task);
339
+ }
340
+ const visited = new Set();
341
+ const result = [];
342
+ function dfs(id) {
343
+ const task = taskMap.get(id);
344
+ if (!task)
345
+ return;
346
+ for (const depId of task.dependsOn) {
347
+ if (!visited.has(depId)) {
348
+ visited.add(depId);
349
+ result.push(depId);
350
+ dfs(depId);
351
+ }
352
+ }
353
+ }
354
+ dfs(taskId);
355
+ return result;
356
+ }
357
+ //# sourceMappingURL=task-dependencies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-dependencies.js","sourceRoot":"","sources":["../../src/lib/task-dependencies.ts"],"names":[],"mappings":"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"}