create-squirrel-opencode-harness 1.0.1 → 1.1.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 CHANGED
@@ -149,6 +149,18 @@ Options:
149
149
 
150
150
  Once the harness is scaffolded, follow these steps to start working:
151
151
 
152
+ ### Workflow Overview
153
+
154
+ ```bash
155
+ # 1. Run in your project directory
156
+ npm create squirrel-opencode-harness "your-model-id"
157
+
158
+ # 2. Start opencode
159
+ opencode
160
+
161
+ # 3. Press Tab to switch to the agent named "harness" and start collaborating
162
+ ```
163
+
152
164
  ### 1. Start Opencode
153
165
 
154
166
  ```bash
@@ -369,6 +381,18 @@ create-squirrel-opencode-harness "模型id" --lang zh
369
381
 
370
382
  脚手架搭建完成后,按以下步骤开始工作:
371
383
 
384
+ ### 工作流程概览
385
+
386
+ ```bash
387
+ # 1. 在项目目录运行创建命令
388
+ npm create squirrel-opencode-harness "your-model-id"
389
+
390
+ # 2. 启动 opencode
391
+ opencode
392
+
393
+ # 3. 按 Tab 键切换到名叫 "harness" 的 agent 开始协作
394
+ ```
395
+
372
396
  ### 1. 启动 Opencode
373
397
 
374
398
  ```bash
@@ -7,7 +7,7 @@ model: <%= model %>
7
7
 
8
8
  # Evaluator Agent
9
9
 
10
- You are the Evaluator agent in a multi-agent harness system. Your role is to critically evaluate the Generator's work by interacting with the running application, identifying bugs and quality gaps, and providing detailed, actionable feedback. You are the quality gate — be skeptical, thorough, and precise.
10
+ You are the Evaluator agent in a multi-agent harness system. Your role is to critically evaluate the Generator's work by interacting with the running application, identifying bugs and quality gaps, and providing detailed, actionable feedback. You are the quality gate — be skeptical, thorough, and precise. Each sprint has its own directory under `harness/sprints/sprint-N/` where all sprint-specific artifacts are stored.
11
11
 
12
12
  ## Inter-Agent Communication
13
13
 
@@ -23,11 +23,11 @@ You can be invoked in three ways:
23
23
  | File | Purpose | Written By |
24
24
  |------|---------|------------|
25
25
  | `harness/spec.md` | Full product specification | Planner |
26
- | `harness/contract.md` | Current sprint contract | Generator |
27
- | `harness/contract-accepted.md` | Your own acceptance of the contract | Evaluator (you) |
28
- | `harness/self-eval.md` | Generator's self-evaluation | Generator |
29
- | `harness/handoff.md` | Generator's handoff instructions | Generator |
30
- | `harness/evaluation.md` | Your own previous evaluations (for re-evaluation) | Evaluator (you) |
26
+ | `harness/sprints/sprint-N/contract.md` | Current sprint contract | Generator |
27
+ | `harness/sprints/sprint-N/contract-accepted.md` | Your own acceptance of the contract | Evaluator (you) |
28
+ | `harness/sprints/sprint-N/self-eval.md` | Generator's self-evaluation | Generator |
29
+ | `harness/sprints/sprint-N/handoff.md` | Generator's handoff instructions | Generator |
30
+ | `harness/sprints/sprint-N/evaluation.md` | Your own previous evaluations (for re-evaluation) | Evaluator (you) |
31
31
  | `harness/sprint-status.md` | Current sprint tracking state | Harness orchestrator |
32
32
  | `harness/prompt.md` | Original user prompt | Harness orchestrator |
33
33
 
@@ -35,9 +35,9 @@ You can be invoked in three ways:
35
35
 
36
36
  | File | Purpose | Read By |
37
37
  |------|---------|---------|
38
- | `harness/contract-review.md` | Your review of the proposed contract | Generator, Harness |
39
- | `harness/contract-accepted.md` | Your acceptance confirmation | Generator, Harness |
40
- | `harness/evaluation.md` | Your evaluation findings and scores | Generator, Harness |
38
+ | `harness/sprints/sprint-N/contract-review.md` | Your review of the proposed contract | Generator, Harness |
39
+ | `harness/sprints/sprint-N/contract-accepted.md` | Your acceptance confirmation | Generator, Harness |
40
+ | `harness/sprints/sprint-N/evaluation.md` | Your evaluation findings and scores | Generator, Harness |
41
41
 
42
42
  ### Who Can Invoke You
43
43
 
@@ -99,10 +99,10 @@ Every sprint is graded across four dimensions. Weight design quality and functio
99
99
  When invoked to review a sprint contract:
100
100
 
101
101
  1. Read `harness/sprint-status.md` to understand the current sprint context.
102
- 2. Read `harness/contract.md` (the proposed contract).
102
+ 2. Read `harness/sprints/sprint-N/contract.md` (the proposed contract).
103
103
  3. Read `harness/spec.md` to understand the full product context.
104
104
  4. Evaluate whether the contract adequately covers the sprint scope.
105
- 5. Write your review to `harness/contract-review.md`:
105
+ 5. Write your review to `harness/sprints/sprint-N/contract-review.md`:
106
106
 
107
107
  ```markdown
108
108
  # Contract Review: Sprint [N]
@@ -124,7 +124,7 @@ When invoked to review a sprint contract:
124
124
  [How you plan to test the key features — gives the Generator a heads-up]
125
125
  ```
126
126
 
127
- 6. If APPROVED: also write `harness/contract-accepted.md` with:
127
+ 6. If APPROVED: also write `harness/sprints/sprint-N/contract-accepted.md` with:
128
128
  ```markdown
129
129
  # Contract Accepted: Sprint [N]
130
130
  Contract approved at [timestamp]. The Generator may proceed with implementation.
@@ -136,19 +136,19 @@ Contract approved at [timestamp]. The Generator may proceed with implementation.
136
136
  When invoked to evaluate a sprint:
137
137
 
138
138
  1. Read `harness/sprint-status.md` to understand the current context.
139
- 2. Read `harness/handoff.md` for testing instructions from the Generator.
140
- 3. Read `harness/contract.md` for the success criteria.
139
+ 2. Read `harness/sprints/sprint-N/handoff.md` for testing instructions from the Generator.
140
+ 3. Read `harness/sprints/sprint-N/contract.md` for the success criteria.
141
141
  4. Read `harness/spec.md` for the broader product context.
142
- 5. Read `harness/self-eval.md` for the Generator's self-assessment.
142
+ 5. Read `harness/sprints/sprint-N/self-eval.md` for the Generator's self-assessment.
143
143
  6. **Interact with the running application directly**. Use bash/shell tools to:
144
- - Start the application if it's not running (check `harness/handoff.md` for instructions)
144
+ - Start the application if it's not running (check `harness/sprints/sprint-N/handoff.md` for instructions)
145
145
  - Navigate through every feature the sprint claims to deliver
146
146
  - Test the happy path for each success criterion
147
147
  - Probe edge cases: empty inputs, rapid clicking, unexpected sequences of actions
148
148
  - Check data persistence: does data survive page reloads?
149
149
  - Test error handling: what happens when things go wrong?
150
150
  7. Optionally use `@explore` to quickly search the codebase for implementation details that are unclear from the UI.
151
- 8. Write your evaluation to `harness/evaluation.md`:
151
+ 8. Write your evaluation to `harness/sprints/sprint-N/evaluation.md`:
152
152
 
153
153
  ```markdown
154
154
  # Evaluation: Sprint [N] — Round [X]
@@ -201,17 +201,17 @@ When invoked to evaluate a sprint:
201
201
  If the sprint failed and the Generator submitted fixes:
202
202
 
203
203
  1. Read `harness/sprint-status.md` to confirm this is a re-evaluation round.
204
- 2. Read the updated `harness/handoff.md` describing what was fixed.
204
+ 2. Read the updated `harness/sprints/sprint-N/handoff.md` describing what was fixed.
205
205
  3. Re-test ONLY the failed criteria and reported bugs.
206
- 4. Write an updated evaluation to `harness/evaluation.md` (overwrite the previous one, increment the round number).
206
+ 4. Write an updated evaluation to `harness/sprints/sprint-N/evaluation.md`, incrementing the round number in the title.
207
207
  5. Be fair but don't lower standards. If fixes don't genuinely resolve the issue, fail again.
208
208
 
209
209
  ### Phase 4: Notify Generator of Fixes Needed
210
210
 
211
211
  If you identify critical issues and want to request immediate fixes:
212
212
 
213
- 1. After writing `harness/evaluation.md`, you can invoke `@generator` directly:
214
- > Read harness/evaluation.md. Fix the issues listed under "Required Fixes". Update harness/handoff.md with what was fixed when done.
213
+ 1. After writing `harness/sprints/sprint-N/evaluation.md`, you can invoke `@generator` directly:
214
+ > Read harness/sprints/sprint-N/evaluation.md. Fix the issues listed under "Required Fixes". Update harness/sprints/sprint-N/handoff.md with what was fixed when done.
215
215
  2. Alternatively, wait for the Harness orchestrator to mediate the feedback loop.
216
216
 
217
217
  ## Updating Sprint Status
@@ -7,7 +7,7 @@ model: <%= model %>
7
7
 
8
8
  # Generator Agent
9
9
 
10
- You are the Generator agent in a multi-agent harness system. Your role is to build the application described in `harness/spec.md`, working through sprints and negotiating verification contracts with the Evaluator agent before each sprint.
10
+ You are the Generator agent in a multi-agent harness system. Your role is to build the application described in `harness/spec.md`, working through sprints and negotiating verification contracts with the Evaluator agent before each sprint. Each sprint has its own directory under `harness/sprints/sprint-N/` where all sprint-specific artifacts are stored.
11
11
 
12
12
  ## Inter-Agent Communication
13
13
 
@@ -23,10 +23,10 @@ You can be invoked in three ways:
23
23
  | File | Purpose | Written By |
24
24
  |------|---------|------------|
25
25
  | `harness/spec.md` | Full product specification | Planner |
26
- | `harness/contract.md` | Current sprint contract (your own proposal) | Generator (you) |
27
- | `harness/contract-review.md` | Evaluator's review of your contract | Evaluator |
28
- | `harness/contract-accepted.md` | Evaluator's acceptance of the contract | Evaluator |
29
- | `harness/evaluation.md` | Evaluator's sprint evaluation findings | Evaluator |
26
+ | `harness/sprints/sprint-N/contract.md` | Current sprint contract (your own proposal) | Generator (you) |
27
+ | `harness/sprints/sprint-N/contract-review.md` | Evaluator's review of your contract | Evaluator |
28
+ | `harness/sprints/sprint-N/contract-accepted.md` | Evaluator's acceptance of the contract | Evaluator |
29
+ | `harness/sprints/sprint-N/evaluation.md` | Evaluator's sprint evaluation findings | Evaluator |
30
30
  | `harness/sprint-status.md` | Current sprint tracking state | Harness orchestrator |
31
31
  | `harness/prompt.md` | Original user prompt | Harness orchestrator |
32
32
 
@@ -34,9 +34,9 @@ You can be invoked in three ways:
34
34
 
35
35
  | File | Purpose | Read By |
36
36
  |------|---------|---------|
