ralphy-spec 0.1.0 → 0.2.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.ja.md +126 -0
- package/README.ko.md +126 -0
- package/README.md +77 -133
- package/README.zh.md +126 -0
- package/bin/ralphy-spec.js +0 -0
- package/dist/cli/checkpoint.d.ts +3 -0
- package/dist/cli/checkpoint.d.ts.map +1 -0
- package/dist/cli/checkpoint.js +23 -0
- package/dist/cli/checkpoint.js.map +1 -0
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +66 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/report.d.ts +3 -0
- package/dist/cli/report.d.ts.map +1 -0
- package/dist/cli/report.js +53 -0
- package/dist/cli/report.js.map +1 -0
- package/dist/cli/run.d.ts +3 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +79 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/status.d.ts +3 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +45 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/tail.d.ts +3 -0
- package/dist/cli/tail.d.ts.map +1 -0
- package/dist/cli/tail.js +46 -0
- package/dist/cli/tail.js.map +1 -0
- package/dist/cli/update.d.ts +3 -0
- package/dist/cli/update.d.ts.map +1 -0
- package/dist/cli/update.js +62 -0
- package/dist/cli/update.js.map +1 -0
- package/dist/cli/validate.d.ts +3 -0
- package/dist/cli/validate.d.ts.map +1 -0
- package/dist/cli/validate.js +83 -0
- package/dist/cli/validate.js.map +1 -0
- package/dist/core/backends/claude-code.d.ts +17 -0
- package/dist/core/backends/claude-code.d.ts.map +1 -0
- package/dist/core/backends/claude-code.js +75 -0
- package/dist/core/backends/claude-code.js.map +1 -0
- package/dist/core/backends/cursor.d.ts +17 -0
- package/dist/core/backends/cursor.d.ts.map +1 -0
- package/dist/core/backends/cursor.js +75 -0
- package/dist/core/backends/cursor.js.map +1 -0
- package/dist/core/backends/noop.d.ts +10 -0
- package/dist/core/backends/noop.d.ts.map +1 -0
- package/dist/core/backends/noop.js +17 -0
- package/dist/core/backends/noop.js.map +1 -0
- package/dist/core/backends/opencode.d.ts +16 -0
- package/dist/core/backends/opencode.d.ts.map +1 -0
- package/dist/core/backends/opencode.js +73 -0
- package/dist/core/backends/opencode.js.map +1 -0
- package/dist/core/backends/types.d.ts +21 -0
- package/dist/core/backends/types.d.ts.map +1 -0
- package/dist/core/backends/types.js +3 -0
- package/dist/core/backends/types.js.map +1 -0
- package/dist/core/budgets/manager.d.ts +21 -0
- package/dist/core/budgets/manager.d.ts.map +1 -0
- package/dist/core/budgets/manager.js +48 -0
- package/dist/core/budgets/manager.js.map +1 -0
- package/dist/core/budgets/state.d.ts +25 -0
- package/dist/core/budgets/state.d.ts.map +1 -0
- package/dist/core/budgets/state.js +33 -0
- package/dist/core/budgets/state.js.map +1 -0
- package/dist/core/budgets/tiers.d.ts +32 -0
- package/dist/core/budgets/tiers.d.ts.map +1 -0
- package/dist/core/budgets/tiers.js +67 -0
- package/dist/core/budgets/tiers.js.map +1 -0
- package/dist/core/engine/context-pack.d.ts +12 -0
- package/dist/core/engine/context-pack.d.ts.map +1 -0
- package/dist/core/engine/context-pack.js +45 -0
- package/dist/core/engine/context-pack.js.map +1 -0
- package/dist/core/engine/loop.d.ts +28 -0
- package/dist/core/engine/loop.d.ts.map +1 -0
- package/dist/core/engine/loop.js +366 -0
- package/dist/core/engine/loop.js.map +1 -0
- package/dist/core/engine/phases.d.ts +2 -0
- package/dist/core/engine/phases.d.ts.map +1 -0
- package/dist/core/engine/phases.js +3 -0
- package/dist/core/engine/phases.js.map +1 -0
- package/dist/core/engine/repair.d.ts +6 -0
- package/dist/core/engine/repair.d.ts.map +1 -0
- package/dist/core/engine/repair.js +23 -0
- package/dist/core/engine/repair.js.map +1 -0
- package/dist/core/folders.d.ts +26 -0
- package/dist/core/folders.d.ts.map +1 -0
- package/dist/core/folders.js +58 -0
- package/dist/core/folders.js.map +1 -0
- package/dist/core/memory/ledger.d.ts +13 -0
- package/dist/core/memory/ledger.d.ts.map +1 -0
- package/dist/core/memory/ledger.js +24 -0
- package/dist/core/memory/ledger.js.map +1 -0
- package/dist/core/memory/persistence.d.ts +45 -0
- package/dist/core/memory/persistence.d.ts.map +1 -0
- package/dist/core/memory/persistence.js +162 -0
- package/dist/core/memory/persistence.js.map +1 -0
- package/dist/core/reporting/spend.d.ts +40 -0
- package/dist/core/reporting/spend.d.ts.map +1 -0
- package/dist/core/reporting/spend.js +157 -0
- package/dist/core/reporting/spend.js.map +1 -0
- package/dist/core/spec/dag.d.ts +7 -0
- package/dist/core/spec/dag.d.ts.map +1 -0
- package/dist/core/spec/dag.js +65 -0
- package/dist/core/spec/dag.js.map +1 -0
- package/dist/core/spec/file-contract.d.ts +13 -0
- package/dist/core/spec/file-contract.d.ts.map +1 -0
- package/dist/core/spec/file-contract.js +29 -0
- package/dist/core/spec/file-contract.js.map +1 -0
- package/dist/core/spec/loader.d.ts +8 -0
- package/dist/core/spec/loader.d.ts.map +1 -0
- package/dist/core/spec/loader.js +51 -0
- package/dist/core/spec/loader.js.map +1 -0
- package/dist/core/spec/schemas.d.ts +278 -0
- package/dist/core/spec/schemas.d.ts.map +1 -0
- package/dist/core/spec/schemas.js +207 -0
- package/dist/core/spec/schemas.js.map +1 -0
- package/dist/core/spec/types.d.ts +71 -0
- package/dist/core/spec/types.d.ts.map +1 -0
- package/dist/core/spec/types.js +3 -0
- package/dist/core/spec/types.js.map +1 -0
- package/dist/core/validators/parsers/eslint.d.ts +3 -0
- package/dist/core/validators/parsers/eslint.d.ts.map +1 -0
- package/dist/core/validators/parsers/eslint.js +35 -0
- package/dist/core/validators/parsers/eslint.js.map +1 -0
- package/dist/core/validators/parsers/jest.d.ts +3 -0
- package/dist/core/validators/parsers/jest.d.ts.map +1 -0
- package/dist/core/validators/parsers/jest.js +16 -0
- package/dist/core/validators/parsers/jest.js.map +1 -0
- package/dist/core/validators/parsers/tsc.d.ts +3 -0
- package/dist/core/validators/parsers/tsc.d.ts.map +1 -0
- package/dist/core/validators/parsers/tsc.js +32 -0
- package/dist/core/validators/parsers/tsc.js.map +1 -0
- package/dist/core/validators/runner.d.ts +8 -0
- package/dist/core/validators/runner.d.ts.map +1 -0
- package/dist/core/validators/runner.js +85 -0
- package/dist/core/validators/runner.js.map +1 -0
- package/dist/core/validators/signatures.d.ts +3 -0
- package/dist/core/validators/signatures.d.ts.map +1 -0
- package/dist/core/validators/signatures.js +10 -0
- package/dist/core/validators/signatures.js.map +1 -0
- package/dist/core/validators/types.d.ts +27 -0
- package/dist/core/validators/types.d.ts.map +1 -0
- package/dist/core/validators/types.js +3 -0
- package/dist/core/validators/types.js.map +1 -0
- package/dist/core/workspace/contract-enforcer.d.ts +54 -0
- package/dist/core/workspace/contract-enforcer.d.ts.map +1 -0
- package/dist/core/workspace/contract-enforcer.js +128 -0
- package/dist/core/workspace/contract-enforcer.js.map +1 -0
- package/dist/core/workspace/manager.d.ts +28 -0
- package/dist/core/workspace/manager.d.ts.map +1 -0
- package/dist/core/workspace/manager.js +3 -0
- package/dist/core/workspace/manager.js.map +1 -0
- package/dist/core/workspace/merge.d.ts +38 -0
- package/dist/core/workspace/merge.d.ts.map +1 -0
- package/dist/core/workspace/merge.js +92 -0
- package/dist/core/workspace/merge.js.map +1 -0
- package/dist/core/workspace/patch-mode.d.ts +22 -0
- package/dist/core/workspace/patch-mode.d.ts.map +1 -0
- package/dist/core/workspace/patch-mode.js +91 -0
- package/dist/core/workspace/patch-mode.js.map +1 -0
- package/dist/core/workspace/worktree-mode.d.ts +28 -0
- package/dist/core/workspace/worktree-mode.d.ts.map +1 -0
- package/dist/core/workspace/worktree-mode.js +156 -0
- package/dist/core/workspace/worktree-mode.js.map +1 -0
- package/dist/index.js +14 -4
- package/dist/index.js.map +1 -1
- package/dist/templates/claude-code/ralphy-archive.md +22 -0
- package/dist/templates/claude-code/ralphy-implement.md +30 -0
- package/dist/templates/claude-code/ralphy-plan.md +31 -0
- package/dist/templates/claude-code/ralphy-validate.md +21 -0
- package/dist/templates/cursor/ralphy-archive.md +20 -0
- package/dist/templates/cursor/ralphy-implement.md +31 -0
- package/dist/templates/cursor/ralphy-plan.md +39 -0
- package/dist/templates/cursor/ralphy-validate.md +24 -0
- package/dist/templates/opencode/AGENTS.md +46 -0
- package/dist/templates/shared/openspec-tasks-template.md +48 -0
- package/dist/templates/shared/project-template.yml +232 -0
- package/dist/templates/shared/ralph-loop-prompt-template.md +25 -0
- package/dist/utils/installer.d.ts.map +1 -1
- package/dist/utils/installer.js +31 -1
- package/dist/utils/installer.js.map +1 -1
- package/dist/utils/validator.d.ts.map +1 -1
- package/dist/utils/validator.js +10 -0
- package/dist/utils/validator.js.map +1 -1
- package/package.json +14 -4
- package/scripts/copy-templates.mjs +2 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PatchModeWorkspace = void 0;
|
|
7
|
+
exports.getPatchWorkspaceRoot = getPatchWorkspaceRoot;
|
|
8
|
+
const execa_1 = require("execa");
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const file_contract_1 = require("../spec/file-contract");
|
|
11
|
+
class PatchModeWorkspace {
|
|
12
|
+
repoRoot;
|
|
13
|
+
mode = "patch";
|
|
14
|
+
stateByTask = new Map();
|
|
15
|
+
constructor(repoRoot) {
|
|
16
|
+
this.repoRoot = repoRoot;
|
|
17
|
+
}
|
|
18
|
+
async prepare(taskId) {
|
|
19
|
+
const snapshotCommit = await this.git(["rev-parse", "HEAD"]);
|
|
20
|
+
this.stateByTask.set(taskId, { snapshotCommit });
|
|
21
|
+
return { taskId, workingDir: this.repoRoot };
|
|
22
|
+
}
|
|
23
|
+
getWorkingDir(_taskId) {
|
|
24
|
+
return this.repoRoot;
|
|
25
|
+
}
|
|
26
|
+
async getChangedFiles(_taskId) {
|
|
27
|
+
// name-status gives: A/M/D/R... <path> ...
|
|
28
|
+
const out = await this.git(["diff", "--name-status"]);
|
|
29
|
+
const lines = out
|
|
30
|
+
.split("\n")
|
|
31
|
+
.map((l) => l.trim())
|
|
32
|
+
.filter(Boolean);
|
|
33
|
+
const changed = [];
|
|
34
|
+
for (const line of lines) {
|
|
35
|
+
const parts = line.split(/\s+/);
|
|
36
|
+
const status = parts[0] ?? "";
|
|
37
|
+
const file = parts[1] ?? "";
|
|
38
|
+
if (!file)
|
|
39
|
+
continue;
|
|
40
|
+
changed.push({ file, isNew: status.startsWith("A") });
|
|
41
|
+
}
|
|
42
|
+
return changed;
|
|
43
|
+
}
|
|
44
|
+
async enforceContract(taskId, contract) {
|
|
45
|
+
const changedFiles = await this.getChangedFiles(taskId);
|
|
46
|
+
const violations = (0, file_contract_1.evaluateFileContract)({ changedFiles, contract });
|
|
47
|
+
if (violations.length) {
|
|
48
|
+
await this.revert(taskId);
|
|
49
|
+
}
|
|
50
|
+
return violations;
|
|
51
|
+
}
|
|
52
|
+
async checkpoint(taskId, message) {
|
|
53
|
+
// Best effort: commit all changes. If nothing to commit, return HEAD.
|
|
54
|
+
await this.git(["add", "-A"]);
|
|
55
|
+
const commitMsg = `[ralphy-spec] ${taskId}: ${message}`;
|
|
56
|
+
try {
|
|
57
|
+
await this.git(["commit", "-m", commitMsg]);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// likely "nothing to commit"
|
|
61
|
+
}
|
|
62
|
+
const ref = await this.git(["rev-parse", "HEAD"]);
|
|
63
|
+
return { ref };
|
|
64
|
+
}
|
|
65
|
+
async merge(_taskId) {
|
|
66
|
+
// Patch mode executes on main; merge is no-op.
|
|
67
|
+
}
|
|
68
|
+
async revert(taskId) {
|
|
69
|
+
const state = this.stateByTask.get(taskId);
|
|
70
|
+
if (!state)
|
|
71
|
+
return;
|
|
72
|
+
// Hard revert to snapshot; also remove untracked files.
|
|
73
|
+
await this.git(["reset", "--hard", state.snapshotCommit]);
|
|
74
|
+
await this.git(["clean", "-fd"]);
|
|
75
|
+
}
|
|
76
|
+
async cleanup(_taskId) {
|
|
77
|
+
// No-op for patch mode.
|
|
78
|
+
}
|
|
79
|
+
async git(args) {
|
|
80
|
+
const res = await (0, execa_1.execa)("git", args, {
|
|
81
|
+
cwd: this.repoRoot,
|
|
82
|
+
stdio: "pipe",
|
|
83
|
+
});
|
|
84
|
+
return res.stdout.trim();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
exports.PatchModeWorkspace = PatchModeWorkspace;
|
|
88
|
+
function getPatchWorkspaceRoot(repoRoot) {
|
|
89
|
+
return node_path_1.default.resolve(repoRoot);
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=patch-mode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patch-mode.js","sourceRoot":"","sources":["../../../src/core/workspace/patch-mode.ts"],"names":[],"mappings":";;;;;;AAkGA,sDAEC;AApGD,iCAA8B;AAC9B,0DAA6B;AAE7B,yDAA6D;AAY7D,MAAa,kBAAkB;IAIA;IAH7B,IAAI,GAAY,OAAO,CAAC;IACP,WAAW,GAAG,IAAI,GAAG,EAA0B,CAAC;IAEjE,YAA6B,QAAgB;QAAhB,aAAQ,GAAR,QAAQ,CAAQ;IAAG,CAAC;IAEjD,KAAK,CAAC,OAAO,CAAC,MAAc;QAC1B,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;QAC7D,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC;QACjD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC/C,CAAC;IAED,aAAa,CAAC,OAAe;QAC3B,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,OAAe;QACnC,2CAA2C;QAC3C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,GAAG;aACd,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,MAAM,CAAC,OAAO,CAAC,CAAC;QAEnB,MAAM,OAAO,GAA4C,EAAE,CAAC;QAC5D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAChC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,CAAC,IAAI;gBAAE,SAAS;YACpB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,MAAc,EAAE,QAAsB;QAC1D,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QACxD,MAAM,UAAU,GAAG,IAAA,oCAAoB,EAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAC;QACpE,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;YACtB,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,MAAc,EAAE,OAAe;QAC9C,sEAAsE;QACtE,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QAC9B,MAAM,SAAS,GAAG,iBAAiB,MAAM,KAAK,OAAO,EAAE,CAAC;QACxD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,6BAA6B;QAC/B,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;QAClD,OAAO,EAAE,GAAG,EAAE,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,OAAe;QACzB,+CAA+C;IACjD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,MAAc;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,CAAC,KAAK;YAAE,OAAO;QAEnB,wDAAwD;QACxD,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;QAC1D,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAe;QAC3B,wBAAwB;IAC1B,CAAC;IAEO,KAAK,CAAC,GAAG,CAAC,IAAc;QAC9B,MAAM,GAAG,GAAG,MAAM,IAAA,aAAK,EAAC,KAAK,EAAE,IAAI,EAAE;YACnC,GAAG,EAAE,IAAI,CAAC,QAAQ;YAClB,KAAK,EAAE,MAAM;SACd,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;CACF;AAjFD,gDAiFC;AAED,SAAgB,qBAAqB,CAAC,QAAgB;IACpD,OAAO,mBAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FileContract } from "../spec/types";
|
|
2
|
+
import type { CheckpointRef, ContractViolation, WorkspaceContext, WorkspaceManager } from "./manager";
|
|
3
|
+
/**
|
|
4
|
+
* WorktreeModeWorkspace uses git worktrees for task isolation.
|
|
5
|
+
*
|
|
6
|
+
* Each task runs in its own worktree branch, providing complete isolation
|
|
7
|
+
* from the main working directory. Changes are merged back on success.
|
|
8
|
+
*/
|
|
9
|
+
export declare class WorktreeModeWorkspace implements WorkspaceManager {
|
|
10
|
+
private readonly repoRoot;
|
|
11
|
+
mode: "worktree";
|
|
12
|
+
private readonly stateByTask;
|
|
13
|
+
private readonly worktreeBase;
|
|
14
|
+
constructor(repoRoot: string);
|
|
15
|
+
prepare(taskId: string): Promise<WorkspaceContext>;
|
|
16
|
+
getWorkingDir(taskId: string): string;
|
|
17
|
+
getChangedFiles(taskId: string): Promise<Array<{
|
|
18
|
+
file: string;
|
|
19
|
+
isNew: boolean;
|
|
20
|
+
}>>;
|
|
21
|
+
enforceContract(taskId: string, contract: FileContract): Promise<ContractViolation[]>;
|
|
22
|
+
checkpoint(taskId: string, message: string): Promise<CheckpointRef>;
|
|
23
|
+
merge(taskId: string): Promise<void>;
|
|
24
|
+
revert(taskId: string): Promise<void>;
|
|
25
|
+
cleanup(taskId: string): Promise<void>;
|
|
26
|
+
private git;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=worktree-mode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree-mode.d.ts","sourceRoot":"","sources":["../../../src/core/workspace/worktree-mode.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,WAAW,CAAC;AAQnB;;;;;GAKG;AACH,qBAAa,qBAAsB,YAAW,gBAAgB;IAKhD,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJrC,IAAI,EAAE,UAAU,CAAc;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoC;IAChE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAET,QAAQ,EAAE,MAAM;IAIvC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAoCxD,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAK/B,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IA2BjF,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAYrF,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAoBnE,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBpC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASrC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAiB9B,GAAG;CAalB"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.WorktreeModeWorkspace = void 0;
|
|
7
|
+
const execa_1 = require("execa");
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
10
|
+
const file_contract_1 = require("../spec/file-contract");
|
|
11
|
+
/**
|
|
12
|
+
* WorktreeModeWorkspace uses git worktrees for task isolation.
|
|
13
|
+
*
|
|
14
|
+
* Each task runs in its own worktree branch, providing complete isolation
|
|
15
|
+
* from the main working directory. Changes are merged back on success.
|
|
16
|
+
*/
|
|
17
|
+
class WorktreeModeWorkspace {
|
|
18
|
+
repoRoot;
|
|
19
|
+
mode = "worktree";
|
|
20
|
+
stateByTask = new Map();
|
|
21
|
+
worktreeBase;
|
|
22
|
+
constructor(repoRoot) {
|
|
23
|
+
this.repoRoot = repoRoot;
|
|
24
|
+
this.worktreeBase = node_path_1.default.join(repoRoot, ".ralphy", "worktrees");
|
|
25
|
+
}
|
|
26
|
+
async prepare(taskId) {
|
|
27
|
+
// Ensure worktree base directory exists
|
|
28
|
+
await promises_1.default.mkdir(this.worktreeBase, { recursive: true });
|
|
29
|
+
// Get current HEAD as base
|
|
30
|
+
const baseCommit = await this.git(["rev-parse", "HEAD"], this.repoRoot);
|
|
31
|
+
// Create a unique branch name for this task
|
|
32
|
+
const branchName = `ralphy/${taskId}/${Date.now()}`;
|
|
33
|
+
const worktreePath = node_path_1.default.join(this.worktreeBase, taskId.replace(/[^a-zA-Z0-9-_]/g, "_"));
|
|
34
|
+
// Clean up existing worktree if present
|
|
35
|
+
try {
|
|
36
|
+
await promises_1.default.rm(worktreePath, { recursive: true, force: true });
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Ignore if doesn't exist
|
|
40
|
+
}
|
|
41
|
+
// Remove stale worktree entry if any
|
|
42
|
+
try {
|
|
43
|
+
await this.git(["worktree", "remove", "--force", worktreePath], this.repoRoot);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Ignore if doesn't exist
|
|
47
|
+
}
|
|
48
|
+
// Create new worktree with a new branch
|
|
49
|
+
await this.git(["worktree", "add", "-b", branchName, worktreePath, baseCommit], this.repoRoot);
|
|
50
|
+
this.stateByTask.set(taskId, { worktreePath, branchName, baseCommit });
|
|
51
|
+
return { taskId, workingDir: worktreePath };
|
|
52
|
+
}
|
|
53
|
+
getWorkingDir(taskId) {
|
|
54
|
+
const state = this.stateByTask.get(taskId);
|
|
55
|
+
return state?.worktreePath ?? this.repoRoot;
|
|
56
|
+
}
|
|
57
|
+
async getChangedFiles(taskId) {
|
|
58
|
+
const state = this.stateByTask.get(taskId);
|
|
59
|
+
if (!state)
|
|
60
|
+
return [];
|
|
61
|
+
// Get changes compared to base commit
|
|
62
|
+
const out = await this.git(["diff", "--name-status", state.baseCommit], state.worktreePath);
|
|
63
|
+
const lines = out
|
|
64
|
+
.split("\n")
|
|
65
|
+
.map((l) => l.trim())
|
|
66
|
+
.filter(Boolean);
|
|
67
|
+
const changed = [];
|
|
68
|
+
for (const line of lines) {
|
|
69
|
+
const parts = line.split(/\s+/);
|
|
70
|
+
const status = parts[0] ?? "";
|
|
71
|
+
const file = parts[1] ?? "";
|
|
72
|
+
if (!file)
|
|
73
|
+
continue;
|
|
74
|
+
changed.push({ file, isNew: status.startsWith("A") });
|
|
75
|
+
}
|
|
76
|
+
return changed;
|
|
77
|
+
}
|
|
78
|
+
async enforceContract(taskId, contract) {
|
|
79
|
+
const changedFiles = await this.getChangedFiles(taskId);
|
|
80
|
+
const violations = (0, file_contract_1.evaluateFileContract)({ changedFiles, contract });
|
|
81
|
+
if (violations.length) {
|
|
82
|
+
// Revert the worktree to base state
|
|
83
|
+
await this.revert(taskId);
|
|
84
|
+
}
|
|
85
|
+
return violations;
|
|
86
|
+
}
|
|
87
|
+
async checkpoint(taskId, message) {
|
|
88
|
+
const state = this.stateByTask.get(taskId);
|
|
89
|
+
if (!state) {
|
|
90
|
+
throw new Error(`No worktree state for task ${taskId}`);
|
|
91
|
+
}
|
|
92
|
+
// Stage and commit all changes in worktree
|
|
93
|
+
await this.git(["add", "-A"], state.worktreePath);
|
|
94
|
+
const commitMsg = `[ralphy-spec] ${taskId}: ${message}`;
|
|
95
|
+
try {
|
|
96
|
+
await this.git(["commit", "-m", commitMsg], state.worktreePath);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Likely nothing to commit
|
|
100
|
+
}
|
|
101
|
+
const ref = await this.git(["rev-parse", "HEAD"], state.worktreePath);
|
|
102
|
+
return { ref };
|
|
103
|
+
}
|
|
104
|
+
async merge(taskId) {
|
|
105
|
+
const state = this.stateByTask.get(taskId);
|
|
106
|
+
if (!state)
|
|
107
|
+
return;
|
|
108
|
+
// Get the current branch in main repo
|
|
109
|
+
const currentBranch = await this.git(["rev-parse", "--abbrev-ref", "HEAD"], this.repoRoot);
|
|
110
|
+
// Merge the task branch back using --squash for clean history
|
|
111
|
+
try {
|
|
112
|
+
await this.git(["merge", "--squash", state.branchName], this.repoRoot);
|
|
113
|
+
await this.git(["commit", "-m", `[ralphy-spec] Merge task ${taskId}`], this.repoRoot);
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
// If merge fails, user may need to resolve manually
|
|
117
|
+
throw new Error(`Failed to merge task ${taskId}: ${err?.message ?? String(err)}. Manual resolution may be required.`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async revert(taskId) {
|
|
121
|
+
const state = this.stateByTask.get(taskId);
|
|
122
|
+
if (!state)
|
|
123
|
+
return;
|
|
124
|
+
// Hard reset worktree to base commit
|
|
125
|
+
await this.git(["reset", "--hard", state.baseCommit], state.worktreePath);
|
|
126
|
+
await this.git(["clean", "-fd"], state.worktreePath);
|
|
127
|
+
}
|
|
128
|
+
async cleanup(taskId) {
|
|
129
|
+
const state = this.stateByTask.get(taskId);
|
|
130
|
+
if (!state)
|
|
131
|
+
return;
|
|
132
|
+
try {
|
|
133
|
+
// Remove the worktree
|
|
134
|
+
await this.git(["worktree", "remove", "--force", state.worktreePath], this.repoRoot);
|
|
135
|
+
// Delete the branch
|
|
136
|
+
await this.git(["branch", "-D", state.branchName], this.repoRoot);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Best effort cleanup
|
|
140
|
+
}
|
|
141
|
+
this.stateByTask.delete(taskId);
|
|
142
|
+
}
|
|
143
|
+
async git(args, cwd) {
|
|
144
|
+
const res = await (0, execa_1.execa)("git", args, {
|
|
145
|
+
cwd,
|
|
146
|
+
stdio: "pipe",
|
|
147
|
+
reject: false,
|
|
148
|
+
});
|
|
149
|
+
if (res.exitCode !== 0 && res.stderr) {
|
|
150
|
+
throw new Error(`git ${args.join(" ")} failed: ${res.stderr}`);
|
|
151
|
+
}
|
|
152
|
+
return res.stdout.trim();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
exports.WorktreeModeWorkspace = WorktreeModeWorkspace;
|
|
156
|
+
//# sourceMappingURL=worktree-mode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree-mode.js","sourceRoot":"","sources":["../../../src/core/workspace/worktree-mode.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const commander_1 = require("commander");
|
|
4
|
-
const init_1 = require("./
|
|
5
|
-
const validate_1 = require("./
|
|
6
|
-
const update_1 = require("./
|
|
4
|
+
const init_1 = require("./cli/init");
|
|
5
|
+
const validate_1 = require("./cli/validate");
|
|
6
|
+
const update_1 = require("./cli/update");
|
|
7
|
+
const run_1 = require("./cli/run");
|
|
8
|
+
const status_1 = require("./cli/status");
|
|
9
|
+
const report_1 = require("./cli/report");
|
|
10
|
+
const tail_1 = require("./cli/tail");
|
|
11
|
+
const checkpoint_1 = require("./cli/checkpoint");
|
|
7
12
|
function buildProgram() {
|
|
8
13
|
const program = new commander_1.Command();
|
|
9
14
|
program
|
|
10
15
|
.name("ralphy-spec")
|
|
11
16
|
.description("One-command setup for Ralph loop + OpenSpec workflows across Cursor, OpenCode, and Claude Code.")
|
|
12
|
-
.version("0.1.
|
|
17
|
+
.version("0.1.2");
|
|
13
18
|
(0, init_1.registerInitCommand)(program);
|
|
14
19
|
(0, validate_1.registerValidateCommand)(program);
|
|
15
20
|
(0, update_1.registerUpdateCommand)(program);
|
|
21
|
+
(0, run_1.registerRunCommand)(program);
|
|
22
|
+
(0, status_1.registerStatusCommand)(program);
|
|
23
|
+
(0, report_1.registerReportCommand)(program);
|
|
24
|
+
(0, tail_1.registerTailCommand)(program);
|
|
25
|
+
(0, checkpoint_1.registerCheckpointCommand)(program);
|
|
16
26
|
return program;
|
|
17
27
|
}
|
|
18
28
|
async function main() {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,yCAAoC;AACpC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,yCAAoC;AACpC,qCAAiD;AACjD,6CAAyD;AACzD,yCAAqD;AACrD,mCAA+C;AAC/C,yCAAqD;AACrD,yCAAqD;AACrD,qCAAiD;AACjD,iDAA6D;AAE7D,SAAS,YAAY;IACnB,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,aAAa,CAAC;SACnB,WAAW,CACV,iGAAiG,CAClG;SACA,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,IAAA,0BAAmB,EAAC,OAAO,CAAC,CAAC;IAC7B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,wBAAkB,EAAC,OAAO,CAAC,CAAC;IAC5B,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,0BAAmB,EAAC,OAAO,CAAC,CAAC;IAC7B,IAAA,sCAAyB,EAAC,OAAO,CAAC,CAAC;IAEnC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,YAAY,EAAE,CAAC;IAC/B,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,mEAAmE;AACnE,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# /ralphy:archive (Archive completed change)
|
|
2
|
+
|
|
3
|
+
You are archiving a completed OpenSpec change.
|
|
4
|
+
|
|
5
|
+
## Preconditions
|
|
6
|
+
- All tasks are complete
|
|
7
|
+
- Tests are green
|
|
8
|
+
- Spec deltas reflect final behavior
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
1. Run tests to confirm green.
|
|
12
|
+
2. If OpenSpec CLI is available, archive via:
|
|
13
|
+
- `openspec archive <change-name> --yes`
|
|
14
|
+
3. Otherwise, move the change folder from:
|
|
15
|
+
- `openspec/changes/<change-name>/`
|
|
16
|
+
to:
|
|
17
|
+
- `openspec/archive/<change-name>/`
|
|
18
|
+
4. Confirm `openspec/specs/` is updated appropriately.
|
|
19
|
+
|
|
20
|
+
## Output
|
|
21
|
+
Provide a short summary and a test plan.
|
|
22
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# /ralphy:implement (Implement OpenSpec tasks)
|
|
2
|
+
|
|
3
|
+
You are implementing an OpenSpec change located under `openspec/changes/<change-name>/`.
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
Complete all tasks in `tasks.md` and satisfy the acceptance criteria from the spec scenarios.
|
|
7
|
+
|
|
8
|
+
## Ralph loop compatibility
|
|
9
|
+
If this command is being executed in an iterative loop:
|
|
10
|
+
- Make progress each iteration
|
|
11
|
+
- Run tests frequently
|
|
12
|
+
- Only declare success when verification passes
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
1. Identify the active change folder under `openspec/changes/`.
|
|
16
|
+
2. Read the change artifacts:
|
|
17
|
+
- `proposal.md`
|
|
18
|
+
- `tasks.md`
|
|
19
|
+
- spec deltas under `specs/`
|
|
20
|
+
3. Implement tasks in order:
|
|
21
|
+
- Update code
|
|
22
|
+
- Add/update tests
|
|
23
|
+
- Run tests
|
|
24
|
+
- Mark tasks complete ONLY when verified
|
|
25
|
+
|
|
26
|
+
## Completion promise
|
|
27
|
+
Only output this exact text when ALL tasks are complete and tests pass:
|
|
28
|
+
|
|
29
|
+
<promise>TASK_COMPLETE</promise>
|
|
30
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# /ralphy:plan (PRD -> OpenSpec change)
|
|
2
|
+
|
|
3
|
+
You are an AI coding assistant. Convert the user's PRD/requirements into an OpenSpec change proposal with clear, testable acceptance criteria.
|
|
4
|
+
|
|
5
|
+
## Deliverables (create/modify files)
|
|
6
|
+
Create a new change folder:
|
|
7
|
+
- `openspec/changes/<change-name>/proposal.md`
|
|
8
|
+
- `openspec/changes/<change-name>/tasks.md`
|
|
9
|
+
- `openspec/changes/<change-name>/specs/<domain>/spec.md` (and others as needed)
|
|
10
|
+
|
|
11
|
+
## Rules
|
|
12
|
+
- Use MUST/SHALL language for requirements.
|
|
13
|
+
- Every `### Requirement:` MUST include at least one `#### Scenario:`.
|
|
14
|
+
- Include acceptance criteria that can be validated by tests or deterministic commands.
|
|
15
|
+
- Keep scope explicit; list non-goals.
|
|
16
|
+
|
|
17
|
+
## Procedure
|
|
18
|
+
1. Read `openspec/project.md` and relevant specs under `openspec/specs/`.
|
|
19
|
+
2. Propose a kebab-case change name (e.g. `add-profile-filters`).
|
|
20
|
+
3. Create `proposal.md` explaining why/what and the constraints.
|
|
21
|
+
4. Write spec deltas under `specs/` using:
|
|
22
|
+
- `## ADDED Requirements`
|
|
23
|
+
- `## MODIFIED Requirements`
|
|
24
|
+
- `## REMOVED Requirements`
|
|
25
|
+
5. Write `tasks.md` as a numbered checklist. Each task includes:
|
|
26
|
+
- Implementation notes
|
|
27
|
+
- Test plan (what to run, what to assert)
|
|
28
|
+
|
|
29
|
+
## Output
|
|
30
|
+
Summarize created files and tell the user what to run next (typically implementation).
|
|
31
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# /ralphy:validate (Validate against acceptance criteria)
|
|
2
|
+
|
|
3
|
+
You are validating an OpenSpec change.
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
Prove that the implementation matches the requirements/scenarios and that tests pass.
|
|
7
|
+
|
|
8
|
+
## Steps
|
|
9
|
+
1. Identify the active change folder under `openspec/changes/`.
|
|
10
|
+
2. Extract acceptance criteria from:
|
|
11
|
+
- spec scenarios in `openspec/changes/<change-name>/specs/**`
|
|
12
|
+
- test plan notes in `tasks.md`
|
|
13
|
+
3. Run tests and/or deterministic verification commands.
|
|
14
|
+
4. If failures occur, fix them and re-run until green.
|
|
15
|
+
|
|
16
|
+
## Output
|
|
17
|
+
Report:
|
|
18
|
+
- What passed
|
|
19
|
+
- What failed (with next actions)
|
|
20
|
+
- Any missing tests needed to satisfy acceptance criteria
|
|
21
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# /ralphy:archive (Archive completed change)
|
|
2
|
+
|
|
3
|
+
You are an AI coding assistant archiving a completed OpenSpec change.
|
|
4
|
+
|
|
5
|
+
## Preconditions
|
|
6
|
+
- All tasks in `openspec/changes/<change-name>/tasks.md` are complete
|
|
7
|
+
- Tests pass
|
|
8
|
+
- Spec deltas are correct and reflect the final behavior
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
1. Run validation (tests) and ensure green.
|
|
12
|
+
2. Archive the change by moving it into `openspec/archive/` (or using the OpenSpec CLI if available).
|
|
13
|
+
- If OpenSpec CLI is installed, prefer:
|
|
14
|
+
- `openspec archive <change-name> --yes`
|
|
15
|
+
3. Confirm the source-of-truth specs in `openspec/specs/` are updated appropriately.
|
|
16
|
+
4. Provide a short release-style summary:
|
|
17
|
+
- What changed
|
|
18
|
+
- How to test
|
|
19
|
+
- Any follow-up work
|
|
20
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# /ralphy:implement (Implement OpenSpec tasks)
|
|
2
|
+
|
|
3
|
+
You are an AI coding assistant implementing an existing OpenSpec change.
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
Implement tasks from `openspec/changes/<change-name>/tasks.md` until acceptance criteria are met and tests pass.
|
|
7
|
+
|
|
8
|
+
## Operating mode (Ralph loop compatible)
|
|
9
|
+
If you are being run in an iterative loop, you may see the same prompt repeatedly. You MUST:
|
|
10
|
+
- Use test failures as feedback
|
|
11
|
+
- Keep making progress on the next incomplete task
|
|
12
|
+
- Only claim completion when verification passes
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
1. Identify the active change folder under `openspec/changes/` (ask the user if multiple exist).
|
|
16
|
+
2. Read:
|
|
17
|
+
- `openspec/changes/<change-name>/proposal.md`
|
|
18
|
+
- `openspec/changes/<change-name>/tasks.md`
|
|
19
|
+
- Relevant spec deltas in `openspec/changes/<change-name>/specs/`
|
|
20
|
+
- Current specs in `openspec/specs/` as the baseline
|
|
21
|
+
3. Implement tasks in order. For each task:
|
|
22
|
+
- Make the smallest correct change
|
|
23
|
+
- Update or add tests required by acceptance criteria
|
|
24
|
+
- Run tests and fix failures
|
|
25
|
+
- Mark the task as complete in `tasks.md` ONLY when verified
|
|
26
|
+
|
|
27
|
+
## Completion promise (for Ralph loop runners)
|
|
28
|
+
Only output this exact text when ALL tasks are complete AND tests pass:
|
|
29
|
+
|
|
30
|
+
<promise>TASK_COMPLETE</promise>
|
|
31
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# /ralphy:plan (PRD -> OpenSpec change)
|
|
2
|
+
|
|
3
|
+
You are an AI coding assistant. Your job is to convert user requirements into an OpenSpec change proposal with clear acceptance criteria.
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
Create a new OpenSpec change folder under `openspec/changes/<change-name>/` containing:
|
|
7
|
+
- `proposal.md`
|
|
8
|
+
- `tasks.md`
|
|
9
|
+
- `specs/<domain>/spec.md` (or multiple specs if needed)
|
|
10
|
+
|
|
11
|
+
## Inputs
|
|
12
|
+
- A PRD, feature request, or requirements description from the user.
|
|
13
|
+
- Existing specs in `openspec/specs/` (source of truth).
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
- Use MUST/SHALL language in requirements.
|
|
17
|
+
- Every requirement MUST have at least one `#### Scenario:` block.
|
|
18
|
+
- Add acceptance criteria that can be validated via tests/commands.
|
|
19
|
+
- Keep changes scoped and explicitly list what is out-of-scope.
|
|
20
|
+
|
|
21
|
+
## Steps
|
|
22
|
+
1. Read `openspec/project.md` and any relevant specs in `openspec/specs/`.
|
|
23
|
+
2. Propose a short, kebab-case `<change-name>` (e.g. `add-profile-filters`).
|
|
24
|
+
3. Create `openspec/changes/<change-name>/proposal.md` describing:
|
|
25
|
+
- Summary
|
|
26
|
+
- Motivation
|
|
27
|
+
- Scope / Non-goals
|
|
28
|
+
- Risks
|
|
29
|
+
4. Create `openspec/changes/<change-name>/specs/...` as spec deltas:
|
|
30
|
+
- `## ADDED Requirements`
|
|
31
|
+
- `## MODIFIED Requirements`
|
|
32
|
+
- `## REMOVED Requirements`
|
|
33
|
+
5. Create `openspec/changes/<change-name>/tasks.md`:
|
|
34
|
+
- Break into numbered tasks with checkboxes
|
|
35
|
+
- Each task includes test plan notes (what to run, what to assert)
|
|
36
|
+
|
|
37
|
+
## Output
|
|
38
|
+
When the OpenSpec change artifacts are created and consistent, summarize what you created and what the next command should be.
|
|
39
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# /ralphy:validate (Validate against acceptance criteria)
|
|
2
|
+
|
|
3
|
+
You are an AI coding assistant validating an OpenSpec change against its acceptance criteria.
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
Confirm the implementation satisfies the requirements/scenarios in:
|
|
7
|
+
- `openspec/changes/<change-name>/specs/**`
|
|
8
|
+
and that all tests pass.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
1. Identify the active change folder under `openspec/changes/`.
|
|
12
|
+
2. Extract acceptance criteria from the spec scenarios and `tasks.md` test plan notes.
|
|
13
|
+
3. Run the project test command (prefer package scripts), for example:
|
|
14
|
+
- `npm test`
|
|
15
|
+
- `pnpm test`
|
|
16
|
+
- `bun test`
|
|
17
|
+
4. If tests fail:
|
|
18
|
+
- Diagnose the failure
|
|
19
|
+
- Fix code/tests
|
|
20
|
+
- Re-run tests
|
|
21
|
+
5. Report results:
|
|
22
|
+
- Which requirements are proven (by which tests/commands)
|
|
23
|
+
- Any gaps (missing tests or unclear acceptance criteria)
|
|
24
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Ralphy-OpenSpec Agent Instructions (OpenCode)
|
|
2
|
+
|
|
3
|
+
You are an AI coding assistant operating in a repository that uses:
|
|
4
|
+
- **OpenSpec** for spec-driven development (`openspec/specs/` + `openspec/changes/`)
|
|
5
|
+
- **Ralph loop** for iterative execution (the same prompt may be repeated)
|
|
6
|
+
|
|
7
|
+
## Golden rules
|
|
8
|
+
- Treat `openspec/specs/` as the source of truth (current behavior).
|
|
9
|
+
- Treat `openspec/changes/<change-name>/` as the proposed/active change.
|
|
10
|
+
- Only mark tasks complete when verification (tests) passes.
|
|
11
|
+
- Keep changes small, deterministic, and test-backed.
|
|
12
|
+
|
|
13
|
+
## Workflow
|
|
14
|
+
|
|
15
|
+
### 1) Plan (PRD -> OpenSpec)
|
|
16
|
+
When asked to plan or create specs:
|
|
17
|
+
1. Read `openspec/project.md` and relevant files in `openspec/specs/`.
|
|
18
|
+
2. Create a new change folder under `openspec/changes/<change-name>/` with:
|
|
19
|
+
- `proposal.md` (why/what/scope/non-goals/risks)
|
|
20
|
+
- `tasks.md` (checklist with test plan notes)
|
|
21
|
+
- `specs/**/spec.md` (deltas: ADDED/MODIFIED/REMOVED)
|
|
22
|
+
3. Ensure requirements use MUST/SHALL and each requirement has at least one scenario.
|
|
23
|
+
|
|
24
|
+
### 2) Implement (Tasks -> Code)
|
|
25
|
+
When asked to implement:
|
|
26
|
+
1. Identify the active change folder under `openspec/changes/`.
|
|
27
|
+
2. Implement tasks in order from `tasks.md`.
|
|
28
|
+
3. Run tests frequently and fix failures.
|
|
29
|
+
4. Update the checkbox status in `tasks.md` only when verified.
|
|
30
|
+
|
|
31
|
+
### 3) Validate (Acceptance criteria)
|
|
32
|
+
When asked to validate:
|
|
33
|
+
1. Map scenarios/acceptance criteria to tests/commands.
|
|
34
|
+
2. Run the project test command (commonly `npm test`).
|
|
35
|
+
3. Report which requirements are proven and what gaps remain.
|
|
36
|
+
|
|
37
|
+
### 4) Archive
|
|
38
|
+
When asked to archive:
|
|
39
|
+
- Prefer `openspec archive <change-name> --yes` if OpenSpec CLI is available.
|
|
40
|
+
- Otherwise, move the change into `openspec/archive/` and ensure `openspec/specs/` reflects the final state.
|
|
41
|
+
|
|
42
|
+
## Ralph loop completion promise
|
|
43
|
+
If you are being run in a loop, only output this exact text when ALL tasks are complete and tests are green:
|
|
44
|
+
|
|
45
|
+
<promise>TASK_COMPLETE</promise>
|
|
46
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# OpenSpec Tasks Template
|
|
2
|
+
|
|
3
|
+
Use this structure for `openspec/changes/<change-name>/tasks.md`.
|
|
4
|
+
|
|
5
|
+
## 1. Planning / Scaffolding
|
|
6
|
+
- [ ] 1.1 Confirm scope, non-goals, and impacted modules
|
|
7
|
+
- Acceptance criteria:
|
|
8
|
+
- GIVEN the current `openspec/specs/` baseline
|
|
9
|
+
- WHEN this change is implemented
|
|
10
|
+
- THEN only the intended domains are affected
|
|
11
|
+
- Test plan: N/A
|
|
12
|
+
|
|
13
|
+
## 2. Implementation
|
|
14
|
+
- [ ] 2.1 Implement feature behavior (spec-driven)
|
|
15
|
+
- Acceptance criteria:
|
|
16
|
+
- GIVEN ...
|
|
17
|
+
- WHEN ...
|
|
18
|
+
- THEN ...
|
|
19
|
+
- Test plan:
|
|
20
|
+
- Run: `npm test`
|
|
21
|
+
- Assert: ...
|
|
22
|
+
|
|
23
|
+
## 3. Validators / Budgets (v2+)
|
|
24
|
+
- [ ] 3.1 Define or update validators used by this change
|
|
25
|
+
- Notes:
|
|
26
|
+
- Validators are the ground truth (deterministic pass/fail)
|
|
27
|
+
- Prefer: `npm run typecheck`, `npm test`, `npm run lint`
|
|
28
|
+
- Test plan:
|
|
29
|
+
- Run the validator commands locally/CI
|
|
30
|
+
|
|
31
|
+
- [ ] 3.2 If the change introduces budgeted execution (v2.1+), document expected behavior
|
|
32
|
+
- Notes:
|
|
33
|
+
- Optimal → Warning → Hard tier expectations
|
|
34
|
+
- WARNING tier should shrink context + enforce repair-only constraints
|
|
35
|
+
- HARD tier should stop safely (no silent retries)
|
|
36
|
+
- Test plan:
|
|
37
|
+
- Run a small demo task that crosses WARNING tier and confirm behaviors
|
|
38
|
+
|
|
39
|
+
## 3. Validation
|
|
40
|
+
- [ ] 3.1 Add/adjust tests to cover all scenarios
|
|
41
|
+
- Test plan:
|
|
42
|
+
- Run: `npm test`
|
|
43
|
+
|
|
44
|
+
## 4. Documentation / Archive
|
|
45
|
+
- [ ] 4.1 Update OpenSpec docs/spec deltas (if needed) and archive change
|
|
46
|
+
- Test plan:
|
|
47
|
+
- Run: `npm test`
|
|
48
|
+
|