@renseiai/agentfactory 0.8.0

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 (246) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -0
  3. package/dist/src/config/index.d.ts +3 -0
  4. package/dist/src/config/index.d.ts.map +1 -0
  5. package/dist/src/config/index.js +1 -0
  6. package/dist/src/config/repository-config.d.ts +44 -0
  7. package/dist/src/config/repository-config.d.ts.map +1 -0
  8. package/dist/src/config/repository-config.js +88 -0
  9. package/dist/src/config/repository-config.test.d.ts +2 -0
  10. package/dist/src/config/repository-config.test.d.ts.map +1 -0
  11. package/dist/src/config/repository-config.test.js +249 -0
  12. package/dist/src/deployment/deployment-checker.d.ts +110 -0
  13. package/dist/src/deployment/deployment-checker.d.ts.map +1 -0
  14. package/dist/src/deployment/deployment-checker.js +242 -0
  15. package/dist/src/deployment/index.d.ts +3 -0
  16. package/dist/src/deployment/index.d.ts.map +1 -0
  17. package/dist/src/deployment/index.js +2 -0
  18. package/dist/src/frontend/index.d.ts +2 -0
  19. package/dist/src/frontend/index.d.ts.map +1 -0
  20. package/dist/src/frontend/index.js +1 -0
  21. package/dist/src/frontend/types.d.ts +106 -0
  22. package/dist/src/frontend/types.d.ts.map +1 -0
  23. package/dist/src/frontend/types.js +11 -0
  24. package/dist/src/governor/decision-engine.d.ts +52 -0
  25. package/dist/src/governor/decision-engine.d.ts.map +1 -0
  26. package/dist/src/governor/decision-engine.js +220 -0
  27. package/dist/src/governor/decision-engine.test.d.ts +2 -0
  28. package/dist/src/governor/decision-engine.test.d.ts.map +1 -0
  29. package/dist/src/governor/decision-engine.test.js +629 -0
  30. package/dist/src/governor/event-bus.d.ts +43 -0
  31. package/dist/src/governor/event-bus.d.ts.map +1 -0
  32. package/dist/src/governor/event-bus.js +8 -0
  33. package/dist/src/governor/event-deduplicator.d.ts +43 -0
  34. package/dist/src/governor/event-deduplicator.d.ts.map +1 -0
  35. package/dist/src/governor/event-deduplicator.js +53 -0
  36. package/dist/src/governor/event-driven-governor.d.ts +131 -0
  37. package/dist/src/governor/event-driven-governor.d.ts.map +1 -0
  38. package/dist/src/governor/event-driven-governor.js +379 -0
  39. package/dist/src/governor/event-driven-governor.test.d.ts +2 -0
  40. package/dist/src/governor/event-driven-governor.test.d.ts.map +1 -0
  41. package/dist/src/governor/event-driven-governor.test.js +673 -0
  42. package/dist/src/governor/event-types.d.ts +78 -0
  43. package/dist/src/governor/event-types.d.ts.map +1 -0
  44. package/dist/src/governor/event-types.js +32 -0
  45. package/dist/src/governor/governor-types.d.ts +82 -0
  46. package/dist/src/governor/governor-types.d.ts.map +1 -0
  47. package/dist/src/governor/governor-types.js +21 -0
  48. package/dist/src/governor/governor.d.ts +100 -0
  49. package/dist/src/governor/governor.d.ts.map +1 -0
  50. package/dist/src/governor/governor.js +262 -0
  51. package/dist/src/governor/governor.test.d.ts +2 -0
  52. package/dist/src/governor/governor.test.d.ts.map +1 -0
  53. package/dist/src/governor/governor.test.js +514 -0
  54. package/dist/src/governor/human-touchpoints.d.ts +131 -0
  55. package/dist/src/governor/human-touchpoints.d.ts.map +1 -0
  56. package/dist/src/governor/human-touchpoints.js +251 -0
  57. package/dist/src/governor/human-touchpoints.test.d.ts +2 -0
  58. package/dist/src/governor/human-touchpoints.test.d.ts.map +1 -0
  59. package/dist/src/governor/human-touchpoints.test.js +366 -0
  60. package/dist/src/governor/in-memory-event-bus.d.ts +29 -0
  61. package/dist/src/governor/in-memory-event-bus.d.ts.map +1 -0
  62. package/dist/src/governor/in-memory-event-bus.js +79 -0
  63. package/dist/src/governor/index.d.ts +14 -0
  64. package/dist/src/governor/index.d.ts.map +1 -0
  65. package/dist/src/governor/index.js +13 -0
  66. package/dist/src/governor/override-parser.d.ts +60 -0
  67. package/dist/src/governor/override-parser.d.ts.map +1 -0
  68. package/dist/src/governor/override-parser.js +98 -0
  69. package/dist/src/governor/override-parser.test.d.ts +2 -0
  70. package/dist/src/governor/override-parser.test.d.ts.map +1 -0
  71. package/dist/src/governor/override-parser.test.js +312 -0
  72. package/dist/src/governor/platform-adapter.d.ts +69 -0
  73. package/dist/src/governor/platform-adapter.d.ts.map +1 -0
  74. package/dist/src/governor/platform-adapter.js +11 -0
  75. package/dist/src/governor/processing-state.d.ts +66 -0
  76. package/dist/src/governor/processing-state.d.ts.map +1 -0
  77. package/dist/src/governor/processing-state.js +43 -0
  78. package/dist/src/governor/processing-state.test.d.ts +2 -0
  79. package/dist/src/governor/processing-state.test.d.ts.map +1 -0
  80. package/dist/src/governor/processing-state.test.js +96 -0
  81. package/dist/src/governor/top-of-funnel.d.ts +118 -0
  82. package/dist/src/governor/top-of-funnel.d.ts.map +1 -0
  83. package/dist/src/governor/top-of-funnel.js +168 -0
  84. package/dist/src/governor/top-of-funnel.test.d.ts +2 -0
  85. package/dist/src/governor/top-of-funnel.test.d.ts.map +1 -0
  86. package/dist/src/governor/top-of-funnel.test.js +331 -0
  87. package/dist/src/index.d.ts +11 -0
  88. package/dist/src/index.d.ts.map +1 -0
  89. package/dist/src/index.js +10 -0
  90. package/dist/src/linear-cli.d.ts +38 -0
  91. package/dist/src/linear-cli.d.ts.map +1 -0
  92. package/dist/src/linear-cli.js +674 -0
  93. package/dist/src/logger.d.ts +117 -0
  94. package/dist/src/logger.d.ts.map +1 -0
  95. package/dist/src/logger.js +430 -0
  96. package/dist/src/manifest/generate.d.ts +20 -0
  97. package/dist/src/manifest/generate.d.ts.map +1 -0
  98. package/dist/src/manifest/generate.js +65 -0
  99. package/dist/src/manifest/index.d.ts +4 -0
  100. package/dist/src/manifest/index.d.ts.map +1 -0
  101. package/dist/src/manifest/index.js +2 -0
  102. package/dist/src/manifest/route-manifest.d.ts +34 -0
  103. package/dist/src/manifest/route-manifest.d.ts.map +1 -0
  104. package/dist/src/manifest/route-manifest.js +148 -0
  105. package/dist/src/orchestrator/activity-emitter.d.ts +119 -0
  106. package/dist/src/orchestrator/activity-emitter.d.ts.map +1 -0
  107. package/dist/src/orchestrator/activity-emitter.js +306 -0
  108. package/dist/src/orchestrator/api-activity-emitter.d.ts +167 -0
  109. package/dist/src/orchestrator/api-activity-emitter.d.ts.map +1 -0
  110. package/dist/src/orchestrator/api-activity-emitter.js +417 -0
  111. package/dist/src/orchestrator/heartbeat-writer.d.ts +57 -0
  112. package/dist/src/orchestrator/heartbeat-writer.d.ts.map +1 -0
  113. package/dist/src/orchestrator/heartbeat-writer.js +137 -0
  114. package/dist/src/orchestrator/index.d.ts +20 -0
  115. package/dist/src/orchestrator/index.d.ts.map +1 -0
  116. package/dist/src/orchestrator/index.js +22 -0
  117. package/dist/src/orchestrator/log-analyzer.d.ts +160 -0
  118. package/dist/src/orchestrator/log-analyzer.d.ts.map +1 -0
  119. package/dist/src/orchestrator/log-analyzer.js +572 -0
  120. package/dist/src/orchestrator/log-config.d.ts +39 -0
  121. package/dist/src/orchestrator/log-config.d.ts.map +1 -0
  122. package/dist/src/orchestrator/log-config.js +45 -0
  123. package/dist/src/orchestrator/orchestrator.d.ts +316 -0
  124. package/dist/src/orchestrator/orchestrator.d.ts.map +1 -0
  125. package/dist/src/orchestrator/orchestrator.js +3290 -0
  126. package/dist/src/orchestrator/parse-work-result.d.ts +16 -0
  127. package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -0
  128. package/dist/src/orchestrator/parse-work-result.js +135 -0
  129. package/dist/src/orchestrator/parse-work-result.test.d.ts +2 -0
  130. package/dist/src/orchestrator/parse-work-result.test.d.ts.map +1 -0
  131. package/dist/src/orchestrator/parse-work-result.test.js +234 -0
  132. package/dist/src/orchestrator/progress-logger.d.ts +72 -0
  133. package/dist/src/orchestrator/progress-logger.d.ts.map +1 -0
  134. package/dist/src/orchestrator/progress-logger.js +135 -0
  135. package/dist/src/orchestrator/session-logger.d.ts +159 -0
  136. package/dist/src/orchestrator/session-logger.d.ts.map +1 -0
  137. package/dist/src/orchestrator/session-logger.js +275 -0
  138. package/dist/src/orchestrator/state-recovery.d.ts +96 -0
  139. package/dist/src/orchestrator/state-recovery.d.ts.map +1 -0
  140. package/dist/src/orchestrator/state-recovery.js +302 -0
  141. package/dist/src/orchestrator/state-types.d.ts +165 -0
  142. package/dist/src/orchestrator/state-types.d.ts.map +1 -0
  143. package/dist/src/orchestrator/state-types.js +7 -0
  144. package/dist/src/orchestrator/stream-parser.d.ts +151 -0
  145. package/dist/src/orchestrator/stream-parser.d.ts.map +1 -0
  146. package/dist/src/orchestrator/stream-parser.js +137 -0
  147. package/dist/src/orchestrator/types.d.ts +232 -0
  148. package/dist/src/orchestrator/types.d.ts.map +1 -0
  149. package/dist/src/orchestrator/types.js +4 -0
  150. package/dist/src/orchestrator/validate-git-remote.test.d.ts +2 -0
  151. package/dist/src/orchestrator/validate-git-remote.test.d.ts.map +1 -0
  152. package/dist/src/orchestrator/validate-git-remote.test.js +61 -0
  153. package/dist/src/providers/a2a-auth.d.ts +81 -0
  154. package/dist/src/providers/a2a-auth.d.ts.map +1 -0
  155. package/dist/src/providers/a2a-auth.js +188 -0
  156. package/dist/src/providers/a2a-auth.test.d.ts +2 -0
  157. package/dist/src/providers/a2a-auth.test.d.ts.map +1 -0
  158. package/dist/src/providers/a2a-auth.test.js +232 -0
  159. package/dist/src/providers/a2a-provider.d.ts +254 -0
  160. package/dist/src/providers/a2a-provider.d.ts.map +1 -0
  161. package/dist/src/providers/a2a-provider.integration.test.d.ts +9 -0
  162. package/dist/src/providers/a2a-provider.integration.test.d.ts.map +1 -0
  163. package/dist/src/providers/a2a-provider.integration.test.js +665 -0
  164. package/dist/src/providers/a2a-provider.js +811 -0
  165. package/dist/src/providers/a2a-provider.test.d.ts +2 -0
  166. package/dist/src/providers/a2a-provider.test.d.ts.map +1 -0
  167. package/dist/src/providers/a2a-provider.test.js +681 -0
  168. package/dist/src/providers/amp-provider.d.ts +20 -0
  169. package/dist/src/providers/amp-provider.d.ts.map +1 -0
  170. package/dist/src/providers/amp-provider.js +24 -0
  171. package/dist/src/providers/claude-provider.d.ts +18 -0
  172. package/dist/src/providers/claude-provider.d.ts.map +1 -0
  173. package/dist/src/providers/claude-provider.js +437 -0
  174. package/dist/src/providers/codex-provider.d.ts +133 -0
  175. package/dist/src/providers/codex-provider.d.ts.map +1 -0
  176. package/dist/src/providers/codex-provider.js +381 -0
  177. package/dist/src/providers/codex-provider.test.d.ts +2 -0
  178. package/dist/src/providers/codex-provider.test.d.ts.map +1 -0
  179. package/dist/src/providers/codex-provider.test.js +387 -0
  180. package/dist/src/providers/index.d.ts +44 -0
  181. package/dist/src/providers/index.d.ts.map +1 -0
  182. package/dist/src/providers/index.js +85 -0
  183. package/dist/src/providers/spring-ai-provider.d.ts +90 -0
  184. package/dist/src/providers/spring-ai-provider.d.ts.map +1 -0
  185. package/dist/src/providers/spring-ai-provider.integration.test.d.ts +13 -0
  186. package/dist/src/providers/spring-ai-provider.integration.test.d.ts.map +1 -0
  187. package/dist/src/providers/spring-ai-provider.integration.test.js +351 -0
  188. package/dist/src/providers/spring-ai-provider.js +317 -0
  189. package/dist/src/providers/spring-ai-provider.test.d.ts +2 -0
  190. package/dist/src/providers/spring-ai-provider.test.d.ts.map +1 -0
  191. package/dist/src/providers/spring-ai-provider.test.js +200 -0
  192. package/dist/src/providers/types.d.ts +165 -0
  193. package/dist/src/providers/types.d.ts.map +1 -0
  194. package/dist/src/providers/types.js +13 -0
  195. package/dist/src/templates/adapters.d.ts +51 -0
  196. package/dist/src/templates/adapters.d.ts.map +1 -0
  197. package/dist/src/templates/adapters.js +104 -0
  198. package/dist/src/templates/adapters.test.d.ts +2 -0
  199. package/dist/src/templates/adapters.test.d.ts.map +1 -0
  200. package/dist/src/templates/adapters.test.js +165 -0
  201. package/dist/src/templates/agent-definition.d.ts +85 -0
  202. package/dist/src/templates/agent-definition.d.ts.map +1 -0
  203. package/dist/src/templates/agent-definition.js +97 -0
  204. package/dist/src/templates/agent-definition.test.d.ts +2 -0
  205. package/dist/src/templates/agent-definition.test.d.ts.map +1 -0
  206. package/dist/src/templates/agent-definition.test.js +209 -0
  207. package/dist/src/templates/index.d.ts +14 -0
  208. package/dist/src/templates/index.d.ts.map +1 -0
  209. package/dist/src/templates/index.js +11 -0
  210. package/dist/src/templates/loader.d.ts +41 -0
  211. package/dist/src/templates/loader.d.ts.map +1 -0
  212. package/dist/src/templates/loader.js +114 -0
  213. package/dist/src/templates/registry.d.ts +80 -0
  214. package/dist/src/templates/registry.d.ts.map +1 -0
  215. package/dist/src/templates/registry.js +177 -0
  216. package/dist/src/templates/registry.test.d.ts +2 -0
  217. package/dist/src/templates/registry.test.d.ts.map +1 -0
  218. package/dist/src/templates/registry.test.js +198 -0
  219. package/dist/src/templates/renderer.d.ts +29 -0
  220. package/dist/src/templates/renderer.d.ts.map +1 -0
  221. package/dist/src/templates/renderer.js +35 -0
  222. package/dist/src/templates/strategy-templates.test.d.ts +2 -0
  223. package/dist/src/templates/strategy-templates.test.d.ts.map +1 -0
  224. package/dist/src/templates/strategy-templates.test.js +619 -0
  225. package/dist/src/templates/types.d.ts +233 -0
  226. package/dist/src/templates/types.d.ts.map +1 -0
  227. package/dist/src/templates/types.js +127 -0
  228. package/dist/src/templates/types.test.d.ts +2 -0
  229. package/dist/src/templates/types.test.d.ts.map +1 -0
  230. package/dist/src/templates/types.test.js +232 -0
  231. package/dist/src/tools/index.d.ts +6 -0
  232. package/dist/src/tools/index.d.ts.map +1 -0
  233. package/dist/src/tools/index.js +3 -0
  234. package/dist/src/tools/linear-runner.d.ts +34 -0
  235. package/dist/src/tools/linear-runner.d.ts.map +1 -0
  236. package/dist/src/tools/linear-runner.js +700 -0
  237. package/dist/src/tools/plugins/linear.d.ts +9 -0
  238. package/dist/src/tools/plugins/linear.d.ts.map +1 -0
  239. package/dist/src/tools/plugins/linear.js +138 -0
  240. package/dist/src/tools/registry.d.ts +9 -0
  241. package/dist/src/tools/registry.d.ts.map +1 -0
  242. package/dist/src/tools/registry.js +18 -0
  243. package/dist/src/tools/types.d.ts +18 -0
  244. package/dist/src/tools/types.d.ts.map +1 -0
  245. package/dist/src/tools/types.js +1 -0
  246. package/package.json +78 -0
