@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.
- package/.claude/commands/continue.md +33 -0
- package/.claude-plugin/marketplace.json +28 -29
- package/.claude-plugin/plugin.json +8 -8
- package/README.md +32 -144
- package/dist/agents/ralph-parser.d.ts +44 -5
- package/dist/agents/ralph-parser.d.ts.map +1 -1
- package/dist/agents/ralph-parser.js +218 -26
- package/dist/agents/ralph-parser.js.map +1 -1
- package/dist/cli/commands.d.ts +65 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +841 -175
- package/dist/cli/commands.js.map +1 -1
- package/dist/core/agent-manager.d.ts +12 -32
- package/dist/core/agent-manager.d.ts.map +1 -1
- package/dist/core/agent-manager.js +150 -220
- package/dist/core/agent-manager.js.map +1 -1
- package/dist/core/claude-mcp.d.ts +17 -0
- package/dist/core/claude-mcp.d.ts.map +1 -1
- package/dist/core/claude-mcp.js +90 -6
- package/dist/core/claude-mcp.js.map +1 -1
- package/dist/core/cli-bridge.js +1 -1
- package/dist/core/cli-bridge.js.map +1 -1
- package/dist/core/config-manager.d.ts.map +1 -1
- package/dist/core/config-manager.js +40 -0
- package/dist/core/config-manager.js.map +1 -1
- package/dist/core/cursor-mdc-generator.d.ts +30 -0
- package/dist/core/cursor-mdc-generator.d.ts.map +1 -0
- package/dist/core/cursor-mdc-generator.js +98 -0
- package/dist/core/cursor-mdc-generator.js.map +1 -0
- package/dist/core/detector.d.ts +25 -1
- package/dist/core/detector.d.ts.map +1 -1
- package/dist/core/detector.js +321 -1
- package/dist/core/detector.js.map +1 -1
- package/dist/core/generator.d.ts +10 -0
- package/dist/core/generator.d.ts.map +1 -1
- package/dist/core/generator.js +182 -9
- package/dist/core/generator.js.map +1 -1
- package/dist/core/github-issues-importer.d.ts +82 -0
- package/dist/core/github-issues-importer.d.ts.map +1 -0
- package/dist/core/github-issues-importer.js +161 -0
- package/dist/core/github-issues-importer.js.map +1 -0
- package/dist/core/health-scorer.d.ts +39 -0
- package/dist/core/health-scorer.d.ts.map +1 -1
- package/dist/core/health-scorer.js +256 -13
- package/dist/core/health-scorer.js.map +1 -1
- package/dist/core/iteration-tracker.d.ts +28 -0
- package/dist/core/iteration-tracker.d.ts.map +1 -1
- package/dist/core/iteration-tracker.js +86 -0
- package/dist/core/iteration-tracker.js.map +1 -1
- package/dist/core/logger.js +1 -1
- package/dist/core/logger.js.map +1 -1
- package/dist/core/migrator.js +1 -1
- package/dist/core/migrator.js.map +1 -1
- package/dist/core/modern-console.d.ts +1 -2
- package/dist/core/modern-console.d.ts.map +1 -1
- package/dist/core/modern-console.js +6 -18
- package/dist/core/modern-console.js.map +1 -1
- package/dist/core/multi-tool-generator.d.ts +59 -0
- package/dist/core/multi-tool-generator.d.ts.map +1 -0
- package/dist/core/multi-tool-generator.js +157 -0
- package/dist/core/multi-tool-generator.js.map +1 -0
- package/dist/core/override-manager.d.ts +23 -0
- package/dist/core/override-manager.d.ts.map +1 -0
- package/dist/core/override-manager.js +82 -0
- package/dist/core/override-manager.js.map +1 -0
- package/dist/core/plans-manager.d.ts +46 -0
- package/dist/core/plans-manager.d.ts.map +1 -0
- package/dist/core/plans-manager.js +158 -0
- package/dist/core/plans-manager.js.map +1 -0
- package/dist/core/prd-generator.d.ts +12 -0
- package/dist/core/prd-generator.d.ts.map +1 -1
- package/dist/core/prd-generator.js +91 -2
- package/dist/core/prd-generator.js.map +1 -1
- package/dist/core/ralph-manager.d.ts +81 -1
- package/dist/core/ralph-manager.d.ts.map +1 -1
- package/dist/core/ralph-manager.js +214 -4
- package/dist/core/ralph-manager.js.map +1 -1
- package/dist/core/ralph-parallel.d.ts +55 -0
- package/dist/core/ralph-parallel.d.ts.map +1 -0
- package/dist/core/ralph-parallel.js +201 -0
- package/dist/core/ralph-parallel.js.map +1 -0
- package/dist/core/ralph-plan-checkpoint.d.ts +58 -0
- package/dist/core/ralph-plan-checkpoint.d.ts.map +1 -0
- package/dist/core/ralph-plan-checkpoint.js +154 -0
- package/dist/core/ralph-plan-checkpoint.js.map +1 -0
- package/dist/core/ralph-scripts.d.ts +12 -0
- package/dist/core/ralph-scripts.d.ts.map +1 -0
- package/dist/core/ralph-scripts.js +49 -0
- package/dist/core/ralph-scripts.js.map +1 -0
- package/dist/core/review-manager.d.ts +74 -0
- package/dist/core/review-manager.d.ts.map +1 -0
- package/dist/core/review-manager.js +371 -0
- package/dist/core/review-manager.js.map +1 -0
- package/dist/core/task-manager.d.ts +1 -1
- package/dist/core/task-manager.js +1 -1
- package/dist/core/workflow-generator.js +1 -1
- package/dist/core/workflow-generator.js.map +1 -1
- package/dist/index.js +96 -4
- package/dist/index.js.map +1 -1
- package/dist/mcp/rulebook-server.d.ts.map +1 -1
- package/dist/mcp/rulebook-server.js +300 -164
- package/dist/mcp/rulebook-server.js.map +1 -1
- package/dist/memory/memory-store.d.ts.map +1 -1
- package/dist/memory/memory-store.js +4 -0
- package/dist/memory/memory-store.js.map +1 -1
- package/dist/types.d.ts +55 -34
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/agents/implementer.md +35 -0
- package/templates/agents/researcher.md +34 -0
- package/templates/agents/team-lead.md +34 -0
- package/templates/agents/tester.md +42 -0
- package/templates/ci/rulebook-review.yml +26 -0
- package/templates/core/AGENTS_LEAN.md +25 -0
- package/templates/core/AGENTS_OVERRIDE.md +16 -0
- package/templates/core/MULTI_AGENT.md +74 -0
- package/templates/core/PLANS.md +28 -0
- package/templates/core/RALPH.md +45 -4
- package/templates/ides/CONTINUE_RULES.md +16 -0
- package/templates/ides/COPILOT_INSTRUCTIONS.md +23 -0
- package/templates/ides/GEMINI_RULES.md +17 -0
- package/templates/ides/WINDSURF_RULES.md +14 -0
- package/templates/ides/cursor-mdc/go.mdc +24 -0
- package/templates/ides/cursor-mdc/python.mdc +24 -0
- package/templates/ides/cursor-mdc/quality.mdc +25 -0
- package/templates/ides/cursor-mdc/ralph.mdc +39 -0
- package/templates/ides/cursor-mdc/rulebook.mdc +38 -0
- package/templates/ides/cursor-mdc/rust.mdc +24 -0
- package/templates/ides/cursor-mdc/typescript.mdc +25 -0
- package/templates/modules/sequential-thinking.md +42 -0
- package/templates/ralph/ralph-history.bat +4 -0
- package/templates/ralph/ralph-history.sh +5 -0
- package/templates/ralph/ralph-init.bat +5 -0
- package/templates/ralph/ralph-init.sh +5 -0
- package/templates/ralph/ralph-pause.bat +5 -0
- package/templates/ralph/ralph-pause.sh +5 -0
- package/templates/ralph/ralph-run.bat +5 -0
- package/templates/ralph/ralph-run.sh +5 -0
- package/templates/ralph/ralph-status.bat +4 -0
- package/templates/ralph/ralph-status.sh +5 -0
- package/templates/services/DATADOG.md +26 -0
- package/templates/services/DOCKER.md +124 -0
- package/templates/services/DOCKER_COMPOSE.md +168 -0
- package/templates/services/HELM.md +194 -0
- package/templates/services/KUBERNETES.md +208 -0
- package/templates/services/OPENTELEMETRY.md +25 -0
- package/templates/services/PINO.md +24 -0
- package/templates/services/PROMETHEUS.md +33 -0
- package/templates/services/SENTRY.md +23 -0
- package/templates/services/WINSTON.md +30 -0
- package/dist/core/openspec-manager.d.ts +0 -133
- package/dist/core/openspec-manager.d.ts.map +0 -1
- package/dist/core/openspec-manager.js +0 -596
- package/dist/core/openspec-manager.js.map +0 -1
- package/dist/core/openspec-migrator.d.ts +0 -27
- package/dist/core/openspec-migrator.d.ts.map +0 -1
- package/dist/core/openspec-migrator.js +0 -262
- package/dist/core/openspec-migrator.js.map +0 -1
- package/dist/core/test-task-manager.d.ts +0 -49
- package/dist/core/test-task-manager.d.ts.map +0 -1
- package/dist/core/test-task-manager.js +0 -121
- package/dist/core/test-task-manager.js.map +0 -1
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
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:
|
|
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:
|
|
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
|
-
*
|
|
58
|
-
|
|
59
|
-
|
|
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":"
|
|
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
|
-
|
|
18
|
-
|
|
16
|
+
initializePromise;
|
|
17
|
+
projectRoot;
|
|
19
18
|
constructor(projectRoot) {
|
|
20
|
-
this.
|
|
19
|
+
this.projectRoot = projectRoot;
|
|
21
20
|
this.logger = initializeLogger(projectRoot);
|
|
22
21
|
this.configManager = createConfigManager(projectRoot);
|
|
23
|
-
this.config = {};
|
|
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
|
-
|
|
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',
|
|
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
|
-
|
|
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
|
-
//
|
|
229
|
-
|
|
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
|
-
|
|
396
|
-
if (
|
|
397
|
-
|
|
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
|
-
*
|
|
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
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
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();
|