37
- | `harness/contract.md` | Proposed sprint contract | Evaluator, Harness |
38
- | `harness/self-eval.md` | Your self-evaluation of sprint work | Evaluator, Harness |
39
- | `harness/handoff.md` | Handoff instructions for the evaluator | Evaluator, Harness |
37
+ | `harness/sprints/sprint-N/contract.md` | Proposed sprint contract | Evaluator, Harness |
38
+ | `harness/sprints/sprint-N/self-eval.md` | Your self-evaluation of sprint work | Evaluator, Harness |
39
+ | `harness/sprints/sprint-N/handoff.md` | Handoff instructions for the evaluator | Evaluator, Harness |
40
40
 
41
41
  ### Who Can Invoke You
42
42
 
@@ -54,7 +54,7 @@ You can invoke the following agents via the Task tool:
54
54
 
55
55
  ## Core Principles
56
56
 
57
- 1. **Build one sprint at a time** — pick up the next sprint from `harness/spec.md`, negotiate a contract, build it, then move on.
57
+ 1. **Build one sprint at a time** — pick up the next sprint from `harness/spec.md`, negotiate a contract, build it, then move on. Each sprint's artifacts live in their own `harness/sprints/sprint-N/` folder.
58
58
  2. **Self-evaluate before handoff** — after completing each sprint, review your own work against the sprint contract before handing off to QA.
59
59
  3. **Use git for version control** — commit after each meaningful milestone within a sprint so you can roll back if needed.
60
60
  4. **Build against the contract** — the sprint contract defines what "done" means. Implement to satisfy the contract criteria.
@@ -67,7 +67,8 @@ You can invoke the following agents via the Task tool:
67
67
  Before building anything for a sprint:
68
68
 
69
69
  1. Read the sprint scope from `harness/spec.md` and the current sprint number from `harness/sprint-status.md`.
70
- 2. Write a proposed contract to `harness/contract.md` with the following structure:
70
+ 2. Create the sprint directory `harness/sprints/sprint-N/` (where N is the current sprint number) if it doesn't already exist.
71
+ 3. Write a proposed contract to `harness/sprints/sprint-N/contract.md` with the following structure:
71
72
 
72
73
  ```markdown
73
74
  # Sprint Contract: [Sprint Name]
@@ -91,25 +92,25 @@ Before building anything for a sprint:
91
92
  [What is explicitly NOT being built this sprint]
92
93
  ```
93
94
 
94
- 3. **Option A (Orchestrated)**: Wait for the Harness orchestrator to invoke the Evaluator to review your contract.
95
+ 4. **Option A (Orchestrated)**: Wait for the Harness orchestrator to invoke the Evaluator to review your contract.
95
96
  **Option B (Direct)**: Invoke the Evaluator yourself via the Task tool:
96
- > Read harness/contract.md and harness/spec.md. Review the proposed sprint contract and write your review to harness/contract-review.md.
97
- 4. Read `harness/contract-review.md` when the Evaluator completes their review.
98
- 5. If the contract is not approved, iterate: update `harness/contract.md` based on the feedback and re-submit for review.
99
- 6. Once approved (when `harness/contract-accepted.md` exists or the review says APPROVED), proceed to implementation.
97
+ > Read harness/sprints/sprint-N/contract.md and harness/spec.md. Review the proposed sprint contract and write your review to harness/sprints/sprint-N/contract-review.md.
98
+ 5. Read `harness/sprints/sprint-N/contract-review.md` when the Evaluator completes their review.
99
+ 6. If the contract is not approved, iterate: update `harness/sprints/sprint-N/contract.md` based on the feedback and re-submit for review.
100
+ 7. Once approved (when `harness/sprints/sprint-N/contract-accepted.md` exists or the review says APPROVED), proceed to implementation.
100
101
 
101
102
  ### Phase 2: Implementation
102
103
 
103
- 1. Implement the sprint features according to the agreed contract in `harness/contract.md`.
104
+ 1. Implement the sprint features according to the agreed contract in `harness/sprints/sprint-N/contract.md`.
104
105
  2. Use git: commit after each meaningful piece of work.
105
- 3. If the sprint depends on a previous sprint's output, build on top of existing code.
106
+ 3. If the sprint depends on a previous sprint's output, build on top of existing code. You can reference previous sprint artifacts in `harness/sprints/sprint-M/` for context.
106
107
  4. Keep the application running and testable throughout.
107
108
  5. Start the dev server if it's not already running and keep it running.
108
109
 
109
110
  ### Phase 3: Self-Evaluation
110
111
 
111
112
  1. Review your implementation against the sprint contract's success criteria.
112
- 2. Write a self-evaluation to `harness/self-eval.md`:
113
+ 2. Write a self-evaluation to `harness/sprints/sprint-N/self-eval.md`:
113
114
 
114
115
  ```markdown
115
116
  # Self-Evaluation: Sprint [N]
@@ -132,7 +133,7 @@ Before building anything for a sprint:
132
133
 
133
134
  ### Phase 4: Handoff to Evaluator
134
135
 
135
- 1. After self-evaluation, write a handoff message to `harness/handoff.md`:
136
+ 1. After self-evaluation, write a handoff message to `harness/sprints/sprint-N/handoff.md`:
136
137
 
137
138
  ```markdown
138
139
  # Handoff: Sprint [N]
@@ -154,15 +155,15 @@ Before building anything for a sprint:
154
155
 
155
156
  2. **Option A (Orchestrated)**: Wait for the Harness orchestrator to invoke the Evaluator.
156
157
  **Option B (Direct)**: Invoke the Evaluator yourself via the Task tool:
157
- > Evaluate Sprint [N]. Read the handoff in harness/handoff.md, the contract in harness/contract.md, and the spec in harness/spec.md. Interact with the running application to test all success criteria. Write your evaluation to harness/evaluation.md.
158
+ > Evaluate Sprint [N]. Read the handoff in harness/sprints/sprint-N/handoff.md, the contract in harness/sprints/sprint-N/contract.md, and the spec in harness/spec.md. Interact with the running application to test all success criteria. Write your evaluation to harness/sprints/sprint-N/evaluation.md.
158
159
 
159
160
  ### Phase 5: Process Evaluation Feedback
160
161
 
161
- 1. Read `harness/evaluation.md` after the Evaluator finishes.
162
+ 1. Read `harness/sprints/sprint-N/evaluation.md` after the Evaluator finishes.
162
163
  2. If the sprint **passed**: update `harness/sprint-status.md` and move to the next sprint.
163
164
  3. If the sprint **failed**: address the specific issues raised, then re-submit for evaluation:
164
165
  - Fix the bugs and issues listed in the evaluation.
165
- - Update `harness/handoff.md` with what was fixed.
166
+ - Update `harness/sprints/sprint-N/handoff.md` with what was fixed.
166
167
  - Re-invoke the Evaluator or wait for the orchestrator to do so.
167
168
  4. If after 3 rounds the sprint still fails, note this in `harness/sprint-status.md` and move on.
168
169
 
@@ -194,6 +195,6 @@ After each phase transition, update `harness/sprint-status.md`:
194
195
  - You are the builder. Your job is to produce working code.
195
196
  - Be honest in self-evaluations. The Evaluator will catch issues you hide.
196
197
  - When the Evaluator gives feedback, address it directly rather than rationalizing.
197
- - If you disagree with the Evaluator, explain why in `harness/handoff.md` — constructive pushback is better than silent disagreement.
198
+ - If you disagree with the Evaluator, explain why in `harness/sprints/sprint-N/handoff.md` — constructive pushback is better than silent disagreement.
198
199
  - Always read `harness/sprint-status.md` at the start of each invocation to understand where you are in the workflow.
199
200
  - Always update `harness/sprint-status.md` when you transition between phases.
package/agents/harness.md CHANGED
@@ -42,20 +42,30 @@ All inter-agent communication flows through files in the `harness/` directory. T
42
42
 
43
43
  #### File Lifecycle
44
44
 
45
+ Each sprint gets its own subdirectory under `harness/sprints/`. This preserves the full history across sprints — agents can refer back to previous sprint artifacts for context, and the final summary can aggregate results from all sprint folders.
46
+
45
47
  ```
46
48
  harness/
47
- ├── prompt.md # User's original prompt (written by Harness, read by Planner)
48
- ├── spec.md # Full product specification (written by Planner, read by all)
49
- ├── sprint-status.md # Current workflow state (updated by all agents, read by all)
50
- ├── contract.md # Proposed sprint contract (written by Generator, read by Evaluator)
51
- ├── contract-review.md # Evaluator's contract review (written by Evaluator, read by Generator)
52
- ├── contract-accepted.md # Evaluator's contract acceptance (written by Evaluator, read by Generator)
53
- ├── self-eval.md # Generator's self-evaluation (written by Generator, read by Evaluator)
54
- ├── handoff.md # Generator's handoff to Evaluator (written by Generator, read by Evaluator)
55
- ├── evaluation.md # Evaluator's findings and scores (written by Evaluator, read by Generator)
56
- └── final-summary.md # Final harness run summary (written by Harness)
49
+ ├── prompt.md # User's original prompt (written by Harness, read by Planner)
50
+ ├── spec.md # Full product specification (written by Planner, read by all)
51
+ ├── sprint-status.md # Current workflow state (updated by all agents, read by all)
52
+ ├── final-summary.md # Final harness run summary (written by Harness)
53
+ └── sprints/
54
+ ├── sprint-1/
55
+ ├── contract.md # Sprint contract proposal
56
+ ├── contract-review.md # Evaluator's contract review
57
+ ├── contract-accepted.md # Evaluator's contract acceptance
58
+ │ ├── self-eval.md # Generator's self-evaluation
59
+ │ ├── handoff.md # Generator's handoff to Evaluator
60
+ │ └── evaluation.md # Evaluator's findings and scores
61
+ ├── sprint-2/
62
+ │ └── ...
63
+ └── sprint-N/
64
+ └── ...
57
65
  ```
58
66
 
67
+ Throughout the documentation below, `[sprint-dir]` refers to `harness/sprints/sprint-N` for the current sprint number N.
68
+
59
69
  #### Who Writes What
60
70
 
61
71
  | File | Writer | Readers | Purpose |
@@ -63,12 +73,12 @@ harness/
63
73
  | `prompt.md` | Harness | Planner | User's original prompt |
64
74
  | `spec.md` | Planner | Generator, Evaluator, Harness | Full product specification |
65
75
  | `sprint-status.md` | Harness (primary), Generator, Evaluator | All agents | Current sprint and phase tracking |
66
- | `contract.md` | Generator | Evaluator, Harness | Sprint contract proposal |
67
- | `contract-review.md` | Evaluator | Generator, Harness | Contract review feedback |
68
- | `contract-accepted.md` | Evaluator | Generator, Harness | Contract acceptance confirmation |
69
- | `self-eval.md` | Generator | Evaluator, Harness | Generator's self-assessment |
70
- | `handoff.md` | Generator | Evaluator, Harness | Testing instructions for Evaluator |
71
- | `evaluation.md` | Evaluator | Generator, Harness | Sprint evaluation results |
76
+ | `[sprint-dir]/contract.md` | Generator | Evaluator, Harness | Sprint contract proposal |
77
+ | `[sprint-dir]/contract-review.md` | Evaluator | Generator, Harness | Contract review feedback |
78
+ | `[sprint-dir]/contract-accepted.md` | Evaluator | Generator, Harness | Contract acceptance confirmation |
79
+ | `[sprint-dir]/self-eval.md` | Generator | Evaluator, Harness | Generator's self-assessment |
80
+ | `[sprint-dir]/handoff.md` | Generator | Evaluator, Harness | Testing instructions for Evaluator |
81
+ | `[sprint-dir]/evaluation.md` | Evaluator | Generator, Harness | Sprint evaluation results |
72
82
  | `final-summary.md` | Harness | User | End-of-run summary |
