opencode-kolchoz-loop 1.0.5 → 1.3.1
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 +33 -5
- package/dist/agents/agents/areczek.md +3 -0
- package/dist/agents/agents/januszek.md +7 -5
- package/dist/agents/areczek.md +3 -0
- package/dist/agents/januszek.md +7 -5
- package/dist/index.js +65 -10
- package/package.json +4 -4
- package/src/agents/areczek.md +3 -0
- package/src/agents/januszek.md +7 -5
package/README.md
CHANGED
|
@@ -122,19 +122,25 @@ The plugin registers 7 tools:
|
|
|
122
122
|
|
|
123
123
|
## Model configuration
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
You can override agent models in `opencode.json`:
|
|
126
126
|
|
|
127
127
|
```json
|
|
128
128
|
{
|
|
129
129
|
"agent": {
|
|
130
|
-
"januszek": { "model": "
|
|
131
|
-
"areczek": { "model": "
|
|
132
|
-
"anetka": { "model": "
|
|
130
|
+
"januszek": { "model": "<provider>/<model-name>" },
|
|
131
|
+
"areczek": { "model": "<provider>/<model-name>" },
|
|
132
|
+
"anetka": { "model": "<provider>/<model-name>" }
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
-
|
|
137
|
+
## Commit gate
|
|
138
|
+
|
|
139
|
+
Before Areczek can submit a story for review, `kolchoz_submit_for_review` validates Git:
|
|
140
|
+
- no uncommitted changes in the working tree
|
|
141
|
+
- latest commit message starts with `feat(<storyId>):`
|
|
142
|
+
|
|
143
|
+
If either check fails, submission is blocked until Areczek creates the correct commit.
|
|
138
144
|
|
|
139
145
|
## Compound engineering
|
|
140
146
|
|
|
@@ -144,6 +150,28 @@ The system builds knowledge in `AGENTS.md` automatically:
|
|
|
144
150
|
2. Anetka discovers a gotcha -> `kolchoz_learn("gotcha", "...")`
|
|
145
151
|
3. Future sessions read AGENTS.md -> better context -> better outcomes
|
|
146
152
|
|
|
153
|
+
## Release automation
|
|
154
|
+
|
|
155
|
+
This repository includes GitHub Actions workflows for a release cycle:
|
|
156
|
+
|
|
157
|
+
1. Run `Prepare Release Draft` manually from GitHub Actions on `main` or `master`.
|
|
158
|
+
2. Choose `RELEASE_TYPE`: `major`, `minor`, or `patch`.
|
|
159
|
+
3. Workflow bumps `package.json` version, commits it, creates a tag, and opens a draft release.
|
|
160
|
+
4. Publish the draft release in GitHub Releases.
|
|
161
|
+
5. Publishing the release triggers npm publishing from the release tag.
|
|
162
|
+
|
|
163
|
+
Workflow files:
|
|
164
|
+
- `.github/workflows/prepare-release.yml`
|
|
165
|
+
- `.github/workflows/publish-on-release.yml`
|
|
166
|
+
|
|
167
|
+
Required npm setup:
|
|
168
|
+
- Configure npm Trusted Publisher for this GitHub repository and workflow file `publish-on-release.yml`.
|
|
169
|
+
- Trusted publishing uses GitHub OIDC, so `NPM_TOKEN` is not required for package publishing.
|
|
170
|
+
|
|
171
|
+
Important:
|
|
172
|
+
- `Prepare Release Draft` guarantees matching `tag` and `package.json` version.
|
|
173
|
+
- If branch protection blocks direct pushes by Actions, allow `GITHUB_TOKEN` pushes for this workflow.
|
|
174
|
+
|
|
147
175
|
## License
|
|
148
176
|
|
|
149
177
|
MIT
|
|
@@ -41,6 +41,9 @@ Use these capabilities consistently:
|
|
|
41
41
|
- Commit format: `feat(story-id): short description`
|
|
42
42
|
- Always inspect `git diff` before committing
|
|
43
43
|
- If something goes wrong: `git stash` or `git reset`
|
|
44
|
+
- `kolchoz_submit_for_review` is hard-gated by Git checks:
|
|
45
|
+
- working tree must be clean (no pending changes)
|
|
46
|
+
- latest commit message must match current story: `feat(<storyId>): ...`
|
|
44
47
|
|
|
45
48
|
### Tests
|
|
46
49
|
- Run existing tests after changes: `npm test` / `bun test` / equivalent
|
|
@@ -10,6 +10,7 @@ tools:
|
|
|
10
10
|
read: true
|
|
11
11
|
glob: true
|
|
12
12
|
todo: true
|
|
13
|
+
question: true
|
|
13
14
|
---
|
|
14
15
|
|
|
15
16
|
# Januszek - Lead Orchestrator
|
|
@@ -29,11 +30,12 @@ You are responsible for:
|
|
|
29
30
|
When a user submits a task:
|
|
30
31
|
|
|
31
32
|
1. Analyze the request - is it specific enough?
|
|
32
|
-
2. If
|
|
33
|
-
3.
|
|
34
|
-
4.
|
|
35
|
-
5.
|
|
36
|
-
6.
|
|
33
|
+
2. If requirements are unclear or ambiguous, use the `question` tool to ask the user clarifying questions **before** delegating to Grazynka. Ask about scope, priorities, constraints, and success criteria. Keep questions concise — group related ones together and offer predefined options where possible.
|
|
34
|
+
3. Delegate to `@grazynka`, passing the user's task **together with all answers** gathered from the `question` tool
|
|
35
|
+
4. Once Grazynka returns a PRD (`.opencode/state/prd.json`), instruct `@areczek` to fetch a task (`kolchoz_next_task`)
|
|
36
|
+
5. After Areczek implements, `@anetka` performs review
|
|
37
|
+
6. If Anetka gives PASS -> next story. If FAIL -> back to Areczek.
|
|
38
|
+
7. When all stories are marked "done", report results to the user
|
|
37
39
|
|
|
38
40
|
## Rules
|
|
39
41
|
|
package/dist/agents/areczek.md
CHANGED
|
@@ -41,6 +41,9 @@ Use these capabilities consistently:
|
|
|
41
41
|
- Commit format: `feat(story-id): short description`
|
|
42
42
|
- Always inspect `git diff` before committing
|
|
43
43
|
- If something goes wrong: `git stash` or `git reset`
|
|
44
|
+
- `kolchoz_submit_for_review` is hard-gated by Git checks:
|
|
45
|
+
- working tree must be clean (no pending changes)
|
|
46
|
+
- latest commit message must match current story: `feat(<storyId>): ...`
|
|
44
47
|
|
|
45
48
|
### Tests
|
|
46
49
|
- Run existing tests after changes: `npm test` / `bun test` / equivalent
|
package/dist/agents/januszek.md
CHANGED
|
@@ -10,6 +10,7 @@ tools:
|
|
|
10
10
|
read: true
|
|
11
11
|
glob: true
|
|
12
12
|
todo: true
|
|
13
|
+
question: true
|
|
13
14
|
---
|
|
14
15
|
|
|
15
16
|
# Januszek - Lead Orchestrator
|
|
@@ -29,11 +30,12 @@ You are responsible for:
|
|
|
29
30
|
When a user submits a task:
|
|
30
31
|
|
|
31
32
|
1. Analyze the request - is it specific enough?
|
|
32
|
-
2. If
|
|
33
|
-
3.
|
|
34
|
-
4.
|
|
35
|
-
5.
|
|
36
|
-
6.
|
|
33
|
+
2. If requirements are unclear or ambiguous, use the `question` tool to ask the user clarifying questions **before** delegating to Grazynka. Ask about scope, priorities, constraints, and success criteria. Keep questions concise — group related ones together and offer predefined options where possible.
|
|
34
|
+
3. Delegate to `@grazynka`, passing the user's task **together with all answers** gathered from the `question` tool
|
|
35
|
+
4. Once Grazynka returns a PRD (`.opencode/state/prd.json`), instruct `@areczek` to fetch a task (`kolchoz_next_task`)
|
|
36
|
+
5. After Areczek implements, `@anetka` performs review
|
|
37
|
+
6. If Anetka gives PASS -> next story. If FAIL -> back to Areczek.
|
|
38
|
+
7. When all stories are marked "done", report results to the user
|
|
37
39
|
|
|
38
40
|
## Rules
|
|
39
41
|
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,9 @@ import { tool } from "@opencode-ai/plugin/tool";
|
|
|
2
2
|
import { readFile, writeFile, mkdir, readdir, copyFile, stat } from "fs/promises";
|
|
3
3
|
import { join, dirname } from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
|
+
import { execFile } from "child_process";
|
|
6
|
+
import { promisify } from "util";
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
5
8
|
// ── Filesystem helpers ──
|
|
6
9
|
async function ensureDir(dir) {
|
|
7
10
|
await mkdir(dir, { recursive: true });
|
|
@@ -41,6 +44,30 @@ async function appendProgress(dir, message) {
|
|
|
41
44
|
await writeFile(file, line, "utf-8");
|
|
42
45
|
}
|
|
43
46
|
}
|
|
47
|
+
async function runGit(projectRoot, args) {
|
|
48
|
+
const { stdout } = await execFileAsync("git", args, { cwd: projectRoot });
|
|
49
|
+
return stdout.trim();
|
|
50
|
+
}
|
|
51
|
+
async function validateStoryCommit(projectRoot, storyId) {
|
|
52
|
+
try {
|
|
53
|
+
await runGit(projectRoot, ["rev-parse", "--is-inside-work-tree"]);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return "ERROR: Git repository not found. Commit is required before review.";
|
|
57
|
+
}
|
|
58
|
+
const status = await runGit(projectRoot, ["status", "--porcelain"]);
|
|
59
|
+
if (status.length > 0) {
|
|
60
|
+
return (`ERROR: Uncommitted changes detected. Commit all changes for ${storyId} before review.\n` +
|
|
61
|
+
`Hint: git add -A && git commit -m "feat(${storyId}): short description"`);
|
|
62
|
+
}
|
|
63
|
+
const lastCommitMessage = await runGit(projectRoot, ["log", "-1", "--pretty=%s"]);
|
|
64
|
+
const requiredPrefix = `feat(${storyId}):`;
|
|
65
|
+
if (!lastCommitMessage.startsWith(requiredPrefix)) {
|
|
66
|
+
return (`ERROR: Last commit message must start with "${requiredPrefix}".\n` +
|
|
67
|
+
`Current: "${lastCommitMessage}"`);
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
44
71
|
// ── Agent provisioning ──
|
|
45
72
|
// Copies bundled .md agent files to .opencode/agents/
|
|
46
73
|
// so OpenCode discovers them as real agents.
|
|
@@ -89,6 +116,16 @@ async function ensureGitignore(projectRoot) {
|
|
|
89
116
|
// ── Main Plugin Export ──
|
|
90
117
|
export const KolchozLoop = async ({ project, client, $, directory, worktree }) => {
|
|
91
118
|
const projectRoot = worktree || directory;
|
|
119
|
+
if (!projectRoot || projectRoot === "/") {
|
|
120
|
+
await client.app.log({
|
|
121
|
+
body: {
|
|
122
|
+
service: "kolchoz-loop",
|
|
123
|
+
level: "warn",
|
|
124
|
+
message: `Kolchoz Loop: no valid project directory (worktree="${worktree}", directory="${directory}"). Plugin inactive.`,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
return { tool: {}, event: async () => { } };
|
|
128
|
+
}
|
|
92
129
|
const stateDir = join(projectRoot, ".opencode", "state");
|
|
93
130
|
// ── Auto-provision on first run ──
|
|
94
131
|
await ensureDir(stateDir);
|
|
@@ -121,16 +158,29 @@ export const KolchozLoop = async ({ project, client, $, directory, worktree }) =
|
|
|
121
158
|
stories: tool.schema.string(), // JSON array
|
|
122
159
|
},
|
|
123
160
|
async execute(args) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
161
|
+
let parsed;
|
|
162
|
+
try {
|
|
163
|
+
const raw = JSON.parse(args.stories);
|
|
164
|
+
if (!Array.isArray(raw))
|
|
165
|
+
throw new Error("stories must be a JSON array");
|
|
166
|
+
parsed = raw;
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
return `ERROR: Invalid stories JSON — ${err instanceof Error ? err.message : String(err)}`;
|
|
170
|
+
}
|
|
171
|
+
const stories = parsed.map((value, i) => {
|
|
172
|
+
const s = value;
|
|
173
|
+
return {
|
|
174
|
+
id: s.id || `story-${i + 1}`,
|
|
175
|
+
title: s.title || `Story ${i + 1}`,
|
|
176
|
+
description: s.description || "",
|
|
177
|
+
acceptanceCriteria: s.acceptanceCriteria || [],
|
|
178
|
+
priority: s.priority || "medium",
|
|
179
|
+
status: "pending",
|
|
180
|
+
assignedTo: "areczek",
|
|
181
|
+
iteration: 0,
|
|
182
|
+
};
|
|
183
|
+
});
|
|
134
184
|
const prd = {
|
|
135
185
|
title: args.title,
|
|
136
186
|
createdBy: "grazynka",
|
|
@@ -202,6 +252,11 @@ export const KolchozLoop = async ({ project, client, $, directory, worktree }) =
|
|
|
202
252
|
const story = prd.userStories.find((s) => s.id === args.storyId);
|
|
203
253
|
if (!story)
|
|
204
254
|
return `ERROR: Story ${args.storyId} not found.`;
|
|
255
|
+
const commitValidationError = await validateStoryCommit(projectRoot, args.storyId);
|
|
256
|
+
if (commitValidationError) {
|
|
257
|
+
await appendProgress(stateDir, `[Areczek] Submission blocked for ${args.storyId}: missing/invalid commit`);
|
|
258
|
+
return commitValidationError;
|
|
259
|
+
}
|
|
205
260
|
story.status = "review";
|
|
206
261
|
await writeJson(stateDir, "prd.json", prd);
|
|
207
262
|
const state = await readJson(stateDir, "loop-state.json", {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-kolchoz-loop",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Multi-agent Ralph Loop plugin for OpenCode - Januszek, Grazynka, Areczek, Anetka",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@opencode-ai/plugin": "
|
|
30
|
+
"@opencode-ai/plugin": "^1.2.7"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@types/node": "
|
|
34
|
-
"typescript": "
|
|
33
|
+
"@types/node": "^25.3.0",
|
|
34
|
+
"typescript": "^5.9.3"
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
37
37
|
"dist/**/*",
|
package/src/agents/areczek.md
CHANGED
|
@@ -41,6 +41,9 @@ Use these capabilities consistently:
|
|
|
41
41
|
- Commit format: `feat(story-id): short description`
|
|
42
42
|
- Always inspect `git diff` before committing
|
|
43
43
|
- If something goes wrong: `git stash` or `git reset`
|
|
44
|
+
- `kolchoz_submit_for_review` is hard-gated by Git checks:
|
|
45
|
+
- working tree must be clean (no pending changes)
|
|
46
|
+
- latest commit message must match current story: `feat(<storyId>): ...`
|
|
44
47
|
|
|
45
48
|
### Tests
|
|
46
49
|
- Run existing tests after changes: `npm test` / `bun test` / equivalent
|
package/src/agents/januszek.md
CHANGED
|
@@ -10,6 +10,7 @@ tools:
|
|
|
10
10
|
read: true
|
|
11
11
|
glob: true
|
|
12
12
|
todo: true
|
|
13
|
+
question: true
|
|
13
14
|
---
|
|
14
15
|
|
|
15
16
|
# Januszek - Lead Orchestrator
|
|
@@ -29,11 +30,12 @@ You are responsible for:
|
|
|
29
30
|
When a user submits a task:
|
|
30
31
|
|
|
31
32
|
1. Analyze the request - is it specific enough?
|
|
32
|
-
2. If
|
|
33
|
-
3.
|
|
34
|
-
4.
|
|
35
|
-
5.
|
|
36
|
-
6.
|
|
33
|
+
2. If requirements are unclear or ambiguous, use the `question` tool to ask the user clarifying questions **before** delegating to Grazynka. Ask about scope, priorities, constraints, and success criteria. Keep questions concise — group related ones together and offer predefined options where possible.
|
|
34
|
+
3. Delegate to `@grazynka`, passing the user's task **together with all answers** gathered from the `question` tool
|
|
35
|
+
4. Once Grazynka returns a PRD (`.opencode/state/prd.json`), instruct `@areczek` to fetch a task (`kolchoz_next_task`)
|
|
36
|
+
5. After Areczek implements, `@anetka` performs review
|
|
37
|
+
6. If Anetka gives PASS -> next story. If FAIL -> back to Areczek.
|
|
38
|
+
7. When all stories are marked "done", report results to the user
|
|
37
39
|
|
|
38
40
|
## Rules
|
|
39
41
|
|