frontier-mcp 0.1.0

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 (115) hide show
  1. package/README.md +104 -0
  2. package/dist/bin.d.ts +3 -0
  3. package/dist/bin.d.ts.map +1 -0
  4. package/dist/bin.js +10 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/domain.d.ts +230 -0
  7. package/dist/domain.d.ts.map +1 -0
  8. package/dist/domain.js +23 -0
  9. package/dist/domain.js.map +1 -0
  10. package/dist/edges.d.ts +35 -0
  11. package/dist/edges.d.ts.map +1 -0
  12. package/dist/edges.js +61 -0
  13. package/dist/edges.js.map +1 -0
  14. package/dist/frontier.d.ts +12 -0
  15. package/dist/frontier.d.ts.map +1 -0
  16. package/dist/frontier.js +34 -0
  17. package/dist/frontier.js.map +1 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/server.d.ts +29 -0
  23. package/dist/server.d.ts.map +1 -0
  24. package/dist/server.js +210 -0
  25. package/dist/server.js.map +1 -0
  26. package/dist/storage/driver.d.ts +131 -0
  27. package/dist/storage/driver.d.ts.map +1 -0
  28. package/dist/storage/driver.js +43 -0
  29. package/dist/storage/driver.js.map +1 -0
  30. package/dist/storage/markdown/create.d.ts +52 -0
  31. package/dist/storage/markdown/create.d.ts.map +1 -0
  32. package/dist/storage/markdown/create.js +298 -0
  33. package/dist/storage/markdown/create.js.map +1 -0
  34. package/dist/storage/markdown/driver.d.ts +7 -0
  35. package/dist/storage/markdown/driver.d.ts.map +1 -0
  36. package/dist/storage/markdown/driver.js +492 -0
  37. package/dist/storage/markdown/driver.js.map +1 -0
  38. package/dist/storage/markdown/frontmatter.d.ts +17 -0
  39. package/dist/storage/markdown/frontmatter.d.ts.map +1 -0
  40. package/dist/storage/markdown/frontmatter.js +36 -0
  41. package/dist/storage/markdown/frontmatter.js.map +1 -0
  42. package/dist/storage/markdown/header-doc.d.ts +56 -0
  43. package/dist/storage/markdown/header-doc.d.ts.map +1 -0
  44. package/dist/storage/markdown/header-doc.js +287 -0
  45. package/dist/storage/markdown/header-doc.js.map +1 -0
  46. package/dist/storage/markdown/legacy.d.ts +16 -0
  47. package/dist/storage/markdown/legacy.d.ts.map +1 -0
  48. package/dist/storage/markdown/legacy.js +152 -0
  49. package/dist/storage/markdown/legacy.js.map +1 -0
  50. package/dist/storage/markdown/migrate.d.ts +27 -0
  51. package/dist/storage/markdown/migrate.d.ts.map +1 -0
  52. package/dist/storage/markdown/migrate.js +158 -0
  53. package/dist/storage/markdown/migrate.js.map +1 -0
  54. package/dist/storage/markdown/serialize.d.ts +37 -0
  55. package/dist/storage/markdown/serialize.d.ts.map +1 -0
  56. package/dist/storage/markdown/serialize.js +321 -0
  57. package/dist/storage/markdown/serialize.js.map +1 -0
  58. package/dist/storage/markdown/ticket.d.ts +12 -0
  59. package/dist/storage/markdown/ticket.d.ts.map +1 -0
  60. package/dist/storage/markdown/ticket.js +97 -0
  61. package/dist/storage/markdown/ticket.js.map +1 -0
  62. package/dist/storage/markdown/write.d.ts +42 -0
  63. package/dist/storage/markdown/write.d.ts.map +1 -0
  64. package/dist/storage/markdown/write.js +134 -0
  65. package/dist/storage/markdown/write.js.map +1 -0
  66. package/dist/tools/create-tickets.d.ts +63 -0
  67. package/dist/tools/create-tickets.d.ts.map +1 -0
  68. package/dist/tools/create-tickets.js +161 -0
  69. package/dist/tools/create-tickets.js.map +1 -0
  70. package/dist/tools/edit-map.d.ts +29 -0
  71. package/dist/tools/edit-map.d.ts.map +1 -0
  72. package/dist/tools/edit-map.js +81 -0
  73. package/dist/tools/edit-map.js.map +1 -0
  74. package/dist/tools/get-board.d.ts +29 -0
  75. package/dist/tools/get-board.d.ts.map +1 -0
  76. package/dist/tools/get-board.js +190 -0
  77. package/dist/tools/get-board.js.map +1 -0
  78. package/dist/tools/get-tickets.d.ts +14 -0
  79. package/dist/tools/get-tickets.d.ts.map +1 -0
  80. package/dist/tools/get-tickets.js +56 -0
  81. package/dist/tools/get-tickets.js.map +1 -0
  82. package/dist/tools/list-efforts.d.ts +16 -0
  83. package/dist/tools/list-efforts.d.ts.map +1 -0
  84. package/dist/tools/list-efforts.js +38 -0
  85. package/dist/tools/list-efforts.js.map +1 -0
  86. package/dist/tools/migrate-effort.d.ts +11 -0
  87. package/dist/tools/migrate-effort.d.ts.map +1 -0
  88. package/dist/tools/migrate-effort.js +54 -0
  89. package/dist/tools/migrate-effort.js.map +1 -0
  90. package/dist/tools/spec.d.ts +12 -0
  91. package/dist/tools/spec.d.ts.map +1 -0
  92. package/dist/tools/spec.js +27 -0
  93. package/dist/tools/spec.js.map +1 -0
  94. package/dist/tools/update-ticket.d.ts +62 -0
  95. package/dist/tools/update-ticket.d.ts.map +1 -0
  96. package/dist/tools/update-ticket.js +164 -0
  97. package/dist/tools/update-ticket.js.map +1 -0
  98. package/dist/tracker-doc.d.ts +10 -0
  99. package/dist/tracker-doc.d.ts.map +1 -0
  100. package/dist/tracker-doc.js +16 -0
  101. package/dist/tracker-doc.js.map +1 -0
  102. package/dist/workspace-index.d.ts +39 -0
  103. package/dist/workspace-index.d.ts.map +1 -0
  104. package/dist/workspace-index.js +83 -0
  105. package/dist/workspace-index.js.map +1 -0
  106. package/dist/workspace-watcher.d.ts +24 -0
  107. package/dist/workspace-watcher.d.ts.map +1 -0
  108. package/dist/workspace-watcher.js +87 -0
  109. package/dist/workspace-watcher.js.map +1 -0
  110. package/dist/workspace.d.ts +23 -0
  111. package/dist/workspace.d.ts.map +1 -0
  112. package/dist/workspace.js +70 -0
  113. package/dist/workspace.js.map +1 -0
  114. package/docs/agents/issue-tracker.md +141 -0
  115. package/package.json +56 -0