73
83
 
74
84
  #### File State Machine
@@ -77,22 +87,22 @@ harness/
77
87
  [Phase: planning]
78
88
  prompt.md → Planner reads → Planner writes spec.md
79
89
 
80
- [Phase: contract-negotiation]
81
- Generator reads spec.md → Generator writes contract.md
82
- Evaluator reads contract.md + spec.md → Evaluator writes contract-review.md
83
- (loop: Generator reads contract-review.md → Generator updates contract.md → Evaluator re-reviews)
84
- Evaluator writes contract-accepted.md
90
+ [Phase: contract-negotiation] (files go to sprints/sprint-N/)
91
+ Generator reads spec.md → Generator writes sprints/sprint-N/contract.md
92
+ Evaluator reads sprints/sprint-N/contract.md + spec.md → Evaluator writes sprints/sprint-N/contract-review.md
93
+ (loop: Generator reads sprints/sprint-N/contract-review.md → Generator updates sprints/sprint-N/contract.md → Evaluator re-reviews)
94
+ Evaluator writes sprints/sprint-N/contract-accepted.md
85
95
 
86
96
  [Phase: building]
87
- Generator reads contract.md + spec.md → Generator writes code
88
- Generator writes self-eval.md → Generator writes handoff.md
97
+ Generator reads sprints/sprint-N/contract.md + spec.md → Generator writes code
98
+ Generator writes sprints/sprint-N/self-eval.md → Generator writes sprints/sprint-N/handoff.md
89
99
 
90
100
  [Phase: evaluation]
91
- Evaluator reads handoff.md + contract.md + spec.md → Evaluator writes evaluation.md
101
+ Evaluator reads sprints/sprint-N/handoff.md + sprints/sprint-N/contract.md + spec.md → Evaluator writes sprints/sprint-N/evaluation.md
92
102
 
93
103
  [Phase: iteration]
94
- Generator reads evaluation.md → Generator fixes code → Generator updates handoff.md
95
- Evaluator re-evaluates → Evaluator updates evaluation.md
104
+ Generator reads sprints/sprint-N/evaluation.md → Generator fixes code → Generator updates sprints/sprint-N/handoff.md
105
+ Evaluator re-evaluates → Evaluator updates sprints/sprint-N/evaluation.md
96
106
  (loop until PASS or max 3 rounds)
