frontier-mcp 0.1.0 → 0.2.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.
- package/README.md +10 -5
- package/dist/driver-registry.d.ts +15 -0
- package/dist/driver-registry.d.ts.map +1 -0
- package/dist/driver-registry.js +19 -0
- package/dist/driver-registry.js.map +1 -0
- package/dist/server.d.ts +7 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +57 -38
- package/dist/server.js.map +1 -1
- package/dist/storage/driver.d.ts +41 -8
- package/dist/storage/driver.d.ts.map +1 -1
- package/dist/storage/driver.js.map +1 -1
- package/dist/storage/markdown/create.d.ts +5 -5
- package/dist/storage/markdown/create.d.ts.map +1 -1
- package/dist/storage/markdown/create.js +17 -17
- package/dist/storage/markdown/create.js.map +1 -1
- package/dist/storage/markdown/driver.d.ts +35 -1
- package/dist/storage/markdown/driver.d.ts.map +1 -1
- package/dist/storage/markdown/driver.js +155 -84
- package/dist/storage/markdown/driver.js.map +1 -1
- package/dist/storage/markdown/migrate.d.ts +4 -4
- package/dist/storage/markdown/migrate.d.ts.map +1 -1
- package/dist/storage/markdown/migrate.js +1 -1
- package/dist/storage/markdown/migrate.js.map +1 -1
- package/dist/storage/markdown/ticket.d.ts +16 -5
- package/dist/storage/markdown/ticket.d.ts.map +1 -1
- package/dist/storage/markdown/ticket.js +19 -7
- package/dist/storage/markdown/ticket.js.map +1 -1
- package/dist/storage/markdown/watcher.d.ts +33 -0
- package/dist/storage/markdown/watcher.d.ts.map +1 -0
- package/dist/storage/markdown/watcher.js +142 -0
- package/dist/storage/markdown/watcher.js.map +1 -0
- package/dist/tools/create-tickets.d.ts +4 -4
- package/dist/tools/create-tickets.d.ts.map +1 -1
- package/dist/tools/create-tickets.js.map +1 -1
- package/dist/tools/list-efforts.d.ts.map +1 -1
- package/dist/tools/list-efforts.js +2 -1
- package/dist/tools/list-efforts.js.map +1 -1
- package/dist/tools/update-ticket.d.ts +3 -3
- package/dist/tools/update-ticket.d.ts.map +1 -1
- package/dist/tools/update-ticket.js.map +1 -1
- package/dist/tools/workspace-line.d.ts +18 -0
- package/dist/tools/workspace-line.d.ts.map +1 -0
- package/dist/tools/workspace-line.js +22 -0
- package/dist/tools/workspace-line.js.map +1 -0
- package/dist/workspace.d.ts +5 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +42 -5
- package/dist/workspace.js.map +1 -1
- package/docs/agents/issue-tracker.md +14 -2
- package/package.json +1 -1
- package/dist/workspace-index.d.ts +0 -39
- package/dist/workspace-index.d.ts.map +0 -1
- package/dist/workspace-index.js +0 -83
- package/dist/workspace-index.js.map +0 -1
- package/dist/workspace-watcher.d.ts +0 -24
- package/dist/workspace-watcher.d.ts.map +0 -1
- package/dist/workspace-watcher.js +0 -87
- package/dist/workspace-watcher.js.map +0 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one rendering of which repository a call served, so `list_efforts` and
|
|
3
|
+
* every write say it the same way and in the same place.
|
|
4
|
+
*
|
|
5
|
+
* A read has never needed it for safety — wrong repository, visibly wrong
|
|
6
|
+
* content. A write is the opposite: Ticket ids are not repo-unique, since every
|
|
7
|
+
* Effort numbers from T1, so `T10 updated` is exactly as plausible against the
|
|
8
|
+
* wrong repository as the right one. It is the one result a caller cannot check
|
|
9
|
+
* from what it got back, which is how the T10 worktree escape stayed invisible.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Labelled `root:` after the argument that overrides it, so the line names the
|
|
13
|
+
* knob a caller would reach for to change it.
|
|
14
|
+
*/
|
|
15
|
+
export declare function workspaceLine(workspace: string): string;
|
|
16
|
+
/** A result that names the workspace that produced it, above the result itself. */
|
|
17
|
+
export declare function withWorkspace(workspace: string, body: string): string;
|
|
18
|
+
//# sourceMappingURL=workspace-line.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-line.d.ts","sourceRoot":"","sources":["../../src/tools/workspace-line.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAErE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one rendering of which repository a call served, so `list_efforts` and
|
|
3
|
+
* every write say it the same way and in the same place.
|
|
4
|
+
*
|
|
5
|
+
* A read has never needed it for safety — wrong repository, visibly wrong
|
|
6
|
+
* content. A write is the opposite: Ticket ids are not repo-unique, since every
|
|
7
|
+
* Effort numbers from T1, so `T10 updated` is exactly as plausible against the
|
|
8
|
+
* wrong repository as the right one. It is the one result a caller cannot check
|
|
9
|
+
* from what it got back, which is how the T10 worktree escape stayed invisible.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Labelled `root:` after the argument that overrides it, so the line names the
|
|
13
|
+
* knob a caller would reach for to change it.
|
|
14
|
+
*/
|
|
15
|
+
export function workspaceLine(workspace) {
|
|
16
|
+
return `root: ${workspace}`;
|
|
17
|
+
}
|
|
18
|
+
/** A result that names the workspace that produced it, above the result itself. */
|
|
19
|
+
export function withWorkspace(workspace, body) {
|
|
20
|
+
return `${workspaceLine(workspace)}\n${body}`;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=workspace-line.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-line.js","sourceRoot":"","sources":["../../src/tools/workspace-line.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,OAAO,SAAS,SAAS,EAAE,CAAC;AAC9B,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,aAAa,CAAC,SAAiB,EAAE,IAAY;IAC3D,OAAO,GAAG,aAAa,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC;AAChD,CAAC"}
|
package/dist/workspace.d.ts
CHANGED
|
@@ -10,7 +10,11 @@ export interface WorkspaceContext {
|
|
|
10
10
|
*
|
|
11
11
|
* 1. an explicit `root` argument on the call,
|
|
12
12
|
* 2. the `FRONTIER_ROOT` environment variable,
|
|
13
|
-
* 3. the working directory, walked upward to the nearest
|
|
13
|
+
* 3. the working directory, walked upward to the nearest root marker.
|
|
14
|
+
*
|
|
15
|
+
* The working directory is the one the client launched this process in, and it
|
|
16
|
+
* is fixed for the session. A client that moves to another worktree mid-session
|
|
17
|
+
* retargets a call with `root`, or restarts the server; nothing here follows it.
|
|
14
18
|
*
|
|
15
19
|
* MCP Roots is deliberately not used: it is deprecated as of protocol revision
|
|
16
20
|
* `2026-07-28` (SEP-2577), which directs implementations to pass directories
|
package/dist/workspace.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAGA,iFAAiF;AACjF,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAGA,iFAAiF;AACjF,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAuBjD,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;CACzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAY5F"}
|
package/dist/workspace.js
CHANGED
|
@@ -1,15 +1,37 @@
|
|
|
1
|
-
import { statSync } from 'node:fs';
|
|
1
|
+
import { lstatSync, statSync } from 'node:fs';
|
|
2
2
|
import { dirname, join, resolve } from 'node:path';
|
|
3
3
|
/** The environment variable that overrides the workspace for a whole session. */
|
|
4
4
|
export const WORKSPACE_ENV_VAR = 'FRONTIER_ROOT';
|
|
5
|
-
/**
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* A directory carrying one of these is the repository root we serve.
|
|
7
|
+
*
|
|
8
|
+
* `.scratch` counts only as a directory — it is the tracker itself. `.git`
|
|
9
|
+
* counts either way: a git worktree and a submodule both carry it as a *file*
|
|
10
|
+
* naming the real git directory. Since a worktree sits nested inside the
|
|
11
|
+
* repository it was made from, a directory-only marker walks straight past it
|
|
12
|
+
* and serves the parent — writing into the working copy the worktree exists to
|
|
13
|
+
* isolate.
|
|
14
|
+
*
|
|
15
|
+
* These stay constants while the markdown driver takes its storage directory as
|
|
16
|
+
* a construction parameter. The upward walk asks "is this a repository this tool
|
|
17
|
+
* serves", which is a different question from "what does a driver call its
|
|
18
|
+
* storage" — and it is asked before any driver exists to be asked, so it cannot
|
|
19
|
+
* come from one.
|
|
20
|
+
*/
|
|
21
|
+
const ROOT_MARKERS = [
|
|
22
|
+
{ name: '.scratch', matches: isDirectory },
|
|
23
|
+
{ name: '.git', matches: exists },
|
|
24
|
+
];
|
|
7
25
|
/**
|
|
8
26
|
* Resolve which workspace a call serves. First match wins:
|
|
9
27
|
*
|
|
10
28
|
* 1. an explicit `root` argument on the call,
|
|
11
29
|
* 2. the `FRONTIER_ROOT` environment variable,
|
|
12
|
-
* 3. the working directory, walked upward to the nearest
|
|
30
|
+
* 3. the working directory, walked upward to the nearest root marker.
|
|
31
|
+
*
|
|
32
|
+
* The working directory is the one the client launched this process in, and it
|
|
33
|
+
* is fixed for the session. A client that moves to another worktree mid-session
|
|
34
|
+
* retargets a call with `root`, or restarts the server; nothing here follows it.
|
|
13
35
|
*
|
|
14
36
|
* MCP Roots is deliberately not used: it is deprecated as of protocol revision
|
|
15
37
|
* `2026-07-28` (SEP-2577), which directs implementations to pass directories
|
|
@@ -45,7 +67,7 @@ function given(value) {
|
|
|
45
67
|
function findRootMarker(from) {
|
|
46
68
|
let dir = from;
|
|
47
69
|
for (;;) {
|
|
48
|
-
if (ROOT_MARKERS.some(marker =>
|
|
70
|
+
if (ROOT_MARKERS.some(marker => marker.matches(join(dir, marker.name))))
|
|
49
71
|
return dir;
|
|
50
72
|
const parent = dirname(dir);
|
|
51
73
|
if (parent === dir)
|
|
@@ -59,6 +81,7 @@ function requireDirectory(path, source) {
|
|
|
59
81
|
}
|
|
60
82
|
return path;
|
|
61
83
|
}
|
|
84
|
+
/** Follows symlinks: a `.scratch` pointed at shared storage is still the tracker. */
|
|
62
85
|
function isDirectory(path) {
|
|
63
86
|
try {
|
|
64
87
|
return statSync(path).isDirectory();
|
|
@@ -67,4 +90,18 @@ function isDirectory(path) {
|
|
|
67
90
|
return false;
|
|
68
91
|
}
|
|
69
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Deliberately does not follow symlinks — the question is whether an entry is
|
|
95
|
+
* there at all, not what it leads to. A dangling `.git` symlink still marks a
|
|
96
|
+
* repository root, and reading it as absent would walk on into the parent.
|
|
97
|
+
*/
|
|
98
|
+
function exists(path) {
|
|
99
|
+
try {
|
|
100
|
+
lstatSync(path);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
70
107
|
//# sourceMappingURL=workspace.js.map
|
package/dist/workspace.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,iFAAiF;AACjF,MAAM,CAAC,MAAM,iBAAiB,GAAG,eAAe,CAAC;AAEjD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,YAAY,GAAG;IACnB,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE;IAC1C,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;CAClC,CAAC;AAQF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAwB,EAAE,OAAyB;IAClF,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,eAAe,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACtD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AACtE,CAAC;AAED;;;;GAIG;AACH,SAAS,KAAK,CAAC,KAAyB;IACtC,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;AACjE,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,IAAY;IAClC,IAAI,GAAG,GAAG,IAAI,CAAC;IAEf,SAAS,CAAC;QACR,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QAEpF,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,SAAS,CAAC;QACrC,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY,EAAE,MAAc;IACpD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,kBAAkB,MAAM,wBAAwB,IAAI,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,qFAAqF;AACrF,SAAS,WAAW,CAAC,IAAY;IAC/B,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,MAAM,CAAC,IAAY;IAC1B,IAAI,CAAC;QACH,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -16,12 +16,24 @@ FrontierMCP resolves which repository it serves on every call:
|
|
|
16
16
|
|
|
17
17
|
1. An explicit `root` argument on the call.
|
|
18
18
|
2. The `FRONTIER_ROOT` environment variable.
|
|
19
|
-
3. The server process working directory, walked upward to the nearest directory containing
|
|
20
|
-
or `.git
|
|
19
|
+
3. The server process working directory, walked upward to the nearest directory containing a
|
|
20
|
+
`.scratch/` directory, or `.git` in either form — a git worktree and a submodule carry `.git` as a
|
|
21
|
+
file, and each is its own workspace rather than part of the repository enclosing it.
|
|
21
22
|
|
|
22
23
|
Register FrontierMCP once at user scope. Opening a project is the only per-repo setup step — no
|
|
23
24
|
`.cursor/mcp.json` entry is required in each repository.
|
|
24
25
|
|
|
26
|
+
**The working directory is the one your client launched the server in, and it is fixed for the
|
|
27
|
+
session.** Moving to another worktree mid-session does not retarget it: pass `root` on the call, set
|
|
28
|
+
`FRONTIER_ROOT`, or restart the server. A worktree whose branch carries no `.scratch/` of its own
|
|
29
|
+
reports no Efforts — that is the worktree being served correctly, not the tracker going missing.
|
|
30
|
+
|
|
31
|
+
**Every call that writes opens with `root: <path>`**, naming the repository it just changed. Read it
|
|
32
|
+
— a Ticket id is not repo-unique, so `T10 updated` looks the same whichever repository answered, and
|
|
33
|
+
this line is the only part of the result that tells you. Reads do not carry it, and neither do the
|
|
34
|
+
read paths of write-capable tools: `edit_map` with no section fields, `spec` with no `content`, and
|
|
35
|
+
`migrate_effort` with `preview`.
|
|
36
|
+
|
|
25
37
|
A repo with no `.scratch/` yet is not an error. Create an Effort by writing to it with `create: true`
|
|
26
38
|
on `create_tickets`, `edit_map`, or `spec`.
|
|
27
39
|
|
package/package.json
CHANGED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { Effort, MapDocument, MapEdit, MigrateOptions, MigrationReport, SpecDocument, Ticket, TicketDraft } from './domain.ts';
|
|
2
|
-
import type { CreateOptions, HeaderDocOptions, StorageDriver, TicketEdit, TicketWriteResult } from './storage/driver.ts';
|
|
3
|
-
/**
|
|
4
|
-
* The in-memory index: a derived, rebuildable view of one workspace, built by a
|
|
5
|
-
* full scan. At the volumes involved a full scan is single-digit milliseconds,
|
|
6
|
-
* so the index rebuilds wholesale rather than tracking entries individually.
|
|
7
|
-
*
|
|
8
|
-
* A filesystem watcher invalidates the scan when `.scratch/` changes on disk,
|
|
9
|
-
* so the next read rebuilds without restarting the process.
|
|
10
|
-
*/
|
|
11
|
-
export interface WorkspaceIndex {
|
|
12
|
-
efforts(): Promise<readonly Effort[]>;
|
|
13
|
-
tickets(): Promise<readonly Ticket[]>;
|
|
14
|
-
/** Apply an edit and discard the scan, since the workspace has moved on. */
|
|
15
|
-
update(handle: string, edit: TicketEdit, expectedRevision: string): Promise<TicketWriteResult>;
|
|
16
|
-
/** Create a batch of Tickets and discard the scan, for the same reason. */
|
|
17
|
-
create(effort: string, drafts: readonly TicketDraft[], options: CreateOptions): Promise<readonly Ticket[]>;
|
|
18
|
-
readMap(effort: string): Promise<MapDocument>;
|
|
19
|
-
editMap(effort: string, edit: MapEdit, expectedRevision: string | undefined, options: HeaderDocOptions): Promise<MapDocument>;
|
|
20
|
-
readSpec(effort: string): Promise<SpecDocument>;
|
|
21
|
-
putSpec(effort: string, body: string, expectedRevision: string | undefined, options: HeaderDocOptions): Promise<SpecDocument>;
|
|
22
|
-
migrate(effort: string, options: MigrateOptions): Promise<MigrationReport>;
|
|
23
|
-
/** Discard cached scans so the next read rebuilds from disk. */
|
|
24
|
-
invalidate(): void;
|
|
25
|
-
}
|
|
26
|
-
export declare function createWorkspaceIndex(driver: StorageDriver): WorkspaceIndex;
|
|
27
|
-
/**
|
|
28
|
-
* One index per workspace. A call carrying an explicit `root` reads a different
|
|
29
|
-
* workspace from the session's own, and each keeps its own scan.
|
|
30
|
-
*/
|
|
31
|
-
export interface IndexRegistry {
|
|
32
|
-
forWorkspace(root: string): WorkspaceIndex;
|
|
33
|
-
closeAll(): void;
|
|
34
|
-
}
|
|
35
|
-
export interface IndexRegistryOptions {
|
|
36
|
-
readonly watcherDebounceMs?: number;
|
|
37
|
-
}
|
|
38
|
-
export declare function createIndexRegistry(createDriver: (root: string) => StorageDriver, options?: IndexRegistryOptions): IndexRegistry;
|
|
39
|
-
//# sourceMappingURL=workspace-index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-index.d.ts","sourceRoot":"","sources":["../src/workspace-index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,WAAW,EACX,OAAO,EACP,cAAc,EACd,eAAe,EACf,YAAY,EACZ,MAAM,EACN,WAAW,EACZ,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAG7B;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,IAAI,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IACtC,OAAO,IAAI,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IACtC,4EAA4E;IAC5E,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC/F,2EAA2E;IAC3E,MAAM,CACJ,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,SAAS,WAAW,EAAE,EAC9B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IAC9B,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9C,OAAO,CACL,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,WAAW,CAAC,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAChD,OAAO,CACL,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,YAAY,CAAC,CAAC;IACzB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3E,gEAAgE;IAChE,UAAU,IAAI,IAAI,CAAC;CACpB;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,cAAc,CA2C1E;AAuBD;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAAC;IAC3C,QAAQ,IAAI,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,aAAa,EAC7C,OAAO,GAAE,oBAAyB,GACjC,aAAa,CAsBf"}
|
package/dist/workspace-index.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { createWatcherRegistry } from './workspace-watcher.js';
|
|
2
|
-
export function createWorkspaceIndex(driver) {
|
|
3
|
-
const efforts = cache(() => driver.listEfforts());
|
|
4
|
-
const tickets = cache(() => driver.listTickets());
|
|
5
|
-
const invalidate = () => {
|
|
6
|
-
efforts.invalidate();
|
|
7
|
-
tickets.invalidate();
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* A write moves the workspace on whether or not it succeeded partway, so the
|
|
11
|
-
* next read rebuilds rather than trusting a scan taken before it.
|
|
12
|
-
*/
|
|
13
|
-
async function moved(operation) {
|
|
14
|
-
try {
|
|
15
|
-
return await operation();
|
|
16
|
-
}
|
|
17
|
-
finally {
|
|
18
|
-
invalidate();
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return {
|
|
22
|
-
efforts: efforts.get,
|
|
23
|
-
tickets: tickets.get,
|
|
24
|
-
async update(handle, edit, expectedRevision) {
|
|
25
|
-
return moved(() => driver.updateTicket(handle, edit, expectedRevision));
|
|
26
|
-
},
|
|
27
|
-
async create(effort, drafts, options) {
|
|
28
|
-
return moved(() => driver.createTickets(effort, drafts, options));
|
|
29
|
-
},
|
|
30
|
-
readMap: effort => driver.readMap(effort),
|
|
31
|
-
async editMap(effort, edit, expectedRevision, options) {
|
|
32
|
-
return moved(() => driver.editMap(effort, edit, expectedRevision, options));
|
|
33
|
-
},
|
|
34
|
-
readSpec: effort => driver.readSpec(effort),
|
|
35
|
-
async putSpec(effort, body, expectedRevision, options) {
|
|
36
|
-
return moved(() => driver.putSpec(effort, body, expectedRevision, options));
|
|
37
|
-
},
|
|
38
|
-
async migrate(effort, options) {
|
|
39
|
-
return moved(() => driver.migrateEffort(effort, options));
|
|
40
|
-
},
|
|
41
|
-
invalidate,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Hold the in-flight scan, not just its result, so concurrent callers share one
|
|
46
|
-
* scan instead of racing several. A failed scan is never cached as the answer.
|
|
47
|
-
*/
|
|
48
|
-
function cache(scan) {
|
|
49
|
-
let pending;
|
|
50
|
-
return {
|
|
51
|
-
get: () => {
|
|
52
|
-
pending ??= scan().catch((error) => {
|
|
53
|
-
pending = undefined;
|
|
54
|
-
throw error;
|
|
55
|
-
});
|
|
56
|
-
return pending;
|
|
57
|
-
},
|
|
58
|
-
invalidate: () => {
|
|
59
|
-
pending = undefined;
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
export function createIndexRegistry(createDriver, options = {}) {
|
|
64
|
-
const indexes = new Map();
|
|
65
|
-
const watchers = createWatcherRegistry(options.watcherDebounceMs === undefined ? {} : { debounceMs: options.watcherDebounceMs });
|
|
66
|
-
return {
|
|
67
|
-
forWorkspace(root) {
|
|
68
|
-
let index = indexes.get(root);
|
|
69
|
-
if (index === undefined) {
|
|
70
|
-
const created = createWorkspaceIndex(createDriver(root));
|
|
71
|
-
indexes.set(root, created);
|
|
72
|
-
watchers.attach(root, () => created.invalidate());
|
|
73
|
-
index = created;
|
|
74
|
-
}
|
|
75
|
-
return index;
|
|
76
|
-
},
|
|
77
|
-
closeAll() {
|
|
78
|
-
watchers.closeAll();
|
|
79
|
-
indexes.clear();
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
//# sourceMappingURL=workspace-index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-index.js","sourceRoot":"","sources":["../src/workspace-index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export interface WorkspaceWatcher {
|
|
2
|
-
close(): void;
|
|
3
|
-
}
|
|
4
|
-
export interface WorkspaceWatcherOptions {
|
|
5
|
-
/** Debounce rapid events before invalidating. Defaults to 50ms. */
|
|
6
|
-
readonly debounceMs?: number;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Watch `.scratch/` under a resolved workspace and call `invalidate` when it
|
|
10
|
-
* changes. Never writes — only schedules invalidation after debouncing.
|
|
11
|
-
*
|
|
12
|
-
* If `.scratch/` does not exist yet, the workspace root is watched until the
|
|
13
|
-
* directory appears, then the recursive scratch watch is attached.
|
|
14
|
-
*/
|
|
15
|
-
export declare function watchWorkspace(root: string, invalidate: () => void, options?: WorkspaceWatcherOptions): WorkspaceWatcher;
|
|
16
|
-
/**
|
|
17
|
-
* One watcher per workspace root. Started when an index is first opened.
|
|
18
|
-
*/
|
|
19
|
-
export interface WatcherRegistry {
|
|
20
|
-
attach(root: string, invalidate: () => void): void;
|
|
21
|
-
closeAll(): void;
|
|
22
|
-
}
|
|
23
|
-
export declare function createWatcherRegistry(options?: WorkspaceWatcherOptions): WatcherRegistry;
|
|
24
|
-
//# sourceMappingURL=workspace-watcher.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-watcher.d.ts","sourceRoot":"","sources":["../src/workspace-watcher.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,mEAAmE;IACnE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,IAAI,EACtB,OAAO,GAAE,uBAA4B,GACpC,gBAAgB,CA2DlB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IACnD,QAAQ,IAAI,IAAI,CAAC;CAClB;AAED,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,uBAA4B,GAAG,eAAe,CAa5F"}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { existsSync, watch } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
const SCRATCH = '.scratch';
|
|
4
|
-
/**
|
|
5
|
-
* Watch `.scratch/` under a resolved workspace and call `invalidate` when it
|
|
6
|
-
* changes. Never writes — only schedules invalidation after debouncing.
|
|
7
|
-
*
|
|
8
|
-
* If `.scratch/` does not exist yet, the workspace root is watched until the
|
|
9
|
-
* directory appears, then the recursive scratch watch is attached.
|
|
10
|
-
*/
|
|
11
|
-
export function watchWorkspace(root, invalidate, options = {}) {
|
|
12
|
-
const debounceMs = options.debounceMs ?? 50;
|
|
13
|
-
const scratchPath = join(root, SCRATCH);
|
|
14
|
-
let timer;
|
|
15
|
-
let scratchWatcher;
|
|
16
|
-
let rootWatcher;
|
|
17
|
-
const scheduleInvalidate = () => {
|
|
18
|
-
if (timer !== undefined)
|
|
19
|
-
clearTimeout(timer);
|
|
20
|
-
timer = setTimeout(() => {
|
|
21
|
-
timer = undefined;
|
|
22
|
-
invalidate();
|
|
23
|
-
}, debounceMs);
|
|
24
|
-
};
|
|
25
|
-
const attachScratchWatcher = () => {
|
|
26
|
-
if (scratchWatcher !== undefined || !existsSync(scratchPath))
|
|
27
|
-
return;
|
|
28
|
-
try {
|
|
29
|
-
scratchWatcher = watch(scratchPath, { recursive: true }, () => {
|
|
30
|
-
scheduleInvalidate();
|
|
31
|
-
});
|
|
32
|
-
scratchWatcher.on('error', () => {
|
|
33
|
-
scratchWatcher?.close();
|
|
34
|
-
scratchWatcher = undefined;
|
|
35
|
-
});
|
|
36
|
-
rootWatcher?.close();
|
|
37
|
-
rootWatcher = undefined;
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
// `.scratch/` may have disappeared between the exists check and watch.
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
attachScratchWatcher();
|
|
44
|
-
if (scratchWatcher === undefined) {
|
|
45
|
-
try {
|
|
46
|
-
rootWatcher = watch(root, (_event, filename) => {
|
|
47
|
-
if (filename !== SCRATCH)
|
|
48
|
-
return;
|
|
49
|
-
attachScratchWatcher();
|
|
50
|
-
scheduleInvalidate();
|
|
51
|
-
});
|
|
52
|
-
rootWatcher.on('error', () => {
|
|
53
|
-
rootWatcher?.close();
|
|
54
|
-
rootWatcher = undefined;
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
// Unusual, but a missing workspace root should not wedge the server.
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
close() {
|
|
63
|
-
if (timer !== undefined)
|
|
64
|
-
clearTimeout(timer);
|
|
65
|
-
scratchWatcher?.close();
|
|
66
|
-
rootWatcher?.close();
|
|
67
|
-
scratchWatcher = undefined;
|
|
68
|
-
rootWatcher = undefined;
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
export function createWatcherRegistry(options = {}) {
|
|
73
|
-
const watchers = new Map();
|
|
74
|
-
return {
|
|
75
|
-
attach(root, invalidate) {
|
|
76
|
-
if (watchers.has(root))
|
|
77
|
-
return;
|
|
78
|
-
watchers.set(root, watchWorkspace(root, invalidate, options));
|
|
79
|
-
},
|
|
80
|
-
closeAll() {
|
|
81
|
-
for (const watcher of watchers.values())
|
|
82
|
-
watcher.close();
|
|
83
|
-
watchers.clear();
|
|
84
|
-
},
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
//# sourceMappingURL=workspace-watcher.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-watcher.js","sourceRoot":"","sources":["../src/workspace-watcher.ts"],"names":[],"mappings":"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"}
|