package/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # frontier-mcp
2
+
3
+ An MCP server that serves the markdown issue tracker under `.scratch/` as a queryable graph — so agents
4
+ stop re-parsing prose to learn what is open, blocked, or takeable.
5
+
6
+ ## Requirements
7
+
8
+ - Node 24 or later
9
+ - npx to run it; pnpm for development of this package
10
+
11
+ ## Install once (user scope)
12
+
13
+ Register FrontierMCP once in your **user-level** MCP config, pinned to a released version. Every
14
+ repository you open then gets the server automatically — no per-repo `mcp.json` file.
15
+
16
+ In Cursor, edit `~/.cursor/mcp.json` (create the file if it does not exist); other MCP clients take the
17
+ same command and arguments in their own user-scope server list:
18
+
19
+ ```json
20
+ {
21
+ "mcpServers": {
22
+ "frontier": {
23
+ "command": "npx",
24
+ "args": ["-y", "frontier-mcp@x.y.z"]
25
+ }
26
+ }
27
+ }
28
+ ```
29
+
30
+ Replace `x.y.z` with a published version — see the
31
+ [releases](https://github.com/51ck/frontier-mcp/releases). The pin is the version you get; nothing
32
+ bumps it for you.
33
+
34
+ Restart your editor after saving.
35
+
36
+ ## First use in a repository
37
+
38
+ 1. Open the repository in your editor. FrontierMCP resolves the workspace from the session working
39
+ directory — walking upward to the nearest `.scratch/` or `.git/` — so opening the project is the
40
+ only setup step.
41
+ 2. Read the tracker configuration document once: fetch MCP resource `frontier://tracker-doc`, or read
42
+ [`docs/agents/issue-tracker.md`](docs/agents/issue-tracker.md) in a repo that vendors it.
43
+ 3. Call `list_efforts` to see whether `.scratch/` exists yet.
44
+ 4. Call `get_board` on an Effort to see the Frontier, then `get_tickets` only for the ids you work.
45
+
46
+ A repository with no `.scratch/` directory is not an error. Create the first Effort with
47
+ `create_tickets`, `edit_map`, or `spec` and `create: true`.
48
+
49
+ ## Override the workspace
50
+
51
+ Optional, per call or per session:
52
+
53
+ - Pass `root` on any tool call to read another directory.
54
+ - Set `FRONTIER_ROOT` in the server environment for a non-standard layout.
55
+
56
+ ## Tools
57
+
58
+ Eight tools: `list_efforts`, `get_board`, `get_tickets`, `create_tickets`, `update_ticket`,
59
+ `edit_map`, `spec`, `migrate_effort`. See the tracker configuration document for when to use each.
60
+
61
+ ## Development
62
+
63
+ ```bash
64
+ pnpm install
65
+ pnpm test
66
+ pnpm run check
67
+ pnpm run build
68
+ node src/bin.ts
69
+ ```
70
+
71
+ ## Releasing
72
+
73
+ Publishing is CI-only via [release-it](https://github.com/release-it/release-it). There is no local
74
+ publish script, and adding one would mean minting the long-lived npm token that the CI setup exists
75
+ to avoid.
76
+
77
+ 1. Merge the work you want to ship to `master`.
78
+ 2. On GitHub: **Actions → Release → Run workflow**, pick `patch` / `minor` / `major`.
79
+ 3. The workflow runs checks + tests, bumps `package.json`, updates `CHANGELOG.md`, tags
80
+ `v*`, creates a GitHub Release, and publishes to npm with `pnpm`.
81
+ 4. Bump the pinned version in your user MCP config (`frontier-mcp@x.y.z`) when you want the
82
+ new build — pins stay manual on purpose.
83
+
84
+ The workflow is dispatchable from any branch but refuses to run off `master`: release-it commits,
85
+ tags and pushes before it publishes, so a release from a feature branch would rewrite that branch.
86
+
87
+ Local dry-run (no tag, no publish). It needs a clean working tree and an upstream branch, so commit
88
+ first:
89
+
90
+ ```bash
91
+ pnpm run release:dry
92
+ ```
93
+
94
+ npm publishing uses **Trusted Publishing** — GitHub Actions OIDC, configured for this repo on
95
+ npmjs.com, so there is no `NPM_TOKEN` to rotate.
96
+
97
+ Before changing `.release-it.json` or the workflow, read the release-it bullet in
98
+ [AGENTS.md](./AGENTS.md) Work Guidance. Several settings there look removable and are not — the
99
+ absent `registry-url`, `npm.skipChecks`, and the pnpm 10 pin each exist for a reason recorded in one
100
+ place so it cannot drift.
101
+
102
+ ## License
103
+
104
+ MIT
package/dist/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
package/dist/bin.js ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { createFrontierMCP } from './server.js';
4
+ const frontier = createFrontierMCP();
5
+ // The index is built by a full scan before the transport connects, so the first
6
+ // call is served warm. A workspace that cannot be scanned yet is not fatal:
7
+ // every call resolves its own workspace regardless.
8
+ await frontier.warmUp().catch(() => { });
9
+ await frontier.server.connect(new StdioServerTransport());
10
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAC;AAErC,gFAAgF;AAChF,4EAA4E;AAC5E,oDAAoD;AACpD,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAExC,MAAM,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC"}
@@ -0,0 +1,230 @@
1
+ /**
2
+ * The vocabulary from CONTEXT.md, as types. Everything above the storage driver
3
+ * speaks in these terms.
4
+ */
5
+ /**
6
+ * Which Header doc an Effort holds. An Effort has zero, one, or two — a Map, a
7
+ * Spec, or a Map that later gained a Spec.
8
+ *
9
+ * Deliberately not named for a "kind": CONTEXT.md reserves Kind for a Ticket's
10
+ * `build`/`decision` shape.
11
+ */
12
+ export type HeaderDoc = 'map' | 'spec';
13
+ /** Which of the two shapes a Ticket is. */
14
+ export type Kind = 'build' | 'decision';
15
+ /**
16
+ * The shape of a minted id: `T<n>`, visibly an id so it is never mistaken for a
17
+ * sort position. Ids preserved verbatim through migration need not match — those
18
+ * occupy their name without being part of the counter.
19
+ *
20
+ * Both sides of the storage seam test against this, so it lives with the rest of
21
+ * the vocabulary rather than in either of them.
22
+ */
23
+ export declare const MINTED_ID: RegExp;
24
+ /** A Ticket's position in the graph's lifecycle. Owned by the server. */
25
+ export type Status = 'open' | 'claimed' | 'resolved' | 'dropped';
26
+ export declare const STATUSES: readonly Status[];
27
+ /**
28
+ * A Ticket without its body — everything a Board line needs. Bodies are fetched
29
+ * separately, so a Board never pays for prose it does not show.
30
+ */
31
+ export interface TicketSummary {
32
+ /** `T<n>`, unique repo-wide. Absent on a Legacy Ticket that carries no id. */
33
+ readonly id: string | undefined;
34
+ /**
35
+ * How to name this Ticket in a call. The id when it has one; otherwise
36
+ * `<effort>#<order>`, which is the only handle an id-less Legacy Ticket has
37
+ * until migration mints it a real one.
38
+ */
39
+ readonly handle: string;
40
+ readonly title: string;
41
+ readonly kind: Kind;
42
+ /** `research` / `prototype` / `grilling` / `task`. Only when `kind` is `decision`. */
43
+ readonly type: string | undefined;
44
+ readonly status: Status;
45
+ /** A `/triage` label. A separate field from {@link status}. */
46
+ readonly triage: string | undefined;
47
+ /** Required when resolved — the one line that makes a Board of finished work readable. */
48
+ readonly answerGist: string | undefined;
49
+ /** Required when dropped. What the Map's Out-of-scope section renders. */
50
+ readonly droppedReason: string | undefined;
51
+ /** Who holds the claim, and when they took it. Set together. */
52
+ readonly claimedBy: string | undefined;
53
+ readonly claimedAt: string | undefined;
54
+ /** Edges: the Tickets that must finish first, as plain ids resolved repo-wide. */
55
+ readonly blockedBy: readonly string[];
56
+ /** The slug of the Effort that owns this Ticket. */
57
+ readonly effort: string;
58
+ /** Sort order within its Effort. Order only — never identity. */
59
+ readonly order: number;
60
+ /**
61
+ * A Ticket predating the schema, parsed best-effort. Its title, status, and
62
+ * Edges are inferences an agent should distrust.
63
+ */
64
+ readonly legacy: boolean;
65
+ /** The raw `Status:` text of a Legacy Ticket whose value did not map. */
66
+ readonly unrecognizedStatus: string | undefined;
67
+ /** Sub-slice references (`T38.1`) coarsened to the Ticket that exists. */
68
+ readonly collapsedRefs: readonly string[];
69
+ /**
70
+ * An opaque token for what was read. A write passes back the one it read and
71
+ * is refused if the stored Ticket has moved on. The markdown driver builds it
72
+ * from the file's modification time and size; another driver would use
73
+ * whatever its store offers, so nothing above the seam may parse it.
74
+ */
75
+ readonly revision: string;
76
+ }
77
+ /** A Ticket with its prose. */
78
+ export interface Ticket extends TicketSummary {
79
+ readonly body: string;
80
+ }
81
+ /**
82
+ * One Effort: enough to choose one without opening it. Frontier size is not a
83
+ * field — it is computed from the Tickets, like the Frontier itself.
84
+ */
85
+ export interface Effort {
86
+ /** The Effort's identity. Unique within a workspace. */
87
+ readonly slug: string;
88
+ /** Which Header docs the Effort holds, `map` before `spec`. */
89
+ readonly headerDocs: readonly HeaderDoc[];
90
+ /** How many Tickets the Effort holds. */
91
+ readonly ticketCount: number;
92
+ /**
93
+ * Where this line of enquiry is going, from the Map's Destination. Absent
94
+ * when the Effort has no Map, or a Map that has not been given one.
95
+ */
96
+ readonly destination: string | undefined;
97
+ /**
98
+ * The opening of the Effort's Spec. Not a Destination — a Spec has no such
99
+ * section — but it is the orienting text a Spec-only Effort does have, and a
100
+ * Board that opens with nothing leaves every Ticket line below uninterpretable.
101
+ */
102
+ readonly specOpening: string | undefined;
103
+ }
104
+ /**
105
+ * The mutation points on a Ticket, in the vocabulary rather than the storage
106
+ * format. A field left out is left alone; `null` clears one that was set.
107
+ *
108
+ * `answer` is a body mutation point rather than a field — a Ticket body is
109
+ * opaque apart from the three places the schema says are edited.
110
+ */
111
+ export interface TicketEdit {
112
+ readonly status?: Status;
113
+ readonly triage?: TriageRole;
114
+ readonly claimedBy?: string | null;
115
+ readonly claimedAt?: string | null;
116
+ readonly answerGist?: string | null;
117
+ readonly droppedReason?: string | null;
118
+ readonly answer?: string;
119
+ /** Replaces the Edge list outright. An empty list clears it. */
120
+ readonly blockedBy?: readonly string[];
121
+ /** Appended to the comment log, verbatim. Nothing is added around it. */
122
+ readonly comment?: string;
123
+ /** Acceptance criteria to tick, addressed by their text. */
124
+ readonly tick?: readonly string[];
125
+ }
126
+ /**
127
+ * A Ticket asked for but not yet minted an id.
128
+ *
129
+ * `key` is the caller's own temporary name for this draft, and exists only for
130
+ * the length of the call: sibling drafts declare Edges on it before any id
131
+ * exists, which is what removes the create-then-wire second pass. It is never
132
+ * stored, and never appears in a file.
133
+ */
134
+ export interface TicketDraft {
135
+ /**
136
+ * The caller's temporary name. Never `T<n>` — a key shaped like an id could
137
+ * not be told apart from the real one an Edge might mean.
138
+ */
139
+ readonly key: string | undefined;
140
+ readonly title: string;
141
+ readonly kind: Kind;
142
+ readonly type: string | undefined;
143
+ readonly triage: TriageRole | undefined;
144
+ /** Edges, each either a real Ticket id or a sibling draft's {@link key}. */
145
+ readonly blockedBy: readonly string[];
146
+ /** The prose, written verbatim. A draft with none produces a Ticket with none. */
147
+ readonly body: string | undefined;
148
+ }
149
+ /** The five roles `/triage` writes. A Status is never one of these. */
150
+ export declare const TRIAGE_ROLES: readonly ['needs-triage', 'needs-info', 'ready-for-agent', 'ready-for-human', 'wontfix'];
151
+ export type TriageRole = (typeof TRIAGE_ROLES)[number];
152
+ /**
153
+ * A Map's typed sections, without Decisions-so-far — that section is derived
154
+ * from resolved Tickets and never read from the file.
155
+ */
156
+ export interface MapDocument {
157
+ readonly destination: string | undefined;
158
+ readonly notes: string | undefined;
159
+ /** Fog patches under Not yet specified, in file order. */
160
+ readonly notYetSpecified: readonly string[];
161
+ /** Hand-ruled Out-of-scope items. Dropped Tickets are not here — they render. */
162
+ readonly outOfScope: readonly string[];
163
+ readonly revision: string;
164
+ }
165
+ /**
166
+ * A section mutation on a Map. A field left out is left alone. Decisions-so-far
167
+ * is never a field — it is regenerated from Tickets on every write.
168
+ */
169
+ export interface MapEdit {
170
+ readonly destination?: string;
171
+ readonly notes?: string;
172
+ /** Append a fog patch to Not yet specified. */
173
+ readonly addFog?: string;
174
+ /** Remove a fog patch matched on its text. Fails when none match. */
175
+ readonly graduateFog?: string;
176
+ /** Append a hand-ruled item to Out of scope. */
177
+ readonly ruleOut?: string;
178
+ }
179
+ /**
180
+ * A Spec as an opaque document. Nothing edits it section by section; the driver
181
+ * stores the bytes and the tool layer may present them with framing the store
182
+ * itself does not know about.
183
+ */
184
+ export interface SpecDocument {
185
+ /** Opaque Spec document as stored. */
186
+ readonly body: string;
187
+ readonly revision: string;
188
+ }
189
+ /** How a migration call may rewrite filenames. */
190
+ export interface MigrateOptions {
191
+ /**
192
+ * Report every change and write nothing. Planned mint ids come from the
193
+ * current scan (`max + 1`) without taking allocation guards.
194
+ */
195
+ readonly preview: boolean;
196
+ /**
197
+ * Rename Ticket files to `<NN>-T<n>-<slug>.md`. Off by default so relative
198
+ * links in Maps and Tickets keep resolving.
199
+ */
200
+ readonly rename: boolean;
201
+ }
202
+ /** One Ticket a migration would touch, or did. */
203
+ export interface MigrationChange {
204
+ /** Handle before the call — the id when it had one, else `<effort>#<order>`. */
205
+ readonly beforeHandle: string;
206
+ /** Id after migration — preserved or newly minted. */
207
+ readonly id: string;
208
+ readonly title: string;
209
+ /** True when this call (or preview) assigns a fresh id. */
210
+ readonly minted: boolean;
211
+ /** True when the file was Legacy and gains schema frontmatter. */
212
+ readonly normalized: boolean;
213
+ /**
214
+ * Cosmetic Ticket filenames (basenames only) when rename would move the file.
215
+ * Absent when filenames stay put. Not filesystem paths — those stay below the
216
+ * storage seam.
217
+ */
218
+ readonly fromName: string | undefined;
219
+ readonly toName: string | undefined;
220
+ /** Edges as they will be written after bare sort-order remapping. */
221
+ readonly blockedBy: readonly string[];
222
+ }
223
+ /** The result of migrating one Effort — enough for the tool to render a report. */
224
+ export interface MigrationReport {
225
+ readonly effort: string;
226
+ readonly preview: boolean;
227
+ readonly renamed: boolean;
228
+ readonly changes: readonly MigrationChange[];
229
+ }
230
+ //# sourceMappingURL=domain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../src/domain.ts"],"names":[],"mappings":"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"}
package/dist/domain.js ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The vocabulary from CONTEXT.md, as types. Everything above the storage driver
3
+ * speaks in these terms.
4
+ */
5
+ /**
6
+ * The shape of a minted id: `T<n>`, visibly an id so it is never mistaken for a
7
+ * sort position. Ids preserved verbatim through migration need not match — those
8
+ * occupy their name without being part of the counter.
9
+ *
10
+ * Both sides of the storage seam test against this, so it lives with the rest of
11
+ * the vocabulary rather than in either of them.
12
+ */
13
+ export const MINTED_ID = /^T(\d+)$/;
14
+ export const STATUSES = ['open', 'claimed', 'resolved', 'dropped'];
15
+ /** The five roles `/triage` writes. A Status is never one of these. */
16
+ export const TRIAGE_ROLES = [
17
+ 'needs-triage',
18
+ 'needs-info',
19
+ 'ready-for-agent',
20
+ 'ready-for-human',
21
+ 'wontfix',
22
+ ];
23
+ //# sourceMappingURL=domain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.js","sourceRoot":"","sources":["../src/domain.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAC;AAKpC,MAAM,CAAC,MAAM,QAAQ,GAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;AAiItF,uEAAuE;AACvE,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,cAAc;IACd,YAAY;IACZ,iBAAiB;IACjB,iBAAiB;IACjB,SAAS;CACD,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Edge-graph validation, above the storage seam because a cycle is a rule about
3
+ * the domain rather than about storage — the same reason the Status model is
4
+ * checked in the tool layer.
5
+ */
6
+ import type { TicketDraft } from './domain.ts';
7
+ /**
8
+ * How an Edge on a batch of drafts reads once the ids are real: a temporary key
9
+ * becomes the id its draft was given, and anything else is already an id.
10
+ *
11
+ * Both sides of the seam need this and must agree — the tool layer resolves to
12
+ * check for cycles, the driver resolves to write the frontmatter, and a graph
13
+ * approved under one resolution and written under another would be neither.
14
+ */
15
+ export declare function resolveDraftEdges(drafts: readonly TicketDraft[], ids: readonly string[]): (edge: string) => string;
16
+ /** The Edges leaving one node, by id. A node nothing knows about has none. */
17
+ export type EdgesOf = (id: string) => readonly string[];
18
+ /**
19
+ * The cycle running through `node`, as the path back to itself, or undefined
20
+ * when there is none.
21
+ *
22
+ * A cycle is rejected at write time because it makes the Frontier undefined:
23
+ * nothing in one is ever takeable, and no reader can tell which Edge was the
24
+ * mistake. It is checked on creation and on Edge updates alike, since the
25
+ * second is the easier way to close a loop.
26
+ *
27
+ * Only a cycle through `node` counts. A cycle elsewhere in the workspace is
28
+ * somebody's hand edit and not this caller's to answer for — refusing it here
29
+ * would make an unrelated Effort unwritable, and the Board already reports the
30
+ * graph damage it causes.
31
+ */
32
+ export declare function cycleThrough(node: string, edgesOf: EdgesOf): readonly string[] | undefined;
33
+ /** How a refused cycle reads: the loop, in the direction the Edges point. */
34
+ export declare function renderCycle(cycle: readonly string[]): string;
35
+ //# sourceMappingURL=edges.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edges.d.ts","sourceRoot":"","sources":["../src/edges.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,WAAW,EAAE,EAC9B,GAAG,EAAE,SAAS,MAAM,EAAE,GACrB,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAS1B;AAED,8EAA8E;AAC9E,MAAM,MAAM,OAAO,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE,CAAC;AAExD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAoB1F;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAE5D"}
package/dist/edges.js ADDED
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Edge-graph validation, above the storage seam because a cycle is a rule about
3
+ * the domain rather than about storage — the same reason the Status model is
4
+ * checked in the tool layer.
5
+ */
6
+ /**
7
+ * How an Edge on a batch of drafts reads once the ids are real: a temporary key
8
+ * becomes the id its draft was given, and anything else is already an id.
9
+ *
10
+ * Both sides of the seam need this and must agree — the tool layer resolves to
11
+ * check for cycles, the driver resolves to write the frontmatter, and a graph
12
+ * approved under one resolution and written under another would be neither.
13
+ */
14
+ export function resolveDraftEdges(drafts, ids) {
15
+ const byKey = new Map();
16
+ drafts.forEach((draft, index) => {
17
+ const id = ids[index];
18
+ if (draft.key !== undefined && id !== undefined)
19
+ byKey.set(draft.key, id);
20
+ });
21
+ return edge => byKey.get(edge) ?? edge;
22
+ }
23
+ /**
24
+ * The cycle running through `node`, as the path back to itself, or undefined
25
+ * when there is none.
26
+ *
27
+ * A cycle is rejected at write time because it makes the Frontier undefined:
28
+ * nothing in one is ever takeable, and no reader can tell which Edge was the
29
+ * mistake. It is checked on creation and on Edge updates alike, since the
30
+ * second is the easier way to close a loop.
31
+ *
32
+ * Only a cycle through `node` counts. A cycle elsewhere in the workspace is
33
+ * somebody's hand edit and not this caller's to answer for — refusing it here
34
+ * would make an unrelated Effort unwritable, and the Board already reports the
35
+ * graph damage it causes.
36
+ */
37
+ export function cycleThrough(node, edgesOf) {
38
+ // Reachability is monotone, so a node explored once and found not to reach
39
+ // `node` never will. That also stops a pre-existing cycle downstream from
40
+ // spinning forever.
41
+ const explored = new Set([node]);
42
+ const walk = (from, trail) => {
43
+ for (const next of edgesOf(from)) {
44
+ if (next === node)
45
+ return [...trail, node];
46
+ if (explored.has(next))
47
+ continue;
48
+ explored.add(next);
49
+ const found = walk(next, [...trail, next]);
50
+ if (found !== undefined)
51
+ return found;
52
+ }
53
+ return undefined;
54
+ };
55
+ return walk(node, [node]);
56
+ }
57
+ /** How a refused cycle reads: the loop, in the direction the Edges point. */
58
+ export function renderCycle(cycle) {
59
+ return cycle.join(' -> ');
60
+ }
61
+ //# sourceMappingURL=edges.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edges.js","sourceRoot":"","sources":["../src/edges.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAA8B,EAC9B,GAAsB;IAEtB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IAExC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC9B,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;QACtB,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,IAAI,EAAE,KAAK,SAAS;YAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AACzC,CAAC;AAKD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,OAAgB;IACzD,2EAA2E;IAC3E,0EAA0E;IAC1E,oBAAoB;IACpB,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAEzC,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,KAAwB,EAAiC,EAAE;QACrF,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,IAAI,IAAI,KAAK,IAAI;gBAAE,OAAO,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC;YAC3C,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAS;YACjC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAEnB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;YAC3C,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,KAAK,CAAC;QACxC,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;IAEF,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5B,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,WAAW,CAAC,KAAwB;IAClD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { TicketSummary } from './domain.ts';
2
+ /**
3
+ * The Frontier: the Tickets takeable right now — open, unblocked, unclaimed.
4
+ * Computed on every read, never stored, because a stored frontier is a second
5
+ * place for the truth to live.
6
+ *
7
+ * Edges resolve repo-wide, so this takes every Ticket in the workspace rather
8
+ * than one Effort's: a Ticket can be blocked by one that lives elsewhere.
9
+ */
10
+ export declare function frontierOf(all: readonly TicketSummary[]): ReadonlySet<TicketSummary>;
11
+ export declare function indexById(all: readonly TicketSummary[]): ReadonlyMap<string, TicketSummary>;
12
+ //# sourceMappingURL=frontier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontier.d.ts","sourceRoot":"","sources":["../src/frontier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,SAAS,aAAa,EAAE,GAAG,WAAW,CAAC,aAAa,CAAC,CAIpF;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,SAAS,aAAa,EAAE,GAAG,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAU3F"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The Frontier: the Tickets takeable right now — open, unblocked, unclaimed.
3
+ * Computed on every read, never stored, because a stored frontier is a second
4
+ * place for the truth to live.
5
+ *
6
+ * Edges resolve repo-wide, so this takes every Ticket in the workspace rather
7
+ * than one Effort's: a Ticket can be blocked by one that lives elsewhere.
8
+ */
9
+ export function frontierOf(all) {
10
+ const byId = indexById(all);
11
+ return new Set(all.filter(ticket => isTakeable(ticket, byId)));
12
+ }
13
+ export function indexById(all) {
14
+ const byId = new Map();
15
+ for (const ticket of all) {
16
+ // A Legacy Ticket may carry no id at all, and the first id wins so that a
17
+ // duplicate cannot silently displace the Ticket already answering to it.
18
+ if (ticket.id !== undefined && !byId.has(ticket.id))
19
+ byId.set(ticket.id, ticket);
20
+ }
21
+ return byId;
22
+ }
23
+ /**
24
+ * Status and Edges decide this, and nothing else. A Triage role never does:
25
+ * `/triage` and the graph must not compete for one field, so a Ticket labelled
26
+ * `wontfix` still counts as takeable here and the Board says so in its
27
+ * warnings, leaving the judgement to the reader.
28
+ */
29
+ function isTakeable(ticket, byId) {
30
+ if (ticket.status !== 'open')
31
+ return false;
32
+ return ticket.blockedBy.every(id => byId.get(id)?.status === 'resolved');
33
+ }
34
+ //# sourceMappingURL=frontier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontier.js","sourceRoot":"","sources":["../src/frontier.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,GAA6B;IACtD,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAE5B,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAA6B;IACrD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE9C,KAAK,MAAM,MAAM,IAAI,GAAG,EAAE,CAAC;QACzB,0EAA0E;QAC1E,yEAAyE;QACzE,IAAI,MAAM,CAAC,EAAE,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACnF,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU,CAAC,MAAqB,EAAE,IAAwC;IACjF,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IAE3C,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,KAAK,UAAU,CAAC,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { createFrontierMCP, SERVER_NAME, SERVER_VERSION } from './server.ts';
2
+ export type { CreateServerOptions, FrontierMCP } from './server.ts';
3
+ export type { Effort, HeaderDoc } from './domain.ts';
4
+ export type { StorageDriver } from './storage/driver.ts';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7E,YAAY,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACpE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACrD,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { createFrontierMCP, SERVER_NAME, SERVER_VERSION } from './server.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { StorageDriver } from './storage/driver.ts';
3
+ export declare const SERVER_NAME = "frontier";
4
+ export declare const SERVER_VERSION = "0.1.0";
5
+ export interface CreateServerOptions {
6
+ /** The server process's working directory. */
7
+ readonly cwd?: string;
8
+ readonly env?: Readonly<Partial<Record<string, string>>>;
9
+ /**
10
+ * The storage driver to bind to a workspace. Markdown in v1; the seam is here
11
+ * so a SQLite driver can be swapped in without touching the tool layer.
12
+ */
13
+ readonly createDriver?: (root: string) => StorageDriver;
14
+ /** Debounce filesystem watcher events. Tests may shorten this. */
15
+ readonly watcherDebounceMs?: number;
16
+ }
17
+ export interface FrontierMCP {
18
+ readonly server: McpServer;
19
+ /**
20
+ * Build the index for the session's own workspace. Calls resolve their own
21
+ * workspace anyway, so a failure here is not fatal — it only forfeits the
22
+ * warm start.
23
+ */
24
+ warmUp(): Promise<void>;
25
+ /** Stop watchers and close the transport. */
26
+ close(): Promise<void>;
27
+ }
28
+ export declare function createFrontierMCP(options?: CreateServerOptions): FrontierMCP;
29
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMpE,OAAO,KAAK,EAAE,aAAa,EAAqB,MAAM,qBAAqB,CAAC;AAwC5E,eAAO,MAAM,WAAW,aAAa,CAAC;AACtC,eAAO,MAAM,cAAc,UAAU,CAAC;AAEtC,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACzD;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,aAAa,CAAC;IACxD,kEAAkE;IAClE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B;;;;OAIG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,6CAA6C;IAC7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAOD,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,mBAAwB,GAAG,WAAW,CAmRhF"}