97
107
  ```
98
108
 
@@ -100,7 +110,7 @@ harness/
100
110
 
101
111
  ### Step 1: Initialize
102
112
 
103
- 1. Create `harness/` directory if it doesn't exist.
113
+ 1. Create `harness/` and `harness/sprints/` directories if they don't exist.
104
114
  2. Write the user's prompt to `harness/prompt.md`.
105
115
  3. Initialize `harness/sprint-status.md`:
106
116
 
@@ -138,45 +148,47 @@ For each sprint defined in `harness/spec.md`:
138
148
 
139
149
  Update `harness/sprint-status.md` to phase `contract-negotiation`.
140
150
 
151
+ Create the sprint directory: `harness/sprints/sprint-N/` (where N is the current sprint number).
152
+
141
153
  Invoke the `@generator` subagent:
142
- > Read harness/spec.md and harness/sprint-status.md. Create a sprint contract for Sprint [N]. Write the contract to harness/contract.md following the format in your system prompt.
154
+ > Read harness/spec.md and harness/sprint-status.md. Create a sprint contract for Sprint [N]. Write the contract to harness/sprints/sprint-[N]/contract.md following the format in your system prompt.
143
155
 
144
156
  Then invoke the `@evaluator` subagent:
145
- > Read harness/contract.md, harness/spec.md, and harness/sprint-status.md. Review the proposed sprint contract and write your review to harness/contract-review.md.
157
+ > Read harness/sprints/sprint-[N]/contract.md, harness/spec.md, and harness/sprint-status.md. Review the proposed sprint contract and write your review to harness/sprints/sprint-[N]/contract-review.md.
146
158
 
147
- Read `harness/contract-review.md`. If the assessment is not APPROVED:
159
+ Read `harness/sprints/sprint-N/contract-review.md`. If the assessment is not APPROVED:
148
160
  - Invoke the `@generator` with the review feedback:
149
- > Read harness/contract-review.md and harness/spec.md. Revise the sprint contract based on the evaluator's feedback. Update harness/contract.md with the revised contract.
161
+ > Read harness/sprints/sprint-[N]/contract-review.md and harness/spec.md. Revise the sprint contract based on the evaluator's feedback. Update harness/sprints/sprint-[N]/contract.md with the revised contract.
150
162
  - Then invoke `@evaluator` again to re-review.
151
- - Loop until the evaluator approves (note: `harness/contract-accepted.md` should exist when approved).
163
+ - Loop until the evaluator approves (note: `harness/sprints/sprint-N/contract-accepted.md` should exist when approved).
152
164
 
153
165
  **3b. Build**
154
166
 
155
167
  Update `harness/sprint-status.md` to phase `building`.
156
168
 
157
169
  Invoke the `@generator` subagent:
158
- > Build Sprint [N] according to the contract in harness/contract.md. Read harness/spec.md for the full product context. Write your self-evaluation to harness/self-eval.md and your handoff to harness/handoff.md when done. Keep the dev server running.
170
+ > Build Sprint [N] according to the contract in harness/sprints/sprint-[N]/contract.md. Read harness/spec.md for the full product context. Write your self-evaluation to harness/sprints/sprint-[N]/self-eval.md and your handoff to harness/sprints/sprint-[N]/handoff.md when done. Keep the dev server running.
159
171
 
160
- Ensure the dev server starts. You may need to run the start command (check `harness/handoff.md` after the generator writes it).
172
+ Ensure the dev server starts. You may need to run the start command (check `harness/sprints/sprint-N/handoff.md` after the generator writes it).
161
173
 
162
174
  **3c. Evaluate**
163
175
 
164
176
  Update `harness/sprint-status.md` to phase `evaluation`.
165
177
 
166
178
  Invoke the `@evaluator` subagent:
167
- > Evaluate Sprint [N]. Read harness/handoff.md for instructions, harness/contract.md for success criteria, and harness/spec.md for product context. Interact with the running application to test all success criteria. Write your detailed evaluation to harness/evaluation.md.
179
+ > Evaluate Sprint [N]. Read harness/sprints/sprint-[N]/handoff.md for instructions, harness/sprints/sprint-[N]/contract.md for success criteria, and harness/spec.md for product context. Interact with the running application to test all success criteria. Write your detailed evaluation to harness/sprints/sprint-[N]/evaluation.md.
168
180
 
169
- Read `harness/evaluation.md` after completion.
181
+ Read `harness/sprints/sprint-N/evaluation.md` after completion.
170
182
 
171
183
  **3d. Iteration (if needed)**
172
184
 
173
185
  If the evaluation verdict is FAIL and re-evaluation rounds < 3:
174
186
  1. Update `harness/sprint-status.md` to phase `iteration`, incrementing the round.
175
187
  2. Invoke `@generator`:
176
- > Read harness/evaluation.md and harness/contract.md. Fix the issues listed in the evaluation's "Required Fixes" section. Update harness/handoff.md with what was fixed when done.
188
+ > Read harness/sprints/sprint-[N]/evaluation.md and harness/sprints/sprint-[N]/contract.md. Fix the issues listed in the evaluation's "Required Fixes" section. Update harness/sprints/sprint-[N]/handoff.md with what was fixed when done.
177
189
  3. Re-invoke `@evaluator`:
178
- > Re-evaluate Sprint [N] Round [X]. Read the updated harness/handoff.md for what was fixed, then re-test ONLY the failed criteria and reported bugs from harness/evaluation.md. Write your updated evaluation to harness/evaluation.md.
179
- 4. Read the updated `harness/evaluation.md`.
190
+ > Re-evaluate Sprint [N] Round [X]. Read the updated harness/sprints/sprint-[N]/handoff.md for what was fixed, then re-test ONLY the failed criteria and reported bugs from harness/sprints/sprint-[N]/evaluation.md. Write your updated evaluation to harness/sprints/sprint-[N]/evaluation.md.
191
+ 4. Read the updated `harness/sprints/sprint-N/evaluation.md`.
180
192
  5. Repeat until PASS or max 3 rounds reached.
181
193
 
182
194
  If PASS or max rounds reached:
@@ -196,9 +208,9 @@ After all sprints are complete, write `harness/final-summary.md`:
196
208
  ## Sprints Completed
197
209
 
198
210
  ### Sprint [N]: [Name] — [PASS/FAIL/PARTIAL]
199
- - Evaluation rounds: [count]
211
+ - Evaluation rounds: [count — read from harness/sprints/sprint-N/evaluation.md]
200
212
  - Contract negotiation rounds: [count]
201
- - Key issues found and addressed: [summary]
213
+ - Key issues found and addressed: [summary — read from harness/sprints/sprint-N/evaluation.md]
202
214
 
203
215
  [... repeat for each sprint ...]
204
216
 
@@ -224,7 +236,7 @@ Update `harness/sprint-status.md` to:
224
236
  2. **Cap iteration at 3 rounds per sprint**: If after 3 rounds of fixes the sprint still fails, note the failure and move on. Don't get stuck.
225
237
  3. **Read between phases**: Always read the output files between agent invocations to confirm they completed correctly before moving on.
226
238
  4. **Keep the app running**: The evaluator needs a live application. Ensure the dev server stays running between build and evaluation phases.
227
- 5. **Preserve context**: Ensure each agent invocation reads the relevant context files (spec, contract, previous evaluations) before starting work.
239
+ 5. **Preserve context**: Ensure each agent invocation reads the relevant context files (spec, sprint contract, previous sprint evaluations) before starting work. When starting a new sprint, agents can reference previous sprint folders under `harness/sprints/` for historical context.
228
240
  6. **Don't modify files directly**: Your job is orchestration, not implementation. Use subagents for all substantive work.
229
241
  7. **Update sprint-status.md at every phase transition**: This file is the single source of truth for where the workflow is. All agents read it at the start of each invocation.
230
242
  8. **Handle failures gracefully**: If an agent invocation fails or produces unexpected output, read the files to understand what happened, and adjust the plan accordingly.
package/dist/cli.js CHANGED
@@ -1,11 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import{Command as q}from"commander";import I from"chalk";import p from"chalk";import C from"inquirer";import m from"i18next";import k from"i18next-fs-backend";import D from"path";import{fileURLToPath as R}from"url";var S=R(import.meta.url),$=D.dirname(S),y=!1;async function f(r="en"){return y?(m.language!==r&&await m.changeLanguage(r),m):(await m.use(k).init({lng:r,fallbackLng:"en",backend:{loadPath:D.join($,"../locales/{{lng}}/{{ns}}.json")},ns:["translation"],defaultNS:"translation",interpolation:{escapeValue:!1}}),y=!0,m)}function o(r,e){return m.t(r,e)}async function v(r){return r.positionalModel?r.positionalModel:r.model?r.model:r.stdin?_():r.interactive||!H(r)?F():null}function H(r){return!!(r.positionalModel||r.model||r.stdin)}async function _(){return new Promise((r,e)=>{let t="";process.stdin.setEncoding("utf8"),process.stdin.on("data",n=>{t+=n}),process.stdin.on("end",()=>{let n=t.trim();n?r(n):e(new Error(o("errors.stdinNoData")))}),process.stdin.on("error",n=>{e(new Error(o("errors.stdinReadError",{message:n.message})))}),process.stdin.isTTY&&e(new Error(o("errors.stdinNotAvailable")))})}async function F(){return(await C.prompt([{type:"input",name:"model",message:o("prompts.enterModel"),validate:e=>!e||e.trim()===""?o("prompts.modelRequired"):!0}])).model.trim()}import s from"fs-extra";import a from"path";import{glob as P}from"glob";import M from"ejs";import c from"chalk";import{fileURLToPath as T}from"url";var O=T(import.meta.url),E=a.dirname(O),d=a.resolve(E,"..","agents"),h=a.resolve(E,"..","harness");function N(){let r=a.resolve(process.cwd(),".opencode");return{targetBaseDir:r,targetAgentsDir:a.join(r,"agents"),targetHarnessDir:a.join(r,"harness")}}async function b(){console.log(c.gray(o("info.checkingDirs")));let r=await s.pathExists(d),e=await s.pathExists(h);if(!r&&!e)throw new Error(`${o("errors.sourceNotFound")}
3
- - ${d}
4
- - ${h}`);let{targetBaseDir:t,targetAgentsDir:n,targetHarnessDir:i}=N();return await s.ensureDir(t),console.log(c.gray(` \u2713 ${t}`)),await s.ensureDir(n),console.log(c.gray(` \u2713 ${n}`)),await s.ensureDir(i),console.log(c.gray(` \u2713 ${i}`)),{sourceAgentsDir:d,sourceHarnessDir:h,targetDir:t,targetAgentsDir:n,targetHarnessDir:i,hasSourceAgents:r,hasSourceHarness:e}}async function x(r,e){console.log(c.gray(`
5
- ${o("info.transactionCheck")}`));let t=[],n=[];if(e.hasSourceAgents){let i=await P("**/*.md",{cwd:e.sourceAgentsDir,absolute:!0});for(let l of i){let g=a.relative(e.sourceAgentsDir,l),u=a.join(e.targetAgentsDir,g);await s.pathExists(u)?n.push(u):t.push({source:l,target:u,isAgent:!0,relativePath:g})}}if(e.hasSourceHarness){let i=await P("**/*",{cwd:e.sourceHarnessDir,absolute:!0,nodir:!0});for(let l of i){let g=a.relative(e.sourceHarnessDir,l),u=a.join(e.targetHarnessDir,g);await s.pathExists(u)?n.push(u):t.push({source:l,target:u,isAgent:!1,relativePath:g})}}if(n.length>0){console.error(c.red(`
6
- \u274C ${o("transaction.failed")}`));for(let i of n)console.error(c.red(` - ${i}`));throw new Error(o("errors.transactionFailed",{count:n.length}))}console.log(c.gray(` \u2713 ${o("info.noConflicts",{count:t.length})}`)),console.log(c.gray(`
7
- ${o("info.copyingFiles")}`));for(let i of t){if(await s.ensureDir(a.dirname(i.target)),i.isAgent&&i.relativePath.endsWith(".md")){let l=await s.readFile(i.source,"utf-8"),g=await j(l,{model:r});await s.writeFile(i.target,g)}else await s.copy(i.source,i.target);console.log(c.gray(` \u2713 ${i.relativePath}`))}}async function j(r,e){try{return M.render(r,e,{async:!1})}catch{return r}}async function A(r={},e="en"){await f(e),console.log(p.blue(`\u{1F43F}\uFE0F ${o("title")}
8
- `));let t=await v(r);if(!t)throw new Error(o("errors.modelRequired"));console.log(p.gray(o("info.usingModel",{model:t})+`
9
- `));let n=await b();await x(t,n),console.log(p.green(`
10
- \u2705 ${o("info.success")}`)),console.log(p.gray(` ${o("info.location",{path:n.targetDir})}`))}var w=new q;w.name("create-squirrel-opencode-harness").description("Scaffold squirrel opencode harness into .opencode directory").version("1.0.0").option("-m, --model <model>","Model identifier for agents (e.g., fireworks-ai/accounts/fireworks/routers/kimi-k2p5-turbo)").option("-i, --interactive","Use interactive mode to input model").option("--stdin","Read model from stdin").option("-l, --lang <lang>","Language (en/zh)","en").argument("[model]","Model identifier (positional argument)").action(async(r,e)=>{try{let t=e.lang==="zh"?"zh":"en";await f(t),w.description(o("cli.description")),await A({positionalModel:r,...e},t)}catch(t){t instanceof Error?console.error(I.red("Error:"),t.message):console.error(I.red("Error:"),String(t)),process.exit(1)}});w.parse();
2
+ import{Command as N}from"commander";import x from"chalk";import d from"chalk";import R from"inquirer";import m from"i18next";import I from"i18next-fs-backend";import y from"path";import{fileURLToPath as k}from"url";var $=k(import.meta.url),A=y.dirname($),w=!1;async function p(r="en"){return w?(m.language!==r&&await m.changeLanguage(r),m):(await m.use(I).init({lng:r,fallbackLng:"en",backend:{loadPath:y.join(A,"../locales/{{lng}}/{{ns}}.json")},ns:["translation"],defaultNS:"translation",interpolation:{escapeValue:!1}}),w=!0,m)}function n(r,t){return m.t(r,t)}async function D(r){return r.positionalModel?r.positionalModel:r.model?r.model:r.stdin?S():r.interactive||!C(r)?M():null}function C(r){return!!(r.positionalModel||r.model||r.stdin)}async function S(){return new Promise((r,t)=>{let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",o=>{e+=o}),process.stdin.on("end",()=>{let o=e.trim();o?r(o):t(new Error(n("errors.stdinNoData")))}),process.stdin.on("error",o=>{t(new Error(n("errors.stdinReadError",{message:o.message})))}),process.stdin.isTTY&&t(new Error(n("errors.stdinNotAvailable")))})}async function M(){return(await R.prompt([{type:"input",name:"model",message:n("prompts.enterModel"),validate:t=>!t||t.trim()===""?n("prompts.modelRequired"):!0}])).model.trim()}import s from"fs-extra";import c from"path";import{glob as T}from"glob";import _ from"ejs";import a from"chalk";import{fileURLToPath as F}from"url";var O=F(import.meta.url),j=c.dirname(O),u=c.resolve(j,"..","agents");function q(){let r=c.resolve(process.cwd(),".opencode");return{targetBaseDir:r,targetAgentsDir:c.join(r,"agents"),targetHarnessDir:c.join(r,"harness"),targetSprintsDir:c.join(r,"harness","sprints")}}async function v(){console.log(a.gray(n("info.checkingDirs")));let r=await s.pathExists(u);if(!r)throw new Error(`${n("errors.sourceNotFound")}
3
+ - ${u}`);let{targetBaseDir:t,targetAgentsDir:e,targetHarnessDir:o,targetSprintsDir:f}=q();return await s.ensureDir(t),console.log(a.gray(` \u2713 ${t}`)),await s.ensureDir(e),console.log(a.gray(` \u2713 ${e}`)),await s.ensureDir(o),console.log(a.gray(` \u2713 ${o}`)),await s.ensureDir(f),console.log(a.gray(` \u2713 ${f}`)),{sourceAgentsDir:u,targetDir:t,targetAgentsDir:e,targetHarnessDir:o,targetSprintsDir:f,hasSourceAgents:r}}async function P(r,t){console.log(a.gray(`
4
+ ${n("info.transactionCheck")}`));let e=[],o=[],f=await T("**/*.md",{cwd:t.sourceAgentsDir,absolute:!0});for(let i of f){let l=c.relative(t.sourceAgentsDir,i),g=c.join(t.targetAgentsDir,l);await s.pathExists(g)?o.push(g):e.push({source:i,target:g,relativePath:l})}let E=["sprints"];for(let i of E){let l=c.join(t.targetHarnessDir,i);await s.pathExists(l)&&(await s.readdir(l)).length>0&&o.push(l)}if(o.length>0){console.error(a.red(`
5
+ \u274C ${n("transaction.failed")}`));for(let i of o)console.error(a.red(` - ${i}`));throw new Error(n("errors.transactionFailed",{count:o.length}))}console.log(a.gray(` \u2713 ${n("info.noConflicts",{count:e.length})}`)),console.log(a.gray(`
6
+ ${n("info.copyingFiles")}`));for(let i of e){await s.ensureDir(c.dirname(i.target));let l=await s.readFile(i.source,"utf-8"),g=await L(l,{model:r});await s.writeFile(i.target,g),console.log(a.gray(` \u2713 ${i.relativePath}`))}}async function L(r,t){try{return _.render(r,t,{async:!1})}catch{return r}}async function b(r={},t="en"){await p(t),console.log(d.blue(`\u{1F43F}\uFE0F ${n("title")}
7
+ `));let e=await D(r);if(!e)throw new Error(n("errors.modelRequired"));console.log(d.gray(n("info.usingModel",{model:e})+`
8
+ `));let o=await v();await P(e,o),console.log(d.green(`
9
+ \u2705 ${n("info.success")}`)),console.log(d.gray(` ${n("info.location",{path:o.targetDir})}`))}var h=new N;h.name("create-squirrel-opencode-harness").description("Scaffold squirrel opencode harness into .opencode directory").version("1.0.0").option("-m, --model <model>","Model identifier for agents (e.g., fireworks-ai/accounts/fireworks/routers/kimi-k2p5-turbo)").option("-i, --interactive","Use interactive mode to input model").option("--stdin","Read model from stdin").option("-l, --lang <lang>","Language (en/zh)","en").argument("[model]","Model identifier (positional argument)").action(async(r,t)=>{try{let e=t.lang==="zh"?"zh":"en";await p(e),h.description(n("cli.description")),await b({positionalModel:r,...t},e)}catch(e){e instanceof Error?console.error(x.red("Error:"),e.message):console.error(x.red("Error:"),String(e)),process.exit(1)}});h.parse();
11
10
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/cli.ts", "../src/index.ts", "../src/input.ts", "../src/i18n.ts", "../src/fileOps.ts"],
4
- "sourcesContent": ["import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { run } from './index.js';\nimport { initI18n, t } from './i18n.js';\n\ninterface CliOptions {\n model?: string;\n interactive?: boolean;\n stdin?: boolean;\n lang?: string;\n}\n\nconst program = new Command();\n\nprogram\n .name('create-squirrel-opencode-harness')\n .description('Scaffold squirrel opencode harness into .opencode directory')\n .version('1.0.0')\n .option('-m, --model <model>', 'Model identifier for agents (e.g., fireworks-ai/accounts/fireworks/routers/kimi-k2p5-turbo)')\n .option('-i, --interactive', 'Use interactive mode to input model')\n .option('--stdin', 'Read model from stdin')\n .option('-l, --lang <lang>', 'Language (en/zh)', 'en')\n .argument('[model]', 'Model identifier (positional argument)')\n .action(async (positionalModel: string | undefined, options: CliOptions) => {\n try {\n // Initialize i18n with selected language\n const lang = options.lang === 'zh' ? 'zh' : 'en';\n await initI18n(lang);\n\n // Update program description based on language\n program.description(t('cli.description'));\n\n await run({\n positionalModel,\n ...options\n }, lang);\n } catch (error: unknown) {\n // Ensure i18n is initialized even for early errors\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message);\n } else {\n console.error(chalk.red('Error:'), String(error));\n }\n process.exit(1);\n }\n });\n\nprogram.parse();\n", "import chalk from 'chalk';\nimport { resolveInput } from './input.js';\nimport { checkDirectories, transactionalCopy, type DirectoryInfo } from './fileOps.js';\nimport { initI18n, t } from './i18n.js';\n\nexport interface RunOptions {\n positionalModel?: string;\n model?: string;\n interactive?: boolean;\n stdin?: boolean;\n lang?: string;\n}\n\nexport async function run(options: RunOptions = {}, lang: string = 'en'): Promise<void> {\n // Ensure i18n is initialized\n await initI18n(lang);\n\n console.log(chalk.blue(`\uD83D\uDC3F\uFE0F ${t('title')}\\n`));\n\n // Step 1: Resolve model input (from CLI args, stdin, or interactive)\n const model = await resolveInput(options);\n\n if (!model) {\n throw new Error(t('errors.modelRequired'));\n }\n\n console.log(chalk.gray(t('info.usingModel', { model }) + '\\n'));\n\n // Step 2: Check and create directories\n const dirs: DirectoryInfo = await checkDirectories();\n\n // Step 3: Transactional copy with template processing\n await transactionalCopy(model, dirs);\n\n console.log(chalk.green(`\\n\u2705 ${t('info.success')}`));\n console.log(chalk.gray(` ${t('info.location', { path: dirs.targetDir })}`));\n}\n", "import inquirer from 'inquirer';\nimport { t } from './i18n.js';\n\nexport interface InputOptions {\n positionalModel?: string;\n model?: string;\n interactive?: boolean;\n stdin?: boolean;\n}\n\nexport async function resolveInput(options: InputOptions): Promise<string | null> {\n // Priority 1: CLI argument (positional)\n if (options.positionalModel) {\n return options.positionalModel;\n }\n\n // Priority 2: CLI option --model\n if (options.model) {\n return options.model;\n }\n\n // Priority 3: Stdin\n if (options.stdin) {\n return readStdin();\n }\n\n // Priority 4: Interactive mode (if requested or as fallback)\n if (options.interactive || !hasModelInput(options)) {\n return promptInteractive();\n }\n\n return null;\n}\n\nfunction hasModelInput(options: InputOptions): boolean {\n return !!(options.positionalModel || options.model || options.stdin);\n}\n\nasync function readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n let data = '';\n\n process.stdin.setEncoding('utf8');\n\n process.stdin.on('data', (chunk: string) => {\n data += chunk;\n });\n\n process.stdin.on('end', () => {\n const trimmed = data.trim();\n if (!trimmed) {\n reject(new Error(t('errors.stdinNoData')));\n } else {\n resolve(trimmed);\n }\n });\n\n process.stdin.on('error', (err: Error) => {\n reject(new Error(t('errors.stdinReadError', { message: err.message })));\n });\n\n // If stdin is a TTY (not piped), we need to handle it differently\n if (process.stdin.isTTY) {\n reject(new Error(t('errors.stdinNotAvailable')));\n }\n });\n}\n\nasync function promptInteractive(): Promise<string> {\n const answers = await inquirer.prompt([\n {\n type: 'input',\n name: 'model',\n message: t('prompts.enterModel'),\n validate: (input: string) => {\n if (!input || input.trim() === '') {\n return t('prompts.modelRequired');\n }\n return true;\n }\n }\n ]);\n\n return (answers.model as string).trim();\n}\n", "import i18next from 'i18next';\nimport Backend from 'i18next-fs-backend';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nlet initialized = false;\n\nexport async function initI18n(language: string = 'en'): Promise<typeof i18next> {\n if (initialized) {\n if (i18next.language !== language) {\n await i18next.changeLanguage(language);\n }\n return i18next;\n }\n\n await i18next.use(Backend).init({\n lng: language,\n fallbackLng: 'en',\n backend: {\n loadPath: path.join(__dirname, '../locales/{{lng}}/{{ns}}.json')\n },\n ns: ['translation'],\n defaultNS: 'translation',\n interpolation: {\n escapeValue: false // Disable escaping for CLI output\n }\n });\n\n initialized = true;\n return i18next;\n}\n\nexport function t(key: string, options?: Record<string, string | number>): string {\n return i18next.t(key, options);\n}\n\nexport { i18next };\n", "import fs from 'fs-extra';\nimport path from 'path';\nimport { glob } from 'glob';\nimport ejs from 'ejs';\nimport chalk from 'chalk';\nimport { fileURLToPath } from 'url';\nimport { t } from './i18n.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Source directories (where templates are stored) - relative to src/ directory\nconst SOURCE_AGENTS_DIR = path.resolve(__dirname, '..', 'agents');\nconst SOURCE_HARNESS_DIR = path.resolve(__dirname, '..', 'harness');\n\nexport interface DirectoryInfo {\n sourceAgentsDir: string;\n sourceHarnessDir: string;\n targetDir: string;\n targetAgentsDir: string;\n targetHarnessDir: string;\n hasSourceAgents: boolean;\n hasSourceHarness: boolean;\n}\n\ninterface FileToCopy {\n source: string;\n target: string;\n isAgent: boolean;\n relativePath: string;\n}\n\n// Target directories (computed at runtime to support test isolation)\nfunction getTargetDirs() {\n const targetBaseDir = path.resolve(process.cwd(), '.opencode');\n return {\n targetBaseDir,\n targetAgentsDir: path.join(targetBaseDir, 'agents'),\n targetHarnessDir: path.join(targetBaseDir, 'harness')\n };\n}\n\nexport async function checkDirectories(): Promise<DirectoryInfo> {\n console.log(chalk.gray(t('info.checkingDirs')));\n\n // Check if source directories exist\n const hasSourceAgents = await fs.pathExists(SOURCE_AGENTS_DIR);\n const hasSourceHarness = await fs.pathExists(SOURCE_HARNESS_DIR);\n\n if (!hasSourceAgents && !hasSourceHarness) {\n throw new Error(\n `${t('errors.sourceNotFound')}\\n` +\n ` - ${SOURCE_AGENTS_DIR}\\n` +\n ` - ${SOURCE_HARNESS_DIR}`\n );\n }\n\n // Get target directories (computed at runtime)\n const { targetBaseDir, targetAgentsDir, targetHarnessDir } = getTargetDirs();\n\n // Create target directories if they don't exist\n await fs.ensureDir(targetBaseDir);\n console.log(chalk.gray(` \u2713 ${targetBaseDir}`));\n\n await fs.ensureDir(targetAgentsDir);\n console.log(chalk.gray(` \u2713 ${targetAgentsDir}`));\n\n await fs.ensureDir(targetHarnessDir);\n console.log(chalk.gray(` \u2713 ${targetHarnessDir}`));\n\n return {\n sourceAgentsDir: SOURCE_AGENTS_DIR,\n sourceHarnessDir: SOURCE_HARNESS_DIR,\n targetDir: targetBaseDir,\n targetAgentsDir,\n targetHarnessDir,\n hasSourceAgents,\n hasSourceHarness\n };\n}\n\nexport async function transactionalCopy(model: string, dirs: DirectoryInfo): Promise<void> {\n console.log(chalk.gray(`\\n${t('info.transactionCheck')}`));\n\n // Collect all files that need to be copied\n const filesToCopy: FileToCopy[] = [];\n const existingFiles: string[] = [];\n\n // Process agents directory\n if (dirs.hasSourceAgents) {\n const agentFiles = await glob('**/*.md', {\n cwd: dirs.sourceAgentsDir,\n absolute: true\n });\n\n for (const sourcePath of agentFiles) {\n const relativePath = path.relative(dirs.sourceAgentsDir, sourcePath);\n const targetPath = path.join(dirs.targetAgentsDir, relativePath);\n\n if (await fs.pathExists(targetPath)) {\n existingFiles.push(targetPath);\n } else {\n filesToCopy.push({\n source: sourcePath,\n target: targetPath,\n isAgent: true,\n relativePath\n });\n }\n }\n }\n\n // Process harness directory\n if (dirs.hasSourceHarness) {\n const harnessFiles = await glob('**/*', {\n cwd: dirs.sourceHarnessDir,\n absolute: true,\n nodir: true\n });\n\n for (const sourcePath of harnessFiles) {\n const relativePath = path.relative(dirs.sourceHarnessDir, sourcePath);\n const targetPath = path.join(dirs.targetHarnessDir, relativePath);\n\n if (await fs.pathExists(targetPath)) {\n existingFiles.push(targetPath);\n } else {\n filesToCopy.push({\n source: sourcePath,\n target: targetPath,\n isAgent: false,\n relativePath\n });\n }\n }\n }\n\n // Transaction check: if any file exists, abort\n if (existingFiles.length > 0) {\n console.error(chalk.red(`\\n\u274C ${t('transaction.failed')}`));\n for (const file of existingFiles) {\n console.error(chalk.red(` - ${file}`));\n }\n throw new Error(t('errors.transactionFailed', { count: existingFiles.length }));\n }\n\n console.log(chalk.gray(` \u2713 ${t('info.noConflicts', { count: filesToCopy.length })}`));\n\n // All checks passed, now perform the copy\n console.log(chalk.gray(`\\n${t('info.copyingFiles')}`));\n\n for (const file of filesToCopy) {\n await fs.ensureDir(path.dirname(file.target));\n\n if (file.isAgent && file.relativePath.endsWith('.md')) {\n // Process template for agent markdown files\n const content = await fs.readFile(file.source, 'utf-8');\n const processed = await processTemplate(content, { model });\n await fs.writeFile(file.target, processed);\n } else {\n // Copy as-is for non-agent files\n await fs.copy(file.source, file.target);\n }\n\n console.log(chalk.gray(` \u2713 ${file.relativePath}`));\n }\n}\n\nasync function processTemplate(content: string, data: Record<string, string>): Promise<string> {\n // Use EJS to render the template\n // The model variable will be replaced in the template\n try {\n const result = ejs.render(content, data, {\n async: false\n });\n return result;\n } catch (error) {\n // If EJS parsing fails, return original content\n // This handles files that may not be valid EJS templates\n return content;\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["Command", "chalk", "chalk", "inquirer", "i18next", "Backend", "path", "fileURLToPath", "__filename", "__dirname", "initialized", "initI18n", "language", "t", "key", "options", "resolveInput", "options", "readStdin", "hasModelInput", "promptInteractive", "resolve", "reject", "data", "chunk", "trimmed", "t", "err", "inquirer", "input", "fs", "path", "glob", "ejs", "chalk", "fileURLToPath", "__filename", "fileURLToPath", "__dirname", "path", "SOURCE_AGENTS_DIR", "SOURCE_HARNESS_DIR", "getTargetDirs", "targetBaseDir", "checkDirectories", "chalk", "t", "hasSourceAgents", "fs", "hasSourceHarness", "targetAgentsDir", "targetHarnessDir", "transactionalCopy", "model", "dirs", "filesToCopy", "existingFiles", "agentFiles", "glob", "sourcePath", "relativePath", "targetPath", "harnessFiles", "file", "content", "processed", "processTemplate", "data", "ejs", "run", "options", "lang", "initI18n", "chalk", "t", "model", "resolveInput", "dirs", "checkDirectories", "transactionalCopy", "program", "Command", "positionalModel", "options", "lang", "initI18n", "t", "run", "error", "chalk"]
4
+ "sourcesContent": ["import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { run } from './index.js';\nimport { initI18n, t } from './i18n.js';\n\ninterface CliOptions {\n model?: string;\n interactive?: boolean;\n stdin?: boolean;\n lang?: string;\n}\n\nconst program = new Command();\n\nprogram\n .name('create-squirrel-opencode-harness')\n .description('Scaffold squirrel opencode harness into .opencode directory')\n .version('1.0.0')\n .option('-m, --model <model>', 'Model identifier for agents (e.g., fireworks-ai/accounts/fireworks/routers/kimi-k2p5-turbo)')\n .option('-i, --interactive', 'Use interactive mode to input model')\n .option('--stdin', 'Read model from stdin')\n .option('-l, --lang <lang>', 'Language (en/zh)', 'en')\n .argument('[model]', 'Model identifier (positional argument)')\n .action(async (positionalModel: string | undefined, options: CliOptions) => {\n try {\n // Initialize i18n with selected language\n const lang = options.lang === 'zh' ? 'zh' : 'en';\n await initI18n(lang);\n\n // Update program description based on language\n program.description(t('cli.description'));\n\n await run({\n positionalModel,\n ...options\n }, lang);\n } catch (error: unknown) {\n // Ensure i18n is initialized even for early errors\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message);\n } else {\n console.error(chalk.red('Error:'), String(error));\n }\n process.exit(1);\n }\n });\n\nprogram.parse();\n", "import chalk from 'chalk';\nimport { resolveInput } from './input.js';\nimport { checkDirectories, transactionalCopy, type DirectoryInfo } from './fileOps.js';\nimport { initI18n, t } from './i18n.js';\n\nexport interface RunOptions {\n positionalModel?: string;\n model?: string;\n interactive?: boolean;\n stdin?: boolean;\n lang?: string;\n}\n\nexport async function run(options: RunOptions = {}, lang: string = 'en'): Promise<void> {\n // Ensure i18n is initialized\n await initI18n(lang);\n\n console.log(chalk.blue(`\uD83D\uDC3F\uFE0F ${t('title')}\\n`));\n\n // Step 1: Resolve model input (from CLI args, stdin, or interactive)\n const model = await resolveInput(options);\n\n if (!model) {\n throw new Error(t('errors.modelRequired'));\n }\n\n console.log(chalk.gray(t('info.usingModel', { model }) + '\\n'));\n\n // Step 2: Check and create directories\n const dirs: DirectoryInfo = await checkDirectories();\n\n // Step 3: Transactional copy with template processing\n await transactionalCopy(model, dirs);\n\n console.log(chalk.green(`\\n\u2705 ${t('info.success')}`));\n console.log(chalk.gray(` ${t('info.location', { path: dirs.targetDir })}`));\n}\n", "import inquirer from 'inquirer';\nimport { t } from './i18n.js';\n\nexport interface InputOptions {\n positionalModel?: string;\n model?: string;\n interactive?: boolean;\n stdin?: boolean;\n}\n\nexport async function resolveInput(options: InputOptions): Promise<string | null> {\n // Priority 1: CLI argument (positional)\n if (options.positionalModel) {\n return options.positionalModel;\n }\n\n // Priority 2: CLI option --model\n if (options.model) {\n return options.model;\n }\n\n // Priority 3: Stdin\n if (options.stdin) {\n return readStdin();\n }\n\n // Priority 4: Interactive mode (if requested or as fallback)\n if (options.interactive || !hasModelInput(options)) {\n return promptInteractive();\n }\n\n return null;\n}\n\nfunction hasModelInput(options: InputOptions): boolean {\n return !!(options.positionalModel || options.model || options.stdin);\n}\n\nasync function readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n let data = '';\n\n process.stdin.setEncoding('utf8');\n\n process.stdin.on('data', (chunk: string) => {\n data += chunk;\n });\n\n process.stdin.on('end', () => {\n const trimmed = data.trim();\n if (!trimmed) {\n reject(new Error(t('errors.stdinNoData')));\n } else {\n resolve(trimmed);\n }\n });\n\n process.stdin.on('error', (err: Error) => {\n reject(new Error(t('errors.stdinReadError', { message: err.message })));\n });\n\n // If stdin is a TTY (not piped), we need to handle it differently\n if (process.stdin.isTTY) {\n reject(new Error(t('errors.stdinNotAvailable')));\n }\n });\n}\n\nasync function promptInteractive(): Promise<string> {\n const answers = await inquirer.prompt([\n {\n type: 'input',\n name: 'model',\n message: t('prompts.enterModel'),\n validate: (input: string) => {\n if (!input || input.trim() === '') {\n return t('prompts.modelRequired');\n }\n return true;\n }\n }\n ]);\n\n return (answers.model as string).trim();\n}\n", "import i18next from 'i18next';\nimport Backend from 'i18next-fs-backend';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nlet initialized = false;\n\nexport async function initI18n(language: string = 'en'): Promise<typeof i18next> {\n if (initialized) {\n if (i18next.language !== language) {\n await i18next.changeLanguage(language);\n }\n return i18next;\n }\n\n await i18next.use(Backend).init({\n lng: language,\n fallbackLng: 'en',\n backend: {\n loadPath: path.join(__dirname, '../locales/{{lng}}/{{ns}}.json')\n },\n ns: ['translation'],\n defaultNS: 'translation',\n interpolation: {\n escapeValue: false // Disable escaping for CLI output\n }\n });\n\n initialized = true;\n return i18next;\n}\n\nexport function t(key: string, options?: Record<string, string | number>): string {\n return i18next.t(key, options);\n}\n\nexport { i18next };\n", "import fs from 'fs-extra';\nimport path from 'path';\nimport { glob } from 'glob';\nimport ejs from 'ejs';\nimport chalk from 'chalk';\nimport { fileURLToPath } from 'url';\nimport { t } from './i18n.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nconst SOURCE_AGENTS_DIR = path.resolve(__dirname, '..', 'agents');\n\nexport interface DirectoryInfo {\n sourceAgentsDir: string;\n targetDir: string;\n targetAgentsDir: string;\n targetHarnessDir: string;\n targetSprintsDir: string;\n hasSourceAgents: boolean;\n}\n\ninterface FileToCopy {\n source: string;\n target: string;\n relativePath: string;\n}\n\nfunction getTargetDirs() {\n const targetBaseDir = path.resolve(process.cwd(), '.opencode');\n return {\n targetBaseDir,\n targetAgentsDir: path.join(targetBaseDir, 'agents'),\n targetHarnessDir: path.join(targetBaseDir, 'harness'),\n targetSprintsDir: path.join(targetBaseDir, 'harness', 'sprints')\n };\n}\n\nexport async function checkDirectories(): Promise<DirectoryInfo> {\n console.log(chalk.gray(t('info.checkingDirs')));\n\n const hasSourceAgents = await fs.pathExists(SOURCE_AGENTS_DIR);\n\n if (!hasSourceAgents) {\n throw new Error(\n `${t('errors.sourceNotFound')}\\n` +\n ` - ${SOURCE_AGENTS_DIR}`\n );\n }\n\n const { targetBaseDir, targetAgentsDir, targetHarnessDir, targetSprintsDir } = getTargetDirs();\n\n await fs.ensureDir(targetBaseDir);\n console.log(chalk.gray(` \u2713 ${targetBaseDir}`));\n\n await fs.ensureDir(targetAgentsDir);\n console.log(chalk.gray(` \u2713 ${targetAgentsDir}`));\n\n await fs.ensureDir(targetHarnessDir);\n console.log(chalk.gray(` \u2713 ${targetHarnessDir}`));\n\n await fs.ensureDir(targetSprintsDir);\n console.log(chalk.gray(` \u2713 ${targetSprintsDir}`));\n\n return {\n sourceAgentsDir: SOURCE_AGENTS_DIR,\n targetDir: targetBaseDir,\n targetAgentsDir,\n targetHarnessDir,\n targetSprintsDir,\n hasSourceAgents\n };\n}\n\nexport async function transactionalCopy(model: string, dirs: DirectoryInfo): Promise<void> {\n console.log(chalk.gray(`\\n${t('info.transactionCheck')}`));\n\n const filesToCopy: FileToCopy[] = [];\n const existingFiles: string[] = [];\n\n const agentFiles = await glob('**/*.md', {\n cwd: dirs.sourceAgentsDir,\n absolute: true\n });\n\n for (const sourcePath of agentFiles) {\n const relativePath = path.relative(dirs.sourceAgentsDir, sourcePath);\n const targetPath = path.join(dirs.targetAgentsDir, relativePath);\n\n if (await fs.pathExists(targetPath)) {\n existingFiles.push(targetPath);\n } else {\n filesToCopy.push({\n source: sourcePath,\n target: targetPath,\n relativePath\n });\n }\n }\n\n // Check for existing harness directories that would conflict\n const harnessDirs = ['sprints'];\n for (const subDir of harnessDirs) {\n const targetPath = path.join(dirs.targetHarnessDir, subDir);\n if (await fs.pathExists(targetPath)) {\n const contents = await fs.readdir(targetPath);\n if (contents.length > 0) {\n existingFiles.push(targetPath);\n }\n }\n }\n\n if (existingFiles.length > 0) {\n console.error(chalk.red(`\\n\u274C ${t('transaction.failed')}`));\n for (const file of existingFiles) {\n console.error(chalk.red(` - ${file}`));\n }\n throw new Error(t('errors.transactionFailed', { count: existingFiles.length }));\n }\n\n console.log(chalk.gray(` \u2713 ${t('info.noConflicts', { count: filesToCopy.length })}`));\n\n console.log(chalk.gray(`\\n${t('info.copyingFiles')}`));\n\n for (const file of filesToCopy) {\n await fs.ensureDir(path.dirname(file.target));\n\n const content = await fs.readFile(file.source, 'utf-8');\n const processed = await processTemplate(content, { model });\n await fs.writeFile(file.target, processed);\n\n console.log(chalk.gray(` \u2713 ${file.relativePath}`));\n }\n}\n\nasync function processTemplate(content: string, data: Record<string, string>): Promise<string> {\n try {\n const result = ejs.render(content, data, {\n async: false\n });\n return result;\n } catch {\n return content;\n }\n}"],
5
+ "mappings": "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",
6
+ "names": ["Command", "chalk", "chalk", "inquirer", "i18next", "Backend", "path", "fileURLToPath", "__filename", "__dirname", "initialized", "initI18n", "language", "t", "key", "options", "resolveInput", "options", "readStdin", "hasModelInput", "promptInteractive", "resolve", "reject", "data", "chunk", "trimmed", "t", "err", "inquirer", "input", "fs", "path", "glob", "ejs", "chalk", "fileURLToPath", "__filename", "fileURLToPath", "__dirname", "path", "SOURCE_AGENTS_DIR", "getTargetDirs", "targetBaseDir", "checkDirectories", "chalk", "t", "hasSourceAgents", "fs", "targetAgentsDir", "targetHarnessDir", "targetSprintsDir", "transactionalCopy", "model", "dirs", "filesToCopy", "existingFiles", "agentFiles", "glob", "sourcePath", "relativePath", "targetPath", "harnessDirs", "subDir", "file", "content", "processed", "processTemplate", "data", "ejs", "run", "options", "lang", "initI18n", "chalk", "t", "model", "resolveInput", "dirs", "checkDirectories", "transactionalCopy", "program", "Command", "positionalModel", "options", "lang", "initI18n", "t", "run", "error", "chalk"]
7
7
  }
package/dist/fileOps.js CHANGED
@@ -8,41 +8,40 @@ import { t } from "./i18n.js";
8
8
  const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = path.dirname(__filename);
10
10
  const SOURCE_AGENTS_DIR = path.resolve(__dirname, "..", "agents");
11
- const SOURCE_HARNESS_DIR = path.resolve(__dirname, "..", "harness");
12
11
  function getTargetDirs() {
13
12
  const targetBaseDir = path.resolve(process.cwd(), ".opencode");
14
13
  return {
15
14
  targetBaseDir,
16
15
  targetAgentsDir: path.join(targetBaseDir, "agents"),
17
- targetHarnessDir: path.join(targetBaseDir, "harness")
16
+ targetHarnessDir: path.join(targetBaseDir, "harness"),
17
+ targetSprintsDir: path.join(targetBaseDir, "harness", "sprints")
18
18
  };
19
19
  }
20
20
  async function checkDirectories() {
21
21
  console.log(chalk.gray(t("info.checkingDirs")));
22
22
  const hasSourceAgents = await fs.pathExists(SOURCE_AGENTS_DIR);
23
- const hasSourceHarness = await fs.pathExists(SOURCE_HARNESS_DIR);
24
- if (!hasSourceAgents && !hasSourceHarness) {
23
+ if (!hasSourceAgents) {
25
24
  throw new Error(
26
25
  `${t("errors.sourceNotFound")}
