goalbuddy 0.3.6 → 0.3.7

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.
Files changed (73) hide show
  1. package/README.md +17 -8
  2. package/RELEASE-0.3.5.md +4 -4
  3. package/RELEASE-0.3.7.md +127 -0
  4. package/goalbuddy/SKILL.md +19 -10
  5. package/goalbuddy/scripts/check-goal-state.mjs +53 -0
  6. package/goalbuddy/scripts/render-task-prompt.mjs +22 -1
  7. package/{plugins/goalbuddy/skills/goalbuddy/extend → goalbuddy/surfaces}/local-goal-board/README.md +7 -9
  8. package/{plugins/goalbuddy/skills/goalbuddy/extend → goalbuddy/surfaces}/local-goal-board/examples/sample-goal/state.yaml +5 -5
  9. package/{plugins/goalbuddy/skills/goalbuddy/extend → goalbuddy/surfaces}/local-goal-board/examples/subgoal-parent/state.yaml +3 -3
  10. package/goalbuddy/{extend → surfaces}/local-goal-board/examples/subgoal-parent/subgoals/T004-board-view/state.yaml +3 -3
  11. package/goalbuddy/{extend → surfaces}/local-goal-board/scripts/lib/goal-board.mjs +2 -2
  12. package/goalbuddy/{extend → surfaces}/local-goal-board/scripts/local-goal-board.mjs +4 -4
  13. package/goalbuddy/{extend → surfaces}/local-goal-board/test/local-goal-board.test.mjs +8 -8
  14. package/goalbuddy/templates/goal.md +9 -0
  15. package/goalbuddy/templates/state.yaml +7 -6
  16. package/internal/assets/goalbuddy-v0.3.7-release.png +0 -0
  17. package/internal/cli/goal-maker.mjs +9 -711
  18. package/package.json +4 -4
  19. package/plugins/goalbuddy/.claude-plugin/plugin.json +3 -4
  20. package/plugins/goalbuddy/.codex-plugin/plugin.json +5 -6
  21. package/plugins/goalbuddy/README.md +4 -3
  22. package/plugins/goalbuddy/skills/goalbuddy/SKILL.md +19 -10
  23. package/plugins/goalbuddy/skills/goalbuddy/scripts/check-goal-state.mjs +53 -0
  24. package/plugins/goalbuddy/skills/goalbuddy/scripts/render-task-prompt.mjs +22 -1
  25. package/{goalbuddy/extend → plugins/goalbuddy/skills/goalbuddy/surfaces}/local-goal-board/README.md +7 -9
  26. package/{goalbuddy/extend → plugins/goalbuddy/skills/goalbuddy/surfaces}/local-goal-board/examples/sample-goal/state.yaml +5 -5
  27. package/{goalbuddy/extend → plugins/goalbuddy/skills/goalbuddy/surfaces}/local-goal-board/examples/subgoal-parent/state.yaml +3 -3
  28. package/plugins/goalbuddy/skills/goalbuddy/{extend → surfaces}/local-goal-board/examples/subgoal-parent/subgoals/T004-board-view/state.yaml +3 -3
  29. package/plugins/goalbuddy/skills/goalbuddy/{extend → surfaces}/local-goal-board/scripts/lib/goal-board.mjs +2 -2
  30. package/plugins/goalbuddy/skills/goalbuddy/{extend → surfaces}/local-goal-board/scripts/local-goal-board.mjs +4 -4
  31. package/plugins/goalbuddy/skills/goalbuddy/{extend → surfaces}/local-goal-board/test/local-goal-board.test.mjs +8 -8
  32. package/plugins/goalbuddy/skills/goalbuddy/templates/goal.md +9 -0
  33. package/plugins/goalbuddy/skills/goalbuddy/templates/state.yaml +7 -6
  34. package/examples/extend-catalog-workflow/goal.md +0 -53
  35. package/examples/extend-catalog-workflow/notes/T001-extension-model-map.md +0 -47
  36. package/examples/extend-catalog-workflow/notes/T002-architecture-decision.md +0 -48
  37. package/examples/extend-catalog-workflow/notes/T003-implementation-summary.md +0 -43
  38. package/examples/extend-catalog-workflow/notes/T004-root-extend-folder.md +0 -24
  39. package/examples/extend-catalog-workflow/notes/T005-layout-cleanup.md +0 -46
  40. package/examples/extend-catalog-workflow/notes/T006-catalog-location.md +0 -50
  41. package/examples/extend-catalog-workflow/notes/T999-completion-audit.md +0 -36
  42. package/examples/extend-catalog-workflow/state.yaml +0 -327
  43. package/examples/github-pr-workflow-extension/pr-handoff.md +0 -46
  44. package/goalbuddy/extend/github-projects/README.md +0 -105
  45. package/goalbuddy/extend/github-projects/examples/goal-board-sync/state.yaml +0 -63
  46. package/goalbuddy/extend/github-projects/extension.yaml +0 -43
  47. package/goalbuddy/extend/github-projects/scripts/lib/github-projects.mjs +0 -728
  48. package/goalbuddy/extend/github-projects/scripts/lib/goal-state.mjs +0 -362
  49. package/goalbuddy/extend/github-projects/scripts/sync-github-project.mjs +0 -193
  50. package/goalbuddy/extend/github-projects/test/github-projects.test.mjs +0 -267
  51. package/goalbuddy/extend/local-goal-board/extension.yaml +0 -39
  52. package/internal/assets/extend-release.png +0 -0
  53. package/internal/assets/extend-release.svg +0 -83
  54. package/plugins/goalbuddy/skills/goalbuddy/extend/github-projects/README.md +0 -105
  55. package/plugins/goalbuddy/skills/goalbuddy/extend/github-projects/examples/goal-board-sync/state.yaml +0 -63
  56. package/plugins/goalbuddy/skills/goalbuddy/extend/github-projects/extension.yaml +0 -43
  57. package/plugins/goalbuddy/skills/goalbuddy/extend/github-projects/scripts/lib/github-projects.mjs +0 -728
  58. package/plugins/goalbuddy/skills/goalbuddy/extend/github-projects/scripts/lib/goal-state.mjs +0 -362
  59. package/plugins/goalbuddy/skills/goalbuddy/extend/github-projects/scripts/sync-github-project.mjs +0 -193
  60. package/plugins/goalbuddy/skills/goalbuddy/extend/github-projects/test/github-projects.test.mjs +0 -267
  61. package/plugins/goalbuddy/skills/goalbuddy/extend/local-goal-board/extension.yaml +0 -39
  62. /package/goalbuddy/{extend → surfaces}/local-goal-board/assets/goalbuddy-mark.png +0 -0
  63. /package/goalbuddy/{extend → surfaces}/local-goal-board/examples/sample-goal/notes/T001-scout.md +0 -0
  64. /package/goalbuddy/{extend → surfaces}/local-goal-board/examples/subgoal-parent/goal.md +0 -0
  65. /package/goalbuddy/{extend → surfaces}/local-goal-board/examples/subgoal-parent/notes/.gitkeep +0 -0
  66. /package/goalbuddy/{extend → surfaces}/local-goal-board/examples/subgoal-parent/subgoals/T004-board-view/goal.md +0 -0
  67. /package/goalbuddy/{extend → surfaces}/local-goal-board/examples/subgoal-parent/subgoals/T004-board-view/notes/.gitkeep +0 -0
  68. /package/plugins/goalbuddy/skills/goalbuddy/{extend → surfaces}/local-goal-board/assets/goalbuddy-mark.png +0 -0
  69. /package/plugins/goalbuddy/skills/goalbuddy/{extend → surfaces}/local-goal-board/examples/sample-goal/notes/T001-scout.md +0 -0
  70. /package/plugins/goalbuddy/skills/goalbuddy/{extend → surfaces}/local-goal-board/examples/subgoal-parent/goal.md +0 -0
  71. /package/plugins/goalbuddy/skills/goalbuddy/{extend → surfaces}/local-goal-board/examples/subgoal-parent/notes/.gitkeep +0 -0
  72. /package/plugins/goalbuddy/skills/goalbuddy/{extend → surfaces}/local-goal-board/examples/subgoal-parent/subgoals/T004-board-view/goal.md +0 -0
  73. /package/plugins/goalbuddy/skills/goalbuddy/{extend → surfaces}/local-goal-board/examples/subgoal-parent/subgoals/T004-board-view/notes/.gitkeep +0 -0
