goalbuddy 0.2.10 → 0.2.12
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/CONTRIBUTING.md +6 -4
- package/README.md +3 -1
- package/goalbuddy/SKILL.md +28 -2
- package/internal/cli/goal-maker.mjs +24 -3
- package/package.json +3 -7
- package/plugins/goalbuddy/skills/goalbuddy/SKILL.md +28 -2
- package/goal-maker/SKILL.md +0 -18
- package/goal-maker/agents/README.md +0 -23
- package/goal-maker/agents/config-snippet.toml +0 -5
- package/goal-maker/agents/goal_judge.toml +0 -29
- package/goal-maker/agents/goal_scout.toml +0 -26
- package/goal-maker/agents/goal_worker.toml +0 -28
- package/goal-maker/agents/openai.yaml +0 -6
- package/goal-maker/scripts/check-goal-state.mjs +0 -370
- package/goal-maker/scripts/install-agents.mjs +0 -28
- package/goal-maker/templates/agents.md +0 -48
- package/goal-maker/templates/goal-prompt.txt +0 -1
- package/goal-maker/templates/goal.md +0 -71
- package/goal-maker/templates/note.md +0 -22
- package/goal-maker/templates/state.yaml +0 -125
package/CONTRIBUTING.md
CHANGED
|
@@ -12,8 +12,6 @@ cd goalbuddy
|
|
|
12
12
|
npm run check
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Until the GitHub repository rename is complete, use the current local clone or the existing `tolibear/goal-maker` remote.
|
|
16
|
-
|
|
17
15
|
## Local Install Test
|
|
18
16
|
|
|
19
17
|
Use a temporary Codex home so local testing does not overwrite your real install:
|
|
@@ -33,13 +31,17 @@ Before opening a PR, verify the npm package contents:
|
|
|
33
31
|
npm pack --dry-run
|
|
34
32
|
```
|
|
35
33
|
|
|
36
|
-
The package should include `README.md`, `internal/assets/`, `package.json`, `internal/cli/`, the canonical `goalbuddy/` skill directory,
|
|
34
|
+
The package should include `README.md`, `internal/assets/`, `package.json`, `internal/cli/`, the canonical `goalbuddy/` skill directory, and `plugins/goalbuddy/`. The temporary `$goal-maker` compatibility skill is generated by the installer; do not add a second tracked skill payload.
|
|
35
|
+
|
|
36
|
+
## Releases
|
|
37
|
+
|
|
38
|
+
GoalBuddy publishes from GitHub Actions with npm trusted publishing. See [RELEASE.md](RELEASE.md) before creating a release.
|
|
37
39
|
|
|
38
40
|
## Contribution Guidelines
|
|
39
41
|
|
|
40
42
|
- Keep the runtime dependency-free unless there is a strong reason.
|
|
41
43
|
- Keep `goalbuddy/` installable as the canonical Codex skill directory.
|
|
42
|
-
- Keep
|
|
44
|
+
- Keep `$goal-maker` working as a generated compatibility alias until the migration window ends.
|
|
43
45
|
- Prefer small, reviewable changes.
|
|
44
46
|
- Update README or templates when behavior changes.
|
|
45
47
|
- Run `npm run check` before submitting changes.
|
package/README.md
CHANGED
|
@@ -125,6 +125,8 @@ npx goalbuddy
|
|
|
125
125
|
|
|
126
126
|
Machine-readable commands such as `npx goal-maker install --json` keep JSON output clean so existing automation can migrate safely.
|
|
127
127
|
|
|
128
|
+
Release automation for future npm publishes is documented in [RELEASE.md](RELEASE.md).
|
|
129
|
+
|
|
128
130
|
Repair only the agent definitions:
|
|
129
131
|
|
|
130
132
|
```bash
|
|
@@ -198,7 +200,7 @@ See `examples/improve-goal-maker/` for a small completed reliability run, `examp
|
|
|
198
200
|
- `goalbuddy/agents/`: Scout, Judge, and Worker definitions
|
|
199
201
|
- `goalbuddy/templates/`: `goal.md`, `state.yaml`, and `note.md`
|
|
200
202
|
- `goalbuddy/scripts/check-goal-state.mjs`: v2 board checker
|
|
201
|
-
-
|
|
203
|
+
- `$goal-maker` compatibility: generated by the installer for existing users
|
|
202
204
|
- `internal/cli/goal-maker.mjs`: npm installer CLI
|
|
203
205
|
- `plugins/goalbuddy/`: repo-local Codex plugin package scaffold
|
|
204
206
|
- `extend/` and `extend/catalog.json`: GitHub-hosted extension surface
|
package/goalbuddy/SKILL.md
CHANGED
|
@@ -15,6 +15,28 @@ The loop is:
|
|
|
15
15
|
raw user intent -> intake compiler -> discovery/plan validation/execution board -> one active task -> receipt -> board update -> repeat
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
## Invocation Boundary
|
|
19
|
+
|
|
20
|
+
There are two different modes:
|
|
21
|
+
|
|
22
|
+
- `$goalbuddy`: prepare intake, `goal.md`, `state.yaml`, and the starter `/goal` command, then stop.
|
|
23
|
+
- `/goal Follow docs/goals/<slug>/goal.md.`: execute the board, including Scout/Judge/Worker work.
|
|
24
|
+
|
|
25
|
+
This boundary is strict. `$goalbuddy` is not a lightweight `/goal`; it is a board compiler.
|
|
26
|
+
|
|
27
|
+
During a `$goalbuddy` turn, do not perform the user's requested work, even if the work looks read-only, preparatory, or obviously useful. Do not refresh or load named skills, inspect implementation files, browse reference repos, research design inspiration, generate design plans, generate images/assets, run app-specific checks, start servers, or edit product files. Put those actions into Scout, Judge, Worker, or PM tasks for the later `/goal` run.
|
|
28
|
+
|
|
29
|
+
Allowed `$goalbuddy` actions:
|
|
30
|
+
|
|
31
|
+
- ask diagnostic intake questions and wait when required;
|
|
32
|
+
- create or repair only `docs/goals/<slug>/goal.md`, `docs/goals/<slug>/state.yaml`, and `docs/goals/<slug>/notes/`;
|
|
33
|
+
- optionally run the GoalBuddy board checker against that `state.yaml`;
|
|
34
|
+
- verify that the GoalBuddy agents are installed, if this can be done without touching implementation work;
|
|
35
|
+
- print exactly `/goal Follow docs/goals/<slug>/goal.md.`;
|
|
36
|
+
- ask whether to start `/goal`, refine the board, or stop.
|
|
37
|
+
|
|
38
|
+
If the prompt names another skill or tool, such as "use the taste skill", "refresh the taste skill", "look at this repo", "use browser", or "generate assets", record that requirement in the charter and seed tasks. Do not load that skill, browse that repo, or generate those assets during `$goalbuddy`.
|
|
39
|
+
|
|
18
40
|
## Intake Compiler
|
|
19
41
|
|
|
20
42
|
Before creating, repairing, or running a board, privately translate the user's input into a Goal Intake. The input may be vague, specific, or detailed with an existing plan. Do not dump the intake to the user unless they ask for it.
|
|
@@ -124,6 +146,8 @@ Do:
|
|
|
124
146
|
Do not:
|
|
125
147
|
|
|
126
148
|
- start `/goal` automatically;
|
|
149
|
+
- use, refresh, inspect, or load named skills requested by the goal; schedule that as `/goal` work instead;
|
|
150
|
+
- browse links, inspect reference repos, read implementation files, generate design plans, generate images, or create assets for the requested outcome;
|
|
127
151
|
- create or repair a board from vague/open-ended input before diagnostic intake is complete;
|
|
128
152
|
- create `evidence.jsonl`, `units/`, or `artifacts/` for new v2 goals;
|
|
129
153
|
- edit implementation files before the board exists;
|
|
@@ -131,9 +155,11 @@ Do not:
|
|
|
131
155
|
- discard a user-provided plan; preserve it as facts and validate it before execution;
|
|
132
156
|
- treat `goal.md` as board truth when it conflicts with `state.yaml`.
|
|
133
157
|
|
|
134
|
-
## Default Bias: Users Want Work Done
|
|
158
|
+
## `/goal` Default Bias: Users Want Work Done
|
|
159
|
+
|
|
160
|
+
This section applies after the user starts `/goal Follow docs/goals/<slug>/goal.md.` It does not apply to the initial `$goalbuddy` board-preparation turn.
|
|
135
161
|
|
|
136
|
-
Unless the user explicitly asks for planning only, treat a
|
|
162
|
+
Unless the user explicitly asks for planning only, treat a `/goal` run as a request for work to happen.
|
|
137
163
|
|
|
138
164
|
Planning, Scout findings, Judge decisions, and a queued Worker task are not terminal outcomes when the user's original ask is for a working capability, automation, fix, cleanup, or backend/frontend behavior. They are setup for execution.
|
|
139
165
|
|
|
@@ -188,9 +188,8 @@ function installSkill({ force = true, quiet = false } = {}) {
|
|
|
188
188
|
|
|
189
189
|
mkdirSync(dirname(legacyTarget), { recursive: true });
|
|
190
190
|
rmSync(legacyTarget, { recursive: true, force: true });
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
});
|
|
191
|
+
mkdirSync(legacyTarget, { recursive: true });
|
|
192
|
+
writeFileSync(join(legacyTarget, "SKILL.md"), compatibilitySkillBody());
|
|
194
193
|
restoreInstalledExtensions(legacyTarget, extensionTempPath);
|
|
195
194
|
writeInstallMetadata(legacyTarget, previousMetadata);
|
|
196
195
|
cleanupPreservedExtensions([extensionTempPath]);
|
|
@@ -211,6 +210,28 @@ function installSkill({ force = true, quiet = false } = {}) {
|
|
|
211
210
|
};
|
|
212
211
|
}
|
|
213
212
|
|
|
213
|
+
function compatibilitySkillBody() {
|
|
214
|
+
return `---
|
|
215
|
+
name: ${legacySkillName}
|
|
216
|
+
description: Compatibility alias for GoalBuddy. Use $${canonicalSkillName} as the canonical skill.
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
# GoalBuddy Compatibility Alias
|
|
220
|
+
|
|
221
|
+
$${legacySkillName} is the previous name for $${canonicalSkillName}.
|
|
222
|
+
|
|
223
|
+
Use $${canonicalSkillName} for new work. This compatibility skill exists so older prompts and local installs do not fail after the rebrand.
|
|
224
|
+
|
|
225
|
+
When invoked through $${legacySkillName}:
|
|
226
|
+
|
|
227
|
+
1. Tell the user Goal Maker has been rebranded to GoalBuddy.
|
|
228
|
+
2. Show the canonical command: $${canonicalSkillName}.
|
|
229
|
+
3. If the user wants to continue immediately, follow the same workflow as $${canonicalSkillName}: run diagnostic intake, create or repair \`docs/goals/<slug>/goal.md\` and \`state.yaml\`, preserve one active task, and print \`/goal Follow docs/goals/<slug>/goal.md.\` without starting \`/goal\` automatically.
|
|
230
|
+
|
|
231
|
+
This alias has the same invocation boundary as \`$${canonicalSkillName}\`: prepare the board only. Do not use or refresh named skills, inspect implementation files, browse references, research, generate assets, or perform the requested work until the user starts the printed \`/goal\` command.
|
|
232
|
+
`;
|
|
233
|
+
}
|
|
234
|
+
|
|
214
235
|
function installAgents({ quiet = false } = {}) {
|
|
215
236
|
const source = join(skillSource, "agents");
|
|
216
237
|
const target = join(codexHome(), "agents");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goalbuddy",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
4
4
|
"description": "Turn open-ended Codex goals into a GoalBuddy Scout/Judge/Worker board with receipts, verification, and optional extensions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,14 +17,10 @@
|
|
|
17
17
|
"goalbuddy/SKILL.md",
|
|
18
18
|
"goalbuddy/agents",
|
|
19
19
|
"goalbuddy/scripts",
|
|
20
|
-
"goalbuddy/templates"
|
|
21
|
-
"goal-maker/SKILL.md",
|
|
22
|
-
"goal-maker/agents",
|
|
23
|
-
"goal-maker/scripts",
|
|
24
|
-
"goal-maker/templates"
|
|
20
|
+
"goalbuddy/templates"
|
|
25
21
|
],
|
|
26
22
|
"scripts": {
|
|
27
|
-
"check": "node --check internal/cli/goal-maker.mjs
|
|
23
|
+
"check": "node --check internal/cli/goal-maker.mjs goalbuddy/scripts/*.mjs && node --test internal/test/*.test.mjs",
|
|
28
24
|
"test": "node --test internal/test/*.test.mjs",
|
|
29
25
|
"pack:dry-run": "npm pack --dry-run",
|
|
30
26
|
"publish:check": "node internal/cli/check-publish-version.mjs && npm run pack:dry-run",
|
|
@@ -15,6 +15,28 @@ The loop is:
|
|
|
15
15
|
raw user intent -> intake compiler -> discovery/plan validation/execution board -> one active task -> receipt -> board update -> repeat
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
## Invocation Boundary
|
|
19
|
+
|
|
20
|
+
There are two different modes:
|
|
21
|
+
|
|
22
|
+
- `$goalbuddy`: prepare intake, `goal.md`, `state.yaml`, and the starter `/goal` command, then stop.
|
|
23
|
+
- `/goal Follow docs/goals/<slug>/goal.md.`: execute the board, including Scout/Judge/Worker work.
|
|
24
|
+
|
|
25
|
+
This boundary is strict. `$goalbuddy` is not a lightweight `/goal`; it is a board compiler.
|
|
26
|
+
|
|
27
|
+
During a `$goalbuddy` turn, do not perform the user's requested work, even if the work looks read-only, preparatory, or obviously useful. Do not refresh or load named skills, inspect implementation files, browse reference repos, research design inspiration, generate design plans, generate images/assets, run app-specific checks, start servers, or edit product files. Put those actions into Scout, Judge, Worker, or PM tasks for the later `/goal` run.
|
|
28
|
+
|
|
29
|
+
Allowed `$goalbuddy` actions:
|
|
30
|
+
|
|
31
|
+
- ask diagnostic intake questions and wait when required;
|
|
32
|
+
- create or repair only `docs/goals/<slug>/goal.md`, `docs/goals/<slug>/state.yaml`, and `docs/goals/<slug>/notes/`;
|
|
33
|
+
- optionally run the GoalBuddy board checker against that `state.yaml`;
|
|
34
|
+
- verify that the GoalBuddy agents are installed, if this can be done without touching implementation work;
|
|
35
|
+
- print exactly `/goal Follow docs/goals/<slug>/goal.md.`;
|
|
36
|
+
- ask whether to start `/goal`, refine the board, or stop.
|
|
37
|
+
|
|
38
|
+
If the prompt names another skill or tool, such as "use the taste skill", "refresh the taste skill", "look at this repo", "use browser", or "generate assets", record that requirement in the charter and seed tasks. Do not load that skill, browse that repo, or generate those assets during `$goalbuddy`.
|
|
39
|
+
|
|
18
40
|
## Intake Compiler
|
|
19
41
|
|
|
20
42
|
Before creating, repairing, or running a board, privately translate the user's input into a Goal Intake. The input may be vague, specific, or detailed with an existing plan. Do not dump the intake to the user unless they ask for it.
|
|
@@ -124,6 +146,8 @@ Do:
|
|
|
124
146
|
Do not:
|
|
125
147
|
|
|
126
148
|
- start `/goal` automatically;
|
|
149
|
+
- use, refresh, inspect, or load named skills requested by the goal; schedule that as `/goal` work instead;
|
|
150
|
+
- browse links, inspect reference repos, read implementation files, generate design plans, generate images, or create assets for the requested outcome;
|
|
127
151
|
- create or repair a board from vague/open-ended input before diagnostic intake is complete;
|
|
128
152
|
- create `evidence.jsonl`, `units/`, or `artifacts/` for new v2 goals;
|
|
129
153
|
- edit implementation files before the board exists;
|
|
@@ -131,9 +155,11 @@ Do not:
|
|
|
131
155
|
- discard a user-provided plan; preserve it as facts and validate it before execution;
|
|
132
156
|
- treat `goal.md` as board truth when it conflicts with `state.yaml`.
|
|
133
157
|
|
|
134
|
-
## Default Bias: Users Want Work Done
|
|
158
|
+
## `/goal` Default Bias: Users Want Work Done
|
|
159
|
+
|
|
160
|
+
This section applies after the user starts `/goal Follow docs/goals/<slug>/goal.md.` It does not apply to the initial `$goalbuddy` board-preparation turn.
|
|
135
161
|
|
|
136
|
-
Unless the user explicitly asks for planning only, treat a
|
|
162
|
+
Unless the user explicitly asks for planning only, treat a `/goal` run as a request for work to happen.
|
|
137
163
|
|
|
138
164
|
Planning, Scout findings, Judge decisions, and a queued Worker task are not terminal outcomes when the user's original ask is for a working capability, automation, fix, cleanup, or backend/frontend behavior. They are setup for execution.
|
|
139
165
|
|
package/goal-maker/SKILL.md
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: goal-maker
|
|
3
|
-
description: Compatibility alias for GoalBuddy during the rebrand migration window. Use $goalbuddy as the canonical skill.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# GoalBuddy Compatibility Alias
|
|
7
|
-
|
|
8
|
-
`$goal-maker` is the previous name for `$goalbuddy`.
|
|
9
|
-
|
|
10
|
-
Use `$goalbuddy` for new work. This compatibility skill exists so older prompts and local installs do not fail during the 60-90 day migration window.
|
|
11
|
-
|
|
12
|
-
When invoked through `$goal-maker`:
|
|
13
|
-
|
|
14
|
-
1. Tell the user Goal Maker has been rebranded to GoalBuddy.
|
|
15
|
-
2. Show the canonical command: `$goalbuddy`.
|
|
16
|
-
3. If the user wants to continue immediately, follow the same workflow as `$goalbuddy`: run diagnostic intake, create or repair `docs/goals/<slug>/goal.md` and `state.yaml`, preserve one active task, and print `/goal Follow docs/goals/<slug>/goal.md.` without starting `/goal` automatically.
|
|
17
|
-
|
|
18
|
-
Do not remove support for existing Goal Maker boards. GoalBuddy remains compatible with v2 `state.yaml` boards, existing `goal_*` agent role files, and installed extensions.
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# Goal Maker Agents
|
|
2
|
-
|
|
3
|
-
This directory contains both skill metadata and bundled agent definitions.
|
|
4
|
-
|
|
5
|
-
- `openai.yaml` stays with the skill as metadata.
|
|
6
|
-
- `goal_*.toml` files can be copied into `.codex/agents/` for project-scoped agents or `~/.codex/agents/` for personal agents.
|
|
7
|
-
|
|
8
|
-
| Agent | File | Reasoning effort | Sandbox |
|
|
9
|
-
|---|---|---:|---|
|
|
10
|
-
| Scout | `goal_scout.toml` | medium | read-only |
|
|
11
|
-
| Worker | `goal_worker.toml` | low | workspace-write |
|
|
12
|
-
| Judge | `goal_judge.toml` | high | read-only |
|
|
13
|
-
|
|
14
|
-
Recommended config:
|
|
15
|
-
|
|
16
|
-
```toml
|
|
17
|
-
[agents]
|
|
18
|
-
max_threads = 4
|
|
19
|
-
max_depth = 1
|
|
20
|
-
job_max_runtime_seconds = 1800
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Only the main `/goal` PM loop may select the active task, mark tasks done, update board truth, or mark the goal complete.
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
name = "goal_judge"
|
|
2
|
-
description = "High-thinking strategic reviewer for Goal Maker escalation: ambiguity, risky scope, source/product conflicts, safety/API/live decisions, and tranche completion."
|
|
3
|
-
model_reasoning_effort = "high"
|
|
4
|
-
sandbox_mode = "read-only"
|
|
5
|
-
nickname_candidates = ["Judge", "Reviewer", "Architect"]
|
|
6
|
-
|
|
7
|
-
developer_instructions = """
|
|
8
|
-
You are Judge for Goal Maker.
|
|
9
|
-
|
|
10
|
-
Thinking level: high.
|
|
11
|
-
Mode: strategic reviewer and escalation authority.
|
|
12
|
-
|
|
13
|
-
Think as a skeptical staff engineer and project-management systems designer. You decide and constrain; you do not broadly implement.
|
|
14
|
-
|
|
15
|
-
Use when source, tests, product behavior, API/live strategy, dirty scope, giant-file risk, safety/auth/money/persistence semantics, task priority, or completion readiness is ambiguous.
|
|
16
|
-
|
|
17
|
-
Do not approve based on lots of docs or lots of tests. Require coherent receipts and current verification.
|
|
18
|
-
|
|
19
|
-
Return a compact Judge receipt for the PM to paste into state.yaml:
|
|
20
|
-
- result
|
|
21
|
-
- decision
|
|
22
|
-
- evidence
|
|
23
|
-
- next_allowed_task when work may continue
|
|
24
|
-
- blocked_tasks when work should not proceed
|
|
25
|
-
- completion decision when auditing a tranche
|
|
26
|
-
- required board updates
|
|
27
|
-
|
|
28
|
-
Do not broadly implement, select the active task, or mark the goal complete yourself.
|
|
29
|
-
"""
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
name = "goal_scout"
|
|
2
|
-
description = "Read-only evidence mapper for one Goal Maker task. Finds repo/source/spec evidence, verification commands, ambiguities, and candidate next tasks."
|
|
3
|
-
model_reasoning_effort = "medium"
|
|
4
|
-
sandbox_mode = "read-only"
|
|
5
|
-
nickname_candidates = ["Scout", "Mapper", "Tracer"]
|
|
6
|
-
|
|
7
|
-
developer_instructions = """
|
|
8
|
-
You are Scout for Goal Maker.
|
|
9
|
-
|
|
10
|
-
Thinking level: medium.
|
|
11
|
-
Mode: read-only evidence mapping.
|
|
12
|
-
|
|
13
|
-
You are read-only. Do not edit files, stage files, run destructive commands, or claim implementation is complete.
|
|
14
|
-
|
|
15
|
-
Given one active Scout task, map repo/source/spec evidence, verification commands, health signals, improvement candidates, target files, tests, and unresolved ambiguity.
|
|
16
|
-
|
|
17
|
-
Return a compact Scout receipt for the PM to paste into state.yaml:
|
|
18
|
-
- result
|
|
19
|
-
- summary
|
|
20
|
-
- evidence paths
|
|
21
|
-
- note path if findings are too large for the task card
|
|
22
|
-
- spawned_tasks when useful
|
|
23
|
-
- ambiguity requiring Judge
|
|
24
|
-
|
|
25
|
-
Do not select the active task or mark the goal complete.
|
|
26
|
-
"""
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
name = "goal_worker"
|
|
2
|
-
description = "Low-thinking bounded implementer for exactly one Goal Maker Worker task with allowed files, verification commands, and stop conditions."
|
|
3
|
-
model_reasoning_effort = "low"
|
|
4
|
-
sandbox_mode = "workspace-write"
|
|
5
|
-
nickname_candidates = ["Worker", "Patch", "Fixer"]
|
|
6
|
-
|
|
7
|
-
developer_instructions = """
|
|
8
|
-
You are Worker for Goal Maker.
|
|
9
|
-
|
|
10
|
-
Thinking level: low.
|
|
11
|
-
Mode: one bounded writer.
|
|
12
|
-
|
|
13
|
-
Execute exactly one active Worker task. Do not broaden scope.
|
|
14
|
-
|
|
15
|
-
You may edit only the task's allowed_files. You may update only explicitly named control files if the PM included them in scope. Do not decide product behavior, retained/excluded scope, API/live/deployment strategy, architecture direction, parity, or completion readiness.
|
|
16
|
-
|
|
17
|
-
Stop immediately if required evidence is missing, files outside scope are needed, source/tests/product conflict, verification fails twice, or the diff exceeds the task budget.
|
|
18
|
-
|
|
19
|
-
Return a compact Worker receipt for the PM to paste into state.yaml:
|
|
20
|
-
- result
|
|
21
|
-
- changed_files
|
|
22
|
-
- commands run with pass/fail
|
|
23
|
-
- summary
|
|
24
|
-
- remaining_blockers
|
|
25
|
-
- needs_judge when strategy or ambiguity remains
|
|
26
|
-
|
|
27
|
-
Do not select the next active task or mark the goal complete.
|
|
28
|
-
"""
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
interface:
|
|
2
|
-
display_name: "Goal Maker"
|
|
3
|
-
short_description: "Diagnose intent before making reliable goal boards."
|
|
4
|
-
default_prompt: "Use goal-maker to run diagnostic intake on my rough or detailed goal. If the goal is vague or improvement-oriented, ask one guided question at a time, surface likely blind spots, and do not create files until intent, proof, scope, and board handling are clear or explicitly defaulted. If it is clear or already planned, prepare or repair goal.md, state.yaml, and notes/, then print the /goal Follow command instead of starting /goal automatically."
|
|
5
|
-
policy:
|
|
6
|
-
allow_implicit_invocation: true
|
|
@@ -1,370 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
3
|
-
import { basename, dirname, join } from "node:path";
|
|
4
|
-
|
|
5
|
-
const statePath = process.argv[2];
|
|
6
|
-
|
|
7
|
-
if (!statePath) {
|
|
8
|
-
console.error("Usage: node scripts/check-goal-state.mjs docs/goals/<slug>/state.yaml");
|
|
9
|
-
process.exit(2);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
if (!existsSync(statePath)) {
|
|
13
|
-
console.error(JSON.stringify({ ok: false, errors: [`state file not found: ${statePath}`], warnings: [] }, null, 2));
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const root = dirname(statePath);
|
|
18
|
-
const text = readFileSync(statePath, "utf8");
|
|
19
|
-
const errors = [];
|
|
20
|
-
const warnings = [];
|
|
21
|
-
|
|
22
|
-
function clean(value) {
|
|
23
|
-
if (value === undefined || value === null) return null;
|
|
24
|
-
const cleaned = value.replace(/#.*/, "").trim().replace(/^[\'\"]|[\'\"]$/g, "");
|
|
25
|
-
if (cleaned === "" || cleaned === "null") return null;
|
|
26
|
-
if (cleaned === "true") return true;
|
|
27
|
-
if (cleaned === "false") return false;
|
|
28
|
-
if (/^\d+$/.test(cleaned)) return Number(cleaned);
|
|
29
|
-
return cleaned;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function topScalar(key) {
|
|
33
|
-
const match = text.match(new RegExp(`^${key}:\\s*(.*?)\\s*$`, "m"));
|
|
34
|
-
return match ? clean(match[1]) : null;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function nestedScalar(section, key) {
|
|
38
|
-
const lines = text.split(/\r?\n/);
|
|
39
|
-
let inSection = false;
|
|
40
|
-
for (const line of lines) {
|
|
41
|
-
if (new RegExp(`^${section}:\\s*$`).test(line)) {
|
|
42
|
-
inSection = true;
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
if (inSection && /^\S/.test(line)) break;
|
|
46
|
-
if (inSection) {
|
|
47
|
-
const match = line.match(new RegExp(`^\\s{2}${key}:\\s*(.*?)\\s*$`));
|
|
48
|
-
if (match) return clean(match[1]);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function sectionText(section) {
|
|
55
|
-
const lines = text.split(/\r?\n/);
|
|
56
|
-
const start = lines.findIndex((line) => new RegExp(`^${section}:\\s*$`).test(line));
|
|
57
|
-
if (start === -1) return "";
|
|
58
|
-
const collected = [];
|
|
59
|
-
for (let i = start + 1; i < lines.length; i += 1) {
|
|
60
|
-
if (/^\S/.test(lines[i])) break;
|
|
61
|
-
collected.push(lines[i]);
|
|
62
|
-
}
|
|
63
|
-
return collected.join("\n");
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function parseTasks() {
|
|
67
|
-
const body = sectionText("tasks");
|
|
68
|
-
if (!body) return [];
|
|
69
|
-
const lines = body.split(/\r?\n/);
|
|
70
|
-
const tasks = [];
|
|
71
|
-
let current = null;
|
|
72
|
-
let currentLines = [];
|
|
73
|
-
|
|
74
|
-
function finish() {
|
|
75
|
-
if (!current) return;
|
|
76
|
-
current.raw = currentLines.join("\n");
|
|
77
|
-
tasks.push(current);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
for (const line of lines) {
|
|
81
|
-
const idMatch = line.match(/^\s{2}-\s+id:\s*(.+?)\s*$/);
|
|
82
|
-
if (idMatch) {
|
|
83
|
-
finish();
|
|
84
|
-
current = { id: clean(idMatch[1]) };
|
|
85
|
-
currentLines = [line];
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
if (current) currentLines.push(line);
|
|
89
|
-
}
|
|
90
|
-
finish();
|
|
91
|
-
return tasks.map((task) => ({
|
|
92
|
-
...task,
|
|
93
|
-
type: taskScalar(task, "type"),
|
|
94
|
-
assignee: taskScalar(task, "assignee"),
|
|
95
|
-
status: taskScalar(task, "status"),
|
|
96
|
-
objective: taskScalar(task, "objective"),
|
|
97
|
-
allowedFiles: taskList(task, "allowed_files"),
|
|
98
|
-
verify: taskList(task, "verify"),
|
|
99
|
-
stopIf: taskList(task, "stop_if"),
|
|
100
|
-
receipt: taskReceipt(task),
|
|
101
|
-
}));
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function taskScalar(task, key) {
|
|
105
|
-
const match = task.raw.match(new RegExp(`^\\s{4}${key}:\\s*(.*?)\\s*$`, "m"));
|
|
106
|
-
return match ? clean(match[1]) : null;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function taskList(task, key) {
|
|
110
|
-
const lines = task.raw.split(/\r?\n/);
|
|
111
|
-
const start = lines.findIndex((line) => new RegExp(`^\\s{4}${key}:\\s*$`).test(line));
|
|
112
|
-
if (start === -1) return [];
|
|
113
|
-
const values = [];
|
|
114
|
-
for (let i = start + 1; i < lines.length; i += 1) {
|
|
115
|
-
if (/^\s{4}\S/.test(lines[i])) break;
|
|
116
|
-
const item = lines[i].match(/^\s{6}-\s*(.+?)\s*$/);
|
|
117
|
-
if (item) values.push(clean(item[1]));
|
|
118
|
-
}
|
|
119
|
-
return values.filter((value) => value !== null);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function taskReceipt(task) {
|
|
123
|
-
const lines = task.raw.split(/\r?\n/);
|
|
124
|
-
const start = lines.findIndex((line) => /^\s{4}receipt:\s*/.test(line));
|
|
125
|
-
if (start === -1) return { present: false, value: null, raw: "" };
|
|
126
|
-
|
|
127
|
-
const inline = clean(lines[start].replace(/^\s{4}receipt:\s*/, ""));
|
|
128
|
-
if (inline === null && !/^(\s{6}|\s{8})/.test(lines[start + 1] || "")) {
|
|
129
|
-
return { present: true, value: null, raw: "" };
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const receiptLines = [];
|
|
133
|
-
for (let i = start + 1; i < lines.length; i += 1) {
|
|
134
|
-
if (/^\s{4}\S/.test(lines[i])) break;
|
|
135
|
-
receiptLines.push(lines[i]);
|
|
136
|
-
}
|
|
137
|
-
const raw = receiptLines.join("\n");
|
|
138
|
-
return {
|
|
139
|
-
present: true,
|
|
140
|
-
value: inline || "object",
|
|
141
|
-
raw,
|
|
142
|
-
has: (key) => new RegExp(`^\\s{6}${key}:`, "m").test(raw),
|
|
143
|
-
list: (key) => receiptList(raw, key),
|
|
144
|
-
commandStatuses: () => receiptCommandStatuses(raw),
|
|
145
|
-
scalar: (key) => {
|
|
146
|
-
const match = raw.match(new RegExp(`^\\s{6}${key}:\\s*(.*?)\\s*$`, "m"));
|
|
147
|
-
return match ? clean(match[1]) : null;
|
|
148
|
-
},
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function receiptList(raw, key) {
|
|
153
|
-
const lines = raw.split(/\r?\n/);
|
|
154
|
-
const start = lines.findIndex((line) => new RegExp(`^\\s{6}${key}:\\s*$`).test(line));
|
|
155
|
-
if (start === -1) return [];
|
|
156
|
-
const values = [];
|
|
157
|
-
for (let i = start + 1; i < lines.length; i += 1) {
|
|
158
|
-
if (/^\s{6}\S/.test(lines[i])) break;
|
|
159
|
-
const item = lines[i].match(/^\s{8}-\s*(.+?)\s*$/);
|
|
160
|
-
if (item) values.push(clean(item[1]));
|
|
161
|
-
}
|
|
162
|
-
return values.filter((value) => value !== null);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function receiptCommandStatuses(raw) {
|
|
166
|
-
return [...raw.matchAll(/^\s{10}status:\s*(.*?)\s*$/gm)]
|
|
167
|
-
.map((match) => clean(match[1]))
|
|
168
|
-
.filter((value) => value !== null);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function rootEntryErrors() {
|
|
172
|
-
const allowed = new Set(["goal.md", "state.yaml", "notes"]);
|
|
173
|
-
const unexpected = [];
|
|
174
|
-
for (const entry of readdirSync(root).filter((item) => item !== ".DS_Store")) {
|
|
175
|
-
const path = join(root, entry);
|
|
176
|
-
const stats = statSync(path);
|
|
177
|
-
if (!allowed.has(entry)) {
|
|
178
|
-
unexpected.push(entry);
|
|
179
|
-
} else if (entry === "notes" && !stats.isDirectory()) {
|
|
180
|
-
unexpected.push("notes (must be a directory)");
|
|
181
|
-
} else if (entry !== "notes" && !stats.isFile()) {
|
|
182
|
-
unexpected.push(`${entry} (must be a file)`);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
return unexpected;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const version = topScalar("version");
|
|
189
|
-
const goalStatus = nestedScalar("goal", "status");
|
|
190
|
-
const activeTask = topScalar("active_task");
|
|
191
|
-
const installedAgents = ["scout", "worker", "judge"].map((agent) => ({
|
|
192
|
-
agent,
|
|
193
|
-
status: nestedScalar("agents", agent),
|
|
194
|
-
}));
|
|
195
|
-
const continuousUntilFullOutcome = nestedScalar("rules", "continuous_until_full_outcome") === true;
|
|
196
|
-
const missingInputOrCredentialsDoNotStopGoal =
|
|
197
|
-
nestedScalar("rules", "missing_input_or_credentials_do_not_stop_goal") === true;
|
|
198
|
-
const legacySignals = [
|
|
199
|
-
/^gate:\s*$/m,
|
|
200
|
-
/^artifact_policy:\s*$/m,
|
|
201
|
-
/^active_unit:/m,
|
|
202
|
-
/^evidence\.jsonl/m,
|
|
203
|
-
].some((pattern) => pattern.test(text)) || ["units", "artifacts", "evidence.jsonl"].some((entry) => existsSync(join(root, entry)));
|
|
204
|
-
|
|
205
|
-
if (version !== 2) {
|
|
206
|
-
if (legacySignals) {
|
|
207
|
-
errors.push("legacy v1 goal state detected; Goal Maker v2 requires version: 2 with a task board. Create a new v2 goal or migrate manually.");
|
|
208
|
-
} else {
|
|
209
|
-
errors.push("state.yaml must declare version: 2");
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (!["active", "blocked", "done"].includes(goalStatus)) {
|
|
214
|
-
errors.push(`goal.status must be active, blocked, or done; got ${goalStatus || "<missing>"}`);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
for (const { agent, status } of installedAgents) {
|
|
218
|
-
if (status !== "installed") {
|
|
219
|
-
errors.push(`agents.${agent} must be installed; got ${status || "<missing>"}`);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (!existsSync(join(root, "goal.md"))) errors.push("missing goal.md");
|
|
224
|
-
if (!existsSync(join(root, "notes")) || !statSync(join(root, "notes")).isDirectory()) {
|
|
225
|
-
errors.push("missing notes/ directory");
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const unexpected = rootEntryErrors();
|
|
229
|
-
if (unexpected.length > 0) {
|
|
230
|
-
errors.push(`unexpected root entries; v2 goal roots may contain only goal.md, state.yaml, and notes/: ${unexpected.join(", ")}`);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const tasks = parseTasks();
|
|
234
|
-
const ids = new Set();
|
|
235
|
-
for (const task of tasks) {
|
|
236
|
-
if (!task.id || !/^T\d{3}$/.test(task.id)) errors.push(`task id must use T### format; got ${task.id || "<missing>"}`);
|
|
237
|
-
if (ids.has(task.id)) errors.push(`duplicate task id: ${task.id}`);
|
|
238
|
-
ids.add(task.id);
|
|
239
|
-
if (!["scout", "judge", "worker", "pm"].includes(task.type)) {
|
|
240
|
-
errors.push(`task ${task.id} type must be scout, judge, worker, or pm`);
|
|
241
|
-
}
|
|
242
|
-
if (!["Scout", "Judge", "Worker", "PM"].includes(task.assignee)) {
|
|
243
|
-
errors.push(`task ${task.id} assignee must be Scout, Judge, Worker, or PM`);
|
|
244
|
-
}
|
|
245
|
-
const expectedAssignee = {
|
|
246
|
-
scout: "Scout",
|
|
247
|
-
judge: "Judge",
|
|
248
|
-
worker: "Worker",
|
|
249
|
-
pm: "PM",
|
|
250
|
-
}[task.type];
|
|
251
|
-
if (expectedAssignee && task.assignee !== expectedAssignee) {
|
|
252
|
-
errors.push(`task ${task.id} assignee must be ${expectedAssignee} for type ${task.type}`);
|
|
253
|
-
}
|
|
254
|
-
if (!["queued", "active", "blocked", "done"].includes(task.status)) {
|
|
255
|
-
errors.push(`task ${task.id} status must be queued, active, blocked, or done`);
|
|
256
|
-
}
|
|
257
|
-
if (!task.objective) errors.push(`task ${task.id} missing objective`);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (tasks.length === 0) errors.push("tasks must contain at least one task");
|
|
261
|
-
|
|
262
|
-
const activeTasks = tasks.filter((task) => task.status === "active");
|
|
263
|
-
if (goalStatus === "done") {
|
|
264
|
-
if (activeTasks.length !== 0) errors.push("done goals must not have an active task");
|
|
265
|
-
if (activeTask !== null) errors.push("done goals must set active_task: null");
|
|
266
|
-
const unfinishedWorkers = tasks
|
|
267
|
-
.filter((task) => task.type === "worker" && ["queued", "active"].includes(task.status))
|
|
268
|
-
.map((task) => task.id);
|
|
269
|
-
if (unfinishedWorkers.length > 0) {
|
|
270
|
-
errors.push(`done goals must not leave queued or active Worker tasks: ${unfinishedWorkers.join(", ")}`);
|
|
271
|
-
}
|
|
272
|
-
} else if (goalStatus === "blocked") {
|
|
273
|
-
if (activeTasks.length > 1) errors.push("blocked goals may have at most one active task");
|
|
274
|
-
if (continuousUntilFullOutcome && missingInputOrCredentialsDoNotStopGoal) {
|
|
275
|
-
errors.push("continuous goals must keep goal.status active; missing input or credentials should block specific tasks, not the whole goal");
|
|
276
|
-
}
|
|
277
|
-
} else if (activeTasks.length !== 1) {
|
|
278
|
-
errors.push(`exactly one active task is required while goal.status is active; found ${activeTasks.length}`);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (activeTasks.length === 1 && activeTask !== activeTasks[0].id) {
|
|
282
|
-
errors.push(`active_task must point to active task ${activeTasks[0].id}; got ${activeTask || "null"}`);
|
|
283
|
-
}
|
|
284
|
-
if (activeTask && !ids.has(activeTask)) errors.push(`active_task points to unknown task: ${activeTask}`);
|
|
285
|
-
|
|
286
|
-
for (const task of tasks) {
|
|
287
|
-
const hasReceipt = task.receipt.present && task.receipt.value !== null;
|
|
288
|
-
const receiptResult = hasReceipt ? task.receipt.scalar("result") : null;
|
|
289
|
-
if (task.status === "done" && !hasReceipt) {
|
|
290
|
-
errors.push(`done task ${task.id} missing receipt`);
|
|
291
|
-
}
|
|
292
|
-
if (task.status === "done" && hasReceipt && receiptResult !== "done") {
|
|
293
|
-
errors.push(`done task ${task.id} receipt must include result: done`);
|
|
294
|
-
}
|
|
295
|
-
if (task.status === "blocked" && !hasReceipt) {
|
|
296
|
-
errors.push(`blocked task ${task.id} missing receipt`);
|
|
297
|
-
}
|
|
298
|
-
if (task.type === "worker" && task.status === "active") {
|
|
299
|
-
if (task.allowedFiles.length === 0) errors.push(`active Worker task ${task.id} must include allowed_files`);
|
|
300
|
-
if (task.verify.length === 0) errors.push(`active Worker task ${task.id} must include verify`);
|
|
301
|
-
if (task.stopIf.length === 0) errors.push(`active Worker task ${task.id} must include stop_if`);
|
|
302
|
-
}
|
|
303
|
-
if (task.type === "worker" && task.status === "done" && hasReceipt) {
|
|
304
|
-
for (const key of ["changed_files", "commands", "summary"]) {
|
|
305
|
-
if (!task.receipt.has(key)) errors.push(`Worker receipt for ${task.id} missing ${key}`);
|
|
306
|
-
}
|
|
307
|
-
const changedFiles = task.receipt.list("changed_files");
|
|
308
|
-
for (const changedFile of changedFiles) {
|
|
309
|
-
if (!task.allowedFiles.includes(changedFile)) {
|
|
310
|
-
errors.push(`Worker receipt for ${task.id} changed file outside allowed_files: ${changedFile}`);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
const commandStatuses = task.receipt.commandStatuses();
|
|
314
|
-
if (task.receipt.has("commands") && commandStatuses.length === 0) {
|
|
315
|
-
errors.push(`Worker receipt for ${task.id} commands must include status fields`);
|
|
316
|
-
}
|
|
317
|
-
for (const status of commandStatuses) {
|
|
318
|
-
if (status !== "pass") {
|
|
319
|
-
errors.push(`Worker receipt for ${task.id} has non-passing command status: ${status}`);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
if (task.type === "scout" && task.status === "done" && hasReceipt) {
|
|
324
|
-
if (!task.receipt.has("summary")) errors.push(`Scout receipt for ${task.id} missing summary`);
|
|
325
|
-
if (!task.receipt.has("evidence") && !task.receipt.has("note")) {
|
|
326
|
-
errors.push(`Scout receipt for ${task.id} must include evidence or note`);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
if (task.type === "judge" && task.status === "done" && hasReceipt && !task.receipt.has("decision")) {
|
|
330
|
-
errors.push(`Judge receipt for ${task.id} missing decision`);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (goalStatus === "done") {
|
|
335
|
-
const finalAudit = tasks.some((task) => {
|
|
336
|
-
if (!["judge", "pm"].includes(task.type) || task.status !== "done") return false;
|
|
337
|
-
if (!task.receipt.present || task.receipt.value === null) return false;
|
|
338
|
-
const decision = task.receipt.scalar("decision");
|
|
339
|
-
return decision === "complete" || decision === "done";
|
|
340
|
-
});
|
|
341
|
-
if (!finalAudit) {
|
|
342
|
-
errors.push("completion requires a final done Judge or PM audit receipt with decision: complete");
|
|
343
|
-
}
|
|
344
|
-
if (continuousUntilFullOutcome) {
|
|
345
|
-
const finalFullOutcomeAudit = tasks.some((task) => {
|
|
346
|
-
if (!["judge", "pm"].includes(task.type) || task.status !== "done") return false;
|
|
347
|
-
if (!task.receipt.present || task.receipt.value === null) return false;
|
|
348
|
-
const decision = task.receipt.scalar("decision");
|
|
349
|
-
const fullOutcomeComplete = task.receipt.scalar("full_outcome_complete");
|
|
350
|
-
return (decision === "complete" || decision === "done") && fullOutcomeComplete === true;
|
|
351
|
-
});
|
|
352
|
-
if (!finalFullOutcomeAudit) {
|
|
353
|
-
errors.push("continuous goals require a final done Judge or PM audit receipt with full_outcome_complete: true before goal.status: done");
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const result = {
|
|
359
|
-
ok: errors.length === 0,
|
|
360
|
-
version,
|
|
361
|
-
state_path: statePath,
|
|
362
|
-
goal_status: goalStatus,
|
|
363
|
-
active_task: activeTask,
|
|
364
|
-
task_count: tasks.length,
|
|
365
|
-
errors,
|
|
366
|
-
warnings,
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
console.log(JSON.stringify(result, null, 2));
|
|
370
|
-
process.exit(result.ok ? 0 : 1);
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { copyFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
3
|
-
import { dirname, join, resolve } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
|
|
6
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
const sourceDir = resolve(__dirname, "../agents");
|
|
8
|
-
const destDir = resolve(process.argv[2] || ".codex/agents");
|
|
9
|
-
const force = process.argv.includes("--force");
|
|
10
|
-
|
|
11
|
-
if (!existsSync(sourceDir)) {
|
|
12
|
-
console.error(`agent definitions not found: ${sourceDir}`);
|
|
13
|
-
process.exit(1);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
mkdirSync(destDir, { recursive: true });
|
|
17
|
-
|
|
18
|
-
const files = readdirSync(sourceDir).filter((f) => f.endsWith(".toml") && !f.includes("config-snippet"));
|
|
19
|
-
for (const file of files) {
|
|
20
|
-
const src = join(sourceDir, file);
|
|
21
|
-
const dest = join(destDir, file);
|
|
22
|
-
if (existsSync(dest) && !force) {
|
|
23
|
-
console.log(`skip existing ${dest} (use --force to overwrite)`);
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
copyFileSync(src, dest);
|
|
27
|
-
console.log(`installed ${dest}`);
|
|
28
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# Goal Maker Agents
|
|
2
|
-
|
|
3
|
-
Use three generic agents. The main `/goal` thread remains PM and owns the board.
|
|
4
|
-
|
|
5
|
-
| Agent | model_reasoning_effort | sandbox_mode | Purpose |
|
|
6
|
-
|---|---:|---|---|
|
|
7
|
-
| goal_scout | medium | read-only | Evidence mapping and candidate tasks |
|
|
8
|
-
| goal_worker | low | workspace-write | One bounded implementation/recovery task |
|
|
9
|
-
| goal_judge | high | read-only | Strategic review, escalation, completion skepticism |
|
|
10
|
-
|
|
11
|
-
## PM Thinking Policy
|
|
12
|
-
|
|
13
|
-
The main `/goal` thread is the PM. It owns board truth, chooses active tasks, and decides when receipts are sufficient.
|
|
14
|
-
|
|
15
|
-
| Goal mode | PM thinking |
|
|
16
|
-
|---|---:|
|
|
17
|
-
| specific, bounded | medium |
|
|
18
|
-
| open-ended | high |
|
|
19
|
-
| recovery | high |
|
|
20
|
-
| audit | high |
|
|
21
|
-
| high-risk or multi-day final audit | xhigh optional |
|
|
22
|
-
|
|
23
|
-
Do not use `xhigh` by default. Use it only when a wrong board, scope, or completion decision would be materially more expensive than latency and cost.
|
|
24
|
-
|
|
25
|
-
Tasks may include optional `reasoning_hint: default | low | medium | high | xhigh`. Treat it as PM guidance, not permission to widen scope.
|
|
26
|
-
|
|
27
|
-
Recommended project config:
|
|
28
|
-
|
|
29
|
-
```toml
|
|
30
|
-
[agents]
|
|
31
|
-
max_threads = 4
|
|
32
|
-
max_depth = 1
|
|
33
|
-
job_max_runtime_seconds = 1800
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Install:
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
mkdir -p .codex/agents
|
|
40
|
-
cp .codex/skills/goal-maker/agents/goal_*.toml .codex/agents/
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Rules:
|
|
44
|
-
|
|
45
|
-
- Only the PM loop chooses active tasks, marks tasks done, or completes the goal.
|
|
46
|
-
- Keep at most one write-capable Worker active unless disjoint write scopes are explicit in `state.yaml`.
|
|
47
|
-
- Scout and Judge are read-only.
|
|
48
|
-
- Judge is high thinking.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/goal Follow docs/goals/<slug>/goal.md.
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# <Goal Title>
|
|
2
|
-
|
|
3
|
-
## Objective
|
|
4
|
-
|
|
5
|
-
<User-editable objective. Keep this bounded to the current tranche, not an infinite mission.>
|
|
6
|
-
|
|
7
|
-
## Original Request
|
|
8
|
-
|
|
9
|
-
<Shortest faithful copy of what the user asked for. Preserve user-provided plan details here or summarize them under Intake Summary.>
|
|
10
|
-
|
|
11
|
-
## Intake Summary
|
|
12
|
-
|
|
13
|
-
- Input shape: `vague | specific | existing_plan | recovery | audit`
|
|
14
|
-
- Audience: <beneficiary or unknown>
|
|
15
|
-
- Authority: `requested | approved | inferred | needs_approval | blocked`
|
|
16
|
-
- Proof type: `test | demo | artifact | metric | review | source_backed_answer | decision`
|
|
17
|
-
- Completion proof: <observable signal that closes the full original outcome>
|
|
18
|
-
- Likely misfire: <how Goal Maker could succeed at the wrong thing>
|
|
19
|
-
- Blind spots considered: <risks, unstated choices, or success dimensions surfaced during diagnostic intake>
|
|
20
|
-
- Existing plan facts: <user-provided steps/files/constraints/sequencing to preserve and validate, or none>
|
|
21
|
-
|
|
22
|
-
## Goal Kind
|
|
23
|
-
|
|
24
|
-
`specific | open_ended | existing_plan | recovery | audit`
|
|
25
|
-
|
|
26
|
-
## Current Tranche
|
|
27
|
-
|
|
28
|
-
<What is enough for the full owner outcome, and what is the current safe slice? For execution goals, the default is continuous: discover enough evidence, choose a safe implementation slice, implement it, verify it, audit it, then immediately advance to the next safe slice until the full original outcome is complete. Plan-only or one-slice-only stopping is valid only when explicitly requested.>
|
|
29
|
-
|
|
30
|
-
## Non-Negotiable Constraints
|
|
31
|
-
|
|
32
|
-
- <Constraint, safety rule, compatibility rule, or owner preference.>
|
|
33
|
-
|
|
34
|
-
## Stop Rule
|
|
35
|
-
|
|
36
|
-
Stop only when a final audit proves the full original outcome is complete.
|
|
37
|
-
|
|
38
|
-
Do not stop after planning, discovery, or Judge selection if the user asked for working software or automation and a safe Worker task can be activated.
|
|
39
|
-
|
|
40
|
-
Do not stop after a single verified Worker slice when the broader owner outcome still has safe local follow-up slices. After each slice audit, advance the board to the next highest-leverage safe Worker task and continue.
|
|
41
|
-
|
|
42
|
-
Do not stop because a slice needs owner input, credentials, production access, destructive operations, or policy decisions. Mark that exact slice blocked with a receipt, create the smallest safe follow-up or workaround task, and continue all local, non-destructive work that can still move the goal toward the full outcome.
|
|
43
|
-
|
|
44
|
-
## Canonical Board
|
|
45
|
-
|
|
46
|
-
Machine truth lives at:
|
|
47
|
-
|
|
48
|
-
`docs/goals/<slug>/state.yaml`
|
|
49
|
-
|
|
50
|
-
If this charter and `state.yaml` disagree, `state.yaml` wins for task status, active task, receipts, verification freshness, and completion truth.
|
|
51
|
-
|
|
52
|
-
## Run Command
|
|
53
|
-
|
|
54
|
-
```text
|
|
55
|
-
/goal Follow docs/goals/<slug>/goal.md.
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## PM Loop
|
|
59
|
-
|
|
60
|
-
On every `/goal` continuation:
|
|
61
|
-
|
|
62
|
-
1. Read this charter.
|
|
63
|
-
2. Read `state.yaml`.
|
|
64
|
-
3. Re-check the intake: original request, input shape, authority, proof, blind spots, existing plan facts, and likely misfire.
|
|
65
|
-
4. Work only on the active board task.
|
|
66
|
-
5. Assign Scout, Judge, Worker, or PM according to the task.
|
|
67
|
-
6. Write a compact task receipt.
|
|
68
|
-
7. Update the board.
|
|
69
|
-
8. If Judge selected a safe Worker task with `allowed_files`, `verify`, and `stop_if`, activate it and continue unless blocked.
|
|
70
|
-
9. Treat a slice audit as a checkpoint, not completion, unless it explicitly proves the full original outcome is complete.
|
|
71
|
-
10. Finish only with a Judge/PM audit receipt that maps receipts and verification back to the original user outcome and records `full_outcome_complete: true`.
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# <Task ID>: <Note Title>
|
|
2
|
-
|
|
3
|
-
Task: `<T###>`
|
|
4
|
-
Kind: `scout | judge | pm`
|
|
5
|
-
Status: `current | superseded | blocked`
|
|
6
|
-
|
|
7
|
-
## Summary
|
|
8
|
-
|
|
9
|
-
<One paragraph the board receipt can point to.>
|
|
10
|
-
|
|
11
|
-
## Details
|
|
12
|
-
|
|
13
|
-
- <Evidence, decision, or context too large for an inline receipt.>
|
|
14
|
-
|
|
15
|
-
## Board Receipt Snippet
|
|
16
|
-
|
|
17
|
-
```yaml
|
|
18
|
-
receipt:
|
|
19
|
-
result: done
|
|
20
|
-
note: notes/<task-id>-<slug>.md
|
|
21
|
-
summary: "<compact result>"
|
|
22
|
-
```
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
# Goal Maker v2 state.yaml
|
|
2
|
-
# Board truth lives here. goal.md is the editable charter; notes/ holds long receipts only.
|
|
3
|
-
|
|
4
|
-
version: 2
|
|
5
|
-
|
|
6
|
-
goal:
|
|
7
|
-
title: "<Goal title>"
|
|
8
|
-
slug: "<goal-slug>"
|
|
9
|
-
kind: open_ended # specific | open_ended | existing_plan | recovery | audit
|
|
10
|
-
tranche: "<continuous execution: complete successive safe verified slices until the full original outcome is complete>"
|
|
11
|
-
status: active # active | blocked | done
|
|
12
|
-
intake:
|
|
13
|
-
original_request: "<shortest faithful user request>"
|
|
14
|
-
interpreted_outcome: "<one sentence>"
|
|
15
|
-
input_shape: vague # vague | specific | existing_plan | recovery | audit
|
|
16
|
-
audience: unknown
|
|
17
|
-
authority: requested # requested | approved | inferred | needs_approval | blocked
|
|
18
|
-
proof_type: artifact # test | demo | artifact | metric | review | source_backed_answer | decision
|
|
19
|
-
completion_proof: "<observable signal that proves the full original outcome is complete>"
|
|
20
|
-
likely_misfire: "<how Goal Maker could succeed at the wrong thing>"
|
|
21
|
-
blind_spots_considered: []
|
|
22
|
-
existing_plan_facts: []
|
|
23
|
-
|
|
24
|
-
rules:
|
|
25
|
-
pm_owns_state: true
|
|
26
|
-
one_active_task: true
|
|
27
|
-
max_write_workers: 1
|
|
28
|
-
no_implementation_without_worker_or_pm_task: true
|
|
29
|
-
no_completion_without_judge_or_pm_audit: true
|
|
30
|
-
planning_is_not_completion: true
|
|
31
|
-
queued_required_worker_blocks_completion: true
|
|
32
|
-
continuous_until_full_outcome: true
|
|
33
|
-
missing_input_or_credentials_do_not_stop_goal: true
|
|
34
|
-
preserve_and_validate_existing_plan: true
|
|
35
|
-
intake_misfire_must_be_audited: true
|
|
36
|
-
|
|
37
|
-
agents:
|
|
38
|
-
scout: installed
|
|
39
|
-
worker: installed
|
|
40
|
-
judge: installed
|
|
41
|
-
|
|
42
|
-
active_task: T001
|
|
43
|
-
|
|
44
|
-
tasks:
|
|
45
|
-
- id: T001
|
|
46
|
-
type: scout
|
|
47
|
-
assignee: Scout
|
|
48
|
-
status: active
|
|
49
|
-
reasoning_hint: default # default | low | medium | high | xhigh
|
|
50
|
-
objective: "Map repo purpose, architecture, verification commands, health signals, and improvement candidates."
|
|
51
|
-
inputs:
|
|
52
|
-
- README.md
|
|
53
|
-
- package.json
|
|
54
|
-
- docs
|
|
55
|
-
- tests
|
|
56
|
-
constraints:
|
|
57
|
-
- "Read-only."
|
|
58
|
-
- "Do not edit implementation files."
|
|
59
|
-
- "Prefer concrete file-path evidence over generic advice."
|
|
60
|
-
expected_output:
|
|
61
|
-
- "Repo map"
|
|
62
|
-
- "Verification commands"
|
|
63
|
-
- "Ranked improvement candidates"
|
|
64
|
-
- "Candidate next tasks"
|
|
65
|
-
receipt: null
|
|
66
|
-
- id: T002
|
|
67
|
-
type: judge
|
|
68
|
-
assignee: Judge
|
|
69
|
-
status: queued
|
|
70
|
-
reasoning_hint: default
|
|
71
|
-
objective: "Review Scout findings and choose the first safe implementation task."
|
|
72
|
-
inputs:
|
|
73
|
-
- "T001 receipt"
|
|
74
|
-
constraints:
|
|
75
|
-
- "Do not implement."
|
|
76
|
-
- "Pick small reviewable work."
|
|
77
|
-
expected_output:
|
|
78
|
-
- "Decision"
|
|
79
|
-
- "Exact Worker objective"
|
|
80
|
-
- "allowed_files"
|
|
81
|
-
- "verify"
|
|
82
|
-
- "stop_if"
|
|
83
|
-
- "Blocked or deferred tasks"
|
|
84
|
-
receipt: null
|
|
85
|
-
- id: T003
|
|
86
|
-
type: worker
|
|
87
|
-
assignee: Worker
|
|
88
|
-
status: queued
|
|
89
|
-
reasoning_hint: default
|
|
90
|
-
objective: "Execute the first safe implementation task selected by Judge."
|
|
91
|
-
allowed_files: []
|
|
92
|
-
verify: []
|
|
93
|
-
stop_if:
|
|
94
|
-
- "Need files outside allowed_files."
|
|
95
|
-
- "Behavior is ambiguous."
|
|
96
|
-
- "Verification fails twice."
|
|
97
|
-
receipt: null
|
|
98
|
-
- id: T999
|
|
99
|
-
type: judge
|
|
100
|
-
assignee: Judge
|
|
101
|
-
status: queued
|
|
102
|
-
reasoning_hint: default
|
|
103
|
-
objective: "Audit whether the implemented slice satisfies the original user outcome for this tranche."
|
|
104
|
-
inputs:
|
|
105
|
-
- "All done task receipts"
|
|
106
|
-
- "Last verification"
|
|
107
|
-
- "Current dirty diff"
|
|
108
|
-
constraints:
|
|
109
|
-
- "Do not implement."
|
|
110
|
-
- "Reject completion if required Worker work is still queued or active."
|
|
111
|
-
- "Reject completion if the broader original outcome still has safe local follow-up slices."
|
|
112
|
-
- "Reject stopping only because a slice needs owner input, credentials, production access, destructive operations, or policy decisions."
|
|
113
|
-
expected_output:
|
|
114
|
-
- "complete | not_complete"
|
|
115
|
-
- "full_outcome_complete: true | false"
|
|
116
|
-
- "missing evidence"
|
|
117
|
-
- "next task if not complete"
|
|
118
|
-
receipt: null
|
|
119
|
-
|
|
120
|
-
checks:
|
|
121
|
-
dirty_fingerprint: unknown
|
|
122
|
-
last_verification:
|
|
123
|
-
result: unknown
|
|
124
|
-
task: null
|
|
125
|
-
commands: []
|