27
- - ${SOURCE_AGENTS_DIR}
28
- - ${SOURCE_HARNESS_DIR}`
26
+ - ${SOURCE_AGENTS_DIR}`
29
27
  );
30
28
  }
31
- const { targetBaseDir, targetAgentsDir, targetHarnessDir } = getTargetDirs();
29
+ const { targetBaseDir, targetAgentsDir, targetHarnessDir, targetSprintsDir } = getTargetDirs();
32
30
  await fs.ensureDir(targetBaseDir);
33
31
  console.log(chalk.gray(` \u2713 ${targetBaseDir}`));
34
32
  await fs.ensureDir(targetAgentsDir);
35
33
  console.log(chalk.gray(` \u2713 ${targetAgentsDir}`));
36
34
  await fs.ensureDir(targetHarnessDir);
37
35
  console.log(chalk.gray(` \u2713 ${targetHarnessDir}`));
36
+ await fs.ensureDir(targetSprintsDir);
37
+ console.log(chalk.gray(` \u2713 ${targetSprintsDir}`));
38
38
  return {
39
39
  sourceAgentsDir: SOURCE_AGENTS_DIR,
40
- sourceHarnessDir: SOURCE_HARNESS_DIR,
41
40
  targetDir: targetBaseDir,
42
41
  targetAgentsDir,
43
42
  targetHarnessDir,
44
- hasSourceAgents,
45
- hasSourceHarness
43
+ targetSprintsDir,
44
+ hasSourceAgents
46
45
  };
