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.
Files changed (187) hide show
  1. package/README.ja.md +126 -0
  2. package/README.ko.md +126 -0
  3. package/README.md +77 -133
  4. package/README.zh.md +126 -0
  5. package/bin/ralphy-spec.js +0 -0
  6. package/dist/cli/checkpoint.d.ts +3 -0
  7. package/dist/cli/checkpoint.d.ts.map +1 -0
  8. package/dist/cli/checkpoint.js +23 -0
  9. package/dist/cli/checkpoint.js.map +1 -0
  10. package/dist/cli/init.d.ts +3 -0
  11. package/dist/cli/init.d.ts.map +1 -0
  12. package/dist/cli/init.js +66 -0
  13. package/dist/cli/init.js.map +1 -0
  14. package/dist/cli/report.d.ts +3 -0
  15. package/dist/cli/report.d.ts.map +1 -0
  16. package/dist/cli/report.js +53 -0
  17. package/dist/cli/report.js.map +1 -0
  18. package/dist/cli/run.d.ts +3 -0
  19. package/dist/cli/run.d.ts.map +1 -0
  20. package/dist/cli/run.js +79 -0
  21. package/dist/cli/run.js.map +1 -0
  22. package/dist/cli/status.d.ts +3 -0
  23. package/dist/cli/status.d.ts.map +1 -0
  24. package/dist/cli/status.js +45 -0
  25. package/dist/cli/status.js.map +1 -0
  26. package/dist/cli/tail.d.ts +3 -0
  27. package/dist/cli/tail.d.ts.map +1 -0
  28. package/dist/cli/tail.js +46 -0
  29. package/dist/cli/tail.js.map +1 -0
  30. package/dist/cli/update.d.ts +3 -0
  31. package/dist/cli/update.d.ts.map +1 -0
  32. package/dist/cli/update.js +62 -0
  33. package/dist/cli/update.js.map +1 -0
  34. package/dist/cli/validate.d.ts +3 -0
  35. package/dist/cli/validate.d.ts.map +1 -0
  36. package/dist/cli/validate.js +83 -0
  37. package/dist/cli/validate.js.map +1 -0
  38. package/dist/core/backends/claude-code.d.ts +17 -0
  39. package/dist/core/backends/claude-code.d.ts.map +1 -0
  40. package/dist/core/backends/claude-code.js +75 -0
  41. package/dist/core/backends/claude-code.js.map +1 -0
  42. package/dist/core/backends/cursor.d.ts +17 -0
  43. package/dist/core/backends/cursor.d.ts.map +1 -0
  44. package/dist/core/backends/cursor.js +75 -0
  45. package/dist/core/backends/cursor.js.map +1 -0
  46. package/dist/core/backends/noop.d.ts +10 -0
  47. package/dist/core/backends/noop.d.ts.map +1 -0
  48. package/dist/core/backends/noop.js +17 -0
  49. package/dist/core/backends/noop.js.map +1 -0
  50. package/dist/core/backends/opencode.d.ts +16 -0
  51. package/dist/core/backends/opencode.d.ts.map +1 -0
  52. package/dist/core/backends/opencode.js +73 -0
  53. package/dist/core/backends/opencode.js.map +1 -0
  54. package/dist/core/backends/types.d.ts +21 -0
  55. package/dist/core/backends/types.d.ts.map +1 -0
  56. package/dist/core/backends/types.js +3 -0
  57. package/dist/core/backends/types.js.map +1 -0
  58. package/dist/core/budgets/manager.d.ts +21 -0
  59. package/dist/core/budgets/manager.d.ts.map +1 -0
  60. package/dist/core/budgets/manager.js +48 -0
  61. package/dist/core/budgets/manager.js.map +1 -0
  62. package/dist/core/budgets/state.d.ts +25 -0
  63. package/dist/core/budgets/state.d.ts.map +1 -0
  64. package/dist/core/budgets/state.js +33 -0
  65. package/dist/core/budgets/state.js.map +1 -0
  66. package/dist/core/budgets/tiers.d.ts +32 -0
  67. package/dist/core/budgets/tiers.d.ts.map +1 -0
  68. package/dist/core/budgets/tiers.js +67 -0
  69. package/dist/core/budgets/tiers.js.map +1 -0
  70. package/dist/core/engine/context-pack.d.ts +12 -0
  71. package/dist/core/engine/context-pack.d.ts.map +1 -0
  72. package/dist/core/engine/context-pack.js +45 -0
  73. package/dist/core/engine/context-pack.js.map +1 -0
  74. package/dist/core/engine/loop.d.ts +28 -0
  75. package/dist/core/engine/loop.d.ts.map +1 -0
  76. package/dist/core/engine/loop.js +366 -0
  77. package/dist/core/engine/loop.js.map +1 -0
  78. package/dist/core/engine/phases.d.ts +2 -0
  79. package/dist/core/engine/phases.d.ts.map +1 -0
  80. package/dist/core/engine/phases.js +3 -0
  81. package/dist/core/engine/phases.js.map +1 -0
  82. package/dist/core/engine/repair.d.ts +6 -0
  83. package/dist/core/engine/repair.d.ts.map +1 -0
  84. package/dist/core/engine/repair.js +23 -0
  85. package/dist/core/engine/repair.js.map +1 -0
  86. package/dist/core/folders.d.ts +26 -0
  87. package/dist/core/folders.d.ts.map +1 -0
  88. package/dist/core/folders.js +58 -0
  89. package/dist/core/folders.js.map +1 -0
  90. package/dist/core/memory/ledger.d.ts +13 -0
  91. package/dist/core/memory/ledger.d.ts.map +1 -0
  92. package/dist/core/memory/ledger.js +24 -0
  93. package/dist/core/memory/ledger.js.map +1 -0
  94. package/dist/core/memory/persistence.d.ts +45 -0
  95. package/dist/core/memory/persistence.d.ts.map +1 -0
  96. package/dist/core/memory/persistence.js +162 -0
  97. package/dist/core/memory/persistence.js.map +1 -0
  98. package/dist/core/reporting/spend.d.ts +40 -0
  99. package/dist/core/reporting/spend.d.ts.map +1 -0
  100. package/dist/core/reporting/spend.js +157 -0
  101. package/dist/core/reporting/spend.js.map +1 -0
  102. package/dist/core/spec/dag.d.ts +7 -0
  103. package/dist/core/spec/dag.d.ts.map +1 -0
  104. package/dist/core/spec/dag.js +65 -0
  105. package/dist/core/spec/dag.js.map +1 -0
  106. package/dist/core/spec/file-contract.d.ts +13 -0
  107. package/dist/core/spec/file-contract.d.ts.map +1 -0
  108. package/dist/core/spec/file-contract.js +29 -0
  109. package/dist/core/spec/file-contract.js.map +1 -0
  110. package/dist/core/spec/loader.d.ts +8 -0
  111. package/dist/core/spec/loader.d.ts.map +1 -0
  112. package/dist/core/spec/loader.js +51 -0
  113. package/dist/core/spec/loader.js.map +1 -0
  114. package/dist/core/spec/schemas.d.ts +278 -0
  115. package/dist/core/spec/schemas.d.ts.map +1 -0
  116. package/dist/core/spec/schemas.js +207 -0
  117. package/dist/core/spec/schemas.js.map +1 -0
  118. package/dist/core/spec/types.d.ts +71 -0
  119. package/dist/core/spec/types.d.ts.map +1 -0
  120. package/dist/core/spec/types.js +3 -0
  121. package/dist/core/spec/types.js.map +1 -0
  122. package/dist/core/validators/parsers/eslint.d.ts +3 -0
  123. package/dist/core/validators/parsers/eslint.d.ts.map +1 -0
  124. package/dist/core/validators/parsers/eslint.js +35 -0
  125. package/dist/core/validators/parsers/eslint.js.map +1 -0
  126. package/dist/core/validators/parsers/jest.d.ts +3 -0
  127. package/dist/core/validators/parsers/jest.d.ts.map +1 -0
  128. package/dist/core/validators/parsers/jest.js +16 -0
  129. package/dist/core/validators/parsers/jest.js.map +1 -0
  130. package/dist/core/validators/parsers/tsc.d.ts +3 -0
  131. package/dist/core/validators/parsers/tsc.d.ts.map +1 -0
  132. package/dist/core/validators/parsers/tsc.js +32 -0
  133. package/dist/core/validators/parsers/tsc.js.map +1 -0
  134. package/dist/core/validators/runner.d.ts +8 -0
  135. package/dist/core/validators/runner.d.ts.map +1 -0
  136. package/dist/core/validators/runner.js +85 -0
  137. package/dist/core/validators/runner.js.map +1 -0
  138. package/dist/core/validators/signatures.d.ts +3 -0
  139. package/dist/core/validators/signatures.d.ts.map +1 -0
  140. package/dist/core/validators/signatures.js +10 -0
  141. package/dist/core/validators/signatures.js.map +1 -0
  142. package/dist/core/validators/types.d.ts +27 -0
  143. package/dist/core/validators/types.d.ts.map +1 -0
  144. package/dist/core/validators/types.js +3 -0
  145. package/dist/core/validators/types.js.map +1 -0
  146. package/dist/core/workspace/contract-enforcer.d.ts +54 -0
  147. package/dist/core/workspace/contract-enforcer.d.ts.map +1 -0
  148. package/dist/core/workspace/contract-enforcer.js +128 -0
  149. package/dist/core/workspace/contract-enforcer.js.map +1 -0
  150. package/dist/core/workspace/manager.d.ts +28 -0
  151. package/dist/core/workspace/manager.d.ts.map +1 -0
  152. package/dist/core/workspace/manager.js +3 -0
  153. package/dist/core/workspace/manager.js.map +1 -0
  154. package/dist/core/workspace/merge.d.ts +38 -0
  155. package/dist/core/workspace/merge.d.ts.map +1 -0
  156. package/dist/core/workspace/merge.js +92 -0
  157. package/dist/core/workspace/merge.js.map +1 -0
  158. package/dist/core/workspace/patch-mode.d.ts +22 -0
  159. package/dist/core/workspace/patch-mode.d.ts.map +1 -0
  160. package/dist/core/workspace/patch-mode.js +91 -0
  161. package/dist/core/workspace/patch-mode.js.map +1 -0
  162. package/dist/core/workspace/worktree-mode.d.ts +28 -0
  163. package/dist/core/workspace/worktree-mode.d.ts.map +1 -0
  164. package/dist/core/workspace/worktree-mode.js +156 -0
  165. package/dist/core/workspace/worktree-mode.js.map +1 -0
  166. package/dist/index.js +14 -4
  167. package/dist/index.js.map +1 -1
  168. package/dist/templates/claude-code/ralphy-archive.md +22 -0
  169. package/dist/templates/claude-code/ralphy-implement.md +30 -0
  170. package/dist/templates/claude-code/ralphy-plan.md +31 -0
  171. package/dist/templates/claude-code/ralphy-validate.md +21 -0
  172. package/dist/templates/cursor/ralphy-archive.md +20 -0
  173. package/dist/templates/cursor/ralphy-implement.md +31 -0
  174. package/dist/templates/cursor/ralphy-plan.md +39 -0
  175. package/dist/templates/cursor/ralphy-validate.md +24 -0
  176. package/dist/templates/opencode/AGENTS.md +46 -0
  177. package/dist/templates/shared/openspec-tasks-template.md +48 -0
  178. package/dist/templates/shared/project-template.yml +232 -0
  179. package/dist/templates/shared/ralph-loop-prompt-template.md +25 -0
  180. package/dist/utils/installer.d.ts.map +1 -1
  181. package/dist/utils/installer.js +31 -1
  182. package/dist/utils/installer.js.map +1 -1
  183. package/dist/utils/validator.d.ts.map +1 -1
  184. package/dist/utils/validator.js +10 -0
  185. package/dist/utils/validator.js.map +1 -1
  186. package/package.json +14 -4
  187. 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("./commands/init");
5
- const validate_1 = require("./commands/validate");
6
- const update_1 = require("./commands/update");
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.0");
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,0CAAsD;AACtD,kDAA8D;AAC9D,8CAA0D;AAE1D,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;IAE/B,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"}
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
+