@@ -1,267 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { readFile } from "node:fs/promises";
4
- import { resolve } from "node:path";
5
- import { GoalStateError, normalizeGoalBoard, parseGoalStateText } from "../scripts/lib/goal-state.mjs";
6
- import {
7
- GITHUB_PROJECT_FIELDS,
8
- GITHUB_PROJECT_VIEWS,
9
- agentLaneForTask,
10
- buildDraftIssueBody,
11
- buildFieldUpdates,
12
- ensureGoalProjectViews,
13
- planGitHubProjectSync,
14
- priorityForTask,
15
- projectStatusForTask,
16
- workTypeForTask,
17
- } from "../scripts/lib/github-projects.mjs";
18
-
19
- describe("goal state parsing", () => {
20
- it("normalizes a GoalBuddy v2 board", async () => {
21
- const text = await readFile(resolve("extend/github-projects/examples/goal-board-sync/state.yaml"), "utf8");
22
- const board = normalizeGoalBoard(parseGoalStateText(text));
23
-
24
- assert.equal(board.title, "Goal board sync MVP");
25
- assert.equal(board.activeTask, "T002");
26
- assert.equal(board.tasks.length, 3);
27
- assert.equal(board.tasks[0].title, "Map external board API requirements");
28
- assert.equal(board.tasks[1].priority, "P1");
29
- assert.equal(board.tasks[0].receiptSummary, "The board sync can read GoalBuddy state.yaml and mirror tasks into an external board.");
30
- assert.equal(board.tasks[0].goalRole, "Scout");
31
- assert.equal(board.tasks[1].agentResponsible, "Worker");
32
- assert.equal(board.tasks[1].credentialGate, "Credentials");
33
- assert.equal(board.tasks[1].parentId, "T001");
34
- assert.deepEqual(board.tasks[1].dependsOn, ["T001"]);
35
- assert.deepEqual(board.tasks[1].verify, [
36
- "node --test extend/github-projects/test/*.test.mjs",
37
- "node extend/github-projects/scripts/sync-github-project.mjs --state extend/github-projects/examples/goal-board-sync/state.yaml --dry-run",
38
- ]);
39
- });
40
-
41
- it("rejects malformed task status", () => {
42
- const parsed = parseGoalStateText(`
43
- version: 2
44
- goal:
45
- title: "Bad board"
46
- slug: "bad-board"
47
- tasks:
48
- - id: T001
49
- status: moving
50
- objective: "Bad status"
51
- `);
52
-
53
- assert.throws(() => normalizeGoalBoard(parsed), GoalStateError);
54
- });
55
- });
56
-
57
- describe("GitHub Projects mapping", () => {
58
- it("plans GitHub draft issue creates and updates by task id", () => {
59
- const tasks = [
60
- {
61
- id: "T001",
62
- title: "T001: Existing",
63
- objective: "Existing",
64
- status: "done",
65
- type: "scout",
66
- assignee: "Scout",
67
- receiptSummary: "Done",
68
- verify: [],
69
- allowedFiles: [],
70
- updatedLabel: "receipt:done",
71
- },
72
- {
73
- id: "T002",
74
- title: "T002: New",
75
- objective: "New",
76
- status: "queued",
77
- type: "worker",
78
- assignee: "Worker",
79
- receiptSummary: "",
80
- verify: [],
81
- allowedFiles: [],
82
- updatedLabel: "receipt:none",
83
- },
84
- ];
85
-
86
- const operations = planGitHubProjectSync(tasks, [
87
- {
88
- id: "PVTI_existing",
89
- taskId: { text: "T001" },
90
- content: { __typename: "DraftIssue", id: "DI_existing" },
91
- },
92
- ]);
93
-
94
- assert.equal(operations[0].type, "update");
95
- assert.equal(operations[0].itemId, "PVTI_existing");
96
- assert.equal(operations[0].draftIssueId, "DI_existing");
97
- assert.equal(operations[1].type, "create");
98
- });
99
-
100
- it("builds GitHub field updates for text and single-select fields", () => {
101
- const fields = {
102
- taskId: { id: "F_task" },
103
- status: { id: "F_status", name: GITHUB_PROJECT_FIELDS.status, options: [{ id: "O_progress", name: "In Progress" }] },
104
- priority: { id: "F_priority", name: GITHUB_PROJECT_FIELDS.priority, options: [{ id: "O_p1", name: "P1" }] },
105
- workType: { id: "F_type", name: GITHUB_PROJECT_FIELDS.workType, options: [{ id: "O_execution", name: "Execution" }] },
106
- agentLane: { id: "F_lane", name: GITHUB_PROJECT_FIELDS.agentLane, options: [{ id: "O_worker", name: "Worker" }] },
107
- owner: { id: "F_owner" },
108
- goalRole: { id: "F_goal_role" },
109
- agentResponsible: { id: "F_agent" },
110
- credentialGate: { id: "F_gate" },
111
- parentId: { id: "F_parent" },
112
- dependsOn: { id: "F_depends" },
113
- receiptSummary: { id: "F_receipt" },
114
- verify: { id: "F_verify" },
115
- allowedFiles: { id: "F_allowed" },
116
- updated: { id: "F_updated" },
117
- };
118
- const task = {
119
- id: "T002",
120
- status: "active",
121
- type: "worker",
122
- assignee: "Worker",
123
- goalRole: "Worker",
124
- agentResponsible: "Worker",
125
- credentialGate: "None",
126
- receiptSummary: "",
127
- verify: ["npm test"],
128
- allowedFiles: ["scripts/**"],
129
- parentId: "T001",
130
- dependsOn: ["T001"],
131
- updatedLabel: "receipt:none",
132
- };
133
-
134
- const updates = buildFieldUpdates(task, fields);
135
- assert.equal(updates.length, 15);
136
- assert.deepEqual(updates.find((update) => update.fieldId === "F_status").value, {
137
- singleSelectOptionId: "O_progress",
138
- });
139
- assert.deepEqual(updates.find((update) => update.fieldId === "F_task").value, {
140
- text: "T002",
141
- });
142
- assert.deepEqual(updates.find((update) => update.fieldId === "F_lane").value, {
143
- singleSelectOptionId: "O_worker",
144
- });
145
- assert.deepEqual(updates.find((update) => update.fieldId === "F_goal_role").value, { text: "Worker" });
146
- assert.deepEqual(updates.find((update) => update.fieldId === "F_agent").value, { text: "Worker" });
147
- assert.deepEqual(updates.find((update) => update.fieldId === "F_gate").value, { text: "None" });
148
- assert.deepEqual(updates.find((update) => update.fieldId === "F_parent").value, { text: "T001" });
149
- assert.deepEqual(updates.find((update) => update.fieldId === "F_depends").value, { text: "T001" });
150
- });
151
-
152
- it("builds a draft issue body that points back to the state file", () => {
153
- const body = buildDraftIssueBody(
154
- {
155
- id: "T002",
156
- objective: "Run sync.",
157
- status: "active",
158
- type: "worker",
159
- assignee: "Worker",
160
- receiptSummary: "",
161
- verify: ["npm test"],
162
- allowedFiles: ["scripts/**"],
163
- parentId: "T001",
164
- dependsOn: ["T001"],
165
- },
166
- {
167
- sourcePath: "extend/github-projects/examples/goal-board-sync/state.yaml",
168
- }
169
- );
170
-
171
- assert.match(body, /YAML remains the source of truth/);
172
- assert.match(body, /Task ID: T002/);
173
- assert.match(body, /Parent: T001/);
174
- assert.match(body, /Depends on:/);
175
- assert.match(body, /extend\/github-projects\/examples\/goal-board-sync\/state\.yaml/);
176
- });
177
-
178
- it("creates only the Goal Board GitHub Project view with visible fields when missing", async () => {
179
- const restCalls = [];
180
- const views = await ensureGoalProjectViews({
181
- client: {
182
- rest: async (path, options) => {
183
- restCalls.push({ path, options });
184
- return {
185
- html_url: "https://github.com/users/example/projects/1/views/2",
186
- };
187
- },
188
- },
189
- project: {
190
- number: 1,
191
- owner: { __typename: "User", login: "example" },
192
- views: { nodes: [] },
193
- },
194
- fields: {
195
- taskId: { databaseId: 1 },
196
- status: { databaseId: 2 },
197
- priority: { databaseId: 3 },
198
- workType: { databaseId: 4 },
199
- owner: { databaseId: 5 },
200
- agentLane: { databaseId: 13 },
201
- goalRole: { databaseId: 10 },
202
- agentResponsible: { databaseId: 11 },
203
- credentialGate: { databaseId: 12 },
204
- receiptSummary: { databaseId: 6 },
205
- verify: { databaseId: 7 },
206
- allowedFiles: { databaseId: 8 },
207
- updated: { databaseId: 9 },
208
- },
209
- });
210
-
211
- assert.equal(views.board.html_url, "https://github.com/users/example/projects/1/views/2");
212
- assert.equal(views.agentWorkboard, undefined);
213
- assert.equal(restCalls.length, 1);
214
- assert.equal(restCalls[0].path, "users/example/projectsV2/1/views");
215
- assert.equal(restCalls[0].options.body.layout, "board");
216
- assert.equal(restCalls[0].options.body.name, GITHUB_PROJECT_VIEWS.board.name);
217
- assert.deepEqual(restCalls[0].options.body.visible_fields, [3, 2, 4, 5, 10, 11, 13, 12]);
218
- assert.equal(restCalls[0].options.body.group_by, undefined);
219
- });
220
-
221
- it("reuses an existing Goal Board view without creating extra views", async () => {
222
- const views = await ensureGoalProjectViews({
223
- client: {
224
- rest: async () => {
225
- throw new Error("REST should not be called for existing views.");
226
- },
227
- },
228
- project: {
229
- number: 1,
230
- owner: { __typename: "User", login: "example" },
231
- views: {
232
- nodes: [
233
- { id: "PVTV_board", name: "Goal Board", layout: "BOARD_LAYOUT" },
234
- { id: "PVTV_agent", name: "Agent Workboard", layout: "BOARD_LAYOUT" },
235
- ],
236
- },
237
- },
238
- fields: {},
239
- });
240
-
241
- assert.equal(views.board.id, "PVTV_board");
242
- assert.equal(views.agentWorkboard, undefined);
243
- });
244
-
245
- it("maps GoalBuddy task statuses to native GitHub board statuses", () => {
246
- assert.equal(projectStatusForTask("queued"), "Todo");
247
- assert.equal(projectStatusForTask("active"), "In Progress");
248
- assert.equal(projectStatusForTask("blocked"), "Blocked");
249
- assert.equal(projectStatusForTask("done"), "Done");
250
- });
251
-
252
- it("maps GoalBuddy task types and priorities to lean PM fields", () => {
253
- assert.equal(workTypeForTask("scout"), "Discovery");
254
- assert.equal(workTypeForTask("judge"), "Decision");
255
- assert.equal(workTypeForTask("worker"), "Execution");
256
- assert.equal(priorityForTask({ status: "blocked", type: "worker" }), "P0");
257
- assert.equal(priorityForTask({ status: "active", type: "worker" }), "P1");
258
- assert.equal(priorityForTask({ status: "queued", type: "scout" }), "P2");
259
- assert.equal(priorityForTask({ status: "done", type: "worker" }), "P3");
260
- assert.equal(priorityForTask({ status: "queued", type: "scout", priority: "P0" }), "P0");
261
- assert.equal(agentLaneForTask({ assignee: "PM" }), "PM");
262
- assert.equal(agentLaneForTask({ agentResponsible: "Scout" }), "Scout");
263
- assert.equal(agentLaneForTask({ goalRole: "Judge" }), "Judge");
264
- assert.equal(agentLaneForTask({ agentResponsible: "Worker" }), "Worker");
265
- assert.equal(agentLaneForTask({ agentResponsible: "User" }), "User");
266
- });
267
- });
@@ -1,39 +0,0 @@
1
- id: local-goal-board
2
- name: Local Goal Board
3
- kind: visualization
4
- version: 0.2.0
5
- source_of_truth: local
6
- description: Generate and serve a GoalBuddy-branded local Kanban board that updates live from a goal directory's state.yaml, notes, and linked depth-1 sub-goals.
7
- local_use_prompt: Run the bundled local board script for docs/goals/<slug>. It writes a tiny web app into docs/goals/<slug>/.goalbuddy-board and serves it through the shared goalbuddy.localhost:41737 local board hub with live SSE updates from state.yaml, notes, and linked depth-1 sub-goals. Multiple active boards are available from the board header switcher. Use --once --json for generation checks and no long-running server.
8
- use_when:
9
- - A human wants to watch a GoalBuddy run in a local board UI.
10
- - The goal needs GitHub-Projects-like visibility without GitHub credentials.
11
- - state.yaml and notes should remain the source of truth while the browser updates live.
12
- - A parent task links a depth-1 child board under subgoals/.
13
- activation: user_requested
14
- outputs:
15
- - Local GoalBuddy board web app
16
- - Live state.yaml and notes viewer
17
- requires_approval: false
18
- safe_by_default: true
19
- reads:
20
- - docs/goals/<slug>/state.yaml
21
- - docs/goals/<slug>/notes
22
- - docs/goals/<slug>/subgoals
23
- writes:
24
- - docs/goals/<slug>/.goalbuddy-board
25
- side_effects:
26
- - Starts or reuses the shared local-only HTTP board hub when not run with --once.
27
- agent_instructions:
28
- - Use the bundled local-goal-board script; do not build a static-only mockup.
29
- - Keep state.yaml authoritative; the app is only a viewer.
30
- - Require live no-manual-reload updates through the local server event stream.
31
- - Keep the generated UI simple, clean, minimal, and GoalBuddy branded.
32
- auth:
33
- env: []
34
- supports:
35
- dry_run: true
36
- live_local_board: true
37
- live_updates: true
38
- credentials_required: false
39
- generated_goal_artifact: true