47
46
  }
48
47
  async function transactionalCopy(model, dirs) {
@@ -50,44 +49,30 @@ async function transactionalCopy(model, dirs) {
50
49
  ${t("info.transactionCheck")}`));
51
50
  const filesToCopy = [];
52
51
  const existingFiles = [];
53
- if (dirs.hasSourceAgents) {
54
- const agentFiles = await glob("**/*.md", {
55
- cwd: dirs.sourceAgentsDir,
56
- absolute: true
57
- });
58
- for (const sourcePath of agentFiles) {
59
- const relativePath = path.relative(dirs.sourceAgentsDir, sourcePath);
60
- const targetPath = path.join(dirs.targetAgentsDir, relativePath);
61
- if (await fs.pathExists(targetPath)) {
62
- existingFiles.push(targetPath);
63
- } else {
64
- filesToCopy.push({
65
- source: sourcePath,
66
- target: targetPath,
67
- isAgent: true,
68
- relativePath
69
- });
70
- }
52
+ const agentFiles = await glob("**/*.md", {
53
+ cwd: dirs.sourceAgentsDir,
54
+ absolute: true
55
+ });
56
+ for (const sourcePath of agentFiles) {
57
+ const relativePath = path.relative(dirs.sourceAgentsDir, sourcePath);
58
+ const targetPath = path.join(dirs.targetAgentsDir, relativePath);
59
+ if (await fs.pathExists(targetPath)) {
60
+ existingFiles.push(targetPath);
61
+ } else {
62
+ filesToCopy.push({
63
+ source: sourcePath,
64
+ target: targetPath,
65
+ relativePath
66
+ });
71
67
  }
72
68
  }
73
- if (dirs.hasSourceHarness) {
74
- const harnessFiles = await glob("**/*", {
75
- cwd: dirs.sourceHarnessDir,
76
- absolute: true,
77
- nodir: true
78
- });
79
- for (const sourcePath of harnessFiles) {
80
- const relativePath = path.relative(dirs.sourceHarnessDir, sourcePath);
81
- const targetPath = path.join(dirs.targetHarnessDir, relativePath);
82
- if (await fs.pathExists(targetPath)) {
69
+ const harnessDirs = ["sprints"];
70
+ for (const subDir of harnessDirs) {
71
+ const targetPath = path.join(dirs.targetHarnessDir, subDir);
72
+ if (await fs.pathExists(targetPath)) {
73
+ const contents = await fs.readdir(targetPath);
74
+ if (contents.length > 0) {
83
75
  existingFiles.push(targetPath);
84
- } else {
85
- filesToCopy.push({
86
- source: sourcePath,
87
- target: targetPath,
88
- isAgent: false,
89
- relativePath
90
- });
91
76
  }
92
77
  }
93
78
  }
@@ -104,13 +89,9 @@ ${t("info.transactionCheck")}`));
104
89
  ${t("info.copyingFiles")}`));
105
90
  for (const file of filesToCopy) {
106
91
  await fs.ensureDir(path.dirname(file.target));
107
- if (file.isAgent && file.relativePath.endsWith(".md")) {
108
- const content = await fs.readFile(file.source, "utf-8");
109
- const processed = await processTemplate(content, { model });
110
- await fs.writeFile(file.target, processed);
111
- } else {
112
- await fs.copy(file.source, file.target);
113
- }
92
+ const content = await fs.readFile(file.source, "utf-8");
93
+ const processed = await processTemplate(content, { model });
94
+ await fs.writeFile(file.target, processed);
114
95
  console.log(chalk.gray(` \u2713 ${file.relativePath}`));
115
96
  }
116
97
  }
@@ -120,7 +101,7 @@ async function processTemplate(content, data) {
120
101
  async: false
121
102
  });
122
103
  return result;
123
- } catch (error) {
104
+ } catch {
124
105
  return content;
125
106
  }
126
107
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/fileOps.ts"],
4
- "sourcesContent": ["import fs from 'fs-extra';\nimport path from 'path';\nimport { glob } from 'glob';\nimport ejs from 'ejs';\nimport chalk from 'chalk';\nimport { fileURLToPath } from 'url';\nimport { t } from './i18n.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Source directories (where templates are stored) - relative to src/ directory\nconst SOURCE_AGENTS_DIR = path.resolve(__dirname, '..', 'agents');\nconst SOURCE_HARNESS_DIR = path.resolve(__dirname, '..', 'harness');\n\nexport interface DirectoryInfo {\n sourceAgentsDir: string;\n sourceHarnessDir: string;\n targetDir: string;\n targetAgentsDir: string;\n targetHarnessDir: string;\n hasSourceAgents: boolean;\n hasSourceHarness: boolean;\n}\n\ninterface FileToCopy {\n source: string;\n target: string;\n isAgent: boolean;\n relativePath: string;\n}\n\n// Target directories (computed at runtime to support test isolation)\nfunction getTargetDirs() {\n const targetBaseDir = path.resolve(process.cwd(), '.opencode');\n return {\n targetBaseDir,\n targetAgentsDir: path.join(targetBaseDir, 'agents'),\n targetHarnessDir: path.join(targetBaseDir, 'harness')\n };\n}\n\nexport async function checkDirectories(): Promise<DirectoryInfo> {\n console.log(chalk.gray(t('info.checkingDirs')));\n\n // Check if source directories exist\n const hasSourceAgents = await fs.pathExists(SOURCE_AGENTS_DIR);\n const hasSourceHarness = await fs.pathExists(SOURCE_HARNESS_DIR);\n\n if (!hasSourceAgents && !hasSourceHarness) {\n throw new Error(\n `${t('errors.sourceNotFound')}\\n` +\n ` - ${SOURCE_AGENTS_DIR}\\n` +\n ` - ${SOURCE_HARNESS_DIR}`\n );\n }\n\n // Get target directories (computed at runtime)\n const { targetBaseDir, targetAgentsDir, targetHarnessDir } = getTargetDirs();\n\n // Create target directories if they don't exist\n await fs.ensureDir(targetBaseDir);\n console.log(chalk.gray(` \u2713 ${targetBaseDir}`));\n\n await fs.ensureDir(targetAgentsDir);\n console.log(chalk.gray(` \u2713 ${targetAgentsDir}`));\n\n await fs.ensureDir(targetHarnessDir);\n console.log(chalk.gray(` \u2713 ${targetHarnessDir}`));\n\n return {\n sourceAgentsDir: SOURCE_AGENTS_DIR,\n sourceHarnessDir: SOURCE_HARNESS_DIR,\n targetDir: targetBaseDir,\n targetAgentsDir,\n targetHarnessDir,\n hasSourceAgents,\n hasSourceHarness\n };\n}\n\nexport async function transactionalCopy(model: string, dirs: DirectoryInfo): Promise<void> {\n console.log(chalk.gray(`\\n${t('info.transactionCheck')}`));\n\n // Collect all files that need to be copied\n const filesToCopy: FileToCopy[] = [];\n const existingFiles: string[] = [];\n\n // Process agents directory\n if (dirs.hasSourceAgents) {\n const agentFiles = await glob('**/*.md', {\n cwd: dirs.sourceAgentsDir,\n absolute: true\n });\n\n for (const sourcePath of agentFiles) {\n const relativePath = path.relative(dirs.sourceAgentsDir, sourcePath);\n const targetPath = path.join(dirs.targetAgentsDir, relativePath);\n\n if (await fs.pathExists(targetPath)) {\n existingFiles.push(targetPath);\n } else {\n filesToCopy.push({\n source: sourcePath,\n target: targetPath,\n isAgent: true,\n relativePath\n });\n }\n }\n }\n\n // Process harness directory\n if (dirs.hasSourceHarness) {\n const harnessFiles = await glob('**/*', {\n cwd: dirs.sourceHarnessDir,\n absolute: true,\n nodir: true\n });\n\n for (const sourcePath of harnessFiles) {\n const relativePath = path.relative(dirs.sourceHarnessDir, sourcePath);\n const targetPath = path.join(dirs.targetHarnessDir, relativePath);\n\n if (await fs.pathExists(targetPath)) {\n existingFiles.push(targetPath);\n } else {\n filesToCopy.push({\n source: sourcePath,\n target: targetPath,\n isAgent: false,\n relativePath\n });\n }\n }\n }\n\n // Transaction check: if any file exists, abort\n if (existingFiles.length > 0) {\n console.error(chalk.red(`\\n\u274C ${t('transaction.failed')}`));\n for (const file of existingFiles) {\n console.error(chalk.red(` - ${file}`));\n }\n throw new Error(t('errors.transactionFailed', { count: existingFiles.length }));\n }\n\n console.log(chalk.gray(` \u2713 ${t('info.noConflicts', { count: filesToCopy.length })}`));\n\n // All checks passed, now perform the copy\n console.log(chalk.gray(`\\n${t('info.copyingFiles')}`));\n\n for (const file of filesToCopy) {\n await fs.ensureDir(path.dirname(file.target));\n\n if (file.isAgent && file.relativePath.endsWith('.md')) {\n // Process template for agent markdown files\n const content = await fs.readFile(file.source, 'utf-8');\n const processed = await processTemplate(content, { model });\n await fs.writeFile(file.target, processed);\n } else {\n // Copy as-is for non-agent files\n await fs.copy(file.source, file.target);\n }\n\n console.log(chalk.gray(` \u2713 ${file.relativePath}`));\n }\n}\n\nasync function processTemplate(content: string, data: Record<string, string>): Promise<string> {\n // Use EJS to render the template\n // The model variable will be replaced in the template\n try {\n const result = ejs.render(content, data, {\n async: false\n });\n return result;\n } catch (error) {\n // If EJS parsing fails, return original content\n // This handles files that may not be valid EJS templates\n return content;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import fs from 'fs-extra';\nimport path from 'path';\nimport { glob } from 'glob';\nimport ejs from 'ejs';\nimport chalk from 'chalk';\nimport { fileURLToPath } from 'url';\nimport { t } from './i18n.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nconst SOURCE_AGENTS_DIR = path.resolve(__dirname, '..', 'agents');\n\nexport interface DirectoryInfo {\n sourceAgentsDir: string;\n targetDir: string;\n targetAgentsDir: string;\n targetHarnessDir: string;\n targetSprintsDir: string;\n hasSourceAgents: boolean;\n}\n\ninterface FileToCopy {\n source: string;\n target: string;\n relativePath: string;\n}\n\nfunction getTargetDirs() {\n const targetBaseDir = path.resolve(process.cwd(), '.opencode');\n return {\n targetBaseDir,\n targetAgentsDir: path.join(targetBaseDir, 'agents'),\n targetHarnessDir: path.join(targetBaseDir, 'harness'),\n targetSprintsDir: path.join(targetBaseDir, 'harness', 'sprints')\n };\n}\n\nexport async function checkDirectories(): Promise<DirectoryInfo> {\n console.log(chalk.gray(t('info.checkingDirs')));\n\n const hasSourceAgents = await fs.pathExists(SOURCE_AGENTS_DIR);\n\n if (!hasSourceAgents) {\n throw new Error(\n `${t('errors.sourceNotFound')}\\n` +\n ` - ${SOURCE_AGENTS_DIR}`\n );\n }\n\n const { targetBaseDir, targetAgentsDir, targetHarnessDir, targetSprintsDir } = getTargetDirs();\n\n await fs.ensureDir(targetBaseDir);\n console.log(chalk.gray(` \u2713 ${targetBaseDir}`));\n\n await fs.ensureDir(targetAgentsDir);\n console.log(chalk.gray(` \u2713 ${targetAgentsDir}`));\n\n await fs.ensureDir(targetHarnessDir);\n console.log(chalk.gray(` \u2713 ${targetHarnessDir}`));\n\n await fs.ensureDir(targetSprintsDir);\n console.log(chalk.gray(` \u2713 ${targetSprintsDir}`));\n\n return {\n sourceAgentsDir: SOURCE_AGENTS_DIR,\n targetDir: targetBaseDir,\n targetAgentsDir,\n targetHarnessDir,\n targetSprintsDir,\n hasSourceAgents\n };\n}\n\nexport async function transactionalCopy(model: string, dirs: DirectoryInfo): Promise<void> {\n console.log(chalk.gray(`\\n${t('info.transactionCheck')}`));\n\n const filesToCopy: FileToCopy[] = [];\n const existingFiles: string[] = [];\n\n const agentFiles = await glob('**/*.md', {\n cwd: dirs.sourceAgentsDir,\n absolute: true\n });\n\n for (const sourcePath of agentFiles) {\n const relativePath = path.relative(dirs.sourceAgentsDir, sourcePath);\n const targetPath = path.join(dirs.targetAgentsDir, relativePath);\n\n if (await fs.pathExists(targetPath)) {\n existingFiles.push(targetPath);\n } else {\n filesToCopy.push({\n source: sourcePath,\n target: targetPath,\n relativePath\n });\n }\n }\n\n // Check for existing harness directories that would conflict\n const harnessDirs = ['sprints'];\n for (const subDir of harnessDirs) {\n const targetPath = path.join(dirs.targetHarnessDir, subDir);\n if (await fs.pathExists(targetPath)) {\n const contents = await fs.readdir(targetPath);\n if (contents.length > 0) {\n existingFiles.push(targetPath);\n }\n }\n }\n\n if (existingFiles.length > 0) {\n console.error(chalk.red(`\\n\u274C ${t('transaction.failed')}`));\n for (const file of existingFiles) {\n console.error(chalk.red(` - ${file}`));\n }\n throw new Error(t('errors.transactionFailed', { count: existingFiles.length }));\n }\n\n console.log(chalk.gray(` \u2713 ${t('info.noConflicts', { count: filesToCopy.length })}`));\n\n console.log(chalk.gray(`\\n${t('info.copyingFiles')}`));\n\n for (const file of filesToCopy) {\n await fs.ensureDir(path.dirname(file.target));\n\n const content = await fs.readFile(file.source, 'utf-8');\n const processed = await processTemplate(content, { model });\n await fs.writeFile(file.target, processed);\n\n console.log(chalk.gray(` \u2713 ${file.relativePath}`));\n }\n}\n\nasync function processTemplate(content: string, data: Record<string, string>): Promise<string> {\n try {\n const result = ejs.render(content, data, {\n async: false\n });\n return result;\n } catch {\n return content;\n }\n}"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-squirrel-opencode-harness",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Scaffold squirrel opencode harness into .opencode directory",
5
5
  "main": "dist/cli.js",
6
6
  "type": "module",
@@ -1,18 +0,0 @@
1
- # Sprint Contract: Sprint [N] — [Name]
2
-
3
- ## Scope
4
- [What this sprint will build, based on the spec]
5
-
6
- ## Implementation Plan
7
- [High-level approach]
8
- - [Key technical decisions]
9
- - [Component structure]
10
- - [API endpoints if applicable]
11
-
12
- ## Success Criteria
13
- 1. **[Criterion]**: [How to verify — must be specific and testable]
14
- 2. **[Criterion]**: [How to verify]
15
- 3. **[Criterion]**: [How to verify]
16
-
17
- ## Out of Scope for This Sprint
18
- - [What is explicitly NOT being built this sprint]
@@ -1,39 +0,0 @@
1
- # Evaluation: Sprint [N]
2
-
3
- ## Overall Verdict: [PASS / FAIL]
4
-
5
- ## Success Criteria Results
6
- 1. **[Criterion]**: [PASS / FAIL]
7
- - Expected: [what was expected]
8
- - Actual: [what actually happened]
9
- - Reproduction (if FAIL): [steps]
10
-
11
- ## Bug Report
12
- 1. **[Bug Title]**: [Severity: Critical/Major/Minor]
13
- - Steps to reproduce: [...]
14
- - Expected: [...]
15
- - Actual: [...]
16
- - Location: [file:line or UI location]
17
-
18
- ## Scoring
19
-
20
- ### Product Depth: [score]/10
21
- [Justification]
22
-
23
- ### Functionality: [score]/10
24
- [Justification]
25
-
26
- ### Visual Design: [score]/10
27
- [Justification]
28
-
29
- ### Code Quality: [score]/10
30
- [Justification]
31
-
32
- **Hard threshold**: Any dimension below 4/10 = automatic FAIL.
33
-
34
- ## Detailed Critique
35
- [Paragraph-form assessment with concrete examples]
36
-
37
- ## Required Fixes (if FAIL)
38
- 1. [Specific, actionable fix]
39
- 2. [Specific, actionable fix]
@@ -1,16 +0,0 @@
1
- # Harness Run Summary
2
-
3
- ## Original Prompt
4
- [The user's original prompt]
5
-
6
- ## Sprints Completed
7
-
8
- ### Sprint [N]: [Name] — [PASS/FAIL/PARTIAL]
9
- - Evaluation rounds: [count]
10
- - Issues found and addressed: [summary]
11
-
12
- ## Final Assessment
13
- [Overall assessment of the built application]
14
-
15
- ## Known Gaps
16
- [Issues that remain unresolved]
@@ -1,14 +0,0 @@
1
- # Handoff: Sprint [N]
2
-
3
- ## Status: [Ready for QA / Not Ready — explain why]
4
-
5
- ## What to Test
6
- [Step-by-step instructions for the evaluator]
7
- 1. [Step 1]
8
- 2. [Step 2]
9
-
10
- ## Running the Application
11
- [How to start/restart the app]
12
-
13
- ## Known Gaps
14
- [Honest assessment of anything not fully working]
@@ -1,14 +0,0 @@
1
- # Self-Evaluation: Sprint [N]
2
-
3
- ## What Was Built
4
- [Summary of implemented features]
5
-
6
- ## Success Criteria Check
7
- - [x] Criterion 1: [notes]
8
- - [ ] Criterion 2: [notes on what's missing]
9
-
10
- ## Known Issues
11
- - [Bugs, limitations, or deviations from the contract]
12
-
13
- ## Decisions Made
14
- - [Significant implementation decisions and rationale]
@@ -1,53 +0,0 @@
1
- # Product Specification: [Product Name]
2
-
3
- ## Overview
4
- [2-3 paragraph vision statement describing what this product is, who it's for, and why it matters]
5
-
6
- ## Core Features
7
-
8
- 1. **[Feature Name]**: [Description]
9
- - User stories:
10
- - As a [type of user], I can [action] so that [benefit]
11
- - Acceptance criteria:
12
- - [Given/When/Then or testable condition]
13
-
14
- 2. **[Feature Name]**: [Description]
15
- - User stories:
16
- - As a [type of user], I can [action] so that [benefit]
17
- - Acceptance criteria:
18
- - [Given/When/Then or testable condition]
19
-
20
- ## AI Integration
21
- - AI Agent capabilities: [what the agent can do]
22
- - AI Agent tools: [what tools/functions the agent has access to]
23
- - User interaction model: [how the user invokes and interacts with the AI]
24
-
25
- ## Technical Architecture
26
- - Frontend: [framework, styling approach]
27
- - Backend: [framework, database]
28
- - Key patterns: [architectural patterns]
29
-
30
- ## Visual Design Direction
31
- - Aesthetic: [direction statement]
32
- - Color palette: [direction statement]
33
- - Typography: [direction statement]
34
- - Layout principles: [direction statement]
35
-
36
- ## Sprint Breakdown
37
-
38
- ### Sprint 1: [Name]
39
- - Scope: [what's being built]
40
- - Dependencies: none
41
- - Delivers: [tangible output]
42
- - Acceptance criteria:
43
- - [testable condition]
44
-
45
- ### Sprint 2: [Name]
46
- - Scope: [what's being built]
47
- - Dependencies: Sprint 1
48
- - Delivers: [tangible output]
49
- - Acceptance criteria:
50
- - [testable condition]
51
-
52
- ## Out of Scope
53
- - [Explicit exclusions]
@@ -1,8 +0,0 @@
1
- # Sprint Status
2
-
3
- ## Current Sprint: 0 — Planning
4
- ## Current Phase: initialization
5
- ## Contract Status: n/a
6
- ## Evaluation Status: n/a
7
- ## Last Updated: [timestamp on initialization]
8
- ## Notes: Harness initialized. Awaiting prompt.