@@ -0,0 +1,79 @@
1
+ /**
2
+ * In-Memory Event Bus
3
+ *
4
+ * Simple event bus implementation for testing and single-process CLI usage.
5
+ * Events are stored in a queue and delivered via an async generator.
6
+ */
7
+ // ---------------------------------------------------------------------------
8
+ // InMemoryEventBus
9
+ // ---------------------------------------------------------------------------
10
+ export class InMemoryEventBus {
11
+ queue = [];
12
+ waiters = [];
13
+ closed = false;
14
+ idCounter = 0;
15
+ ackedIds = new Set();
16
+ async publish(event) {
17
+ if (this.closed) {
18
+ throw new Error('Event bus is closed');
19
+ }
20
+ const id = `mem-${++this.idCounter}`;
21
+ const item = { id, event };
22
+ // If a subscriber is waiting, deliver directly
23
+ const waiter = this.waiters.shift();
24
+ if (waiter) {
25
+ waiter(item);
26
+ }
27
+ else {
28
+ this.queue.push(item);
29
+ }
30
+ return id;
31
+ }
32
+ async *subscribe() {
33
+ while (!this.closed) {
34
+ // Try to pull from the queue
35
+ const item = this.queue.shift();
36
+ if (item) {
37
+ yield item;
38
+ continue;
39
+ }
40
+ // Wait for a new event or close
41
+ const next = await new Promise((resolve) => {
42
+ if (this.closed) {
43
+ resolve(null);
44
+ return;
45
+ }
46
+ this.waiters.push(resolve);
47
+ });
48
+ if (next === null) {
49
+ return;
50
+ }
51
+ yield next;
52
+ }
53
+ }
54
+ async ack(eventId) {
55
+ this.ackedIds.add(eventId);
56
+ }
57
+ async close() {
58
+ this.closed = true;
59
+ // Resolve all waiting subscribers with null to end iteration
60
+ for (const waiter of this.waiters) {
61
+ ;
62
+ waiter(null);
63
+ }
64
+ this.waiters = [];
65
+ }
66
+ // ---- Test helpers ----
67
+ /** Check if an event ID has been acknowledged */
68
+ isAcked(eventId) {
69
+ return this.ackedIds.has(eventId);
70
+ }
71
+ /** Get the number of pending (undelivered) events */
72
+ get pendingCount() {
73
+ return this.queue.length;
74
+ }
75
+ /** Check if the bus has been closed */
76
+ get isClosed() {
77
+ return this.closed;
78
+ }
79
+ }
@@ -0,0 +1,14 @@
1
+ export * from './override-parser.js';
2
+ export * from './human-touchpoints.js';
3
+ export * from './top-of-funnel.js';
4
+ export * from './processing-state.js';
5
+ export * from './governor-types.js';
6
+ export * from './decision-engine.js';
7
+ export * from './governor.js';
8
+ export * from './event-types.js';
9
+ export * from './event-bus.js';
10
+ export * from './in-memory-event-bus.js';
11
+ export * from './event-deduplicator.js';
12
+ export * from './event-driven-governor.js';
13
+ export * from './platform-adapter.js';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/governor/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAA;AACpC,cAAc,wBAAwB,CAAA;AACtC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA;AACvC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,uBAAuB,CAAA"}
@@ -0,0 +1,13 @@
1
+ export * from './override-parser.js';
2
+ export * from './human-touchpoints.js';
3
+ export * from './top-of-funnel.js';
4
+ export * from './processing-state.js';
5
+ export * from './governor-types.js';
6
+ export * from './decision-engine.js';
7
+ export * from './governor.js';
8
+ export * from './event-types.js';
9
+ export * from './event-bus.js';
10
+ export * from './in-memory-event-bus.js';
11
+ export * from './event-deduplicator.js';
12
+ export * from './event-driven-governor.js';
13
+ export * from './platform-adapter.js';
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Override Parser Module
3
+ *
4
+ * Parses issue comments for structured human override directives.
5
+ * Directives must appear at the start of a comment (case-insensitive)
6
+ * and only non-bot comments are processed.
7
+ */
8
+ /**
9
+ * Supported override directive types
10
+ */
11
+ export type OverrideDirectiveType = 'hold' | 'resume' | 'skip-qa' | 'decompose' | 'reassign' | 'priority';
12
+ /**
13
+ * Priority levels for the PRIORITY directive
14
+ */
15
+ export type OverridePriority = 'high' | 'medium' | 'low';
16
+ /**
17
+ * A parsed override directive extracted from a comment
18
+ */
19
+ export interface OverrideDirective {
20
+ type: OverrideDirectiveType;
21
+ reason?: string;
22
+ priority?: OverridePriority;
23
+ commentId?: string;
24
+ userId?: string;
25
+ timestamp: number;
26
+ }
27
+ /**
28
+ * Information about a comment to be parsed for directives
29
+ */
30
+ export interface CommentInfo {
31
+ id: string;
32
+ body: string;
33
+ userId: string;
34
+ isBot: boolean;
35
+ createdAt: number;
36
+ }
37
+ /**
38
+ * Parse a single comment for an override directive.
39
+ *
40
+ * Rules:
41
+ * - Bot comments are always ignored (returns null)
42
+ * - Only the first line of the comment is checked for a directive
43
+ * - Directives are case-insensitive
44
+ * - Returns null if no directive is found
45
+ *
46
+ * @param comment - The comment to parse
47
+ * @returns The parsed directive, or null if no directive found
48
+ */
49
+ export declare function parseOverrideDirective(comment: CommentInfo): OverrideDirective | null;
50
+ /**
51
+ * Find the most recent override directive from a list of comments.
52
+ *
53
+ * Parses all comments (skipping bots) and returns the directive with
54
+ * the latest timestamp. If no directive is found, returns null.
55
+ *
56
+ * @param comments - Array of comments to scan, in any order
57
+ * @returns The most recent directive, or null if none found
58
+ */
59
+ export declare function findLatestOverride(comments: CommentInfo[]): OverrideDirective | null;
60
+ //# sourceMappingURL=override-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"override-parser.d.ts","sourceRoot":"","sources":["../../../src/governor/override-parser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAAA;AAEzG;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAA;AAExD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,qBAAqB,CAAA;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,gBAAgB,CAAA;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,OAAO,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB;AAkCD;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,iBAAiB,GAAG,IAAI,CAqCrF;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,iBAAiB,GAAG,IAAI,CAapF"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Override Parser Module
3
+ *
4
+ * Parses issue comments for structured human override directives.
5
+ * Directives must appear at the start of a comment (case-insensitive)
6
+ * and only non-bot comments are processed.
7
+ */
8
+ // ============================================
9
+ // Directive Patterns
10
+ // ============================================
11
+ /**
12
+ * Regex patterns for each directive type.
13
+ * All patterns are anchored to the start of the comment body (after trimming).
14
+ * The HOLD pattern captures an optional reason after a dash or em-dash.
15
+ * The PRIORITY pattern captures the priority level.
16
+ */
17
+ const DIRECTIVE_PATTERNS = [
18
+ // HOLD or HOLD — reason or HOLD - reason
19
+ { type: 'hold', pattern: /^hold(?:\s*[—–-]\s*(.+))?$/i },
20
+ // RESUME
21
+ { type: 'resume', pattern: /^resume$/i },
22
+ // SKIP QA or SKIP-QA
23
+ { type: 'skip-qa', pattern: /^skip[\s-]+qa$/i },
24
+ // DECOMPOSE
25
+ { type: 'decompose', pattern: /^decompose$/i },
26
+ // REASSIGN
27
+ { type: 'reassign', pattern: /^reassign$/i },
28
+ // PRIORITY: high|medium|low
29
+ { type: 'priority', pattern: /^priority:\s*(high|medium|low)$/i },
30
+ ];
31
+ // ============================================
32
+ // Parser Functions
33
+ // ============================================
34
+ /**
35
+ * Parse a single comment for an override directive.
36
+ *
37
+ * Rules:
38
+ * - Bot comments are always ignored (returns null)
39
+ * - Only the first line of the comment is checked for a directive
40
+ * - Directives are case-insensitive
41
+ * - Returns null if no directive is found
42
+ *
43
+ * @param comment - The comment to parse
44
+ * @returns The parsed directive, or null if no directive found
45
+ */
46
+ export function parseOverrideDirective(comment) {
47
+ // Ignore bot comments
48
+ if (comment.isBot) {
49
+ return null;
50
+ }
51
+ // Take the first line of the comment body, trimmed
52
+ const firstLine = comment.body.trim().split('\n')[0]?.trim();
53
+ if (!firstLine) {
54
+ return null;
55
+ }
56
+ for (const { type, pattern } of DIRECTIVE_PATTERNS) {
57
+ const match = firstLine.match(pattern);
58
+ if (match) {
59
+ const directive = {
60
+ type,
61
+ commentId: comment.id,
62
+ userId: comment.userId,
63
+ timestamp: comment.createdAt,
64
+ };
65
+ // Extract reason for HOLD directive
66
+ if (type === 'hold' && match[1]) {
67
+ directive.reason = match[1].trim();
68
+ }
69
+ // Extract priority level for PRIORITY directive
70
+ if (type === 'priority' && match[1]) {
71
+ directive.priority = match[1].toLowerCase();
72
+ }
73
+ return directive;
74
+ }
75
+ }
76
+ return null;
77
+ }
78
+ /**
79
+ * Find the most recent override directive from a list of comments.
80
+ *
81
+ * Parses all comments (skipping bots) and returns the directive with
82
+ * the latest timestamp. If no directive is found, returns null.
83
+ *
84
+ * @param comments - Array of comments to scan, in any order
85
+ * @returns The most recent directive, or null if none found
86
+ */
87
+ export function findLatestOverride(comments) {
88
+ let latest = null;
89
+ for (const comment of comments) {
90
+ const directive = parseOverrideDirective(comment);
91
+ if (directive) {
92
+ if (!latest || directive.timestamp > latest.timestamp) {
93
+ latest = directive;
94
+ }
95
+ }
96
+ }
97
+ return latest;
98
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=override-parser.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"override-parser.test.d.ts","sourceRoot":"","sources":["../../../src/governor/override-parser.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,312 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseOverrideDirective, findLatestOverride } from './override-parser.js';
3
+ // ============================================
4
+ // Helpers
5
+ // ============================================
6
+ function makeComment(overrides = {}) {
7
+ return {
8
+ id: 'comment-1',
9
+ body: '',
10
+ userId: 'user-1',
11
+ isBot: false,
12
+ createdAt: Date.now(),
13
+ ...overrides,
14
+ };
15
+ }
16
+ // ============================================
17
+ // Tests
18
+ // ============================================
19
+ describe('parseOverrideDirective', () => {
20
+ describe('HOLD directive', () => {
21
+ it('parses plain HOLD', () => {
22
+ const comment = makeComment({ body: 'HOLD' });
23
+ const result = parseOverrideDirective(comment);
24
+ expect(result).not.toBeNull();
25
+ expect(result.type).toBe('hold');
26
+ expect(result.reason).toBeUndefined();
27
+ });
28
+ it('parses HOLD with em-dash reason', () => {
29
+ const comment = makeComment({ body: 'HOLD — waiting for design review' });
30
+ const result = parseOverrideDirective(comment);
31
+ expect(result).not.toBeNull();
32
+ expect(result.type).toBe('hold');
33
+ expect(result.reason).toBe('waiting for design review');
34
+ });
35
+ it('parses HOLD with regular dash reason', () => {
36
+ const comment = makeComment({ body: 'HOLD - need more info' });
37
+ const result = parseOverrideDirective(comment);
38
+ expect(result).not.toBeNull();
39
+ expect(result.type).toBe('hold');
40
+ expect(result.reason).toBe('need more info');
41
+ });
42
+ it('parses HOLD with en-dash reason', () => {
43
+ const comment = makeComment({ body: 'HOLD – performance concerns' });
44
+ const result = parseOverrideDirective(comment);
45
+ expect(result).not.toBeNull();
46
+ expect(result.type).toBe('hold');
47
+ expect(result.reason).toBe('performance concerns');
48
+ });
49
+ it('parses HOLD case-insensitively', () => {
50
+ const comment = makeComment({ body: 'hold' });
51
+ const result = parseOverrideDirective(comment);
52
+ expect(result).not.toBeNull();
53
+ expect(result.type).toBe('hold');
54
+ });
55
+ it('parses Hold (mixed case)', () => {
56
+ const comment = makeComment({ body: 'Hold — mixed case' });
57
+ const result = parseOverrideDirective(comment);
58
+ expect(result).not.toBeNull();
59
+ expect(result.type).toBe('hold');
60
+ expect(result.reason).toBe('mixed case');
61
+ });
62
+ });
63
+ describe('RESUME directive', () => {
64
+ it('parses RESUME', () => {
65
+ const comment = makeComment({ body: 'RESUME' });
66
+ const result = parseOverrideDirective(comment);
67
+ expect(result).not.toBeNull();
68
+ expect(result.type).toBe('resume');
69
+ });
70
+ it('parses resume (lowercase)', () => {
71
+ const comment = makeComment({ body: 'resume' });
72
+ const result = parseOverrideDirective(comment);
73
+ expect(result).not.toBeNull();
74
+ expect(result.type).toBe('resume');
75
+ });
76
+ });
77
+ describe('SKIP QA directive', () => {
78
+ it('parses SKIP QA', () => {
79
+ const comment = makeComment({ body: 'SKIP QA' });
80
+ const result = parseOverrideDirective(comment);
81
+ expect(result).not.toBeNull();
82
+ expect(result.type).toBe('skip-qa');
83
+ });
84
+ it('parses skip qa (lowercase)', () => {
85
+ const comment = makeComment({ body: 'skip qa' });
86
+ const result = parseOverrideDirective(comment);
87
+ expect(result).not.toBeNull();
88
+ expect(result.type).toBe('skip-qa');
89
+ });
90
+ it('handles extra whitespace between SKIP and QA', () => {
91
+ const comment = makeComment({ body: 'SKIP QA' });
92
+ const result = parseOverrideDirective(comment);
93
+ expect(result).not.toBeNull();
94
+ expect(result.type).toBe('skip-qa');
95
+ });
96
+ it('parses SKIP-QA (hyphenated)', () => {
97
+ const comment = makeComment({ body: 'SKIP-QA' });
98
+ const result = parseOverrideDirective(comment);
99
+ expect(result).not.toBeNull();
100
+ expect(result.type).toBe('skip-qa');
101
+ });
102
+ it('parses skip-qa (hyphenated lowercase)', () => {
103
+ const comment = makeComment({ body: 'skip-qa' });
104
+ const result = parseOverrideDirective(comment);
105
+ expect(result).not.toBeNull();
106
+ expect(result.type).toBe('skip-qa');
107
+ });
108
+ });
109
+ describe('DECOMPOSE directive', () => {
110
+ it('parses DECOMPOSE', () => {
111
+ const comment = makeComment({ body: 'DECOMPOSE' });
112
+ const result = parseOverrideDirective(comment);
113
+ expect(result).not.toBeNull();
114
+ expect(result.type).toBe('decompose');
115
+ });
116
+ it('parses decompose (lowercase)', () => {
117
+ const comment = makeComment({ body: 'decompose' });
118
+ const result = parseOverrideDirective(comment);
119
+ expect(result).not.toBeNull();
120
+ expect(result.type).toBe('decompose');
121
+ });
122
+ });
123
+ describe('REASSIGN directive', () => {
124
+ it('parses REASSIGN', () => {
125
+ const comment = makeComment({ body: 'REASSIGN' });
126
+ const result = parseOverrideDirective(comment);
127
+ expect(result).not.toBeNull();
128
+ expect(result.type).toBe('reassign');
129
+ });
130
+ it('parses Reassign (mixed case)', () => {
131
+ const comment = makeComment({ body: 'Reassign' });
132
+ const result = parseOverrideDirective(comment);
133
+ expect(result).not.toBeNull();
134
+ expect(result.type).toBe('reassign');
135
+ });
136
+ });
137
+ describe('PRIORITY directive', () => {
138
+ it('parses PRIORITY: high', () => {
139
+ const comment = makeComment({ body: 'PRIORITY: high' });
140
+ const result = parseOverrideDirective(comment);
141
+ expect(result).not.toBeNull();
142
+ expect(result.type).toBe('priority');
143
+ expect(result.priority).toBe('high');
144
+ });
145
+ it('parses PRIORITY: medium', () => {
146
+ const comment = makeComment({ body: 'PRIORITY: medium' });
147
+ const result = parseOverrideDirective(comment);
148
+ expect(result).not.toBeNull();
149
+ expect(result.type).toBe('priority');
150
+ expect(result.priority).toBe('medium');
151
+ });
152
+ it('parses PRIORITY: low', () => {
153
+ const comment = makeComment({ body: 'PRIORITY: low' });
154
+ const result = parseOverrideDirective(comment);
155
+ expect(result).not.toBeNull();
156
+ expect(result.type).toBe('priority');
157
+ expect(result.priority).toBe('low');
158
+ });
159
+ it('parses priority case-insensitively', () => {
160
+ const comment = makeComment({ body: 'priority: HIGH' });
161
+ const result = parseOverrideDirective(comment);
162
+ expect(result).not.toBeNull();
163
+ expect(result.type).toBe('priority');
164
+ expect(result.priority).toBe('high');
165
+ });
166
+ it('handles extra whitespace after colon', () => {
167
+ const comment = makeComment({ body: 'PRIORITY: high' });
168
+ const result = parseOverrideDirective(comment);
169
+ expect(result).not.toBeNull();
170
+ expect(result.type).toBe('priority');
171
+ expect(result.priority).toBe('high');
172
+ });
173
+ });
174
+ describe('bot filtering', () => {
175
+ it('ignores bot comments', () => {
176
+ const comment = makeComment({ body: 'HOLD', isBot: true });
177
+ const result = parseOverrideDirective(comment);
178
+ expect(result).toBeNull();
179
+ });
180
+ it('ignores bot comments for all directive types', () => {
181
+ const directives = ['HOLD', 'RESUME', 'SKIP QA', 'DECOMPOSE', 'REASSIGN', 'PRIORITY: high'];
182
+ for (const body of directives) {
183
+ const comment = makeComment({ body, isBot: true });
184
+ const result = parseOverrideDirective(comment);
185
+ expect(result).toBeNull();
186
+ }
187
+ });
188
+ });
189
+ describe('no directive found', () => {
190
+ it('returns null for empty body', () => {
191
+ const comment = makeComment({ body: '' });
192
+ expect(parseOverrideDirective(comment)).toBeNull();
193
+ });
194
+ it('returns null for regular comment text', () => {
195
+ const comment = makeComment({ body: 'This looks good, nice work!' });
196
+ expect(parseOverrideDirective(comment)).toBeNull();
197
+ });
198
+ it('returns null for directive-like text not at start', () => {
199
+ const comment = makeComment({ body: 'I think we should HOLD on this' });
200
+ expect(parseOverrideDirective(comment)).toBeNull();
201
+ });
202
+ it('returns null for unsupported directives', () => {
203
+ const comment = makeComment({ body: 'CANCEL' });
204
+ expect(parseOverrideDirective(comment)).toBeNull();
205
+ });
206
+ it('returns null for PRIORITY with invalid level', () => {
207
+ const comment = makeComment({ body: 'PRIORITY: urgent' });
208
+ expect(parseOverrideDirective(comment)).toBeNull();
209
+ });
210
+ });
211
+ describe('metadata extraction', () => {
212
+ it('captures commentId and userId', () => {
213
+ const comment = makeComment({
214
+ id: 'comment-42',
215
+ body: 'HOLD',
216
+ userId: 'user-99',
217
+ createdAt: 1700000000000,
218
+ });
219
+ const result = parseOverrideDirective(comment);
220
+ expect(result).not.toBeNull();
221
+ expect(result.commentId).toBe('comment-42');
222
+ expect(result.userId).toBe('user-99');
223
+ expect(result.timestamp).toBe(1700000000000);
224
+ });
225
+ });
226
+ describe('multiline comments', () => {
227
+ it('only checks the first line for directives', () => {
228
+ const comment = makeComment({
229
+ body: 'HOLD — security concern\nThis needs review from the security team.\nPlease check the auth flow.',
230
+ });
231
+ const result = parseOverrideDirective(comment);
232
+ expect(result).not.toBeNull();
233
+ expect(result.type).toBe('hold');
234
+ expect(result.reason).toBe('security concern');
235
+ });
236
+ it('ignores directives on subsequent lines', () => {
237
+ const comment = makeComment({
238
+ body: 'Great work on this!\nHOLD\nNot really.',
239
+ });
240
+ expect(parseOverrideDirective(comment)).toBeNull();
241
+ });
242
+ });
243
+ describe('whitespace handling', () => {
244
+ it('trims leading whitespace from body', () => {
245
+ const comment = makeComment({ body: ' HOLD ' });
246
+ const result = parseOverrideDirective(comment);
247
+ expect(result).not.toBeNull();
248
+ expect(result.type).toBe('hold');
249
+ });
250
+ it('trims whitespace-only body', () => {
251
+ const comment = makeComment({ body: ' ' });
252
+ expect(parseOverrideDirective(comment)).toBeNull();
253
+ });
254
+ });
255
+ });
256
+ describe('findLatestOverride', () => {
257
+ it('returns null for empty comment list', () => {
258
+ expect(findLatestOverride([])).toBeNull();
259
+ });
260
+ it('returns null when no comments contain directives', () => {
261
+ const comments = [
262
+ makeComment({ body: 'Looks good!' }),
263
+ makeComment({ body: 'Nice work' }),
264
+ ];
265
+ expect(findLatestOverride(comments)).toBeNull();
266
+ });
267
+ it('returns the only directive found', () => {
268
+ const comments = [
269
+ makeComment({ body: 'Looks good!', createdAt: 1000 }),
270
+ makeComment({ body: 'HOLD', createdAt: 2000 }),
271
+ makeComment({ body: 'What about tests?', createdAt: 3000 }),
272
+ ];
273
+ const result = findLatestOverride(comments);
274
+ expect(result).not.toBeNull();
275
+ expect(result.type).toBe('hold');
276
+ expect(result.timestamp).toBe(2000);
277
+ });
278
+ it('returns the most recent directive when multiple exist', () => {
279
+ const comments = [
280
+ makeComment({ id: 'c1', body: 'HOLD', createdAt: 1000 }),
281
+ makeComment({ id: 'c2', body: 'RESUME', createdAt: 2000 }),
282
+ makeComment({ id: 'c3', body: 'HOLD — second hold', createdAt: 3000 }),
283
+ ];
284
+ const result = findLatestOverride(comments);
285
+ expect(result).not.toBeNull();
286
+ expect(result.type).toBe('hold');
287
+ expect(result.reason).toBe('second hold');
288
+ expect(result.timestamp).toBe(3000);
289
+ });
290
+ it('returns the most recent directive regardless of order', () => {
291
+ // Comments may be passed in any order
292
+ const comments = [
293
+ makeComment({ id: 'c3', body: 'DECOMPOSE', createdAt: 3000 }),
294
+ makeComment({ id: 'c1', body: 'HOLD', createdAt: 1000 }),
295
+ makeComment({ id: 'c2', body: 'RESUME', createdAt: 5000 }),
296
+ ];
297
+ const result = findLatestOverride(comments);
298
+ expect(result).not.toBeNull();
299
+ expect(result.type).toBe('resume');
300
+ expect(result.timestamp).toBe(5000);
301
+ });
302
+ it('skips bot comments when finding latest', () => {
303
+ const comments = [
304
+ makeComment({ body: 'HOLD', createdAt: 1000, isBot: false }),
305
+ makeComment({ body: 'RESUME', createdAt: 2000, isBot: true }),
306
+ ];
307
+ const result = findLatestOverride(comments);
308
+ expect(result).not.toBeNull();
309
+ expect(result.type).toBe('hold');
310
+ expect(result.timestamp).toBe(1000);
311
+ });
312
+ });
@@ -0,0 +1,69 @@
1
+ /**
2
+ * PlatformAdapter Interface
3
+ *
4
+ * Extends the WorkSchedulingFrontend concept with methods needed by the
5
+ * EventDrivenGovernor to process webhook events and poll project state.
6
+ *
7
+ * Platform adapters (e.g., LinearPlatformAdapter) implement this interface
8
+ * structurally, translating between platform-native concepts and the
9
+ * Governor's abstract event/issue types.
10
+ */
11
+ import type { GovernorEvent } from './event-types.js';
12
+ import type { GovernorIssue } from './governor-types.js';
13
+ /**
14
+ * Adapter that bridges a project-management platform (Linear, Jira, etc.)
15
+ * to the Governor's event-driven processing model.
16
+ *
17
+ * Responsibilities:
18
+ * - Convert platform webhook payloads into GovernorEvents
19
+ * - Scan a project for all non-terminal issues (poll sweep)
20
+ * - Convert platform-native issue objects to GovernorIssue
21
+ * - Detect parent/child issue relationships
22
+ */
23
+ export interface PlatformAdapter {
24
+ /** Human-readable name of the platform (e.g., 'linear', 'jira'). */
25
+ readonly name: string;
26
+ /**
27
+ * Normalize a raw webhook payload from the platform into one or more
28
+ * GovernorEvents.
29
+ *
30
+ * Returns `null` if the payload type is not recognized or not relevant
31
+ * to the Governor (e.g., label-only changes, unrelated resource types).
32
+ *
33
+ * @param payload - Raw webhook payload from the platform
34
+ * @returns Array of GovernorEvents, or null if unrecognized
35
+ */
36
+ normalizeWebhookEvent(payload: unknown): GovernorEvent[] | null;
37
+ /**
38
+ * Fetch all non-terminal issues for the given project.
39
+ *
40
+ * Terminal statuses (e.g., Accepted, Canceled, Duplicate) are excluded
41
+ * so the Governor only evaluates issues that may need action.
42
+ *
43
+ * @param project - Project name to scan
44
+ * @returns Array of GovernorIssue representing active issues
45
+ */
46
+ scanProjectIssues(project: string): Promise<GovernorIssue[]>;
47
+ /**
48
+ * Convert a platform-native issue object to a GovernorIssue.
49
+ *
50
+ * The `native` parameter is typed as `unknown` to keep the interface
51
+ * platform-agnostic. Implementations should cast to the appropriate
52
+ * platform SDK type internally.
53
+ *
54
+ * @param native - Platform-native issue object
55
+ * @returns GovernorIssue representation
56
+ */
57
+ toGovernorIssue(native: unknown): Promise<GovernorIssue>;
58
+ /**
59
+ * Check whether the given issue is a parent issue (has child/sub-issues).
60
+ *
61
+ * Parent issues receive special treatment in the Governor: they are
62
+ * typically managed by a coordinator rather than dispatched directly.
63
+ *
64
+ * @param issueId - Issue ID to check
65
+ * @returns True if the issue has child issues
66
+ */
67
+ isParentIssue(issueId: string): Promise<boolean>;
68
+ }
69
+ //# sourceMappingURL=platform-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-adapter.d.ts","sourceRoot":"","sources":["../../../src/governor/platform-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAMxD;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB;;;;;;;;;OASG;IACH,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,aAAa,EAAE,GAAG,IAAI,CAAA;IAE/D;;;;;;;;OAQG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAA;IAE5D;;;;;;;;;OASG;IACH,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;IAExD;;;;;;;;OAQG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACjD"}