@selfagency/beans-mcp 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,11 +18,12 @@ npx @selfagency/beans-mcp /path/to/workspace
18
18
 
19
19
  ### Versioning
20
20
 
21
- `@selfagency/beans-mcp` tracks upstream [Beans](https://github.com/hmans/beans) versions.
22
- For example, Beans `v0.4.2` maps to `@selfagency/beans-mcp@0.4.2`.
21
+ `@selfagency/beans-mcp` has its own package versioning. Compatibility with the
22
+ [Beans](https://github.com/hmans/beans) CLI is tracked separately.
23
23
 
24
- At startup, the server compares its own package version against the installed `beans`
25
- CLI version. If they differ, it prints a warning to stderr and continues startup.
24
+ At startup, the server compares the installed `beans` CLI version against the
25
+ hardcoded supported Beans version: `0.4.2`. If they differ, it prints a warning
26
+ to stderr and continues startup.
26
27
 
27
28
  ### Parameters
28
29
 
@@ -31549,7 +31549,7 @@ var import_node_util2 = require("util");
31549
31549
  // package.json
31550
31550
  var package_default = {
31551
31551
  name: "@selfagency/beans-mcp",
31552
- version: "0.6.0",
31552
+ version: "0.6.2",
31553
31553
  private: false,
31554
31554
  description: "MCP (Model Context Protocol) server for Beans issue tracker",
31555
31555
  keywords: [
@@ -31595,18 +31595,18 @@ var package_default = {
31595
31595
  },
31596
31596
  scripts: {
31597
31597
  build: "tsup",
31598
- "docs:dev": "vitepress dev docs",
31598
+ postbuild: "node ./scripts/write-dist-package.js",
31599
31599
  "docs:build": "vitepress build docs",
31600
+ "docs:dev": "vitepress dev docs",
31600
31601
  "docs:preview": "vitepress preview docs",
31601
31602
  format: "oxfmt",
31602
- "lint:fix": "oxlint --fix",
31603
31603
  lint: "oxlint",
31604
- postbuild: "node ./scripts/write-dist-package.js",
31604
+ "lint:fix": "oxlint --fix",
31605
31605
  prepare: "husky",
31606
31606
  release: "zx ./scripts/release.js",
31607
+ test: "vitest run",
31607
31608
  "test:coverage": "vitest run --coverage",
31608
31609
  "test:watch": "vitest",
31609
- test: "vitest run",
31610
31610
  "type-check": "tsc --noEmit"
31611
31611
  },
31612
31612
  devDependencies: {
@@ -31801,6 +31801,7 @@ var MAX_PATH_LENGTH = 1024;
31801
31801
  // src/server/BeansMcpServer.ts
31802
31802
  init_utils();
31803
31803
  var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
31804
+ var COMPATIBLE_BEANS_VERSION = "0.4.2";
31804
31805
  var PACKAGE_VERSION = package_default.version ?? "0.0.0-dev";
31805
31806
  var CLOSED_STATUSES = /* @__PURE__ */ new Set(["completed", "scrapped"]);
31806
31807
  var BEAN_ID_HINT = "Missing required field `beanId`. Did you mean `beanId`?";
@@ -31985,13 +31986,13 @@ async function checkVersionCompatibility(cliPath, workspaceRoot, detector) {
31985
31986
  const detectedBeansVersion = await detector(cliPath, workspaceRoot);
31986
31987
  if (!detectedBeansVersion) {
31987
31988
  console.error(
31988
- `[beans-mcp] warning: unable to determine Beans CLI version from \`${cliPath}\`; proceeding without version compatibility checks.`
31989
+ `[beans-mcp] warning: unable to determine Beans CLI version from \`${cliPath}\`; expected Beans ${COMPATIBLE_BEANS_VERSION}, but proceeding without compatibility checks.`
31989
31990
  );
31990
31991
  return;
31991
31992
  }
31992
- if (detectedBeansVersion !== PACKAGE_VERSION) {
31993
+ if (detectedBeansVersion !== COMPATIBLE_BEANS_VERSION) {
31993
31994
  console.error(
31994
- `[beans-mcp] warning: version mismatch detected (beans=${detectedBeansVersion}, beans-mcp=${PACKAGE_VERSION}); continuing startup.`
31995
+ `[beans-mcp] warning: version mismatch detected (beans=${detectedBeansVersion}, supported=${COMPATIBLE_BEANS_VERSION}, beans-mcp=${PACKAGE_VERSION}); continuing startup.`
31995
31996
  );
31996
31997
  }
31997
31998
  }
package/index.cjs CHANGED
@@ -31567,7 +31567,7 @@ var import_node_util2 = require("util");
31567
31567
  // package.json
31568
31568
  var package_default = {
31569
31569
  name: "@selfagency/beans-mcp",
31570
- version: "0.6.0",
31570
+ version: "0.6.2",
31571
31571
  private: false,
31572
31572
  description: "MCP (Model Context Protocol) server for Beans issue tracker",
31573
31573
  keywords: [
@@ -31613,18 +31613,18 @@ var package_default = {
31613
31613
  },
31614
31614
  scripts: {
31615
31615
  build: "tsup",
31616
- "docs:dev": "vitepress dev docs",
31616
+ postbuild: "node ./scripts/write-dist-package.js",
31617
31617
  "docs:build": "vitepress build docs",
31618
+ "docs:dev": "vitepress dev docs",
31618
31619
  "docs:preview": "vitepress preview docs",
31619
31620
  format: "oxfmt",
31620
- "lint:fix": "oxlint --fix",
31621
31621
  lint: "oxlint",
31622
- postbuild: "node ./scripts/write-dist-package.js",
31622
+ "lint:fix": "oxlint --fix",
31623
31623
  prepare: "husky",
31624
31624
  release: "zx ./scripts/release.js",
31625
+ test: "vitest run",
31625
31626
  "test:coverage": "vitest run --coverage",
31626
31627
  "test:watch": "vitest",
31627
- test: "vitest run",
31628
31628
  "type-check": "tsc --noEmit"
31629
31629
  },
31630
31630
  devDependencies: {
@@ -31819,6 +31819,7 @@ var MAX_PATH_LENGTH = 1024;
31819
31819
  // src/server/BeansMcpServer.ts
31820
31820
  init_utils();
31821
31821
  var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
31822
+ var COMPATIBLE_BEANS_VERSION = "0.4.2";
31822
31823
  var PACKAGE_VERSION = package_default.version ?? "0.0.0-dev";
31823
31824
  var CLOSED_STATUSES = /* @__PURE__ */ new Set(["completed", "scrapped"]);
31824
31825
  var BEAN_ID_HINT = "Missing required field `beanId`. Did you mean `beanId`?";
@@ -32003,13 +32004,13 @@ async function checkVersionCompatibility(cliPath, workspaceRoot, detector) {
32003
32004
  const detectedBeansVersion = await detector(cliPath, workspaceRoot);
32004
32005
  if (!detectedBeansVersion) {
32005
32006
  console.error(
32006
- `[beans-mcp] warning: unable to determine Beans CLI version from \`${cliPath}\`; proceeding without version compatibility checks.`
32007
+ `[beans-mcp] warning: unable to determine Beans CLI version from \`${cliPath}\`; expected Beans ${COMPATIBLE_BEANS_VERSION}, but proceeding without compatibility checks.`
32007
32008
  );
32008
32009
  return;
32009
32010
  }
32010
- if (detectedBeansVersion !== PACKAGE_VERSION) {
32011
+ if (detectedBeansVersion !== COMPATIBLE_BEANS_VERSION) {
32011
32012
  console.error(
32012
- `[beans-mcp] warning: version mismatch detected (beans=${detectedBeansVersion}, beans-mcp=${PACKAGE_VERSION}); continuing startup.`
32013
+ `[beans-mcp] warning: version mismatch detected (beans=${detectedBeansVersion}, supported=${COMPATIBLE_BEANS_VERSION}, beans-mcp=${PACKAGE_VERSION}); continuing startup.`
32013
32014
  );
32014
32015
  }
32015
32016
  }
package/index.js CHANGED
@@ -31546,7 +31546,7 @@ import { promisify as promisify2 } from "util";
31546
31546
  // package.json
31547
31547
  var package_default = {
31548
31548
  name: "@selfagency/beans-mcp",
31549
- version: "0.6.0",
31549
+ version: "0.6.2",
31550
31550
  private: false,
31551
31551
  description: "MCP (Model Context Protocol) server for Beans issue tracker",
31552
31552
  keywords: [
@@ -31592,18 +31592,18 @@ var package_default = {
31592
31592
  },
31593
31593
  scripts: {
31594
31594
  build: "tsup",
31595
- "docs:dev": "vitepress dev docs",
31595
+ postbuild: "node ./scripts/write-dist-package.js",
31596
31596
  "docs:build": "vitepress build docs",
31597
+ "docs:dev": "vitepress dev docs",
31597
31598
  "docs:preview": "vitepress preview docs",
31598
31599
  format: "oxfmt",
31599
- "lint:fix": "oxlint --fix",
31600
31600
  lint: "oxlint",
31601
- postbuild: "node ./scripts/write-dist-package.js",
31601
+ "lint:fix": "oxlint --fix",
31602
31602
  prepare: "husky",
31603
31603
  release: "zx ./scripts/release.js",
31604
+ test: "vitest run",
31604
31605
  "test:coverage": "vitest run --coverage",
31605
31606
  "test:watch": "vitest",
31606
- test: "vitest run",
31607
31607
  "type-check": "tsc --noEmit"
31608
31608
  },
31609
31609
  devDependencies: {
@@ -31798,6 +31798,7 @@ var MAX_PATH_LENGTH = 1024;
31798
31798
  // src/server/BeansMcpServer.ts
31799
31799
  init_utils();
31800
31800
  var execFileAsync2 = promisify2(execFile2);
31801
+ var COMPATIBLE_BEANS_VERSION = "0.4.2";
31801
31802
  var PACKAGE_VERSION = package_default.version ?? "0.0.0-dev";
31802
31803
  var CLOSED_STATUSES = /* @__PURE__ */ new Set(["completed", "scrapped"]);
31803
31804
  var BEAN_ID_HINT = "Missing required field `beanId`. Did you mean `beanId`?";
@@ -31982,13 +31983,13 @@ async function checkVersionCompatibility(cliPath, workspaceRoot, detector) {
31982
31983
  const detectedBeansVersion = await detector(cliPath, workspaceRoot);
31983
31984
  if (!detectedBeansVersion) {
31984
31985
  console.error(
31985
- `[beans-mcp] warning: unable to determine Beans CLI version from \`${cliPath}\`; proceeding without version compatibility checks.`
31986
+ `[beans-mcp] warning: unable to determine Beans CLI version from \`${cliPath}\`; expected Beans ${COMPATIBLE_BEANS_VERSION}, but proceeding without compatibility checks.`
31986
31987
  );
31987
31988
  return;
31988
31989
  }
31989
- if (detectedBeansVersion !== PACKAGE_VERSION) {
31990
+ if (detectedBeansVersion !== COMPATIBLE_BEANS_VERSION) {
31990
31991
  console.error(
31991
- `[beans-mcp] warning: version mismatch detected (beans=${detectedBeansVersion}, beans-mcp=${PACKAGE_VERSION}); continuing startup.`
31992
+ `[beans-mcp] warning: version mismatch detected (beans=${detectedBeansVersion}, supported=${COMPATIBLE_BEANS_VERSION}, beans-mcp=${PACKAGE_VERSION}); continuing startup.`
31992
31993
  );
31993
31994
  }
31994
31995
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selfagency/beans-mcp",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "MCP (Model Context Protocol) server for Beans issue tracker",
5
5
  "keywords": [
6
6
  "ai",
@@ -30,7 +30,9 @@
30
30
  "index.cjs",
31
31
  "index.js",
32
32
  "index.d.ts",
33
- "beans-mcp-server.cjs"
33
+ "beans-mcp-server.cjs",
34
+ "skills",
35
+ "skills-lock.json"
34
36
  ],
35
37
  "bin": {
36
38
  "beans-mcp": "beans-mcp-server.cjs"
@@ -0,0 +1,390 @@
1
+ ---
2
+ name: beans-mcp
3
+ description: Use this skill when the user needs to manage Beans work items in a Beans workspace—create, view, update, reopen, archive, query, or bulk-manage beans, edit `.beans` files/frontmatter, or manage parent/blocking relationships. Use it even when the user says tickets/issues/backlog instead of “beans.” Do not use it for generic GitHub issue workflows that are not backed by Beans.
4
+ ---
5
+
6
+ # Beans MCP Skill
7
+
8
+ ## When to Use
9
+
10
+ Use this skill whenever you need to:
11
+
12
+ - Create, view, edit, or delete beans (tasks, bugs, features, epics, milestones)
13
+ - Query, filter, sort, or search beans
14
+ - Bulk-create or bulk-assign beans to a parent
15
+ - Read or write bean markdown files under `.beans/`
16
+ - Generate Copilot workspace instructions from the live Beans context
17
+
18
+ Do **not** use this skill for:
19
+
20
+ - Generic GitHub Issues/Projects workflows unrelated to a Beans workspace
21
+ - One-off markdown editing outside `.beans/` records
22
+
23
+ ---
24
+
25
+ ## Available Tools
26
+
27
+ | Tool | Purpose |
28
+ | ---------------------- | ----------------------------------------------- |
29
+ | `beans_init` | Initialize Beans in a workspace |
30
+ | `beans_archive` | Archive completed/scrapped beans |
31
+ | `beans_view` | View one or more beans by ID |
32
+ | `beans_create` | Create one bean |
33
+ | `beans_bulk_create` | Create many beans |
34
+ | `beans_update` | Update bean metadata/body |
35
+ | `beans_bulk_update` | Update many beans |
36
+ | `beans_edit` | Metadata-only update helper |
37
+ | `beans_reopen` | Reopen a completed/scrapped bean |
38
+ | `beans_complete_tasks` | Complete markdown checklist tasks in body |
39
+ | `beans_delete` | Delete one or more beans |
40
+ | `beans_query` | Query/list/filter/sort/ready/graphql operations |
41
+ | `beans_bean_file` | Read/create/edit/delete `.beans` files |
42
+ | `beans_output` | Read extension output logs |
43
+
44
+ ---
45
+
46
+ ## Default Workflow (Use This First)
47
+
48
+ When the user asks for bean work and intent is unclear, default to this sequence:
49
+
50
+ 1. **Discover**: `beans_query` with `operation: "ready"` (or `refresh` if broad list needed)
51
+ 2. **Inspect**: `beans_view` for the specific bean(s)
52
+ 3. **Mutate**: `beans_update` (or bulk variants) with minimal required changes
53
+ 4. **Body tasks**: `beans_complete_tasks` for checklist completion
54
+ 5. **Close/archive**: `beans_update` to `completed`/`scrapped`, then `beans_archive` when appropriate
55
+
56
+ Use alternatives only when this default is insufficient.
57
+
58
+ ---
59
+
60
+ ## Field Reference
61
+
62
+ ### Creating a bean (`beans_create`, `beans_bulk_create`)
63
+
64
+ | Field | Required | Notes |
65
+ | ------------- | -------- | ------------------------------------------------------------ |
66
+ | `title` | ✅ | String, max 1024 chars |
67
+ | `type` | ✅ | e.g. `task`, `bug`, `feature`, `epic`, `milestone` |
68
+ | `status` | — | e.g. `todo`, `in-progress`, `draft`, `completed`, `scrapped` |
69
+ | `priority` | — | `critical`, `high`, `normal`, `low`, `deferred` |
70
+ | `body` | — | Markdown body content |
71
+ | `description` | — | Deprecated alias for `body`; use `body` instead |
72
+ | `parent` | — | Parent bean ID |
73
+
74
+ ### Updating a bean (`beans_update`, `beans_bulk_update`)
75
+
76
+ | Field | Notes |
77
+ | ------------- | ---------------------------------------------------------------------- |
78
+ | `beanId` | Required — ID of bean to update |
79
+ | `status` | New status |
80
+ | `type` | New type |
81
+ | `priority` | New priority |
82
+ | `parent` | Assign to a new parent |
83
+ | `clearParent` | Set `true` to detach from current parent |
84
+ | `blocking` | Array of bean IDs this bean now blocks |
85
+ | `blockedBy` | Array of bean IDs this bean is blocked by |
86
+ | `body` | Full body replacement (cannot combine with `bodyAppend`/`bodyReplace`) |
87
+ | `bodyAppend` | Append text to the end of the body |
88
+ | `bodyReplace` | Array of `{ old, new }` string substitutions in the body |
89
+ | `ifMatch` | Optimistic concurrency guard — pass the bean's `etag` |
90
+
91
+ ---
92
+
93
+ ## Archive and GraphQL Parity
94
+
95
+ ### Archive completed work
96
+
97
+ Use `beans_archive` to archive completed/scrapped beans.
98
+
99
+ ```json
100
+ {}
101
+ ```
102
+
103
+ ### Raw GraphQL queries/mutations
104
+
105
+ Use `beans_query` with `operation: "graphql"` for CLI parity with `beans query --json`.
106
+
107
+ ```json
108
+ {
109
+ "operation": "graphql",
110
+ "graphql": "{ beans(filter: { type: [\"bug\"] }) { id title status } }"
111
+ }
112
+ ```
113
+
114
+ With variables:
115
+
116
+ ```json
117
+ {
118
+ "operation": "graphql",
119
+ "graphql": "query($q: String!) { beans(filter: { search: $q }) { id title } }",
120
+ "variables": { "q": "authentication" }
121
+ }
122
+ ```
123
+
124
+ ---
125
+
126
+ ## CLI-Aligned Workflow Guidance
127
+
128
+ - Prefer `beans_view` / `beans_query` over ad-hoc file parsing for bean state.
129
+ - Use `beans_bulk_create` / `beans_bulk_update` for batch parent/relationship updates.
130
+ - Use `beans_complete_tasks` for markdown checklist completion inside a bean body.
131
+ - Use `beans_archive` only after work is completed/scrapped and user intent is to archive.
132
+
133
+ ### Gotchas (High-Value Corrections)
134
+
135
+ - `beans_update` rejects combining `body` with `bodyAppend`/`bodyReplace` in one request.
136
+ - `beans_delete` allows only `draft`/`scrapped` unless `force: true`.
137
+ - `beans_reopen` requires the current status to match `requiredCurrentStatus` (`completed` or `scrapped`).
138
+ - Omitting `beanId` produces a validation hint; prefer `beanId` (not `id`).
139
+ - For concurrent edits, pass `ifMatch` with the current bean `etag` from `beans_view`.
140
+
141
+ ### Relationship semantics
142
+
143
+ - **Parent**: hierarchy (milestone → epic → feature → task/bug).
144
+ - **Blocking**: this bean blocks another bean.
145
+ - **BlockedBy**: this bean depends on another bean.
146
+
147
+ ### Issue types
148
+
149
+ - `milestone`, `epic`, `bug`, `feature`, `task`
150
+
151
+ ### Statuses
152
+
153
+ - `in-progress`, `todo`, `draft`, `completed`, `scrapped`
154
+
155
+ ### Priorities
156
+
157
+ - `critical`, `high`, `normal`, `low`, `deferred`
158
+
159
+ ### Body modification guidance
160
+
161
+ - Use `body` for full replacement.
162
+ - Use `bodyAppend` to append content.
163
+ - Use `bodyReplace` for exact replacements.
164
+ - Do **not** combine `body` with `bodyAppend`/`bodyReplace` in one request.
165
+
166
+ ### Concurrency guidance
167
+
168
+ - Use `ifMatch` with a current `etag` from `beans_view` when concurrent edits are possible.
169
+
170
+ ---
171
+
172
+ ## Bulk Operations
173
+
174
+ ### Bulk create under a shared parent
175
+
176
+ ```json
177
+ {
178
+ "beans": [
179
+ { "title": "Design API schema", "type": "task" },
180
+ { "title": "Implement endpoints", "type": "task" },
181
+ { "title": "Write tests", "type": "task" }
182
+ ],
183
+ "parent": "feature-auth-42"
184
+ }
185
+ ```
186
+
187
+ Each item can specify its own `parent` to override the top-level `parent`.
188
+
189
+ ### Bulk re-assign existing beans to a parent
190
+
191
+ ```json
192
+ {
193
+ "beans": [{ "beanId": "task-001", "status": "todo" }, { "beanId": "task-002" }, { "beanId": "task-003" }],
194
+ "parent": "epic-q2-roadmap"
195
+ }
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Querying
201
+
202
+ ### Refresh (list all beans)
203
+
204
+ ```json
205
+ { "operation": "refresh" }
206
+ ```
207
+
208
+ ### Filter
209
+
210
+ ```json
211
+ {
212
+ "operation": "filter",
213
+ "statuses": ["in-progress", "todo"],
214
+ "types": ["bug", "feature"],
215
+ "tags": ["auth"]
216
+ }
217
+ ```
218
+
219
+ ### Search
220
+
221
+ ```json
222
+ { "operation": "search", "search": "authentication", "includeClosed": false }
223
+ ```
224
+
225
+ ### Sort
226
+
227
+ ```json
228
+ { "operation": "sort", "mode": "updated" }
229
+ ```
230
+
231
+ Modes: `status-priority-type-title` (default), `updated`, `created`, `id`
232
+
233
+ ### Ready (actionable, unblocked beans)
234
+
235
+ ```json
236
+ { "operation": "ready" }
237
+ ```
238
+
239
+ ### LLM context — generate Copilot instructions
240
+
241
+ ```json
242
+ { "operation": "llm_context", "writeToWorkspaceInstructions": true }
243
+ ```
244
+
245
+ Writes to `.github/instructions/beans-prime.instructions.md` when `writeToWorkspaceInstructions` is true.
246
+
247
+ ---
248
+
249
+ ## File Operations (`beans_bean_file`)
250
+
251
+ ### Path rules
252
+
253
+ - Pass the filename **without** the `.beans/` prefix — it is resolved automatically.
254
+ - Both `foo.md` and `.beans/foo.md` are accepted; the leading `.beans/` is stripped.
255
+ - Use `update_frontmatter` to atomically update frontmatter fields without rewriting the body.
256
+
257
+ ### `update_frontmatter` defaults
258
+
259
+ - Prefer `update_frontmatter` over `edit` when changing only metadata fields.
260
+ - Set nullable fields (`parent_id`, `tags`, `blocking_ids`, `blocked_by_ids`, `pr`, `branch`) to `null` to remove them.
261
+
262
+ ```json
263
+ { "operation": "read", "path": "task-abc--fix-login.md" }
264
+ ```
265
+
266
+ ```json
267
+ {
268
+ "operation": "edit",
269
+ "path": "task-abc--fix-login.md",
270
+ "content": "---\ntitle: \"Fix login timeout\"\nstatus: in-progress\ntype: bug\n---\n\nBody here.\n"
271
+ }
272
+ ```
273
+
274
+ ```json
275
+ {
276
+ "operation": "create",
277
+ "path": "my-note.md",
278
+ "content": "---\ntitle: \"My note\"\n---\nContent.\n",
279
+ "overwrite": false
280
+ }
281
+ ```
282
+
283
+ ```json
284
+ { "operation": "delete", "path": "old-note.md" }
285
+ ```
286
+
287
+ ```json
288
+ {
289
+ "operation": "update_frontmatter",
290
+ "path": "task-abc--fix-login.md",
291
+ "fields": {
292
+ "status": "in-progress",
293
+ "pr": "123",
294
+ "branch": "feature/cascade-status-and-skills-npm"
295
+ }
296
+ }
297
+ ```
298
+
299
+ ### Frontmatter conventions
300
+
301
+ - `title` values are **always double-quoted** in frontmatter.
302
+ - ✅ `title: "Fix login timeout"`
303
+ - ❌ `title: Fix login timeout`
304
+ - Dates use ISO 8601: `2026-01-01T00:00:00Z`
305
+ - Standard fields: `title`, `status`, `type`, `priority`, `tags`, `parent_id`, `blocking_ids`, `blocked_by_ids`, `pr`, `branch`, `created_at`, `updated_at`
306
+
307
+ ---
308
+
309
+ ## Caching Behaviour
310
+
311
+ The server caches unfiltered `list` calls (no status/type/search filter) using a two-layer strategy:
312
+
313
+ 1. **Burst TTL (5 s):** Repeated calls within 5 seconds return the in-memory cache instantly.
314
+ 2. **Timestamp check:** After 5 s, a lightweight query fetches only `id + updatedAt` for all beans. If nothing has changed, the full cached result is returned without a full GraphQL round-trip.
315
+ 3. **Mutations invalidate:** `create`, `update`, and `delete` always invalidate the cache immediately.
316
+
317
+ Filtered queries (status/type/search) are **never cached** and always hit the CLI.
318
+
319
+ ---
320
+
321
+ ## Common Patterns
322
+
323
+ ### Create an epic with child tasks in one workflow
324
+
325
+ ```jsonc
326
+ // Step 1 — create the parent epic
327
+ { "title": "User Authentication", "type": "epic", "status": "todo", "priority": "high" }
328
+
329
+ // Step 2 — bulk create children under it
330
+ {
331
+ "beans": [
332
+ { "title": "Design auth schema", "type": "task" },
333
+ { "title": "Implement JWT flow", "type": "task" },
334
+ { "title": "Add refresh token support", "type": "task" },
335
+ { "title": "Write integration tests", "type": "task" }
336
+ ],
337
+ "parent": "<epic-id-from-step-1>"
338
+ }
339
+ ```
340
+
341
+ ### Mark a task in-progress and add body notes
342
+
343
+ ```json
344
+ {
345
+ "beanId": "task-xyz",
346
+ "status": "in-progress",
347
+ "bodyAppend": "\n## Progress\n\n- [x] Schema designed\n- [ ] Implementation started\n"
348
+ }
349
+ ```
350
+
351
+ ### Reassign a group of tasks to a new epic
352
+
353
+ ```json
354
+ {
355
+ "beans": [{ "beanId": "task-001" }, { "beanId": "task-002" }, { "beanId": "task-003" }],
356
+ "parent": "epic-new-parent"
357
+ }
358
+ ```
359
+
360
+ ---
361
+
362
+ ## Trigger Guidance (Description Optimization)
363
+
364
+ This skill should trigger for prompts like:
365
+
366
+ - “Can you update this backlog item and link it to its blocker?”
367
+ - “Move these tasks under an epic and mark one in-progress.”
368
+ - “Edit the bean frontmatter and set PR/branch metadata.”
369
+
370
+ This skill should **not** trigger for prompts like:
371
+
372
+ - “Open a GitHub issue in repo X” (without Beans workspace context)
373
+ - “Update Jira ticket ABC-123” (external tracker)
374
+
375
+ ---
376
+
377
+ ## Evaluation Starter (Output Quality + Triggering)
378
+
379
+ Use `evals/evals.json` as the canonical test set and iterate in `iteration-N/` workspace folders.
380
+
381
+ - Start with 2–3 realistic prompts, then expand.
382
+ - Include both should-trigger and should-not-trigger cases.
383
+ - Add assertions after first outputs to avoid brittle checks.
384
+
385
+ Use this file layout:
386
+
387
+ - `evals/evals.json`
388
+ - optional fixtures under `evals/files/`
389
+
390
+ If you add scripts later, keep them non-interactive, expose `--help`, and emit structured output (JSON) on stdout.
@@ -0,0 +1,32 @@
1
+ {
2
+ "skill_name": "beans-mcp",
3
+ "evals": [
4
+ {
5
+ "id": 1,
6
+ "prompt": "Please move bean task-001 under epic-auth and set it to in-progress, then add PR 123 and branch feature/auth-refresh in frontmatter.",
7
+ "expected_output": "The bean is reassigned to the target parent, status is updated to in-progress, and frontmatter includes pr=123 and branch=feature/auth-refresh without body corruption.",
8
+ "assertions": [
9
+ "The output confirms parent reassignment for task-001",
10
+ "The output confirms status is in-progress",
11
+ "The output confirms frontmatter pr is set to 123",
12
+ "The output confirms frontmatter branch is set to feature/auth-refresh"
13
+ ]
14
+ },
15
+ {
16
+ "id": 2,
17
+ "prompt": "Mark all markdown checklist tasks complete for bean task-ops-22 and close it if all tasks are done.",
18
+ "expected_output": "Checklist items are marked completed using beans_complete_tasks, then bean status is updated to completed only if appropriate.",
19
+ "assertions": [
20
+ "The output shows totalTaskCount and updatedTaskCount from beans_complete_tasks",
21
+ "No invalid body/bodyAppend/bodyReplace combination is attempted",
22
+ "Final status transition is explicitly reported"
23
+ ]
24
+ },
25
+ {
26
+ "id": 3,
27
+ "prompt": "Open a GitHub issue in selfagency/beans-mcp titled 'Docs typo'.",
28
+ "expected_output": "The skill should not trigger; this is a generic GitHub issue workflow and not Beans workspace work.",
29
+ "assertions": ["The response indicates this request is outside Beans skill scope"]
30
+ }
31
+ ]
32
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 1,
3
+ "skills": {
4
+ "git-mcp-workflow": {
5
+ "source": "selfagency/git-mcp",
6
+ "sourceType": "github",
7
+ "computedHash": "ce8b4a5f89898d15302e9725bad35002a0ac615e59f83fdfec8a149b1fee7926"
8
+ }
9
+ }
10
+ }