bmalph 2.4.0 → 2.6.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/README.md +93 -47
- package/dist/cli.js +17 -2
- package/dist/cli.js.map +1 -0
- package/dist/commands/check-updates.js +2 -17
- package/dist/commands/check-updates.js.map +1 -0
- package/dist/commands/doctor.js +6 -57
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/implement.js +6 -4
- package/dist/commands/implement.js.map +1 -0
- package/dist/commands/init.js +28 -47
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/reset.js +7 -10
- package/dist/commands/reset.js.map +1 -0
- package/dist/commands/run.js +46 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/status.js +18 -3
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/upgrade.js +7 -10
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/watch.js +19 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/installer.js +11 -38
- package/dist/installer.js.map +1 -0
- package/dist/platform/aider.js +7 -63
- package/dist/platform/aider.js.map +1 -0
- package/dist/platform/claude-code.js +5 -39
- package/dist/platform/claude-code.js.map +1 -0
- package/dist/platform/codex.js +7 -59
- package/dist/platform/codex.js.map +1 -0
- package/dist/platform/copilot.js +10 -65
- package/dist/platform/copilot.js.map +1 -0
- package/dist/platform/cursor.js +7 -63
- package/dist/platform/cursor.js.map +1 -0
- package/dist/platform/detect.js +2 -1
- package/dist/platform/detect.js.map +1 -0
- package/dist/platform/doctor-checks.js +61 -0
- package/dist/platform/doctor-checks.js.map +1 -0
- package/dist/platform/index.js +1 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/instructions-snippet.js +75 -0
- package/dist/platform/instructions-snippet.js.map +1 -0
- package/dist/platform/registry.js +7 -0
- package/dist/platform/registry.js.map +1 -0
- package/dist/platform/resolve.js +1 -0
- package/dist/platform/resolve.js.map +1 -0
- package/dist/platform/types.js +1 -0
- package/dist/platform/types.js.map +1 -0
- package/dist/platform/windsurf.js +7 -63
- package/dist/platform/windsurf.js.map +1 -0
- package/dist/reset.js +6 -20
- package/dist/reset.js.map +1 -0
- package/dist/run/ralph-process.js +89 -0
- package/dist/run/ralph-process.js.map +1 -0
- package/dist/run/run-dashboard.js +104 -0
- package/dist/run/run-dashboard.js.map +1 -0
- package/dist/run/types.js +2 -0
- package/dist/run/types.js.map +1 -0
- package/dist/transition/artifact-scan.js +3 -2
- package/dist/transition/artifact-scan.js.map +1 -0
- package/dist/transition/artifacts.js +2 -28
- package/dist/transition/artifacts.js.map +1 -0
- package/dist/transition/context.js +1 -0
- package/dist/transition/context.js.map +1 -0
- package/dist/transition/fix-plan.js +1 -0
- package/dist/transition/fix-plan.js.map +1 -0
- package/dist/transition/index.js +2 -1
- package/dist/transition/index.js.map +1 -0
- package/dist/transition/orchestration.js +3 -2
- package/dist/transition/orchestration.js.map +1 -0
- package/dist/transition/preflight.js +1 -0
- package/dist/transition/preflight.js.map +1 -0
- package/dist/transition/specs-changelog.js +3 -2
- package/dist/transition/specs-changelog.js.map +1 -0
- package/dist/transition/specs-index.js +3 -2
- package/dist/transition/specs-index.js.map +1 -0
- package/dist/transition/story-parsing.js +1 -0
- package/dist/transition/story-parsing.js.map +1 -0
- package/dist/transition/tech-stack.js +1 -0
- package/dist/transition/tech-stack.js.map +1 -0
- package/dist/transition/types.js +1 -0
- package/dist/transition/types.js.map +1 -0
- package/dist/utils/config.js +3 -2
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/constants.js +8 -49
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/dryrun.js +1 -20
- package/dist/utils/dryrun.js.map +1 -0
- package/dist/utils/errors.js +1 -19
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/file-system.js +34 -3
- package/dist/utils/file-system.js.map +1 -0
- package/dist/utils/github.js +21 -8
- package/dist/utils/github.js.map +1 -0
- package/dist/utils/json.js +2 -1
- package/dist/utils/json.js.map +1 -0
- package/dist/utils/logger.js +1 -14
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/state.js +3 -2
- package/dist/utils/state.js.map +1 -0
- package/dist/utils/validate.js +10 -1
- package/dist/utils/validate.js.map +1 -0
- package/dist/watch/dashboard.js +61 -0
- package/dist/watch/dashboard.js.map +1 -0
- package/dist/watch/file-watcher.js +30 -0
- package/dist/watch/file-watcher.js.map +1 -0
- package/dist/watch/renderer.js +242 -0
- package/dist/watch/renderer.js.map +1 -0
- package/dist/watch/state-reader.js +200 -0
- package/dist/watch/state-reader.js.map +1 -0
- package/dist/watch/types.js +2 -0
- package/dist/watch/types.js.map +1 -0
- package/package.json +11 -8
- package/ralph/drivers/copilot.sh +89 -0
- package/ralph/lib/circuit_breaker.sh +86 -59
- package/ralph/lib/enable_core.sh +3 -6
- package/ralph/lib/response_analyzer.sh +5 -29
- package/ralph/lib/task_sources.sh +45 -11
- package/ralph/lib/wizard_utils.sh +9 -0
- package/ralph/ralph_import.sh +7 -2
- package/ralph/ralph_loop.sh +29 -34
- package/ralph/ralph_monitor.sh +4 -0
- package/ralph/templates/ralphrc.template +1 -1
- package/slash-commands/bmalph-watch.md +22 -0
- package/dist/cli.d.ts +0 -1
- package/dist/commands/check-updates.d.ts +0 -5
- package/dist/commands/doctor.d.ts +0 -51
- package/dist/commands/implement.d.ts +0 -6
- package/dist/commands/init.d.ts +0 -9
- package/dist/commands/reset.d.ts +0 -7
- package/dist/commands/status.d.ts +0 -7
- package/dist/commands/upgrade.d.ts +0 -7
- package/dist/installer.d.ts +0 -39
- package/dist/platform/aider.d.ts +0 -2
- package/dist/platform/claude-code.d.ts +0 -2
- package/dist/platform/codex.d.ts +0 -2
- package/dist/platform/copilot.d.ts +0 -2
- package/dist/platform/cursor.d.ts +0 -2
- package/dist/platform/detect.d.ts +0 -7
- package/dist/platform/index.d.ts +0 -4
- package/dist/platform/registry.d.ts +0 -4
- package/dist/platform/resolve.d.ts +0 -8
- package/dist/platform/types.d.ts +0 -41
- package/dist/platform/windsurf.d.ts +0 -2
- package/dist/reset.d.ts +0 -18
- package/dist/transition/artifact-scan.d.ts +0 -27
- package/dist/transition/artifacts.d.ts +0 -3
- package/dist/transition/context.d.ts +0 -19
- package/dist/transition/fix-plan.d.ts +0 -21
- package/dist/transition/index.d.ts +0 -9
- package/dist/transition/orchestration.d.ts +0 -2
- package/dist/transition/preflight.d.ts +0 -6
- package/dist/transition/specs-changelog.d.ts +0 -3
- package/dist/transition/specs-index.d.ts +0 -22
- package/dist/transition/story-parsing.d.ts +0 -7
- package/dist/transition/tech-stack.d.ts +0 -3
- package/dist/transition/types.d.ts +0 -82
- package/dist/utils/config.d.ts +0 -13
- package/dist/utils/constants.d.ts +0 -70
- package/dist/utils/dryrun.d.ts +0 -7
- package/dist/utils/errors.d.ts +0 -63
- package/dist/utils/file-system.d.ts +0 -24
- package/dist/utils/github.d.ts +0 -83
- package/dist/utils/json.d.ts +0 -7
- package/dist/utils/logger.d.ts +0 -9
- package/dist/utils/state.d.ts +0 -26
- package/dist/utils/validate.d.ts +0 -44
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { open, readFile } from "node:fs/promises";
|
|
3
|
+
import { readJsonFile } from "../utils/json.js";
|
|
4
|
+
import { RALPH_DIR } from "../utils/constants.js";
|
|
5
|
+
import { parseFixPlan } from "../transition/fix-plan.js";
|
|
6
|
+
import { debug } from "../utils/logger.js";
|
|
7
|
+
import { formatError } from "../utils/errors.js";
|
|
8
|
+
import { validateCircuitBreakerState, validateRalphSession, normalizeRalphStatus, } from "../utils/validate.js";
|
|
9
|
+
const LOG_LINE_PATTERN = /^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] \[(\w+)\] (.+)$/;
|
|
10
|
+
const DEFAULT_MAX_LOG_LINES = 8;
|
|
11
|
+
const TAIL_BYTES = 4096;
|
|
12
|
+
export async function readDashboardState(projectDir) {
|
|
13
|
+
const [loop, circuitBreaker, stories, analysis, execution, session, recentLogs] = await Promise.all([
|
|
14
|
+
readLoopInfo(projectDir),
|
|
15
|
+
readCircuitBreakerInfo(projectDir),
|
|
16
|
+
readStoryProgress(projectDir),
|
|
17
|
+
readAnalysisInfo(projectDir),
|
|
18
|
+
readExecutionProgress(projectDir),
|
|
19
|
+
readSessionInfo(projectDir),
|
|
20
|
+
readRecentLogs(projectDir),
|
|
21
|
+
]);
|
|
22
|
+
const ralphCompleted = loop !== null && loop.status === "completed";
|
|
23
|
+
return {
|
|
24
|
+
loop,
|
|
25
|
+
circuitBreaker,
|
|
26
|
+
stories,
|
|
27
|
+
analysis,
|
|
28
|
+
execution,
|
|
29
|
+
session,
|
|
30
|
+
recentLogs,
|
|
31
|
+
ralphCompleted,
|
|
32
|
+
lastUpdated: new Date(),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export async function readLoopInfo(projectDir) {
|
|
36
|
+
try {
|
|
37
|
+
const data = await readJsonFile(join(projectDir, RALPH_DIR, "status.json"));
|
|
38
|
+
if (data === null)
|
|
39
|
+
return null;
|
|
40
|
+
const normalized = normalizeRalphStatus(data);
|
|
41
|
+
const lastAction = typeof data.last_action === "string" ? data.last_action : "";
|
|
42
|
+
const callsMadeThisHour = typeof data.calls_made_this_hour === "number" ? data.calls_made_this_hour : 0;
|
|
43
|
+
const maxCallsPerHour = typeof data.max_calls_per_hour === "number" ? data.max_calls_per_hour : 0;
|
|
44
|
+
return {
|
|
45
|
+
loopCount: normalized.loopCount,
|
|
46
|
+
status: normalized.status,
|
|
47
|
+
lastAction,
|
|
48
|
+
callsMadeThisHour,
|
|
49
|
+
maxCallsPerHour,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
debug(`Failed to read loop info: ${formatError(err)}`);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export async function readCircuitBreakerInfo(projectDir) {
|
|
58
|
+
try {
|
|
59
|
+
const data = await readJsonFile(join(projectDir, RALPH_DIR, ".circuit_breaker_state"));
|
|
60
|
+
if (data === null)
|
|
61
|
+
return null;
|
|
62
|
+
const validated = validateCircuitBreakerState(data);
|
|
63
|
+
const totalOpens = typeof data.total_opens === "number" ? data.total_opens : 0;
|
|
64
|
+
return {
|
|
65
|
+
state: validated.state,
|
|
66
|
+
consecutiveNoProgress: validated.consecutive_no_progress,
|
|
67
|
+
totalOpens,
|
|
68
|
+
reason: validated.reason,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
debug(`Failed to read circuit breaker info: ${formatError(err)}`);
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export async function readStoryProgress(projectDir) {
|
|
77
|
+
let content;
|
|
78
|
+
try {
|
|
79
|
+
content = await readFile(join(projectDir, RALPH_DIR, "@fix_plan.md"), "utf-8");
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
debug(`Failed to read fix plan: ${formatError(err)}`);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const items = parseFixPlan(content);
|
|
86
|
+
const completed = items.filter((item) => item.completed).length;
|
|
87
|
+
const total = items.length;
|
|
88
|
+
const nextItem = items.find((item) => !item.completed);
|
|
89
|
+
const nextStory = nextItem ? `Story ${nextItem.id}: ${nextItem.title ?? ""}`.trim() : null;
|
|
90
|
+
return { completed, total, nextStory };
|
|
91
|
+
}
|
|
92
|
+
export async function readAnalysisInfo(projectDir) {
|
|
93
|
+
try {
|
|
94
|
+
const data = await readJsonFile(join(projectDir, RALPH_DIR, ".response_analysis"));
|
|
95
|
+
if (data === null)
|
|
96
|
+
return null;
|
|
97
|
+
const analysis = data.analysis;
|
|
98
|
+
if (typeof analysis !== "object" || analysis === null)
|
|
99
|
+
return null;
|
|
100
|
+
const a = analysis;
|
|
101
|
+
const filesModified = typeof a.files_modified === "number" ? a.files_modified : 0;
|
|
102
|
+
const confidenceScore = typeof a.confidence_score === "number" ? a.confidence_score : 0;
|
|
103
|
+
const isTestOnly = typeof a.is_test_only === "boolean" ? a.is_test_only : false;
|
|
104
|
+
const isStuck = typeof a.is_stuck === "boolean" ? a.is_stuck : false;
|
|
105
|
+
const exitSignal = typeof a.exit_signal === "boolean" ? a.exit_signal : false;
|
|
106
|
+
const hasPermissionDenials = typeof a.has_permission_denials === "boolean" ? a.has_permission_denials : false;
|
|
107
|
+
const permissionDenialCount = typeof a.permission_denial_count === "number" ? a.permission_denial_count : 0;
|
|
108
|
+
return {
|
|
109
|
+
filesModified,
|
|
110
|
+
confidenceScore,
|
|
111
|
+
isTestOnly,
|
|
112
|
+
isStuck,
|
|
113
|
+
exitSignal,
|
|
114
|
+
hasPermissionDenials,
|
|
115
|
+
permissionDenialCount,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
debug(`Failed to read analysis info: ${formatError(err)}`);
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export async function readExecutionProgress(projectDir) {
|
|
124
|
+
try {
|
|
125
|
+
const data = await readJsonFile(join(projectDir, RALPH_DIR, "progress.json"));
|
|
126
|
+
if (data === null)
|
|
127
|
+
return null;
|
|
128
|
+
const status = typeof data.status === "string" ? data.status : "";
|
|
129
|
+
if (status !== "executing")
|
|
130
|
+
return null;
|
|
131
|
+
const elapsedSeconds = typeof data.elapsed_seconds === "number" ? data.elapsed_seconds : 0;
|
|
132
|
+
return { status, elapsedSeconds };
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
debug(`Failed to read execution progress: ${formatError(err)}`);
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export async function readSessionInfo(projectDir) {
|
|
140
|
+
try {
|
|
141
|
+
const data = await readJsonFile(join(projectDir, RALPH_DIR, ".ralph_session"));
|
|
142
|
+
if (data === null)
|
|
143
|
+
return null;
|
|
144
|
+
const validated = validateRalphSession(data);
|
|
145
|
+
return {
|
|
146
|
+
createdAt: validated.created_at,
|
|
147
|
+
lastUsed: validated.last_used,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
debug(`Failed to read session info: ${formatError(err)}`);
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
export async function readRecentLogs(projectDir, maxLines = DEFAULT_MAX_LOG_LINES) {
|
|
156
|
+
const logPath = join(projectDir, RALPH_DIR, "logs", "ralph.log");
|
|
157
|
+
let content;
|
|
158
|
+
try {
|
|
159
|
+
const fh = await open(logPath, "r");
|
|
160
|
+
try {
|
|
161
|
+
const stats = await fh.stat();
|
|
162
|
+
if (stats.size === 0) {
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
if (stats.size <= TAIL_BYTES) {
|
|
166
|
+
content = await fh.readFile("utf-8");
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
const position = stats.size - TAIL_BYTES;
|
|
170
|
+
const buf = Buffer.alloc(TAIL_BYTES);
|
|
171
|
+
const { bytesRead } = await fh.read(buf, 0, TAIL_BYTES, position);
|
|
172
|
+
const raw = buf.toString("utf-8", 0, bytesRead);
|
|
173
|
+
const newlineIdx = raw.indexOf("\n");
|
|
174
|
+
content = newlineIdx >= 0 ? raw.slice(newlineIdx + 1) : raw;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
await fh.close();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
debug(`Failed to read recent logs: ${formatError(err)}`);
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
const lines = content.split(/\r?\n/).filter((line) => line.length > 0);
|
|
186
|
+
const tail = lines.slice(-maxLines);
|
|
187
|
+
const entries = [];
|
|
188
|
+
for (const line of tail) {
|
|
189
|
+
const match = LOG_LINE_PATTERN.exec(line);
|
|
190
|
+
if (match) {
|
|
191
|
+
entries.push({
|
|
192
|
+
timestamp: match[1],
|
|
193
|
+
level: match[2],
|
|
194
|
+
message: match[3],
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return entries;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=state-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-reader.js","sourceRoot":"","sources":["../../src/watch/state-reader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/watch/types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bmalph",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Unified AI Development Framework - BMAD phases with Ralph execution loop
|
|
3
|
+
"version": "2.6.0",
|
|
4
|
+
"description": "Unified AI Development Framework - BMAD phases with Ralph execution loop",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"bmalph": "./bin/bmalph.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"prepare": "tsc",
|
|
11
10
|
"build": "tsc",
|
|
12
11
|
"test": "vitest run",
|
|
13
12
|
"test:watch": "vitest",
|
|
14
13
|
"test:e2e": "vitest run --config vitest.config.e2e.ts",
|
|
15
14
|
"test:coverage": "vitest run --coverage",
|
|
16
|
-
"test:
|
|
15
|
+
"test:bash": "bash -c 'command -v bats &>/dev/null && bats tests/bash/*.bats tests/bash/drivers/*.bats || echo \"[skip] bats not installed\"'",
|
|
16
|
+
"test:all": "npm run test && npm run test:e2e && npm run test:bash",
|
|
17
17
|
"check": "npm run lint && npm run build && npm test",
|
|
18
18
|
"dev": "tsc --watch",
|
|
19
19
|
"lint": "eslint src tests",
|
|
20
20
|
"lint:fix": "eslint src tests --fix",
|
|
21
|
-
"format": "prettier --write .",
|
|
22
21
|
"fmt:check": "prettier --check .",
|
|
23
22
|
"fmt:fix": "prettier --write .",
|
|
24
23
|
"type-check": "tsc --noEmit",
|
|
@@ -31,7 +30,11 @@
|
|
|
31
30
|
"ai",
|
|
32
31
|
"development",
|
|
33
32
|
"framework",
|
|
34
|
-
"agents"
|
|
33
|
+
"agents",
|
|
34
|
+
"bmad",
|
|
35
|
+
"ralph",
|
|
36
|
+
"autonomous",
|
|
37
|
+
"coding-assistant"
|
|
35
38
|
],
|
|
36
39
|
"author": "Lars Cowe",
|
|
37
40
|
"license": "MIT",
|
|
@@ -58,9 +61,9 @@
|
|
|
58
61
|
"bundled-versions.json"
|
|
59
62
|
],
|
|
60
63
|
"dependencies": {
|
|
64
|
+
"@inquirer/prompts": "^8.3.0",
|
|
61
65
|
"chalk": "^5.6.2",
|
|
62
|
-
"commander": "^14.0.2"
|
|
63
|
-
"inquirer": "^13.2.1"
|
|
66
|
+
"commander": "^14.0.2"
|
|
64
67
|
},
|
|
65
68
|
"devDependencies": {
|
|
66
69
|
"@eslint/js": "^10.0.1",
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# GitHub Copilot CLI driver for Ralph (EXPERIMENTAL)
|
|
3
|
+
# Provides platform-specific CLI invocation logic for Copilot CLI.
|
|
4
|
+
#
|
|
5
|
+
# Known limitations:
|
|
6
|
+
# - No session continuity (session IDs not capturable from -p output)
|
|
7
|
+
# - No structured output (plain text only, no --json flag)
|
|
8
|
+
# - Coarse tool permissions (only shell, shell(git:*), shell(npm:*), write)
|
|
9
|
+
# - CLI is new (GA Feb 25, 2026) — scripting interface may change
|
|
10
|
+
|
|
11
|
+
driver_name() {
|
|
12
|
+
echo "copilot"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
driver_display_name() {
|
|
16
|
+
echo "GitHub Copilot CLI"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
driver_cli_binary() {
|
|
20
|
+
echo "copilot"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
driver_min_version() {
|
|
24
|
+
echo "0.0.418"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
driver_check_available() {
|
|
28
|
+
command -v "$(driver_cli_binary)" &>/dev/null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# Copilot CLI tool names
|
|
32
|
+
driver_valid_tools() {
|
|
33
|
+
VALID_TOOL_PATTERNS=(
|
|
34
|
+
"shell"
|
|
35
|
+
"shell(git:*)"
|
|
36
|
+
"shell(npm:*)"
|
|
37
|
+
"write"
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Build Copilot CLI command
|
|
42
|
+
# Context is prepended to the prompt (same pattern as Codex driver).
|
|
43
|
+
# Uses --autopilot --yolo for autonomous mode, -s to strip stats, -p for prompt.
|
|
44
|
+
driver_build_command() {
|
|
45
|
+
local prompt_file=$1
|
|
46
|
+
local loop_context=$2
|
|
47
|
+
# $3 (session_id) is intentionally ignored — Copilot CLI does not
|
|
48
|
+
# expose session IDs in -p output, so resume is not possible.
|
|
49
|
+
|
|
50
|
+
CLAUDE_CMD_ARGS=("$(driver_cli_binary)")
|
|
51
|
+
|
|
52
|
+
if [[ ! -f "$prompt_file" ]]; then
|
|
53
|
+
echo "ERROR: Prompt file not found: $prompt_file" >&2
|
|
54
|
+
return 1
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Autonomous execution flags
|
|
58
|
+
CLAUDE_CMD_ARGS+=("--autopilot" "--yolo")
|
|
59
|
+
|
|
60
|
+
# Limit auto-continuation loops
|
|
61
|
+
CLAUDE_CMD_ARGS+=("--max-autopilot-continues" "50")
|
|
62
|
+
|
|
63
|
+
# Disable interactive prompts
|
|
64
|
+
CLAUDE_CMD_ARGS+=("--no-ask-user")
|
|
65
|
+
|
|
66
|
+
# Strip stats for cleaner output
|
|
67
|
+
CLAUDE_CMD_ARGS+=("-s")
|
|
68
|
+
|
|
69
|
+
# Build prompt with context prepended
|
|
70
|
+
local prompt_content
|
|
71
|
+
prompt_content=$(cat "$prompt_file")
|
|
72
|
+
if [[ -n "$loop_context" ]]; then
|
|
73
|
+
prompt_content="$loop_context
|
|
74
|
+
|
|
75
|
+
$prompt_content"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
CLAUDE_CMD_ARGS+=("-p" "$prompt_content")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
driver_supports_sessions() {
|
|
82
|
+
return 1 # false — session IDs not capturable from -p output
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Copilot CLI outputs plain text only (no JSON streaming).
|
|
86
|
+
# Passthrough filter — no transformation needed.
|
|
87
|
+
driver_stream_filter() {
|
|
88
|
+
echo '.'
|
|
89
|
+
}
|
|
@@ -43,18 +43,19 @@ init_circuit_breaker() {
|
|
|
43
43
|
fi
|
|
44
44
|
|
|
45
45
|
if [[ ! -f "$CB_STATE_FILE" ]]; then
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
46
|
+
jq -n \
|
|
47
|
+
--arg state "$CB_STATE_CLOSED" \
|
|
48
|
+
--arg last_change "$(get_iso_timestamp)" \
|
|
49
|
+
'{
|
|
50
|
+
state: $state,
|
|
51
|
+
last_change: $last_change,
|
|
52
|
+
consecutive_no_progress: 0,
|
|
53
|
+
consecutive_same_error: 0,
|
|
54
|
+
consecutive_permission_denials: 0,
|
|
55
|
+
last_progress_loop: 0,
|
|
56
|
+
total_opens: 0,
|
|
57
|
+
reason: ""
|
|
58
|
+
}' > "$CB_STATE_FILE"
|
|
58
59
|
fi
|
|
59
60
|
|
|
60
61
|
# Ensure history file exists before any transition logging
|
|
@@ -81,18 +82,20 @@ EOF
|
|
|
81
82
|
total_opens=$(jq -r '.total_opens // 0' "$CB_STATE_FILE" 2>/dev/null || echo "0")
|
|
82
83
|
log_circuit_transition "$CB_STATE_OPEN" "$CB_STATE_CLOSED" "Auto-reset on startup (CB_AUTO_RESET=true)" "$current_loop"
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
85
|
+
jq -n \
|
|
86
|
+
--arg state "$CB_STATE_CLOSED" \
|
|
87
|
+
--arg last_change "$(get_iso_timestamp)" \
|
|
88
|
+
--argjson total_opens "$total_opens" \
|
|
89
|
+
'{
|
|
90
|
+
state: $state,
|
|
91
|
+
last_change: $last_change,
|
|
92
|
+
consecutive_no_progress: 0,
|
|
93
|
+
consecutive_same_error: 0,
|
|
94
|
+
consecutive_permission_denials: 0,
|
|
95
|
+
last_progress_loop: 0,
|
|
96
|
+
total_opens: $total_opens,
|
|
97
|
+
reason: "Auto-reset on startup"
|
|
98
|
+
}' > "$CB_STATE_FILE"
|
|
96
99
|
else
|
|
97
100
|
# Cooldown: check if enough time has elapsed to transition to HALF_OPEN
|
|
98
101
|
local opened_at
|
|
@@ -296,20 +299,34 @@ record_loop_result() {
|
|
|
296
299
|
opened_at=$(echo "$state_data" | jq -r '.opened_at // .last_change // ""' 2>/dev/null)
|
|
297
300
|
fi
|
|
298
301
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
302
|
+
jq -n \
|
|
303
|
+
--arg state "$new_state" \
|
|
304
|
+
--arg last_change "$(get_iso_timestamp)" \
|
|
305
|
+
--argjson consecutive_no_progress "$consecutive_no_progress" \
|
|
306
|
+
--argjson consecutive_same_error "$consecutive_same_error" \
|
|
307
|
+
--argjson consecutive_permission_denials "$consecutive_permission_denials" \
|
|
308
|
+
--argjson last_progress_loop "$last_progress_loop" \
|
|
309
|
+
--argjson total_opens "$total_opens" \
|
|
310
|
+
--arg reason "$reason" \
|
|
311
|
+
--argjson current_loop "$loop_number" \
|
|
312
|
+
'{
|
|
313
|
+
state: $state,
|
|
314
|
+
last_change: $last_change,
|
|
315
|
+
consecutive_no_progress: $consecutive_no_progress,
|
|
316
|
+
consecutive_same_error: $consecutive_same_error,
|
|
317
|
+
consecutive_permission_denials: $consecutive_permission_denials,
|
|
318
|
+
last_progress_loop: $last_progress_loop,
|
|
319
|
+
total_opens: $total_opens,
|
|
320
|
+
reason: $reason,
|
|
321
|
+
current_loop: $current_loop
|
|
322
|
+
}' > "$CB_STATE_FILE"
|
|
323
|
+
|
|
324
|
+
# Add opened_at if set (entering or staying in OPEN state)
|
|
325
|
+
if [[ -n "$opened_at" ]]; then
|
|
326
|
+
local tmp
|
|
327
|
+
tmp=$(jq --arg opened_at "$opened_at" '. + {opened_at: $opened_at}' "$CB_STATE_FILE")
|
|
328
|
+
echo "$tmp" > "$CB_STATE_FILE"
|
|
329
|
+
fi
|
|
313
330
|
|
|
314
331
|
# Log state transition
|
|
315
332
|
if [[ "$new_state" != "$current_state" ]]; then
|
|
@@ -331,15 +348,23 @@ log_circuit_transition() {
|
|
|
331
348
|
local reason=$3
|
|
332
349
|
local loop_number=$4
|
|
333
350
|
|
|
334
|
-
local
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
351
|
+
local transition
|
|
352
|
+
transition=$(jq -n -c \
|
|
353
|
+
--arg timestamp "$(get_iso_timestamp)" \
|
|
354
|
+
--argjson loop "$loop_number" \
|
|
355
|
+
--arg from_state "$from_state" \
|
|
356
|
+
--arg to_state "$to_state" \
|
|
357
|
+
--arg reason "$reason" \
|
|
358
|
+
'{
|
|
359
|
+
timestamp: $timestamp,
|
|
360
|
+
loop: $loop,
|
|
361
|
+
from_state: $from_state,
|
|
362
|
+
to_state: $to_state,
|
|
363
|
+
reason: $reason
|
|
364
|
+
}')
|
|
365
|
+
|
|
366
|
+
local history
|
|
367
|
+
history=$(cat "$CB_HISTORY_FILE")
|
|
343
368
|
history=$(echo "$history" | jq ". += [$transition]")
|
|
344
369
|
echo "$history" > "$CB_HISTORY_FILE"
|
|
345
370
|
|
|
@@ -406,18 +431,20 @@ show_circuit_status() {
|
|
|
406
431
|
reset_circuit_breaker() {
|
|
407
432
|
local reason=${1:-"Manual reset"}
|
|
408
433
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
434
|
+
jq -n \
|
|
435
|
+
--arg state "$CB_STATE_CLOSED" \
|
|
436
|
+
--arg last_change "$(get_iso_timestamp)" \
|
|
437
|
+
--arg reason "$reason" \
|
|
438
|
+
'{
|
|
439
|
+
state: $state,
|
|
440
|
+
last_change: $last_change,
|
|
441
|
+
consecutive_no_progress: 0,
|
|
442
|
+
consecutive_same_error: 0,
|
|
443
|
+
consecutive_permission_denials: 0,
|
|
444
|
+
last_progress_loop: 0,
|
|
445
|
+
total_opens: 0,
|
|
446
|
+
reason: $reason
|
|
447
|
+
}' > "$CB_STATE_FILE"
|
|
421
448
|
|
|
422
449
|
echo -e "${GREEN}✅ Circuit breaker reset to CLOSED state${NC}"
|
|
423
450
|
}
|
package/ralph/lib/enable_core.sh
CHANGED
|
@@ -328,10 +328,9 @@ detect_project_context() {
|
|
|
328
328
|
DETECTED_TEST_CMD="pnpm test"
|
|
329
329
|
DETECTED_RUN_CMD="pnpm start"
|
|
330
330
|
fi
|
|
331
|
-
fi
|
|
332
331
|
|
|
333
332
|
# Detect from pyproject.toml or setup.py (Python)
|
|
334
|
-
|
|
333
|
+
elif [[ -f "pyproject.toml" ]] || [[ -f "setup.py" ]]; then
|
|
335
334
|
DETECTED_PROJECT_TYPE="python"
|
|
336
335
|
|
|
337
336
|
# Extract project name from pyproject.toml
|
|
@@ -358,19 +357,17 @@ detect_project_context() {
|
|
|
358
357
|
DETECTED_TEST_CMD="pytest"
|
|
359
358
|
DETECTED_RUN_CMD="python -m ${DETECTED_PROJECT_NAME:-main}"
|
|
360
359
|
fi
|
|
361
|
-
fi
|
|
362
360
|
|
|
363
361
|
# Detect from Cargo.toml (Rust)
|
|
364
|
-
|
|
362
|
+
elif [[ -f "Cargo.toml" ]]; then
|
|
365
363
|
DETECTED_PROJECT_TYPE="rust"
|
|
366
364
|
DETECTED_PROJECT_NAME=$(grep -m1 '^name' Cargo.toml | sed 's/.*= *"\([^"]*\)".*/\1/' 2>/dev/null)
|
|
367
365
|
DETECTED_BUILD_CMD="cargo build"
|
|
368
366
|
DETECTED_TEST_CMD="cargo test"
|
|
369
367
|
DETECTED_RUN_CMD="cargo run"
|
|
370
|
-
fi
|
|
371
368
|
|
|
372
369
|
# Detect from go.mod (Go)
|
|
373
|
-
|
|
370
|
+
elif [[ -f "go.mod" ]]; then
|
|
374
371
|
DETECTED_PROJECT_TYPE="go"
|
|
375
372
|
DETECTED_PROJECT_NAME=$(head -1 go.mod | sed 's/module //' 2>/dev/null)
|
|
376
373
|
DETECTED_BUILD_CMD="go build"
|
|
@@ -829,37 +829,13 @@ should_resume_session() {
|
|
|
829
829
|
fi
|
|
830
830
|
|
|
831
831
|
# Calculate session age using date utilities
|
|
832
|
-
local now
|
|
832
|
+
local now
|
|
833
|
+
now=$(get_epoch_seconds)
|
|
833
834
|
local session_time
|
|
835
|
+
session_time=$(parse_iso_to_epoch "$timestamp")
|
|
834
836
|
|
|
835
|
-
#
|
|
836
|
-
#
|
|
837
|
-
local clean_timestamp="${timestamp}"
|
|
838
|
-
if [[ "$timestamp" =~ \.[0-9]+[+-Z] ]]; then
|
|
839
|
-
clean_timestamp=$(echo "$timestamp" | sed 's/\.[0-9]*\([+-Z]\)/\1/')
|
|
840
|
-
fi
|
|
841
|
-
|
|
842
|
-
if command -v gdate &>/dev/null; then
|
|
843
|
-
# macOS with coreutils
|
|
844
|
-
session_time=$(gdate -d "$clean_timestamp" +%s 2>/dev/null)
|
|
845
|
-
elif date --version 2>&1 | grep -q GNU; then
|
|
846
|
-
# GNU date (Linux)
|
|
847
|
-
session_time=$(date -d "$clean_timestamp" +%s 2>/dev/null)
|
|
848
|
-
else
|
|
849
|
-
# BSD date (macOS without coreutils) - try parsing ISO format
|
|
850
|
-
# Format: 2026-01-09T10:30:00+00:00 or 2026-01-09T10:30:00Z
|
|
851
|
-
# Strip timezone suffix for BSD date parsing
|
|
852
|
-
local date_only="${clean_timestamp%[+-Z]*}"
|
|
853
|
-
session_time=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$date_only" +%s 2>/dev/null)
|
|
854
|
-
fi
|
|
855
|
-
|
|
856
|
-
# If we couldn't parse the timestamp, consider session expired
|
|
857
|
-
if [[ -z "$session_time" || ! "$session_time" =~ ^[0-9]+$ ]]; then
|
|
858
|
-
echo "false"
|
|
859
|
-
return 1
|
|
860
|
-
fi
|
|
861
|
-
|
|
862
|
-
# Calculate age in seconds
|
|
837
|
+
# If parse_iso_to_epoch fell back to current epoch, session_time ≈ now → age ≈ 0.
|
|
838
|
+
# That's a safe default: treat unparseable timestamps as fresh rather than expired.
|
|
863
839
|
local age=$((now - session_time))
|
|
864
840
|
|
|
865
841
|
# Check if session is still valid (less than expiration time)
|