@hivehub/rulebook 3.3.1 → 4.0.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 (162) hide show
  1. package/.claude/commands/continue.md +33 -0
  2. package/.claude-plugin/marketplace.json +28 -29
  3. package/.claude-plugin/plugin.json +8 -8
  4. package/README.md +32 -144
  5. package/dist/agents/ralph-parser.d.ts +44 -5
  6. package/dist/agents/ralph-parser.d.ts.map +1 -1
  7. package/dist/agents/ralph-parser.js +218 -26
  8. package/dist/agents/ralph-parser.js.map +1 -1
  9. package/dist/cli/commands.d.ts +65 -0
  10. package/dist/cli/commands.d.ts.map +1 -1
  11. package/dist/cli/commands.js +841 -175
  12. package/dist/cli/commands.js.map +1 -1
  13. package/dist/core/agent-manager.d.ts +12 -32
  14. package/dist/core/agent-manager.d.ts.map +1 -1
  15. package/dist/core/agent-manager.js +150 -220
  16. package/dist/core/agent-manager.js.map +1 -1
  17. package/dist/core/claude-mcp.d.ts +17 -0
  18. package/dist/core/claude-mcp.d.ts.map +1 -1
  19. package/dist/core/claude-mcp.js +90 -6
  20. package/dist/core/claude-mcp.js.map +1 -1
  21. package/dist/core/cli-bridge.js +1 -1
  22. package/dist/core/cli-bridge.js.map +1 -1
  23. package/dist/core/config-manager.d.ts.map +1 -1
  24. package/dist/core/config-manager.js +40 -0
  25. package/dist/core/config-manager.js.map +1 -1
  26. package/dist/core/cursor-mdc-generator.d.ts +30 -0
  27. package/dist/core/cursor-mdc-generator.d.ts.map +1 -0
  28. package/dist/core/cursor-mdc-generator.js +98 -0
  29. package/dist/core/cursor-mdc-generator.js.map +1 -0
  30. package/dist/core/detector.d.ts +25 -1
  31. package/dist/core/detector.d.ts.map +1 -1
  32. package/dist/core/detector.js +321 -1
  33. package/dist/core/detector.js.map +1 -1
  34. package/dist/core/generator.d.ts +10 -0
  35. package/dist/core/generator.d.ts.map +1 -1
  36. package/dist/core/generator.js +182 -9
  37. package/dist/core/generator.js.map +1 -1
  38. package/dist/core/github-issues-importer.d.ts +82 -0
  39. package/dist/core/github-issues-importer.d.ts.map +1 -0
  40. package/dist/core/github-issues-importer.js +161 -0
  41. package/dist/core/github-issues-importer.js.map +1 -0
  42. package/dist/core/health-scorer.d.ts +39 -0
  43. package/dist/core/health-scorer.d.ts.map +1 -1
  44. package/dist/core/health-scorer.js +256 -13
  45. package/dist/core/health-scorer.js.map +1 -1
  46. package/dist/core/iteration-tracker.d.ts +28 -0
  47. package/dist/core/iteration-tracker.d.ts.map +1 -1
  48. package/dist/core/iteration-tracker.js +86 -0
  49. package/dist/core/iteration-tracker.js.map +1 -1
  50. package/dist/core/logger.js +1 -1
  51. package/dist/core/logger.js.map +1 -1
  52. package/dist/core/migrator.js +1 -1
  53. package/dist/core/migrator.js.map +1 -1
  54. package/dist/core/modern-console.d.ts +1 -2
  55. package/dist/core/modern-console.d.ts.map +1 -1
  56. package/dist/core/modern-console.js +6 -18
  57. package/dist/core/modern-console.js.map +1 -1
  58. package/dist/core/multi-tool-generator.d.ts +59 -0
  59. package/dist/core/multi-tool-generator.d.ts.map +1 -0
  60. package/dist/core/multi-tool-generator.js +157 -0
  61. package/dist/core/multi-tool-generator.js.map +1 -0
  62. package/dist/core/override-manager.d.ts +23 -0
  63. package/dist/core/override-manager.d.ts.map +1 -0
  64. package/dist/core/override-manager.js +82 -0
  65. package/dist/core/override-manager.js.map +1 -0
  66. package/dist/core/plans-manager.d.ts +46 -0
  67. package/dist/core/plans-manager.d.ts.map +1 -0
  68. package/dist/core/plans-manager.js +158 -0
  69. package/dist/core/plans-manager.js.map +1 -0
  70. package/dist/core/prd-generator.d.ts +12 -0
  71. package/dist/core/prd-generator.d.ts.map +1 -1
  72. package/dist/core/prd-generator.js +91 -2
  73. package/dist/core/prd-generator.js.map +1 -1
  74. package/dist/core/ralph-manager.d.ts +81 -1
  75. package/dist/core/ralph-manager.d.ts.map +1 -1
  76. package/dist/core/ralph-manager.js +214 -4
  77. package/dist/core/ralph-manager.js.map +1 -1
  78. package/dist/core/ralph-parallel.d.ts +55 -0
  79. package/dist/core/ralph-parallel.d.ts.map +1 -0
  80. package/dist/core/ralph-parallel.js +201 -0
  81. package/dist/core/ralph-parallel.js.map +1 -0
  82. package/dist/core/ralph-plan-checkpoint.d.ts +58 -0
  83. package/dist/core/ralph-plan-checkpoint.d.ts.map +1 -0
  84. package/dist/core/ralph-plan-checkpoint.js +154 -0
  85. package/dist/core/ralph-plan-checkpoint.js.map +1 -0
  86. package/dist/core/ralph-scripts.d.ts +12 -0
  87. package/dist/core/ralph-scripts.d.ts.map +1 -0
  88. package/dist/core/ralph-scripts.js +49 -0
  89. package/dist/core/ralph-scripts.js.map +1 -0
  90. package/dist/core/review-manager.d.ts +74 -0
  91. package/dist/core/review-manager.d.ts.map +1 -0
  92. package/dist/core/review-manager.js +371 -0
  93. package/dist/core/review-manager.js.map +1 -0
  94. package/dist/core/task-manager.d.ts +1 -1
  95. package/dist/core/task-manager.js +1 -1
  96. package/dist/core/workflow-generator.js +1 -1
  97. package/dist/core/workflow-generator.js.map +1 -1
  98. package/dist/index.js +96 -4
  99. package/dist/index.js.map +1 -1
  100. package/dist/mcp/rulebook-server.d.ts.map +1 -1
  101. package/dist/mcp/rulebook-server.js +300 -164
  102. package/dist/mcp/rulebook-server.js.map +1 -1
  103. package/dist/memory/memory-store.d.ts.map +1 -1
  104. package/dist/memory/memory-store.js +4 -0
  105. package/dist/memory/memory-store.js.map +1 -1
  106. package/dist/types.d.ts +55 -34
  107. package/dist/types.d.ts.map +1 -1
  108. package/package.json +1 -1
  109. package/templates/agents/implementer.md +35 -0
  110. package/templates/agents/researcher.md +34 -0
  111. package/templates/agents/team-lead.md +34 -0
  112. package/templates/agents/tester.md +42 -0
  113. package/templates/ci/rulebook-review.yml +26 -0
  114. package/templates/core/AGENTS_LEAN.md +25 -0
  115. package/templates/core/AGENTS_OVERRIDE.md +16 -0
  116. package/templates/core/MULTI_AGENT.md +74 -0
  117. package/templates/core/PLANS.md +28 -0
  118. package/templates/core/RALPH.md +45 -4
  119. package/templates/ides/CONTINUE_RULES.md +16 -0
  120. package/templates/ides/COPILOT_INSTRUCTIONS.md +23 -0
  121. package/templates/ides/GEMINI_RULES.md +17 -0
  122. package/templates/ides/WINDSURF_RULES.md +14 -0
  123. package/templates/ides/cursor-mdc/go.mdc +24 -0
  124. package/templates/ides/cursor-mdc/python.mdc +24 -0
  125. package/templates/ides/cursor-mdc/quality.mdc +25 -0
  126. package/templates/ides/cursor-mdc/ralph.mdc +39 -0
  127. package/templates/ides/cursor-mdc/rulebook.mdc +38 -0
  128. package/templates/ides/cursor-mdc/rust.mdc +24 -0
  129. package/templates/ides/cursor-mdc/typescript.mdc +25 -0
  130. package/templates/modules/sequential-thinking.md +42 -0
  131. package/templates/ralph/ralph-history.bat +4 -0
  132. package/templates/ralph/ralph-history.sh +5 -0
  133. package/templates/ralph/ralph-init.bat +5 -0
  134. package/templates/ralph/ralph-init.sh +5 -0
  135. package/templates/ralph/ralph-pause.bat +5 -0
  136. package/templates/ralph/ralph-pause.sh +5 -0
  137. package/templates/ralph/ralph-run.bat +5 -0
  138. package/templates/ralph/ralph-run.sh +5 -0
  139. package/templates/ralph/ralph-status.bat +4 -0
  140. package/templates/ralph/ralph-status.sh +5 -0
  141. package/templates/services/DATADOG.md +26 -0
  142. package/templates/services/DOCKER.md +124 -0
  143. package/templates/services/DOCKER_COMPOSE.md +168 -0
  144. package/templates/services/HELM.md +194 -0
  145. package/templates/services/KUBERNETES.md +208 -0
  146. package/templates/services/OPENTELEMETRY.md +25 -0
  147. package/templates/services/PINO.md +24 -0
  148. package/templates/services/PROMETHEUS.md +33 -0
  149. package/templates/services/SENTRY.md +23 -0
  150. package/templates/services/WINSTON.md +30 -0
  151. package/dist/core/openspec-manager.d.ts +0 -133
  152. package/dist/core/openspec-manager.d.ts.map +0 -1
  153. package/dist/core/openspec-manager.js +0 -596
  154. package/dist/core/openspec-manager.js.map +0 -1
  155. package/dist/core/openspec-migrator.d.ts +0 -27
  156. package/dist/core/openspec-migrator.d.ts.map +0 -1
  157. package/dist/core/openspec-migrator.js +0 -262
  158. package/dist/core/openspec-migrator.js.map +0 -1
  159. package/dist/core/test-task-manager.d.ts +0 -49
  160. package/dist/core/test-task-manager.d.ts.map +0 -1
  161. package/dist/core/test-task-manager.js +0 -121
  162. package/dist/core/test-task-manager.js.map +0 -1
