ralphctl 0.6.3 → 0.7.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 +250 -138
- package/dist/cli.mjs +20349 -21147
- package/dist/manifest.json +17 -19
- package/dist/prompts/_partials/signals-evaluation.md +14 -0
- package/dist/prompts/_partials/signals-task.md +26 -0
- package/dist/prompts/_partials/validation-checklist.md +24 -0
- package/dist/prompts/apply-feedback/template.md +118 -0
- package/dist/prompts/detect-scripts/template.md +118 -0
- package/dist/prompts/detect-skills/template.md +136 -0
- package/dist/prompts/evaluate/template.md +236 -0
- package/dist/prompts/ideate/template.md +172 -0
- package/dist/prompts/implement/template.md +203 -0
- package/dist/prompts/plan/template.md +347 -0
- package/dist/prompts/readiness/template.md +132 -0
- package/dist/prompts/refine/template.md +254 -0
- package/dist/skills/{default/abstraction-first → ralphctl-abstraction-first}/SKILL.md +1 -1
- package/dist/skills/{default/alignment → ralphctl-alignment}/SKILL.md +1 -1
- package/dist/skills/{default/iterative-review → ralphctl-iterative-review}/SKILL.md +1 -1
- package/package.json +25 -28
- package/dist/absolute-path-WUTZQ37D.mjs +0 -8
- package/dist/chunk-6RDMCLWU.mjs +0 -108
- package/dist/chunk-HIU74KTO.mjs +0 -1046
- package/dist/chunk-S3PTDH57.mjs +0 -78
- package/dist/chunk-WV4D2CPG.mjs +0 -26
- package/dist/prompt-adapter-JQICGVX7.mjs +0 -7
- package/dist/prompts/ideate.md +0 -204
- package/dist/prompts/plan-auto.md +0 -182
- package/dist/prompts/plan-common-examples.md +0 -82
- package/dist/prompts/plan-common.md +0 -200
- package/dist/prompts/plan-interactive.md +0 -212
- package/dist/prompts/repo-onboard.md +0 -201
- package/dist/prompts/signals-evaluation.md +0 -6
- package/dist/prompts/signals-planning.md +0 -5
- package/dist/prompts/signals-task.md +0 -10
- package/dist/prompts/sprint-feedback.md +0 -64
- package/dist/prompts/task-evaluation.md +0 -276
- package/dist/prompts/task-execution.md +0 -233
- package/dist/prompts/ticket-refine.md +0 -242
- package/dist/prompts/validation-checklist.md +0 -19
- package/dist/skills/exec/.gitkeep +0 -0
- package/dist/skills/plan/.gitkeep +0 -0
- package/dist/skills/refine/.gitkeep +0 -0
- package/dist/storage-paths-IPNZZM5D.mjs +0 -15
- package/dist/validation-error-QT6Q7FYU.mjs +0 -7
- /package/dist/prompts/{harness-context.md → _partials/harness-context.md} +0 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# Requirements Refinement Protocol
|
|
2
|
+
|
|
3
|
+
You are a requirements analyst working interactively with a user. Produce a complete,
|
|
4
|
+
implementation-agnostic specification that answers WHAT needs to be built, not HOW. Read the
|
|
5
|
+
ticket carefully — what it says, what it assumes, what it leaves ambiguous — before asking
|
|
6
|
+
anything. A question the ticket already answers is a wasted turn. Clarify genuine gaps with
|
|
7
|
+
focused questions, and stop when acceptance criteria are unambiguous.
|
|
8
|
+
|
|
9
|
+
{{HARNESS_CONTEXT}}
|
|
10
|
+
|
|
11
|
+
## Output target
|
|
12
|
+
|
|
13
|
+
When approved by the user, write your final markdown body to this file:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
{{OUTPUT_FILE}}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Write a single markdown document — no JSON wrapper, no commentary, no code fence around the
|
|
20
|
+
document body. The harness reads this file verbatim and stores it on the ticket aggregate.
|
|
21
|
+
|
|
22
|
+
The expected document shape is at the bottom of this prompt under "Output format".
|
|
23
|
+
|
|
24
|
+
<constraints>
|
|
25
|
+
|
|
26
|
+
- **Stay implementation-agnostic** — frame requirements as observable behaviour ("user can
|
|
27
|
+
filter by date") rather than technical jargon ("add a SQL `WHERE` clause"). The planner that
|
|
28
|
+
runs after you needs maximum flexibility on HOW; you supply WHAT.
|
|
29
|
+
- **One concern per question** — combining "what should it do AND how should it look" forces
|
|
30
|
+
the user to give a fuzzy answer to both. Ask each dimension separately.
|
|
31
|
+
|
|
32
|
+
</constraints>
|
|
33
|
+
|
|
34
|
+
## Anti-patterns
|
|
35
|
+
|
|
36
|
+
- Asking what the ticket already says — read the ticket first; only ask about gaps.
|
|
37
|
+
- Over-specifying — constrain WHAT, not HOW (e.g., "must support undo", not "use command pattern").
|
|
38
|
+
- Combining multiple concerns in one question — fuzzy in, fuzzy out.
|
|
39
|
+
- Adding a free-form "Other" option — users get one automatically; do not duplicate.
|
|
40
|
+
|
|
41
|
+
## Ticket
|
|
42
|
+
|
|
43
|
+
{{TICKET}}
|
|
44
|
+
|
|
45
|
+
{{ISSUE_CONTEXT}}
|
|
46
|
+
|
|
47
|
+
## Protocol
|
|
48
|
+
|
|
49
|
+
### Step 1 — Analyse the ticket (think first)
|
|
50
|
+
|
|
51
|
+
Before producing any output, write your reasoning in a `<thinking>...</thinking>` block. Use
|
|
52
|
+
it to surface what's clear, what's ambiguous, and what edge cases the ticket omits. The
|
|
53
|
+
harness strips `<thinking>` blocks before persisting; explicit reasoning produces sharper
|
|
54
|
+
requirements than jumping straight to output.
|
|
55
|
+
|
|
56
|
+
Then identify, in order:
|
|
57
|
+
|
|
58
|
+
1. What is already clear and does NOT need clarification.
|
|
59
|
+
2. What is ambiguous, missing, or underspecified.
|
|
60
|
+
3. What the user likely has not considered (edge cases, error states, scope boundaries).
|
|
61
|
+
|
|
62
|
+
### Step 2 — Interview the user
|
|
63
|
+
|
|
64
|
+
Ask focused questions one at a time using `AskUserQuestion`, starting with the most critical
|
|
65
|
+
gap. Work through these dimensions in priority order; skip any the ticket already nails down.
|
|
66
|
+
|
|
67
|
+
**Dimension A — Problem and scope.** What problem are we solving and for whom? What is in
|
|
68
|
+
scope vs explicitly out of scope? What is deferred to future work?
|
|
69
|
+
|
|
70
|
+
**Dimension B — Functional behaviour.** What should the system do, described as observable
|
|
71
|
+
behaviour?
|
|
72
|
+
|
|
73
|
+
- Good: "User can filter results by date range."
|
|
74
|
+
- Bad: "Add a SQL `WHERE` clause for date filtering."
|
|
75
|
+
|
|
76
|
+
**Dimension C — Acceptance criteria.** Each criterion covers multiple scenarios, not just the
|
|
77
|
+
happy path. Use Given/When/Then phrasing. Include the happy path, alternate paths (different
|
|
78
|
+
input states or roles), and error/edge cases. Each scenario must be independently testable.
|
|
79
|
+
|
|
80
|
+
**Dimension D — Edge cases and error states.** What happens with invalid inputs, under
|
|
81
|
+
failure conditions, at boundaries?
|
|
82
|
+
|
|
83
|
+
**Dimension E — Business constraints.** Performance budgets, offline behaviour, regulatory
|
|
84
|
+
limits. Phrase as observable constraints, not implementation hints.
|
|
85
|
+
|
|
86
|
+
#### Asking clarifying questions
|
|
87
|
+
|
|
88
|
+
Use `AskUserQuestion` with 2–4 options per question:
|
|
89
|
+
|
|
90
|
+
- First option = your recommendation (label ends with " (Recommended)").
|
|
91
|
+
- Descriptions explain trade-offs or implications.
|
|
92
|
+
- Ask one question at a time.
|
|
93
|
+
- Labels: 1–5 words (UI rendering constraint).
|
|
94
|
+
- Headers: 12 characters or fewer (UI rendering constraint).
|
|
95
|
+
- `multiSelect: true` when choices are not mutually exclusive.
|
|
96
|
+
- Users automatically get an "Other" option — do not add your own.
|
|
97
|
+
|
|
98
|
+
#### Example interactions
|
|
99
|
+
|
|
100
|
+
**Example 1 — clarifying scope:**
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
Question: "Should password reset send a confirmation email after the password is changed?"
|
|
104
|
+
Header: "Reset email"
|
|
105
|
+
Options:
|
|
106
|
+
- "Send confirmation (Recommended)" — "Standard security practice; alerts user if reset was unauthorized."
|
|
107
|
+
- "No confirmation" — "Simpler flow; user already confirmed via reset link."
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Example 2 — surfacing edge cases:**
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
Question: "What should happen if a user exports more than 10,000 records?"
|
|
114
|
+
Header: "Large export"
|
|
115
|
+
Options:
|
|
116
|
+
- "Multiple files (Recommended)" — "Prevents timeouts and memory issues."
|
|
117
|
+
- "Error with limit" — "Simple; forces user to filter first."
|
|
118
|
+
- "Background job" — "Best UX, but more complex."
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Example 3 — resolving ambiguity:**
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
Question: "The ticket says 'support multiple formats'. Which formats are required for the initial release?"
|
|
125
|
+
Header: "Formats"
|
|
126
|
+
multiSelect: true
|
|
127
|
+
Options:
|
|
128
|
+
- "CSV (Recommended)" — "Universal compatibility; simple structure."
|
|
129
|
+
- "JSON (Recommended)" — "API-friendly; structured data."
|
|
130
|
+
- "PDF" — "Human-readable reports; requires additional library."
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Step 3 — Stop interviewing
|
|
134
|
+
|
|
135
|
+
Stop when ALL of these are true:
|
|
136
|
+
|
|
137
|
+
1. The problem statement is clear and agreed.
|
|
138
|
+
2. Every functional requirement has at least one acceptance criterion.
|
|
139
|
+
3. Scope boundaries (in / out / deferred) are explicit.
|
|
140
|
+
4. Major edge cases and error states are addressed.
|
|
141
|
+
5. Two developers reading these requirements would build the same thing.
|
|
142
|
+
|
|
143
|
+
If the user wants to keep adding scope, push back: "this is heading toward a separate ticket;
|
|
144
|
+
should we split?"
|
|
145
|
+
|
|
146
|
+
### Step 4 — Present requirements for approval
|
|
147
|
+
|
|
148
|
+
Present the complete requirements in readable markdown. Use proper headers, bullets, and
|
|
149
|
+
formatting. Make it easy to scan.
|
|
150
|
+
|
|
151
|
+
Then ask for approval using `AskUserQuestion`:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
Question: "Does this look correct? Any changes needed?"
|
|
155
|
+
Header: "Approval"
|
|
156
|
+
Options:
|
|
157
|
+
- "Approved, write it" — "Requirements are complete and accurate."
|
|
158
|
+
- "Needs changes" — "I'll describe what to adjust."
|
|
159
|
+
- "Give feedback" — "Type specific corrections in my own words."
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
If the user selects "Needs changes" or "Give feedback", apply their input and re-present.
|
|
163
|
+
Iterate until approved.
|
|
164
|
+
|
|
165
|
+
### Step 5 — Pre-output quality check
|
|
166
|
+
|
|
167
|
+
Before writing to file, verify ALL of these are true:
|
|
168
|
+
|
|
169
|
+
- [ ] Problem statement is clear and agreed.
|
|
170
|
+
- [ ] Every requirement has acceptance criteria covering happy path + edge / error cases.
|
|
171
|
+
- [ ] Scope boundaries are explicit (what's in AND what's out).
|
|
172
|
+
- [ ] Edge cases and error states are addressed.
|
|
173
|
+
- [ ] No implementation details leaked.
|
|
174
|
+
- [ ] Given/When/Then format used where it fits.
|
|
175
|
+
- [ ] Multi-topic tickets use numbered headings (`# 1.`, `# 2.`, …) with `---` dividers.
|
|
176
|
+
|
|
177
|
+
### Step 6 — Write to file
|
|
178
|
+
|
|
179
|
+
Once approved AND every checklist item is true, write the markdown body to:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
{{OUTPUT_FILE}}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Write the markdown document only — no JSON wrapper, no surrounding fence, no chat commentary
|
|
186
|
+
after the write.
|
|
187
|
+
|
|
188
|
+
## Output format
|
|
189
|
+
|
|
190
|
+
```markdown
|
|
191
|
+
# {Ticket title}
|
|
192
|
+
|
|
193
|
+
## Problem
|
|
194
|
+
|
|
195
|
+
{1–3 sentences naming the problem and the user.}
|
|
196
|
+
|
|
197
|
+
## Scope
|
|
198
|
+
|
|
199
|
+
**In scope:**
|
|
200
|
+
|
|
201
|
+
- {bullet}
|
|
202
|
+
- {bullet}
|
|
203
|
+
|
|
204
|
+
**Out of scope:**
|
|
205
|
+
|
|
206
|
+
- {bullet}
|
|
207
|
+
- {bullet}
|
|
208
|
+
|
|
209
|
+
## Acceptance criteria
|
|
210
|
+
|
|
211
|
+
### AC1 — {short label}
|
|
212
|
+
|
|
213
|
+
- **Given** {happy path precondition}, **When** {action}, **Then** {expected result}
|
|
214
|
+
- **Given** {alternate precondition}, **When** {action}, **Then** {alternate result}
|
|
215
|
+
- **Given** {error/edge case}, **When** {action}, **Then** {graceful handling}
|
|
216
|
+
|
|
217
|
+
(Repeat for each AC. 2–5 scenario bullets per AC covering happy / alternate / error.)
|
|
218
|
+
|
|
219
|
+
## Edge cases
|
|
220
|
+
|
|
221
|
+
- {bullet — invalid input, boundary, failure}
|
|
222
|
+
|
|
223
|
+
## Constraints
|
|
224
|
+
|
|
225
|
+
- {bullet — performance, offline, security, etc. when applicable}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
For multi-topic tickets, prefix each topic block with a numbered top-level heading and
|
|
229
|
+
separate them with `---`:
|
|
230
|
+
|
|
231
|
+
```markdown
|
|
232
|
+
# 1. First sub-topic
|
|
233
|
+
|
|
234
|
+
## Problem
|
|
235
|
+
|
|
236
|
+
…
|
|
237
|
+
|
|
238
|
+
## Acceptance criteria
|
|
239
|
+
|
|
240
|
+
…
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
# 2. Second sub-topic
|
|
245
|
+
|
|
246
|
+
…
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Failure modes
|
|
250
|
+
|
|
251
|
+
If, after the interview, you determine the ticket cannot be refined as stated (contradictory
|
|
252
|
+
requirements, missing information you cannot extract from the user), still write to
|
|
253
|
+
`{{OUTPUT_FILE}}` with whatever you have, ending with a final section explaining the gap.
|
|
254
|
+
Do not silently invent requirements.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: abstraction-first
|
|
2
|
+
name: ralphctl-abstraction-first
|
|
3
3
|
description: Cross-phase skill — design the shape of the change (entities, boundaries, seams) before generating code, tasks, or acceptance criteria. Failure mode is "big blob" output that obscures the core change.
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: iterative-review
|
|
2
|
+
name: ralphctl-iterative-review
|
|
3
3
|
description: Cross-phase skill — treat AI output as a controlled feedback loop, not a one-shot generation. Run the cheap check after each meaningful change; re-read your own output before signalling completion.
|
|
4
4
|
---
|
|
5
5
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ralphctl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Agent harness for long-running AI coding tasks — orchestrates Claude Code & GitHub Copilot across repositories",
|
|
5
5
|
"homepage": "https://github.com/lukas-grigis/ralphctl",
|
|
6
6
|
"type": "module",
|
|
@@ -38,38 +38,30 @@
|
|
|
38
38
|
"node": ">=24.0.0"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@inkjs/ui": "^2.0.0",
|
|
42
|
-
"colorette": "^2.0.20",
|
|
43
41
|
"commander": "^14.0.3",
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"react": "^19.2.5",
|
|
47
|
-
"tabtab": "^3.0.2",
|
|
42
|
+
"ink": "^7.0.3",
|
|
43
|
+
"react": "^19.2.6",
|
|
48
44
|
"typescript-result": "^3.5.2",
|
|
49
45
|
"zod": "^4.4.3"
|
|
50
46
|
},
|
|
51
47
|
"devDependencies": {
|
|
52
48
|
"@eslint/js": "^10.0.1",
|
|
53
|
-
"@types/node": "^25.
|
|
49
|
+
"@types/node": "^25.8.0",
|
|
54
50
|
"@types/react": "^19.2.14",
|
|
55
|
-
"@
|
|
56
|
-
"
|
|
57
|
-
"@vitest/eslint-plugin": "^1.6.16",
|
|
58
|
-
"eslint": "^10.3.0",
|
|
59
|
-
"eslint-config-prettier": "^10.1.8",
|
|
60
|
-
"eslint-plugin-import-x": "^4.16.2",
|
|
61
|
-
"eslint-plugin-react-hooks": "^7.1.1",
|
|
51
|
+
"@vitest/coverage-v8": "^4.1.6",
|
|
52
|
+
"eslint": "^10.4.0",
|
|
62
53
|
"globals": "^17.6.0",
|
|
63
54
|
"husky": "^9.1.7",
|
|
64
55
|
"ink-testing-library": "^4.0.0",
|
|
65
|
-
"
|
|
66
|
-
"
|
|
56
|
+
"jiti": "^2.7.0",
|
|
57
|
+
"knip": "^6.14.1",
|
|
58
|
+
"lint-staged": "^17.0.5",
|
|
67
59
|
"prettier": "^3.8.3",
|
|
68
60
|
"tsup": "^8.5.1",
|
|
69
|
-
"tsx": "^4.
|
|
61
|
+
"tsx": "^4.22.1",
|
|
70
62
|
"typescript": "^6.0.3",
|
|
71
|
-
"typescript-eslint": "^8.59.
|
|
72
|
-
"vitest": "^4.1.
|
|
63
|
+
"typescript-eslint": "^8.59.3",
|
|
64
|
+
"vitest": "^4.1.6"
|
|
73
65
|
},
|
|
74
66
|
"lint-staged": {
|
|
75
67
|
"*.{ts,tsx}": [
|
|
@@ -79,16 +71,21 @@
|
|
|
79
71
|
"*.{md,json,yml,yaml}": "prettier --write"
|
|
80
72
|
},
|
|
81
73
|
"scripts": {
|
|
82
|
-
"build": "tsup &&
|
|
83
|
-
"dev": "tsx src/
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"format": "prettier --write .",
|
|
87
|
-
"format:check": "prettier --check .",
|
|
88
|
-
"typecheck": "tsc --noEmit",
|
|
74
|
+
"build": "tsup && tsx scripts/build-assets.ts",
|
|
75
|
+
"dev": "NODE_OPTIONS=--max-old-space-size=8192 tsx src/index.ts",
|
|
76
|
+
"start": "NODE_OPTIONS=--max-old-space-size=8192 tsx src/index.ts",
|
|
77
|
+
"typecheck": "tsc",
|
|
89
78
|
"test": "vitest run",
|
|
79
|
+
"test:unit": "vitest run tests/unit",
|
|
80
|
+
"test:integration": "vitest run tests/integration",
|
|
81
|
+
"test:e2e": "vitest run tests/e2e",
|
|
90
82
|
"test:watch": "vitest",
|
|
91
83
|
"test:coverage": "vitest run --coverage",
|
|
92
|
-
"
|
|
84
|
+
"coverage:unused": "tsx scripts/find-unused.ts",
|
|
85
|
+
"deadcode": "knip",
|
|
86
|
+
"lint": "eslint .",
|
|
87
|
+
"lint:fix": "eslint . --fix",
|
|
88
|
+
"format": "prettier --write .",
|
|
89
|
+
"format:check": "prettier --check ."
|
|
93
90
|
}
|
|
94
91
|
}
|
package/dist/chunk-6RDMCLWU.mjs
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
AbsolutePath
|
|
4
|
-
} from "./chunk-S3PTDH57.mjs";
|
|
5
|
-
|
|
6
|
-
// src/integration/persistence/storage-paths.ts
|
|
7
|
-
import { mkdir } from "fs/promises";
|
|
8
|
-
import { homedir } from "os";
|
|
9
|
-
import { join } from "path";
|
|
10
|
-
function defaultRoot() {
|
|
11
|
-
const fromEnv = process.env["RALPHCTL_ROOT"];
|
|
12
|
-
if (fromEnv !== void 0 && fromEnv.length > 0) {
|
|
13
|
-
return AbsolutePath.trustString(fromEnv);
|
|
14
|
-
}
|
|
15
|
-
return AbsolutePath.trustString(join(homedir(), ".ralphctl"));
|
|
16
|
-
}
|
|
17
|
-
function asAbsolute(p) {
|
|
18
|
-
return AbsolutePath.trustString(p);
|
|
19
|
-
}
|
|
20
|
-
function resolveStoragePaths(opts = {}) {
|
|
21
|
-
const root = opts.root ?? defaultRoot();
|
|
22
|
-
const configDir = asAbsolute(join(root, "config"));
|
|
23
|
-
const dataDir = asAbsolute(join(root, "data"));
|
|
24
|
-
const sprintsDir = asAbsolute(join(dataDir, "sprints"));
|
|
25
|
-
const cacheDir = asAbsolute(join(root, "cache"));
|
|
26
|
-
const logsDir = asAbsolute(join(root, "logs"));
|
|
27
|
-
const backupsDir = asAbsolute(join(root, "backups"));
|
|
28
|
-
const configFile = asAbsolute(join(configDir, "config.json"));
|
|
29
|
-
const projectsFile = asAbsolute(join(configDir, "projects.json"));
|
|
30
|
-
return {
|
|
31
|
-
root,
|
|
32
|
-
configDir,
|
|
33
|
-
dataDir,
|
|
34
|
-
sprintsDir,
|
|
35
|
-
cacheDir,
|
|
36
|
-
logsDir,
|
|
37
|
-
backupsDir,
|
|
38
|
-
configFile,
|
|
39
|
-
projectsFile,
|
|
40
|
-
sprintDir(id) {
|
|
41
|
-
return asAbsolute(join(sprintsDir, id));
|
|
42
|
-
},
|
|
43
|
-
sprintFile(id) {
|
|
44
|
-
return asAbsolute(join(sprintsDir, id, "sprint.json"));
|
|
45
|
-
},
|
|
46
|
-
tasksFile(id) {
|
|
47
|
-
return asAbsolute(join(sprintsDir, id, "tasks.json"));
|
|
48
|
-
},
|
|
49
|
-
progressFile(id) {
|
|
50
|
-
return asAbsolute(join(sprintsDir, id, "progress.md"));
|
|
51
|
-
},
|
|
52
|
-
requirementsAggregateFile(id) {
|
|
53
|
-
return asAbsolute(join(sprintsDir, id, "requirements.json"));
|
|
54
|
-
},
|
|
55
|
-
feedbackFile(id) {
|
|
56
|
-
return asAbsolute(join(sprintsDir, id, "feedback.md"));
|
|
57
|
-
},
|
|
58
|
-
refinementUnitDir(id, unitSlug) {
|
|
59
|
-
return asAbsolute(join(sprintsDir, id, "refinement", unitSlug));
|
|
60
|
-
},
|
|
61
|
-
ideationUnitDir(id, unitSlug) {
|
|
62
|
-
return asAbsolute(join(sprintsDir, id, "ideation", unitSlug));
|
|
63
|
-
},
|
|
64
|
-
planningDir(id) {
|
|
65
|
-
return asAbsolute(join(sprintsDir, id, "planning"));
|
|
66
|
-
},
|
|
67
|
-
executionUnitDir(id, unitSlug) {
|
|
68
|
-
return asAbsolute(join(sprintsDir, id, "execution", unitSlug));
|
|
69
|
-
},
|
|
70
|
-
doneCriteriaFile(id) {
|
|
71
|
-
return asAbsolute(join(sprintsDir, id, "done-criteria.md"));
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
async function ensureLayoutDirs(paths) {
|
|
76
|
-
const dirs = [
|
|
77
|
-
paths.configDir,
|
|
78
|
-
paths.sprintsDir,
|
|
79
|
-
paths.cacheDir,
|
|
80
|
-
paths.logsDir,
|
|
81
|
-
paths.backupsDir
|
|
82
|
-
];
|
|
83
|
-
await Promise.all(dirs.map((d) => mkdir(d, { recursive: true })));
|
|
84
|
-
}
|
|
85
|
-
var ensuredRoots = /* @__PURE__ */ new Map();
|
|
86
|
-
async function ensureLayoutDirsOnce(paths) {
|
|
87
|
-
const key = paths.root;
|
|
88
|
-
const existing = ensuredRoots.get(key);
|
|
89
|
-
if (existing !== void 0) return existing;
|
|
90
|
-
const pending = ensureLayoutDirs(paths);
|
|
91
|
-
ensuredRoots.set(key, pending);
|
|
92
|
-
try {
|
|
93
|
-
await pending;
|
|
94
|
-
} catch (err) {
|
|
95
|
-
ensuredRoots.delete(key);
|
|
96
|
-
throw err;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
function resetEnsureLayoutDirsCache() {
|
|
100
|
-
ensuredRoots.clear();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export {
|
|
104
|
-
resolveStoragePaths,
|
|
105
|
-
ensureLayoutDirs,
|
|
106
|
-
ensureLayoutDirsOnce,
|
|
107
|
-
resetEnsureLayoutDirsCache
|
|
108
|
-
};
|