goalbuddy 0.3.7 → 0.3.8
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/CHANGELOG.md +61 -0
- package/CONTRIBUTING.md +2 -2
- package/README.md +11 -3
- package/{RELEASE-0.3.7.md → docs/releases/0.3.7.md} +2 -0
- package/docs/releases/0.3.8.md +40 -0
- package/docs/releases/README.md +83 -0
- package/goalbuddy/SKILL.md +2 -0
- package/goalbuddy/scripts/render-task-prompt.mjs +17 -3
- package/goalbuddy/surfaces/local-goal-board/scripts/lib/goal-board.mjs +15 -11
- package/goalbuddy/surfaces/local-goal-board/scripts/local-goal-board.mjs +23 -2
- package/goalbuddy/surfaces/local-goal-board/test/local-goal-board.test.mjs +55 -4
- package/internal/cli/goal-maker.mjs +168 -6
- package/package.json +5 -6
- package/plugins/goalbuddy/.claude-plugin/plugin.json +1 -1
- package/plugins/goalbuddy/.codex-plugin/plugin.json +1 -1
- package/plugins/goalbuddy/README.md +1 -1
- package/plugins/goalbuddy/skills/goalbuddy/SKILL.md +2 -0
- package/plugins/goalbuddy/skills/goalbuddy/scripts/render-task-prompt.mjs +17 -3
- package/plugins/goalbuddy/skills/goalbuddy/surfaces/local-goal-board/scripts/local-goal-board.mjs +23 -2
- package/plugins/goalbuddy/skills/goalbuddy/surfaces/local-goal-board/test/local-goal-board.test.mjs +27 -0
- package/examples/improve-goal-maker/goal.md +0 -51
- package/examples/improve-goal-maker/notes/T001-repo-map.md +0 -59
- package/examples/improve-goal-maker/notes/T002-risk-map.md +0 -37
- package/examples/improve-goal-maker/state.yaml +0 -224
- /package/{RELEASE-0.3.5.md → docs/releases/0.3.5.md} +0 -0
|
@@ -49,6 +49,7 @@ const optionsWithValues = new Set([
|
|
|
49
49
|
"--task",
|
|
50
50
|
"--board",
|
|
51
51
|
]);
|
|
52
|
+
const pathOptions = new Set(["--board", "--goal"]);
|
|
52
53
|
|
|
53
54
|
const args = process.argv.slice(2);
|
|
54
55
|
const command = args[0] === "--help" || args[0] === "-h"
|
|
@@ -111,6 +112,17 @@ async function main() {
|
|
|
111
112
|
doctorClaude();
|
|
112
113
|
}
|
|
113
114
|
break;
|
|
115
|
+
case "reset":
|
|
116
|
+
if (wantsHelp()) {
|
|
117
|
+
usage();
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
if (targetMode() !== "codex") {
|
|
121
|
+
console.error("Reset currently supports --target codex only.");
|
|
122
|
+
process.exit(2);
|
|
123
|
+
}
|
|
124
|
+
resetCodex();
|
|
125
|
+
break;
|
|
114
126
|
case "check-update":
|
|
115
127
|
case "update-check":
|
|
116
128
|
checkUpdate();
|
|
@@ -193,6 +205,35 @@ function positionalArgs() {
|
|
|
193
205
|
return values;
|
|
194
206
|
}
|
|
195
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Resolve goal-related paths in raw args to absolute paths.
|
|
210
|
+
* Child processes spawned with cwd=packageRoot cannot resolve
|
|
211
|
+
* relative goal paths from the user's working directory.
|
|
212
|
+
*/
|
|
213
|
+
function resolveChildGoalArgs(rawArgs) {
|
|
214
|
+
const out = [];
|
|
215
|
+
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
216
|
+
const arg = rawArgs[index];
|
|
217
|
+
const joinedMatch = [...pathOptions].find((opt) => arg.startsWith(opt + "="));
|
|
218
|
+
if (joinedMatch) {
|
|
219
|
+
const value = arg.slice(joinedMatch.length + 1);
|
|
220
|
+
out.push(`${joinedMatch}=${value ? resolve(value) : value}`);
|
|
221
|
+
} else if (pathOptions.has(arg)) {
|
|
222
|
+
out.push(arg);
|
|
223
|
+
const value = rawArgs[++index] || "";
|
|
224
|
+
out.push(value ? resolve(value) : value);
|
|
225
|
+
} else if (optionsWithValues.has(arg)) {
|
|
226
|
+
out.push(arg);
|
|
227
|
+
out.push(rawArgs[++index] || "");
|
|
228
|
+
} else if (!arg.startsWith("-")) {
|
|
229
|
+
out.push(resolve(arg));
|
|
230
|
+
} else {
|
|
231
|
+
out.push(arg);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return out;
|
|
235
|
+
}
|
|
236
|
+
|
|
196
237
|
function usage() {
|
|
197
238
|
console.log(`${canonicalProductName} for Claude Code and Codex
|
|
198
239
|
|
|
@@ -203,6 +244,7 @@ Usage:
|
|
|
203
244
|
${canonicalCliName} update [--target claude|codex] [--claude-home <path>] [--codex-home <path>] [--json]
|
|
204
245
|
${canonicalCliName} agents [--target claude|codex] [--claude-home <path>] [--codex-home <path>] [--force]
|
|
205
246
|
${canonicalCliName} doctor [--target claude|codex] [--claude-home <path>] [--codex-home <path>] [--goal-ready]
|
|
247
|
+
${canonicalCliName} reset --target codex [--codex-home <path>] [--json]
|
|
206
248
|
${canonicalCliName} check-update [--json]
|
|
207
249
|
${canonicalCliName} board <docs/goals/slug> [--host <host>] [--port <port>] [--once] [--json]
|
|
208
250
|
${canonicalCliName} prompt <docs/goals/slug> [--task T###] [--board <path/to/state.yaml>] [--json]
|
|
@@ -576,20 +618,36 @@ function doctor() {
|
|
|
576
618
|
const agents = existsSync(agentsPath)
|
|
577
619
|
? readdirSync(agentsPath).filter((file) => file.startsWith("goal_") && file.endsWith(".toml"))
|
|
578
620
|
: [];
|
|
579
|
-
const
|
|
621
|
+
const installSurfacePresent = plugin.skill_installed || installed || legacyInstalled;
|
|
622
|
+
const residualAgents = installSurfacePresent ? [] : agents.filter((file) => requiredAgentFiles.includes(file));
|
|
623
|
+
const missingAgents = installSurfacePresent || residualAgents.length > 0
|
|
624
|
+
? requiredAgentFiles.filter((file) => !agents.includes(file))
|
|
625
|
+
: [];
|
|
580
626
|
const staleAgents = requiredAgentFiles.filter((file) => {
|
|
581
627
|
const installedAgent = join(agentsPath, file);
|
|
582
628
|
const bundledAgent = join(skillSource, "agents", file);
|
|
583
629
|
if (!existsSync(installedAgent) || !existsSync(bundledAgent)) return false;
|
|
584
630
|
return sha256(readFileSync(installedAgent)) !== sha256(readFileSync(bundledAgent));
|
|
585
631
|
});
|
|
632
|
+
const runtimeState = codexInstallState({
|
|
633
|
+
plugin,
|
|
634
|
+
installed,
|
|
635
|
+
legacyInstalled,
|
|
636
|
+
residualAgents,
|
|
637
|
+
missingAgents,
|
|
638
|
+
staleAgents,
|
|
639
|
+
});
|
|
586
640
|
const goalRuntime = codexGoalRuntimeStatus();
|
|
587
641
|
const warnings = [];
|
|
588
642
|
const errors = [];
|
|
589
643
|
if (!goalRuntime.ready) {
|
|
590
644
|
warnings.push("native Codex /goal runtime is not ready; run `codex login` and `codex features enable goals` before using /goal.");
|
|
591
645
|
}
|
|
592
|
-
if (
|
|
646
|
+
if (runtimeState === "fully-removed") {
|
|
647
|
+
errors.push("Codex GoalBuddy is fully removed; run `npx goalbuddy --target codex` to install.");
|
|
648
|
+
} else if (runtimeState === "residual-agents-only") {
|
|
649
|
+
errors.push(`Residual GoalBuddy Codex agents remain without plugin cache/config: ${residualAgents.join(", ")}; run a GoalBuddy reset/cleanup before treating it as removed.`);
|
|
650
|
+
} else if (!plugin.skill_installed && !installed) {
|
|
593
651
|
errors.push("Codex GoalBuddy plugin is not installed; run `npx goalbuddy --target codex`.");
|
|
594
652
|
}
|
|
595
653
|
if (plugin.skill_installed && !plugin.enabled) {
|
|
@@ -621,7 +679,9 @@ function doctor() {
|
|
|
621
679
|
skill_path: skillPath,
|
|
622
680
|
compatibility_skill_installed: legacyInstalled,
|
|
623
681
|
compatibility_skill_path: legacySkillPath,
|
|
682
|
+
runtime_state: runtimeState,
|
|
624
683
|
installed_agents: agents,
|
|
684
|
+
residual_agents: residualAgents,
|
|
625
685
|
missing_agents: missingAgents,
|
|
626
686
|
stale_agents: staleAgents,
|
|
627
687
|
goal_runtime: goalRuntime,
|
|
@@ -636,6 +696,20 @@ function doctor() {
|
|
|
636
696
|
process.exit(installOk && goalReadyOk && errors.length === 0 ? 0 : 1);
|
|
637
697
|
}
|
|
638
698
|
|
|
699
|
+
function codexInstallState({ plugin, installed, legacyInstalled, residualAgents, missingAgents, staleAgents }) {
|
|
700
|
+
if (residualAgents.length > 0 && !plugin.skill_installed && !installed && !legacyInstalled) {
|
|
701
|
+
return "residual-agents-only";
|
|
702
|
+
}
|
|
703
|
+
if (!plugin.skill_installed && !installed && !legacyInstalled) {
|
|
704
|
+
return "fully-removed";
|
|
705
|
+
}
|
|
706
|
+
if (staleAgents.length > 0) return "stale-agents";
|
|
707
|
+
if (missingAgents.length > 0) return "incomplete";
|
|
708
|
+
if (plugin.skill_installed && !plugin.enabled) return "disabled";
|
|
709
|
+
if ((plugin.skill_installed && plugin.enabled) || installed) return "installed";
|
|
710
|
+
return "incomplete";
|
|
711
|
+
}
|
|
712
|
+
|
|
639
713
|
function checkUpdate() {
|
|
640
714
|
const report = updateReport();
|
|
641
715
|
|
|
@@ -782,8 +856,95 @@ function cleanupLegacyCodexSkills() {
|
|
|
782
856
|
return removed;
|
|
783
857
|
}
|
|
784
858
|
|
|
859
|
+
function resetCodex() {
|
|
860
|
+
const configPath = join(codexHome(), "config.toml");
|
|
861
|
+
const removedConfigSections = [];
|
|
862
|
+
if (existsSync(configPath)) {
|
|
863
|
+
const existing = readFileSync(configPath, "utf8");
|
|
864
|
+
let updated = existing;
|
|
865
|
+
for (const header of [`[plugins."${pluginName}@${pluginName}"]`, `[marketplaces.${pluginName}]`]) {
|
|
866
|
+
const next = removeTomlTable(updated, header);
|
|
867
|
+
if (next !== updated) {
|
|
868
|
+
removedConfigSections.push(header);
|
|
869
|
+
updated = next;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
if (updated !== existing) writeFileSync(configPath, updated);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
const removedPluginCachePaths = [];
|
|
876
|
+
const cacheRoot = pluginCacheOwnerRoot();
|
|
877
|
+
if (existsSync(cacheRoot)) {
|
|
878
|
+
rmSync(cacheRoot, { recursive: true, force: true });
|
|
879
|
+
removedPluginCachePaths.push(cacheRoot);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
const removedAgents = [];
|
|
883
|
+
const agentsRoot = join(codexHome(), "agents");
|
|
884
|
+
for (const file of requiredAgentFiles) {
|
|
885
|
+
const path = join(agentsRoot, file);
|
|
886
|
+
if (!existsSync(path)) continue;
|
|
887
|
+
rmSync(path, { recursive: true, force: true });
|
|
888
|
+
removedAgents.push(path);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
const removedLegacySkillPaths = cleanupLegacyCodexSkills();
|
|
892
|
+
const report = {
|
|
893
|
+
reset: true,
|
|
894
|
+
target: "codex",
|
|
895
|
+
codex_home: codexHome(),
|
|
896
|
+
config_path: configPath,
|
|
897
|
+
removed_config_sections: removedConfigSections,
|
|
898
|
+
removed_plugin_cache_paths: removedPluginCachePaths,
|
|
899
|
+
removed_agents: removedAgents,
|
|
900
|
+
removed_legacy_skill_paths: removedLegacySkillPaths,
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
if (hasFlag("--json")) {
|
|
904
|
+
printJson(report);
|
|
905
|
+
return report;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
console.log(`Reset ${canonicalProductName} Codex-owned runtime files`);
|
|
909
|
+
console.log(`Config sections: ${removedConfigSections.length ? removedConfigSections.join(", ") : "none"}`);
|
|
910
|
+
console.log(`Plugin cache: ${removedPluginCachePaths.length ? removedPluginCachePaths.join(", ") : "none"}`);
|
|
911
|
+
console.log(`Agents: ${removedAgents.length ? removedAgents.join(", ") : "none"}`);
|
|
912
|
+
console.log(`Legacy personal skills: ${removedLegacySkillPaths.length ? removedLegacySkillPaths.join(", ") : "none"}`);
|
|
913
|
+
return report;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
function removeTomlTable(text, header) {
|
|
917
|
+
const normalized = text.endsWith("\n") || text.length === 0 ? text : `${text}\n`;
|
|
918
|
+
const lines = normalized.split("\n");
|
|
919
|
+
const output = [];
|
|
920
|
+
let skipping = false;
|
|
921
|
+
let removed = false;
|
|
922
|
+
const descendantPrefix = `${header.slice(0, -1)}.`;
|
|
923
|
+
|
|
924
|
+
for (const line of lines) {
|
|
925
|
+
const trimmed = line.trim();
|
|
926
|
+
if (trimmed === header || trimmed.startsWith(descendantPrefix)) {
|
|
927
|
+
skipping = true;
|
|
928
|
+
removed = true;
|
|
929
|
+
continue;
|
|
930
|
+
}
|
|
931
|
+
if (skipping && /^\s*\[/.test(line)) {
|
|
932
|
+
skipping = trimmed.startsWith(descendantPrefix);
|
|
933
|
+
if (skipping) continue;
|
|
934
|
+
}
|
|
935
|
+
if (!skipping) output.push(line);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
if (!removed) return text;
|
|
939
|
+
return output.join("\n").replace(/\n{3,}/g, "\n\n").replace(/\n*$/, "\n");
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
function pluginCacheOwnerRoot() {
|
|
943
|
+
return join(codexHome(), "plugins", "cache", pluginName);
|
|
944
|
+
}
|
|
945
|
+
|
|
785
946
|
function pluginCacheRoot(version) {
|
|
786
|
-
return join(
|
|
947
|
+
return join(pluginCacheOwnerRoot(), pluginName, version);
|
|
787
948
|
}
|
|
788
949
|
|
|
789
950
|
function enablePluginConfig() {
|
|
@@ -913,8 +1074,9 @@ async function board() {
|
|
|
913
1074
|
process.exit(2);
|
|
914
1075
|
}
|
|
915
1076
|
|
|
1077
|
+
const absoluteGoal = resolve(goal);
|
|
916
1078
|
const script = ensureLocalBoardSurface();
|
|
917
|
-
const scriptArgs = [script, "--goal",
|
|
1079
|
+
const scriptArgs = [script, "--goal", absoluteGoal];
|
|
918
1080
|
for (const option of ["--host", "--port"]) {
|
|
919
1081
|
const value = optionValue(option);
|
|
920
1082
|
if (value) scriptArgs.push(option, value);
|
|
@@ -945,7 +1107,7 @@ async function prompt() {
|
|
|
945
1107
|
}
|
|
946
1108
|
|
|
947
1109
|
const script = join(skillSource, "scripts", "render-task-prompt.mjs");
|
|
948
|
-
const scriptArgs = [script, ...args.slice(1)];
|
|
1110
|
+
const scriptArgs = [script, ...resolveChildGoalArgs(args.slice(1))];
|
|
949
1111
|
const result = spawnSync(process.execPath, scriptArgs, {
|
|
950
1112
|
cwd: packageRoot,
|
|
951
1113
|
encoding: "utf8",
|
|
@@ -959,7 +1121,7 @@ async function prompt() {
|
|
|
959
1121
|
|
|
960
1122
|
async function parallelPlan() {
|
|
961
1123
|
const script = join(skillSource, "scripts", "parallel-plan.mjs");
|
|
962
|
-
const scriptArgs = [script, ...args.slice(1).filter((arg) => arg !== "--parallel-plan")];
|
|
1124
|
+
const scriptArgs = [script, ...resolveChildGoalArgs(args.slice(1).filter((arg) => arg !== "--parallel-plan"))];
|
|
963
1125
|
const result = spawnSync(process.execPath, scriptArgs, {
|
|
964
1126
|
cwd: packageRoot,
|
|
965
1127
|
encoding: "utf8",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goalbuddy",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "A /goal operating loop for Codex and Claude Code: goal oracles, local boards, receipts, and verification.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,11 +9,10 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
".agents/plugins/marketplace.json",
|
|
12
|
+
"CHANGELOG.md",
|
|
12
13
|
"README.md",
|
|
13
|
-
"RELEASE-0.3.5.md",
|
|
14
|
-
"RELEASE-0.3.7.md",
|
|
15
14
|
"CONTRIBUTING.md",
|
|
16
|
-
"
|
|
15
|
+
"docs/releases",
|
|
17
16
|
"plugins/goalbuddy",
|
|
18
17
|
"internal/assets",
|
|
19
18
|
"internal/cli",
|
|
@@ -24,8 +23,8 @@
|
|
|
24
23
|
"goalbuddy/templates"
|
|
25
24
|
],
|
|
26
25
|
"scripts": {
|
|
27
|
-
"check": "node --check internal/cli/*.mjs goalbuddy/scripts/*.mjs && node --test internal/test/*.test.mjs",
|
|
28
|
-
"test": "node --test internal/test/*.test.mjs",
|
|
26
|
+
"check": "node --check internal/cli/*.mjs goalbuddy/scripts/*.mjs goalbuddy/surfaces/local-goal-board/scripts/*.mjs goalbuddy/surfaces/local-goal-board/scripts/lib/*.mjs && node --test internal/test/*.test.mjs goalbuddy/surfaces/local-goal-board/test/*.test.mjs",
|
|
27
|
+
"test": "node --test internal/test/*.test.mjs goalbuddy/surfaces/local-goal-board/test/*.test.mjs",
|
|
29
28
|
"pack:dry-run": "npm pack --dry-run",
|
|
30
29
|
"postinstall": "node internal/cli/postinstall.mjs",
|
|
31
30
|
"publish:check": "node internal/cli/check-publish-version.mjs && npm run pack:dry-run",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
GoalBuddy packages the canonical `goal-prep` skill as a plugin so teams can install the reusable workflow in **Codex** and **Claude Code**, while keeping the npm CLI for local setup, doctor checks, and the built-in local board surface.
|
|
4
4
|
|
|
5
|
-
Version 0.3.
|
|
5
|
+
Version 0.3.8 keeps the Goalmaxxed core and adds board-hub guardrails so agents reuse the shared local board hub instead of stopping it after an unregistered board URL returns 404.
|
|
6
6
|
|
|
7
7
|
## What It Contains
|
|
8
8
|
|
|
@@ -95,6 +95,8 @@ Recommended options:
|
|
|
95
95
|
|
|
96
96
|
If the user chooses the local live board, create the goal directory, `notes/`, and an initial minimal `state.yaml` as soon as the slug is known, then run `npx goalbuddy board docs/goals/<slug>` and open the printed local URL in the AI coding agent's in-app browser (the Codex in-app Browser, the Claude Code preview, or the user's regular browser). The default local hub is `http://goalbuddy.localhost:41737/`, and board URLs normally look like `http://goalbuddy.localhost:41737/<slug>/`. In short: start the local board before filling the task list so the board pops up right away and cards populate live as `state.yaml` changes. Include the printed board URL in the final prep response as an actual clickable Markdown link, for example `[Open GoalBuddy board](http://goalbuddy.localhost:41737/<slug>/)`. Do not put the board URL only in a code block, quote, HTML comment, or prose that the UI cannot click.
|
|
97
97
|
|
|
98
|
+
If `http://goalbuddy.localhost:41737/<slug>/` returns 404, do not assume the existing process is stale and do not stop it. First check `http://127.0.0.1:41737/api/boards`. If that endpoint returns board JSON, the port is the shared multi-board hub; rerun `npx goalbuddy board docs/goals/<slug>` with the absolute goal path if needed so the new goal registers on the same port. Only stop a specific process on 41737 when `/api/boards` is missing, returns 404, or otherwise proves the listener is not a current GoalBuddy multi-board hub.
|
|
99
|
+
|
|
98
100
|
If the user wants an external board, GitHub sync, Slack digest, Linear handoff, or any other custom integration, do not install a GoalBuddy catalog item. Treat it as normal implementation work: create a concrete task that designs and verifies that integration inside the target repo or asks the operator for the required credentials and scope.
|
|
99
101
|
|
|
100
102
|
Ask before board creation when the request is vague, strategic, improvement-oriented, or open-ended and the user has not explicitly said to use defaults. Ask one guided question at a time with 2-3 options and a recommended default, then wait. Continue the diagnostic intake until the user's answers are sufficient to choose the board shape. Do not create or repair `docs/goals/<slug>/` until the diagnostic intake is complete or the user explicitly accepts defaults.
|
|
@@ -237,29 +237,43 @@ function receiptSchema(role) {
|
|
|
237
237
|
if (role === "worker") {
|
|
238
238
|
return {
|
|
239
239
|
result: "done | blocked",
|
|
240
|
+
task_id: "<T###>",
|
|
241
|
+
board_path: "<path to state.yaml>",
|
|
240
242
|
changed_files: [],
|
|
241
|
-
commands: [
|
|
242
|
-
summary: "<=120 words",
|
|
243
|
+
commands: [],
|
|
244
|
+
summary: "<=120 words>",
|
|
243
245
|
remaining_blockers: [],
|
|
246
|
+
verification_attempts: 1,
|
|
247
|
+
stopped_because: null,
|
|
244
248
|
};
|
|
245
249
|
}
|
|
246
250
|
if (role === "judge") {
|
|
247
251
|
return {
|
|
248
252
|
result: "done | blocked",
|
|
253
|
+
task_id: "<T###>",
|
|
254
|
+
board_path: "<path to state.yaml>",
|
|
249
255
|
decision: "approved | rejected | approve_subgoal | reject_subgoal | not_complete | complete",
|
|
250
256
|
full_outcome_complete: false,
|
|
257
|
+
rationale: "<=120 words>",
|
|
251
258
|
evidence: [],
|
|
259
|
+
subgoal_contract: null,
|
|
260
|
+
parallel_safety: null,
|
|
252
261
|
blocked_tasks: [],
|
|
262
|
+
missing_evidence: [],
|
|
253
263
|
required_board_updates: [],
|
|
254
264
|
};
|
|
255
265
|
}
|
|
256
266
|
return {
|
|
257
267
|
result: "done | blocked",
|
|
258
|
-
|
|
268
|
+
task_id: "<T###>",
|
|
269
|
+
board_path: "<path to state.yaml>",
|
|
270
|
+
summary: "<=120 words>",
|
|
259
271
|
evidence: [],
|
|
260
272
|
facts: [],
|
|
261
273
|
contradictions: [],
|
|
262
274
|
ambiguity_requiring_judge: [],
|
|
275
|
+
commands: [],
|
|
276
|
+
note_needed: false,
|
|
263
277
|
};
|
|
264
278
|
}
|
|
265
279
|
|
package/plugins/goalbuddy/skills/goalbuddy/surfaces/local-goal-board/scripts/local-goal-board.mjs
CHANGED
|
@@ -228,8 +228,7 @@ export async function startBoardServer(options = {}) {
|
|
|
228
228
|
|
|
229
229
|
const route = routeBoardRequest(url.pathname, boards, initialBoard);
|
|
230
230
|
if (!route.board) {
|
|
231
|
-
response.
|
|
232
|
-
response.end("Not found");
|
|
231
|
+
sendUnregisteredBoardPath(response, url.pathname, boards, baseUrl);
|
|
233
232
|
return;
|
|
234
233
|
}
|
|
235
234
|
if (route.pathname === "/api/board") {
|
|
@@ -400,6 +399,28 @@ function routeBoardRequest(pathname, boards, initialBoard) {
|
|
|
400
399
|
return matches[0] || { board: null, pathname };
|
|
401
400
|
}
|
|
402
401
|
|
|
402
|
+
function sendUnregisteredBoardPath(response, pathname, boards, baseUrl) {
|
|
403
|
+
response.writeHead(404, {
|
|
404
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
405
|
+
"Cache-Control": "no-store",
|
|
406
|
+
});
|
|
407
|
+
const registeredBoards = [...boards.values()].map((board) => {
|
|
408
|
+
const summary = boardSummary(board, baseUrl);
|
|
409
|
+
return `- ${summary.title}: ${summary.url}`;
|
|
410
|
+
});
|
|
411
|
+
response.end([
|
|
412
|
+
`GoalBuddy board path is not registered in this local hub: ${pathname}`,
|
|
413
|
+
"",
|
|
414
|
+
"This server is the GoalBuddy multi-board hub. Do not stop it just because a /<slug>/ board URL returned 404.",
|
|
415
|
+
"Start or rerun `npx goalbuddy board <goal-dir>` to register that goal on this same port, then open the printed /<slug>/ URL.",
|
|
416
|
+
"",
|
|
417
|
+
"Registered boards:",
|
|
418
|
+
registeredBoards.length ? registeredBoards.join("\n") : "- none",
|
|
419
|
+
"",
|
|
420
|
+
`Hub API: ${baseUrl}/api/boards`,
|
|
421
|
+
].join("\n"));
|
|
422
|
+
}
|
|
423
|
+
|
|
403
424
|
function stripBoardPathPrefix(pathname, boardPath) {
|
|
404
425
|
const prefix = boardPath.endsWith("/") ? boardPath.slice(0, -1) : boardPath;
|
|
405
426
|
if (pathname === prefix) return "/";
|
package/plugins/goalbuddy/skills/goalbuddy/surfaces/local-goal-board/test/local-goal-board.test.mjs
CHANGED
|
@@ -558,6 +558,33 @@ test("serves multiple local boards from one shared hub URL", async () => {
|
|
|
558
558
|
}
|
|
559
559
|
});
|
|
560
560
|
|
|
561
|
+
test("unregistered board paths explain hub reuse instead of stale-port cleanup", async () => {
|
|
562
|
+
const root = mkdtempSync(join(tmpdir(), "goalbuddy-local-board-unregistered-"));
|
|
563
|
+
const goalDir = join(root, "first-goal");
|
|
564
|
+
try {
|
|
565
|
+
mkdirSync(join(goalDir, "notes"), { recursive: true });
|
|
566
|
+
writeFileSync(join(goalDir, "state.yaml"), stateYaml("active", { title: "First Goal", slug: "first-goal" }));
|
|
567
|
+
|
|
568
|
+
const server = await startBoardServer({ goalDir, host: "127.0.0.1", port: 0 });
|
|
569
|
+
try {
|
|
570
|
+
const baseUrl = new URL(server.url).origin;
|
|
571
|
+
const missingResponse = await fetch(`${baseUrl}/rinova-client-revision-redesign/`);
|
|
572
|
+
assert.equal(missingResponse.status, 404);
|
|
573
|
+
const message = await missingResponse.text();
|
|
574
|
+
assert.match(message, /board path is not registered/i);
|
|
575
|
+
assert.match(message, /multi-board hub/i);
|
|
576
|
+
assert.match(message, /Do not stop it just because a \/<slug>\/ board URL returned 404/);
|
|
577
|
+
assert.match(message, /npx goalbuddy board <goal-dir>/);
|
|
578
|
+
assert.match(message, /First Goal/);
|
|
579
|
+
assert.match(message, /\/api\/boards/);
|
|
580
|
+
} finally {
|
|
581
|
+
await server.close();
|
|
582
|
+
}
|
|
583
|
+
} finally {
|
|
584
|
+
rmSync(root, { recursive: true, force: true });
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
|
|
561
588
|
async function readUntil(reader, pattern) {
|
|
562
589
|
const decoder = new TextDecoder();
|
|
563
590
|
let text = "";
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# Improve Goal Maker
|
|
2
|
-
|
|
3
|
-
## Objective
|
|
4
|
-
|
|
5
|
-
Improve the local Goal Maker skill by discovering the highest-leverage repo-backed changes, completing the first safe implementation tranche, and leaving a reviewable handoff for anything larger.
|
|
6
|
-
|
|
7
|
-
## Goal Kind
|
|
8
|
-
|
|
9
|
-
`open_ended`
|
|
10
|
-
|
|
11
|
-
## Current Tranche
|
|
12
|
-
|
|
13
|
-
Map the current Goal Maker implementation, identify concrete improvement candidates with verification paths, select one small and reversible tranche, implement it, verify it, and finish with an audit receipt.
|
|
14
|
-
|
|
15
|
-
## Non-Negotiable Constraints
|
|
16
|
-
|
|
17
|
-
- Keep Goal Maker v2 control-file semantics intact: `goal.md`, `state.yaml`, and `notes/` only at each goal root.
|
|
18
|
-
- Do not implement from assumptions; Scout evidence or Judge selection must precede Worker edits.
|
|
19
|
-
- Preserve compatibility with Codex agent roles and the existing checker unless a Judge task explicitly chooses otherwise.
|
|
20
|
-
- Keep changes small, reviewable, and backed by local verification commands.
|
|
21
|
-
- Do not use destructive git operations or require credentials.
|
|
22
|
-
|
|
23
|
-
## Stop Rule
|
|
24
|
-
|
|
25
|
-
Stop when the tranche audit passes, all safe local work is blocked, or continuing would require owner input, credentials, destructive operations, or strategy the board cannot decide.
|
|
26
|
-
|
|
27
|
-
## Canonical Board
|
|
28
|
-
|
|
29
|
-
Machine truth lives at:
|
|
30
|
-
|
|
31
|
-
`examples/improve-goal-maker/state.yaml`
|
|
32
|
-
|
|
33
|
-
If this charter and `state.yaml` disagree, `state.yaml` wins for task status, active task, receipts, verification freshness, and completion truth.
|
|
34
|
-
|
|
35
|
-
## Run Command
|
|
36
|
-
|
|
37
|
-
```text
|
|
38
|
-
/goal Follow examples/improve-goal-maker/goal.md
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## PM Loop
|
|
42
|
-
|
|
43
|
-
On every `/goal` continuation:
|
|
44
|
-
|
|
45
|
-
1. Read this charter.
|
|
46
|
-
2. Read `state.yaml`.
|
|
47
|
-
3. Work only on the active board task.
|
|
48
|
-
4. Assign Scout, Judge, Worker, or PM according to the task.
|
|
49
|
-
5. Write a compact task receipt.
|
|
50
|
-
6. Update the board.
|
|
51
|
-
7. Select the next active task or finish with a Judge/PM audit receipt.
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# T001: Goal Maker Repo Map
|
|
2
|
-
|
|
3
|
-
Task: `T001`
|
|
4
|
-
Kind: `scout`
|
|
5
|
-
Status: `current`
|
|
6
|
-
|
|
7
|
-
## Summary
|
|
8
|
-
|
|
9
|
-
Goal Maker is a small dependency-free npm package that installs a Codex skill, three agent role definitions, templates, and a v2 board checker. The core verification path is healthy: syntax checks and all current checker tests pass, a temporary install plus doctor run succeeds, and the package dry run includes the expected runtime files. The highest-leverage next investigation is not packaging correctness; it is workflow reliability around checker blind spots, agent-installation assumptions, and template/skill contract drift.
|
|
10
|
-
|
|
11
|
-
## Repo Map
|
|
12
|
-
|
|
13
|
-
- `README.md` describes the package purpose, install commands, board model, repository layout, and status caveats.
|
|
14
|
-
- `CONTRIBUTING.md` defines the local verification contract: `npm run check`, temporary install/doctor, and `npm pack --dry-run`.
|
|
15
|
-
- `package.json` exposes the `goal-maker` bin, keeps runtime dependency-free, packages the skill runtime plus docs/assets, and defines `check`, `test`, and `pack:dry-run`.
|
|
16
|
-
- `goal-maker/SKILL.md` is the installed skill contract: direct `$goal-maker` creates or repairs a v2 board, stops for user choice, and `/goal` owns the PM loop.
|
|
17
|
-
- `goal-maker/bin/goal-maker.mjs` installs the skill and bundled `goal_*.toml` agents into a Codex home and supports `install`, `update`, `agents`, `doctor`, and help.
|
|
18
|
-
- `goal-maker/scripts/check-goal-state.mjs` validates v2 board shape with a lightweight line-oriented parser.
|
|
19
|
-
- `goal-maker/scripts/install-agents.mjs` is a secondary agent copy helper.
|
|
20
|
-
- `goal-maker/templates/goal.md`, `state.yaml`, and `note.md` seed new goal control files.
|
|
21
|
-
- `goal-maker/agents/goal_scout.toml`, `goal_worker.toml`, and `goal_judge.toml` define the intended role constraints.
|
|
22
|
-
- `goal-maker/test/check-goal-state.test.mjs` covers key checker invariants.
|
|
23
|
-
|
|
24
|
-
## Verification Commands
|
|
25
|
-
|
|
26
|
-
- `npm run check` passed: Node syntax checks and 7 node:test cases passed.
|
|
27
|
-
- Temporary install plus doctor passed: `skill_installed: true`; installed agents were `goal_judge.toml`, `goal_scout.toml`, and `goal_worker.toml`.
|
|
28
|
-
- `npm pack --dry-run` passed and listed the expected package files, including `README.md`, `CONTRIBUTING.md`, `internal/assets/`, `package.json`, and `goal-maker/` runtime files.
|
|
29
|
-
- `node goal-maker/scripts/check-goal-state.mjs examples/improve-goal-maker/state.yaml` passed with `ok: true`.
|
|
30
|
-
|
|
31
|
-
## Health Signals
|
|
32
|
-
|
|
33
|
-
- Current tests cover a valid Scout board, active Worker requirements, legacy v1 rejection, one-active-task enforcement, done-task receipt requirements, unexpected root entries, and final audit completion.
|
|
34
|
-
- The checker intentionally avoids dependencies, but it parses YAML with regular expressions and line indentation. This is acceptable for the template subset but creates likely blind spots for richer valid YAML or misleading strings inside quoted values.
|
|
35
|
-
- The package has two agent-installation paths: the CLI in `goal-maker/bin/goal-maker.mjs` and the helper in `goal-maker/scripts/install-agents.mjs`. They are similar but not identical.
|
|
36
|
-
- The direct skill contract says `$goal-maker` must verify Scout, Worker, and Judge agents are installed or explain what is missing; current templates mark them `installed`, while actual verification is behavior implemented by the PM thread, not the CLI/checker.
|
|
37
|
-
|
|
38
|
-
## Ranked Improvement Candidates
|
|
39
|
-
|
|
40
|
-
1. Add checker tests and fixes for root-path robustness and YAML parsing edge cases, especially misleading legacy markers inside quoted values or comments.
|
|
41
|
-
2. Reconcile or document the two agent-installation paths so CLI behavior, helper behavior, and README guidance cannot drift.
|
|
42
|
-
3. Make agent installation status less assumption-driven in generated boards or add checker warnings when agent status values are placeholders.
|
|
43
|
-
4. Add tests for the CLI install/doctor/package path so the contributor verification contract is covered by `npm run check`, not just manual commands.
|
|
44
|
-
5. Tighten docs/templates around what `/goal` PM may do in Codex environments where actual subagent dispatch is unavailable and roles are simulated in-thread.
|
|
45
|
-
|
|
46
|
-
## Candidate Next Tasks
|
|
47
|
-
|
|
48
|
-
- Investigate checker blind spots with focused failing test candidates before choosing a fix.
|
|
49
|
-
- Compare `goal-maker/bin/goal-maker.mjs` and `goal-maker/scripts/install-agents.mjs` and decide whether to consolidate, test, or document the helper.
|
|
50
|
-
- Judge the first implementation tranche after the risk map, preferring a small test-backed checker or CLI reliability fix.
|
|
51
|
-
|
|
52
|
-
## Board Receipt Snippet
|
|
53
|
-
|
|
54
|
-
```yaml
|
|
55
|
-
receipt:
|
|
56
|
-
result: done
|
|
57
|
-
note: notes/T001-repo-map.md
|
|
58
|
-
summary: "Repo map completed; verification is green, install/package checks work, and the top improvement area is Goal Maker workflow reliability around checker blind spots, agent installation assumptions, and template/skill drift."
|
|
59
|
-
```
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# T002: Workflow Risk Map
|
|
2
|
-
|
|
3
|
-
Task: `T002`
|
|
4
|
-
Kind: `scout`
|
|
5
|
-
Status: `current`
|
|
6
|
-
|
|
7
|
-
## Summary
|
|
8
|
-
|
|
9
|
-
The highest-confidence risks are small and testable. First, `goal-maker doctor` reports success when one of the required bundled agents is missing, which weakens the "verify Scout, Worker, and Judge agents" contract. Second, the board checker accepts mismatched task `type` and `assignee` values, even though Goal Maker semantics depend on those fields agreeing. A lower-priority drift risk is that `update` does not overwrite stale existing agent definitions unless `--force` is supplied, despite README wording that says install or update the skill and bundled agents.
|
|
10
|
-
|
|
11
|
-
## Evidence
|
|
12
|
-
|
|
13
|
-
- `goal-maker/bin/goal-maker.mjs` `doctor()` exits based only on whether `skills/goal-maker/SKILL.md` exists. In a temp Codex home with `goal_worker.toml` removed, `doctor` exited 0 and reported only `goal_judge.toml` and `goal_scout.toml`.
|
|
14
|
-
- `goal-maker/scripts/check-goal-state.mjs` independently validates `task.type` and `task.assignee`, but does not validate pairs. A temp board with `type: scout` and `assignee: Worker` passed with `ok: true`.
|
|
15
|
-
- `goal-maker/scripts/check-goal-state.mjs` does not require or validate the `agents:` section. A temp board with the section omitted passed with `ok: true`.
|
|
16
|
-
- `goal-maker/bin/goal-maker.mjs` `installAgents()` skips existing `goal_*.toml` files unless `--force` is present. In a temp Codex home with a stale `goal_scout.toml`, `goal-maker update` left the stale file unchanged.
|
|
17
|
-
- `goal-maker/scripts/install-agents.mjs` copies any `.toml` except names containing `config-snippet`, while the CLI copies only `goal_*.toml`. Current files make this harmless, but it is a drift point.
|
|
18
|
-
|
|
19
|
-
## Small Safe Implementation Candidates
|
|
20
|
-
|
|
21
|
-
1. Add CLI tests for `doctor` and make `doctor` fail when any required agent file is missing. Allowed files would likely be `goal-maker/bin/goal-maker.mjs` and a new focused CLI test file.
|
|
22
|
-
2. Add checker tests for task type/assignee mismatch and fix the checker to reject mismatches. Allowed files would be `goal-maker/scripts/check-goal-state.mjs` and `goal-maker/test/check-goal-state.test.mjs`.
|
|
23
|
-
3. Add checker tests for missing or invalid `agents:` statuses and reject or warn. This is slightly more policy-heavy because templates currently treat agent status as PM-populated board metadata.
|
|
24
|
-
4. Decide whether `update` should overwrite existing bundled agents or whether README/CLI wording should clarify that `--force` is required for agent refresh.
|
|
25
|
-
|
|
26
|
-
## Recommended First Tranche
|
|
27
|
-
|
|
28
|
-
Prefer the `doctor` missing-agent fix. It directly supports the `$goal-maker` requirement to verify Scout, Worker, and Judge availability, has tight behavior, and can be verified with a new CLI test plus existing checks. The checker mismatch fix is also small, but it is less directly connected to the user-visible setup flow.
|
|
29
|
-
|
|
30
|
-
## Board Receipt Snippet
|
|
31
|
-
|
|
32
|
-
```yaml
|
|
33
|
-
receipt:
|
|
34
|
-
result: done
|
|
35
|
-
note: notes/T002-risk-map.md
|
|
36
|
-
summary: "Risk map completed; doctor exits green with missing agents, checker accepts type/assignee mismatches, and update leaves stale agents unless forced."
|
|
37
|
-
```
|