@@ -1,4 +1,8 @@
1
- import type { OpenSpecTask } from '../types.js';
1
+ export interface AgentTask {
2
+ id: string;
3
+ title: string;
4
+ description: string;
5
+ }
2
6
  export interface AgentOptions {
3
7
  dryRun?: boolean;
4
8
  tool?: string;
@@ -6,10 +10,9 @@ export interface AgentOptions {
6
10
  watchMode?: boolean;
7
11
  onLog?: (type: 'info' | 'success' | 'warning' | 'error' | 'tool', message: string) => void;
8
12
  onTaskStatusChange?: (taskId: string, status: string) => void;
9
- onTasksReloaded?: (tasks: OpenSpecTask[]) => void;
13
+ onTasksReloaded?: (tasks: AgentTask[]) => void;
10
14
  }
11
15
  export declare class AgentManager {
12
- private openspecManager;
13
16
  private logger;
14
17
  private configManager;
15
18
  private cliBridge;
@@ -18,8 +21,8 @@ export declare class AgentManager {
18
21
  private currentTool?;
19
22
  private onLog?;
20
23
  private onTaskStatusChange?;
21
- private onTasksReloaded?;
22
24
  private initializePromise?;
25
+ private projectRoot;
23
26
  constructor(projectRoot: string);
24
27
  /**
25
28
  * Initialize agent manager (only once, thread-safe)
@@ -40,42 +43,19 @@ export declare class AgentManager {
40
43
  /**
41
44
  * Execute workflow for a single task
42
45
  */
43
- executeTaskWorkflow(task: OpenSpecTask, options: AgentOptions): Promise<boolean>;
44
- /**
45
- * Run quality checks (lint, format)
46
- */
46
+ executeTaskWorkflow(task: AgentTask, options: AgentOptions): Promise<boolean>;
47
47
  private runQualityChecks;
48
- /**
49
- * Run tests
50
- */
51
48
  private runTests;
52
- /**
53
- * Check coverage
54
- */
55
49
  private checkCoverage;
56
50
  /**
57
- * Test workflows
58
- */
59
- private testWorkflows;
60
- /**
61
- * Commit changes
51
+ * Run the security gate using npm audit (or other available tools).
52
+ * Returns true if the gate passes, false if it fails.
53
+ * If no tool is available, logs a warning and returns true (skip).
62
54
  */
55
+ private runSecurityGate;
63
56
  private commitChanges;
64
- /**
65
- * Start watcher in background
66
- */
67
57
  private startWatcherInBackground;
68
- /**
69
- * Cleanup resources
70
- */
71
58
  private cleanup;
72
- /**
73
- * Delay execution
74
- */
75
- private delay;
76
- /**
77
- * Stop agent
78
- */
79
59
  stop(): Promise<void>;
80
60
  }
81
61
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"agent-manager.d.ts","sourceRoot":"","sources":["../../src/core/agent-manager.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,aAAa,CAAC;AAEhE,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3F,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9D,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;CACnD;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,eAAe,CAA2C;IAClE,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,aAAa,CAAyC;IAC9D,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,KAAK,CAAC,CAAwB;IACtC,OAAO,CAAC,kBAAkB,CAAC,CAAqC;IAChE,OAAO,CAAC,eAAe,CAAC,CAAkC;IAC1D,OAAO,CAAC,iBAAiB,CAAC,CAAgB;gBAE9B,WAAW,EAAE,MAAM;IAO/B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAsCjC;;OAEG;IACG,UAAU,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAuE3D;;OAEG;IACG,aAAa,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAqDnE;;OAEG;IACG,gBAAgB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAiF5D;;OAEG;IACG,mBAAmB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IAyMtF;;OAEG;YACW,gBAAgB;IAoC9B;;OAEG;YACW,QAAQ;IAWtB;;OAEG;YACW,aAAa;IAW3B;;OAEG;YACW,aAAa;IAM3B;;OAEG;YACW,aAAa;IAS3B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAMhC;;OAEG;YACW,OAAO;IAUrB;;OAEG;IACH,OAAO,CAAC,KAAK;IAIb;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAI5B;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG/F;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,CAEpE"}
1
+ {"version":3,"file":"agent-manager.d.ts","sourceRoot":"","sources":["../../src/core/agent-manager.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3F,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9D,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;CAChD;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,aAAa,CAAyC;IAC9D,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,KAAK,CAAC,CAAwB;IACtC,OAAO,CAAC,kBAAkB,CAAC,CAAqC;IAChE,OAAO,CAAC,iBAAiB,CAAC,CAAgB;IAC1C,OAAO,CAAC,WAAW,CAAS;gBAEhB,WAAW,EAAE,MAAM;IAO/B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA4BjC;;OAEG;IACG,UAAU,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6C3D;;OAEG;IACG,aAAa,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAkDnE;;OAEG;IACG,gBAAgB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IA6C5D;;OAEG;IACG,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;YAiFrE,gBAAgB;YAkChB,QAAQ;YAWR,aAAa;IAqB3B;;;;OAIG;YACW,eAAe;YA8Hf,aAAa;IAS3B,OAAO,CAAC,wBAAwB;YAIlB,OAAO;IAUf,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAI5B;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG/F;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,CAEpE"}
@@ -1,11 +1,10 @@
1
1
  import chalk from 'chalk';
2
2
  import inquirer from 'inquirer';
3
- import { createOpenSpecManager } from './openspec-manager.js';
4
3
  import { initializeLogger } from './logger.js';
5
4
  import { createConfigManager } from './config-manager.js';
6
5
  import { createCLIBridge } from './cli-bridge.js';
6
+ import { RalphParser } from '../agents/ralph-parser.js';
7
7
  export class AgentManager {
8
- openspecManager;
9
8
  logger;
10
9
  configManager;
11
10
  cliBridge;
@@ -14,43 +13,35 @@ export class AgentManager {
14
13
  currentTool;
15
14
  onLog;
16
15
  onTaskStatusChange;
17
- onTasksReloaded;
18
- initializePromise; // Track initialization promise to prevent race conditions
16
+ initializePromise;
17
+ projectRoot;
19
18
  constructor(projectRoot) {
20
- this.openspecManager = createOpenSpecManager(projectRoot);
19
+ this.projectRoot = projectRoot;
21
20
  this.logger = initializeLogger(projectRoot);
22
21
  this.configManager = createConfigManager(projectRoot);
23
- this.config = {}; // Will be loaded in initialize()
22
+ this.config = {};
24
23
  }
25
24
  /**
26
25
  * Initialize agent manager (only once, thread-safe)
27
26
  */
28
27
  async initialize() {
29
- // If already initializing or initialized, return the same promise
30
28
  if (this.initializePromise) {
31
- // Update callbacks even if already initialized (they might have changed)
32
29
  if (this.cliBridge && this.onLog) {
33
30
  this.cliBridge.setLogCallback(this.onLog);
34
- this.openspecManager.setLogCallback(this.onLog);
35
31
  }
36
32
  return this.initializePromise;
37
33
  }
38
- // Create and store the initialization promise
39
34
  this.initializePromise = (async () => {
40
35
  try {
41
36
  this.config = await this.configManager.loadConfig();
42
37
  this.cliBridge = createCLIBridge(this.logger, this.config);
43
- // Pass onLog callback to CLI bridge if available
44
38
  if (this.onLog) {
45
39
  this.cliBridge.setLogCallback(this.onLog);
46
- this.openspecManager.setLogCallback(this.onLog);
47
40
  }
48
- await this.openspecManager.initialize();
49
41
  this.logger.info('Agent Manager initialized');
50
42
  }
51
43
  catch (error) {
52
44
  this.logger.error('Failed to initialize Agent Manager', { error: String(error) });
53
- // Clear the promise so it can be retried
54
45
  this.initializePromise = undefined;
55
46
  throw error;
56
47
  }
@@ -62,10 +53,8 @@ export class AgentManager {
62
53
  */
63
54
  async startAgent(options = {}) {
64
55
  try {
65
- // Set callbacks BEFORE initialize
66
56
  this.onLog = options.onLog;
67
57
  this.onTaskStatusChange = options.onTaskStatusChange;
68
- this.onTasksReloaded = options.onTasksReloaded;
69
58
  await this.initialize();
70
59
  if (!this.onLog) {
71
60
  console.log(chalk.bold.blue('\n🤖 Rulebook Autonomous Agent\n'));
@@ -73,7 +62,6 @@ export class AgentManager {
73
62
  else {
74
63
  this.onLog('info', '🤖 Rulebook Autonomous Agent');
75
64
  }
76
- // Detect and select CLI tool
77
65
  const selectedTool = await this.selectCLITool(options.tool);
78
66
  if (!selectedTool) {
79
67
  const msg = 'No CLI tool selected. Exiting.';
@@ -87,30 +75,9 @@ export class AgentManager {
87
75
  }
88
76
  this.currentTool = selectedTool;
89
77
  this.isRunning = true;
90
- // Sync task status first
91
- if (this.onLog) {
92
- this.onLog('info', '📋 Syncing task status...');
93
- }
94
- else {
95
- console.log(chalk.gray('📋 Syncing task status...'));
96
- }
97
- await this.openspecManager.syncTaskStatus();
98
- // Notify watcher with reloaded tasks
99
- if (this.onTasksReloaded) {
100
- const data = await this.openspecManager.loadOpenSpec();
101
- this.onTasksReloaded(data.tasks);
102
- }
103
- if (this.onLog) {
104
- this.onLog('success', '✅ Task status synced');
105
- }
106
- else {
107
- console.log(chalk.green('✅ Task status synced\n'));
108
- }
109
- // Start watcher if requested
110
78
  if (options.watchMode) {
111
79
  this.startWatcherInBackground();
112
80
  }
113
- // Run main workflow loop
114
81
  await this.runAgentWorkflow(options);
115
82
  }
116
83
  catch (error) {
@@ -142,7 +109,6 @@ export class AgentManager {
142
109
  }
143
110
  return null;
144
111
  }
145
- // If preferred tool is specified and available, use it
146
112
  if (preferredTool && availableTools.some((tool) => tool.name === preferredTool)) {
147
113
  const msg = `Using preferred tool: ${preferredTool}`;
148
114
  if (this.onLog) {
@@ -153,7 +119,6 @@ export class AgentManager {
153
119
  }
154
120
  return preferredTool;
155
121
  }
156
- // If only one tool available, use it
157
122
  if (availableTools.length === 1) {
158
123
  const msg = `Using available tool: ${availableTools[0].name}`;
159
124
  if (this.onLog) {
@@ -164,7 +129,6 @@ export class AgentManager {
164
129
  }
165
130
  return availableTools[0].name;
166
131
  }
167
- // Multiple tools available, let user choose
168
132
  const choices = availableTools.map((tool) => ({
169
133
  name: `${tool.name} ${tool.version ? `(${tool.version})` : ''}`,
170
134
  value: tool.name,
@@ -190,43 +154,15 @@ export class AgentManager {
190
154
  try {
191
155
  iteration++;
192
156
  this.logger.info(`Workflow iteration ${iteration}/${maxIterations}`);
193
- // Get next task
194
- const nextTask = await this.openspecManager.getNextTask();
195
- if (!nextTask) {
196
- this.logger.info('No more tasks available');
197
- const msg = '✅ All tasks completed!';
198
- if (this.onLog) {
199
- this.onLog('success', msg);
200
- }
201
- else {
202
- console.log(chalk.green(`\n${msg}`));
203
- }
204
- break;
205
- }
206
- // Debug: Log task being executed
157
+ const msg = `🔄 Agent workflow iteration ${iteration}/${maxIterations} (use Ralph for task-driven automation)`;
207
158
  if (this.onLog) {
208
- this.onLog('info', `[DEBUG] Agent picked task: ${nextTask.id} - ${nextTask.title.substring(0, 50)}`);
209
- }
210
- // Execute task workflow
211
- const success = await this.executeTaskWorkflow(nextTask, options);
212
- if (success) {
213
- await this.openspecManager.markTaskComplete(nextTask.id);
214
- this.logger.taskComplete(nextTask.id, nextTask.title, 0);
215
- // Notify watcher about completion
216
- if (this.onTaskStatusChange) {
217
- this.onTaskStatusChange(nextTask.id, 'completed');
218
- }
159
+ this.onLog('info', msg);
219
160
  }
220
161
  else {
221
- await this.openspecManager.updateTaskStatus(nextTask.id, 'failed');
222
- this.logger.taskFailed(nextTask.id, nextTask.title, 'Task execution failed');
223
- // Notify watcher about failure
224
- if (this.onTaskStatusChange) {
225
- this.onTaskStatusChange(nextTask.id, 'failed');
226
- }
162
+ console.log(chalk.gray(msg));
227
163
  }
228
- // Small delay between tasks
229
- await this.delay(2000);
164
+ // Continue iterating no unconditional break
165
+ // Loop exits naturally when: isRunning=false, maxIterations reached
230
166
  }
231
167
  catch (error) {
232
168
  this.logger.error(`Workflow iteration ${iteration} failed`, { error: String(error) });
@@ -237,7 +173,6 @@ export class AgentManager {
237
173
  else {
238
174
  console.error(chalk.red('\n' + msg));
239
175
  }
240
- // Continue with next iteration unless it's a critical error
241
176
  if (iteration >= maxIterations) {
242
177
  break;
243
178
  }
@@ -267,21 +202,9 @@ export class AgentManager {
267
202
  console.log(chalk.blue('\n' + msg));
268
203
  }
269
204
  try {
270
- // Set task as in-progress
271
- await this.openspecManager.setCurrentTask(task.id);
272
- await this.openspecManager.updateTaskStatus(task.id, 'in-progress');
273
- // Notify watcher about status change
274
205
  if (this.onTaskStatusChange) {
275
- if (this.onLog) {
276
- this.onLog('info', `[DEBUG] Calling onTaskStatusChange for ${task.id}`);
277
- }
278
206
  this.onTaskStatusChange(task.id, 'in-progress');
279
207
  }
280
- else {
281
- if (this.onLog) {
282
- this.onLog('warning', '[DEBUG] onTaskStatusChange callback is undefined!');
283
- }
284
- }
285
208
  if (options.dryRun) {
286
209
  if (this.onLog) {
287
210
  this.onLog('warning', '🔍 DRY RUN MODE - No actual execution');
@@ -293,7 +216,6 @@ export class AgentManager {
293
216
  }
294
217
  return true;
295
218
  }
296
- // Step 1: Send task to CLI
297
219
  if (this.onLog) {
298
220
  this.onLog('info', '📤 Sending task to CLI...');
299
221
  }
@@ -301,111 +223,21 @@ export class AgentManager {
301
223
  console.log(chalk.gray('📤 Sending task to CLI...'));
302
224
  }
303
225
  const taskResponse = await this.cliBridge.sendTaskCommand(this.currentTool, task);
304
- if (this.onLog) {
305
- this.onLog('info', '📥 Agent Response:');
306
- this.onLog('info', '─'.repeat(80));
307
- this.onLog('info', taskResponse.output || '(no output)');
308
- this.onLog('info', '─'.repeat(80));
309
- this.onLog('info', `Exit Code: ${taskResponse.exitCode} | Duration: ${Math.round(taskResponse.duration / 1000)}s`);
310
- if (taskResponse.error) {
311
- this.onLog('warning', '⚠️ Error Output:');
312
- this.onLog('error', taskResponse.error);
313
- }
314
- }
315
- else {
316
- console.log(chalk.blue('\n📥 Agent Response:'));
317
- console.log(chalk.gray('─'.repeat(80)));
318
- console.log(taskResponse.output || '(no output)');
319
- console.log(chalk.gray('─'.repeat(80)));
320
- console.log(chalk.gray(`Exit Code: ${taskResponse.exitCode} | Duration: ${Math.round(taskResponse.duration / 1000)}s`));
321
- if (taskResponse.error) {
322
- console.log(chalk.red('\n⚠️ Error Output:'));
323
- console.log(chalk.red(taskResponse.error));
324
- }
325
- }
326
226
  if (!taskResponse.success) {
327
227
  throw new Error(`Task command failed: ${taskResponse.error}`);
328
228
  }
329
- // Step 2: Continue implementation loop
330
- if (this.onLog) {
331
- this.onLog('info', '🔄 Continuing implementation...');
332
- }
333
- else {
334
- console.log(chalk.gray('\n🔄 Continuing implementation...'));
335
- }
336
- const continueResponse = await this.cliBridge.sendContinueCommand(this.currentTool, 10);
337
- if (this.onLog) {
338
- this.onLog('info', '📥 Continue Response:');
339
- this.onLog('info', '─'.repeat(80));
340
- this.onLog('info', continueResponse.output || '(no output)');
341
- this.onLog('info', '─'.repeat(80));
342
- this.onLog('info', `Exit Code: ${continueResponse.exitCode} | Duration: ${Math.round(continueResponse.duration / 1000)}s`);
343
- if (continueResponse.error) {
344
- this.onLog('warning', '⚠️ Error Output:');
345
- this.onLog('error', continueResponse.error);
346
- }
347
- if (!continueResponse.success) {
348
- this.onLog('warning', '⚠️ Continue command failed, but continuing workflow');
349
- }
350
- }
351
- else {
352
- console.log(chalk.blue('\n📥 Continue Response:'));
353
- console.log(chalk.gray('─'.repeat(80)));
354
- console.log(continueResponse.output || '(no output)');
355
- console.log(chalk.gray('─'.repeat(80)));
356
- console.log(chalk.gray(`Exit Code: ${continueResponse.exitCode} | Duration: ${Math.round(continueResponse.duration / 1000)}s`));
357
- if (continueResponse.error) {
358
- console.log(chalk.red('\n⚠️ Error Output:'));
359
- console.log(chalk.red(continueResponse.error));
360
- }
361
- if (!continueResponse.success) {
362
- console.log(chalk.yellow('⚠️ Continue command failed, but continuing workflow'));
363
- }
364
- }
365
- // Step 3: Run quality checks
366
- if (this.onLog) {
367
- this.onLog('info', '🔍 Running quality checks...');
368
- }
369
- else {
370
- console.log(chalk.gray('🔍 Running quality checks...'));
371
- }
372
229
  await this.runQualityChecks();
373
- // Step 4: Run tests
374
- if (this.onLog) {
375
- this.onLog('info', '🧪 Running tests...');
376
- }
377
- else {
378
- console.log(chalk.gray('🧪 Running tests...'));
379
- }
380
230
  const testSuccess = await this.runTests();
381
231
  if (!testSuccess) {
382
232
  throw new Error('Tests failed');
383
233
  }
384
- // Step 5: Check coverage
385
- if (this.onLog) {
386
- this.onLog('info', '📊 Checking coverage...');
387
- }
388
- else {
389
- console.log(chalk.gray('📊 Checking coverage...'));
390
- }
391
234
  const coverageSuccess = await this.checkCoverage();
392
235
  if (!coverageSuccess) {
393
236
  throw new Error('Coverage below threshold');
394
237
  }
395
- // Step 6: Test workflows
396
- if (this.onLog) {
397
- this.onLog('info', '⚙️ Testing workflows...');
398
- }
399
- else {
400
- console.log(chalk.gray('⚙️ Testing workflows...'));
401
- }
402
- await this.testWorkflows();
403
- // Step 7: Commit changes
404
- if (this.onLog) {
405
- this.onLog('info', '💾 Committing changes...');
406
- }
407
- else {
408
- console.log(chalk.gray('💾 Committing changes...'));
238
+ const securitySuccess = await this.runSecurityGate(this.projectRoot);
239
+ if (!securitySuccess) {
240
+ throw new Error('Security gate failed');
409
241
  }
410
242
  await this.commitChanges(task);
411
243
  const duration = Date.now() - startTime;
@@ -432,11 +264,7 @@ export class AgentManager {
432
264
  return false;
433
265
  }
434
266
  }
435
- /**
436
- * Run quality checks (lint, format)
437
- */
438
267
  async runQualityChecks() {
439
- // Run lint
440
268
  const lintResponse = await this.cliBridge.sendLintCommand(this.currentTool);
441
269
  this.logger.testExecution('lint', lintResponse.success ? 'passed' : 'failed', lintResponse.duration);
442
270
  if (!lintResponse.success) {
@@ -448,7 +276,6 @@ export class AgentManager {
448
276
  console.log(chalk.yellow(msg));
449
277
  }
450
278
  }
451
- // Run format
452
279
  const formatResponse = await this.cliBridge.sendFormatCommand(this.currentTool);
453
280
  this.logger.testExecution('format', formatResponse.success ? 'passed' : 'failed', formatResponse.duration);
454
281
  if (!formatResponse.success) {
@@ -461,36 +288,156 @@ export class AgentManager {
461
288
  }
462
289
  }
463
290
  }
464
- /**
465
- * Run tests
466
- */
467
291
  async runTests() {
468
292
  const testResponse = await this.cliBridge.sendTestCommand(this.currentTool);
469
293
  this.logger.testExecution('tests', testResponse.success ? 'passed' : 'failed', testResponse.duration);
470
294
  return testResponse.success;
471
295
  }
472
- /**
473
- * Check coverage
474
- */
475
296
  async checkCoverage() {
476
- // This would integrate with your coverage checker
477
- // For now, assume coverage is OK
478
- const coverage = 95; // This should come from actual coverage check
479
297
  const threshold = this.config.coverageThreshold;
298
+ // Run tests with coverage to get real output
299
+ const coverageResponse = await this.cliBridge.sendTestCommand(this.currentTool);
300
+ const output = coverageResponse.output ?? '';
301
+ const coverage = RalphParser.parseCoveragePercentage(output);
302
+ if (coverage === null) {
303
+ // Coverage output not parseable — warn but don't fail the gate
304
+ process.stderr.write('[rulebook] Coverage output could not be parsed; skipping coverage gate\n');
305
+ return true;
306
+ }
480
307
  this.logger.coverageCheck(coverage, threshold);
481
308
  return coverage >= threshold;
482
309
  }
483
310
  /**
484
- * Test workflows
311
+ * Run the security gate using npm audit (or other available tools).
312
+ * Returns true if the gate passes, false if it fails.
313
+ * If no tool is available, logs a warning and returns true (skip).
485
314
  */
486
- async testWorkflows() {
487
- // This would test GitHub Actions workflows
488
- // For now, just log
489
- this.logger.info('Testing workflows (placeholder)');
315
+ async runSecurityGate(projectRoot) {
316
+ const secConfig = this.config.ralph?.securityGate;
317
+ const enabled = secConfig?.enabled !== false; // default: true
318
+ if (!enabled) {
319
+ return true;
320
+ }
321
+ const failOn = (secConfig?.failOn ?? 'high');
322
+ try {
323
+ const { execSync } = await import('child_process');
324
+ // Detect available tool
325
+ let tool = secConfig?.tool ?? 'auto';
326
+ if (tool === 'auto') {
327
+ // Auto-detect: trivy > semgrep > npm audit
328
+ try {
329
+ execSync('trivy --version', { stdio: 'ignore', timeout: 5000 });
330
+ tool = 'trivy';
331
+ }
332
+ catch {
333
+ try {
334
+ execSync('semgrep --version', { stdio: 'ignore', timeout: 5000 });
335
+ tool = 'semgrep';
336
+ }
337
+ catch {
338
+ try {
339
+ execSync('npm audit --version', { cwd: projectRoot, stdio: 'ignore', timeout: 5000 });
340
+ tool = 'npm-audit';
341
+ }
342
+ catch {
343
+ // no tool available
344
+ }
345
+ }
346
+ }
347
+ }
348
+ if (tool === 'trivy') {
349
+ try {
350
+ const output = execSync(`trivy fs --exit-code 0 --format json ${projectRoot}`, {
351
+ encoding: 'utf-8',
352
+ stdio: ['ignore', 'pipe', 'ignore'],
353
+ timeout: 60000,
354
+ });
355
+ const severity = RalphParser.parseTrivySeverity(output);
356
+ const passes = RalphParser.securityGatePasses(severity, failOn);
357
+ if (!passes) {
358
+ const msg = `🔒 Security gate (trivy) failed: ${severity} vulnerabilities found (failOn: ${failOn})`;
359
+ if (this.onLog)
360
+ this.onLog('warning', msg);
361
+ else
362
+ console.log(chalk.yellow(msg));
363
+ }
364
+ return passes;
365
+ }
366
+ catch (err) {
367
+ const execError = err;
368
+ if (execError.stdout) {
369
+ const severity = RalphParser.parseTrivySeverity(execError.stdout);
370
+ return RalphParser.securityGatePasses(severity, failOn);
371
+ }
372
+ }
373
+ }
374
+ if (tool === 'semgrep') {
375
+ try {
376
+ const output = execSync(`semgrep --config auto --json ${projectRoot}`, {
377
+ encoding: 'utf-8',
378
+ stdio: ['ignore', 'pipe', 'ignore'],
379
+ timeout: 60000,
380
+ });
381
+ const severity = RalphParser.parseSemgrepSeverity(output);
382
+ const passes = RalphParser.securityGatePasses(severity, failOn);
383
+ if (!passes) {
384
+ const msg = `🔒 Security gate (semgrep) failed: ${severity} issues found (failOn: ${failOn})`;
385
+ if (this.onLog)
386
+ this.onLog('warning', msg);
387
+ else
388
+ console.log(chalk.yellow(msg));
389
+ }
390
+ return passes;
391
+ }
392
+ catch (err) {
393
+ const execError = err;
394
+ if (execError.stdout) {
395
+ const severity = RalphParser.parseSemgrepSeverity(execError.stdout);
396
+ return RalphParser.securityGatePasses(severity, failOn);
397
+ }
398
+ }
399
+ }
400
+ if (tool === 'npm-audit') {
401
+ try {
402
+ const output = execSync('npm audit --json', {
403
+ cwd: projectRoot,
404
+ encoding: 'utf-8',
405
+ stdio: ['ignore', 'pipe', 'ignore'],
406
+ timeout: 30000,
407
+ });
408
+ const severity = RalphParser.parseNpmAuditSeverity(output);
409
+ const passes = RalphParser.securityGatePasses(severity, failOn);
410
+ if (!passes) {
411
+ const msg = `🔒 Security gate failed: found ${severity} severity vulnerabilities (failOn: ${failOn})`;
412
+ if (this.onLog) {
413
+ this.onLog('warning', msg);
414
+ }
415
+ else {
416
+ console.log(chalk.yellow(msg));
417
+ }
418
+ }
419
+ return passes;
420
+ }
421
+ catch (err) {
422
+ // npm audit exits with non-zero when vulnerabilities found
423
+ const execError = err;
424
+ if (execError.stdout) {
425
+ const severity = RalphParser.parseNpmAuditSeverity(execError.stdout);
426
+ return RalphParser.securityGatePasses(severity, failOn);
427
+ }
428
+ }
429
+ }
430
+ // No scanner available — skip gate with warning
431
+ const skipMsg = '[rulebook] No security scanner available; skipping security gate';
432
+ process.stderr.write(skipMsg + '\n');
433
+ return true;
434
+ }
435
+ catch {
436
+ // Gate itself threw — skip rather than fail
437
+ process.stderr.write('[rulebook] Security gate error; skipping\n');
438
+ return true;
439
+ }
490
440
  }
491
- /**
492
- * Commit changes
493
- */
494
441
  async commitChanges(task) {
495
442
  const message = `feat: ${task.title}\n\n${task.description}`;
496
443
  const commitResponse = await this.cliBridge.sendCommitCommand(this.currentTool, message);
@@ -498,17 +445,9 @@ export class AgentManager {
498
445
  throw new Error(`Commit failed: ${commitResponse.error}`);
499
446
  }
500
447
  }
501
- /**
502
- * Start watcher in background
503
- */
504
448
  startWatcherInBackground() {
505
- // This would start the watcher in a separate process or thread
506
- // For now, just log
507
449
  this.logger.info('Watcher mode requested (placeholder)');
508
450
  }
509
- /**
510
- * Cleanup resources
511
- */
512
451
  async cleanup() {
513
452
  this.isRunning = false;
514
453
  if (this.cliBridge) {
@@ -516,15 +455,6 @@ export class AgentManager {
516
455
  }
517
456
  await this.logger.close();
518
457
  }
519
- /**
520
- * Delay execution
521
- */
522
- delay(ms) {
523
- return new Promise((resolve) => setTimeout(resolve, ms));
524
- }
525
- /**
526
- * Stop agent
527
- */
528
458
  async stop() {
529
459
  this.isRunning = false;
530
460
  await this.cleanup();