@noesis-brain/mcp-server 2.0.1 → 2.1.1

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 (45) hide show
  1. package/README.md +91 -3
  2. package/dist/agent/runner.d.ts +29 -0
  3. package/dist/agent/runner.d.ts.map +1 -0
  4. package/dist/agent/runner.js +306 -0
  5. package/dist/agent/runner.js.map +1 -0
  6. package/dist/api/NoesisClient.d.ts +119 -0
  7. package/dist/api/NoesisClient.d.ts.map +1 -1
  8. package/dist/api/NoesisClient.js +130 -6
  9. package/dist/api/NoesisClient.js.map +1 -1
  10. package/dist/cli/setup.d.ts.map +1 -1
  11. package/dist/cli/setup.js +409 -35
  12. package/dist/cli/setup.js.map +1 -1
  13. package/dist/index.js +31 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/resolve/noteRef.d.ts +101 -0
  16. package/dist/resolve/noteRef.d.ts.map +1 -0
  17. package/dist/resolve/noteRef.js +186 -0
  18. package/dist/resolve/noteRef.js.map +1 -0
  19. package/dist/resolve/rootsFormat.d.ts +12 -0
  20. package/dist/resolve/rootsFormat.d.ts.map +1 -0
  21. package/dist/resolve/rootsFormat.js +63 -0
  22. package/dist/resolve/rootsFormat.js.map +1 -0
  23. package/dist/tools/backlog.d.ts +4 -0
  24. package/dist/tools/backlog.d.ts.map +1 -0
  25. package/dist/tools/backlog.js +161 -0
  26. package/dist/tools/backlog.js.map +1 -0
  27. package/dist/tools/index.d.ts +92 -1
  28. package/dist/tools/index.d.ts.map +1 -1
  29. package/dist/tools/index.js +529 -207
  30. package/dist/tools/index.js.map +1 -1
  31. package/dist/tools/navis.d.ts.map +1 -1
  32. package/dist/tools/navis.js +4 -0
  33. package/dist/tools/navis.js.map +1 -1
  34. package/package.json +8 -3
  35. package/scripts/agent-e2e.mjs +69 -0
  36. package/scripts/noesis-capture-session-end.mjs +94 -0
  37. package/scripts/noesis-capture-watcher.mjs +953 -0
  38. package/skill-templates/noesis-capture.md +166 -0
  39. package/skill-templates/noesis-create-study-note.md +149 -0
  40. package/skill-templates/noesis-find-related.md +89 -0
  41. package/skill-templates/noesis-investigate.md +90 -0
  42. package/skill-templates/noesis-manage-news.md +80 -0
  43. package/skill-templates/noesis-skim-read.md +72 -0
  44. package/templates/claude-md-block.md +5 -1
  45. package/templates/claude-md-restructure-on-sync.md +10 -0
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Unified note-reference resolution (single-vault refactor, M1).
3
+ *
4
+ * Pure logic — no network, no filesystem, no process state. Everything the
5
+ * resolver needs arrives in a ResolverContext built by the caller (M2 wires
6
+ * this to `GET /api/mcp/roots?includeArchived=1`, which since V3 returns the
7
+ * archived translation entries plus `{vault_root_id, device_home_dirs}`).
8
+ *
9
+ * Accepted reference forms (the plan's reference grammar):
10
+ * - note URLs on any host: https://noesisbrain.com/notes/2761[#bm=...]
11
+ * - noesis://note/2761[/optional-slug]
12
+ * - bare numeric ids: "2761"
13
+ * - absolute paths of ANY OS shape: /Users/me/Noesis/x.md,
14
+ * c:/Users/me/Noesis/x.md, C:\temp_cGit\.noesis\x.md
15
+ * - tilde / %USERPROFILE% forms: ~/Noesis/x.md, %USERPROFILE%\Noesis\x.md
16
+ * - LEGACY pre-migration absolute paths, translated through archived roots
17
+ * (vault_subfolder + '.noesis/'-collapse candidates, collapsed first)
18
+ *
19
+ * Fixes the pre-M1 footgun where a root with an empty current-OS path
20
+ * resolved to process.cwd(): empty/missing slots never produce a comparable
21
+ * form here, and local paths are composed (never path.resolve'd) from a
22
+ * verified non-empty base.
23
+ */
24
+ export type OsKey = 'win32' | 'darwin' | 'linux';
25
+ export interface ResolverRoot {
26
+ id: number;
27
+ name: string;
28
+ local_paths: Record<string, string>;
29
+ archived_at?: string | null;
30
+ vault_subfolder?: string | null;
31
+ }
32
+ export interface ResolverContext {
33
+ /** This machine's OS family (CLIENT_OS). */
34
+ clientOs: OsKey;
35
+ /** This machine's home directory (os.homedir()). */
36
+ homeDir: string;
37
+ /** The user's designated vault root id (V3 roots meta), if known. */
38
+ vaultRootId: number | null;
39
+ /** Per-OS home dirs of the user's machines (V3 roots meta), if known. */
40
+ deviceHomeDirs: Record<string, string> | null;
41
+ /** Active + archived roots (GET /roots?includeArchived=1). */
42
+ roots: ResolverRoot[];
43
+ }
44
+ export type NoteRef = {
45
+ kind: 'id';
46
+ id: number;
47
+ } | {
48
+ kind: 'path';
49
+ path: string;
50
+ };
51
+ export interface ResolvedPathRef {
52
+ /** Root to look candidates up under (the matched root, or the vault). */
53
+ rootId: number;
54
+ /** relative_path candidates, in lookup priority order. */
55
+ candidates: string[];
56
+ via: 'active-root' | 'legacy-translation';
57
+ /** Set when the input matched an archived root. */
58
+ archivedRootId?: number;
59
+ }
60
+ /** Forward slashes + lowercase drive letter (win32 canonical compare form). */
61
+ export declare function normalizePath(p: string): string;
62
+ /** Shape-sniff which OS family a path belongs to (mirrors the backend). */
63
+ export declare function detectPathOsKey(p: string): OsKey;
64
+ /**
65
+ * Parse an input string into a note reference. URLs and ids resolve by note
66
+ * id (the caller fetches the note to learn its relative_path); everything
67
+ * else is treated as a path.
68
+ */
69
+ export declare function parseNoteReference(input: string): NoteRef;
70
+ interface ComparableForm {
71
+ cmp: string;
72
+ orig: string;
73
+ }
74
+ /**
75
+ * Comparable spellings of one path under an OS family: the normalized raw
76
+ * form plus one home-expanded form per known home dir for that family (this
77
+ * machine's home when the family is the client OS, and the cloud-cached
78
+ * device home). Empty inputs produce NO forms (the cwd-footgun fix).
79
+ */
80
+ export declare function comparableForms(p: string | null | undefined, osKey: OsKey, homes: string[]): ComparableForm[];
81
+ /**
82
+ * Resolve a path-form reference against the context's roots: longest-prefix
83
+ * across active + archived roots on the sniffed OS family, tilde/home
84
+ * expansion on both sides, legacy translation into the vault for archived
85
+ * matches. Null when nothing matches (or an archived match has no
86
+ * translation target).
87
+ */
88
+ export declare function resolvePathReference(ctx: ResolverContext, inputPath: string): ResolvedPathRef | null;
89
+ /**
90
+ * The local base directory (this machine, CLIENT_OS) of a root — tilde-form
91
+ * expanded against THIS machine's home dir — or null when the root has no
92
+ * usable path on this OS (never a cwd-relative guess).
93
+ */
94
+ export declare function rootLocalBase(ctx: ResolverContext, rootId: number): string | null;
95
+ /**
96
+ * The local absolute path (this machine, CLIENT_OS) for a note of `rootId` at
97
+ * `relativePath` — or null when that root has no usable path on this OS.
98
+ */
99
+ export declare function localAbsFor(ctx: ResolverContext, rootId: number, relativePath: string): string | null;
100
+ export {};
101
+ //# sourceMappingURL=noteRef.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noteRef.d.ts","sourceRoot":"","sources":["../../src/resolve/noteRef.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEjD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,4CAA4C;IAC5C,QAAQ,EAAE,KAAK,CAAC;IAChB,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,yEAAyE;IACzE,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC9C,8DAA8D;IAC9D,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,MAAM,OAAO,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAElF,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,GAAG,EAAE,aAAa,GAAG,oBAAoB,CAAC;IAC1C,mDAAmD;IACnD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAM/C;AAED,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,KAAK,CAKhD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAgBzD;AAED,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAQD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,cAAc,EAAE,CAiB7G;AAYD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAwCpG;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAejF;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKrG"}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Unified note-reference resolution (single-vault refactor, M1).
3
+ *
4
+ * Pure logic — no network, no filesystem, no process state. Everything the
5
+ * resolver needs arrives in a ResolverContext built by the caller (M2 wires
6
+ * this to `GET /api/mcp/roots?includeArchived=1`, which since V3 returns the
7
+ * archived translation entries plus `{vault_root_id, device_home_dirs}`).
8
+ *
9
+ * Accepted reference forms (the plan's reference grammar):
10
+ * - note URLs on any host: https://noesisbrain.com/notes/2761[#bm=...]
11
+ * - noesis://note/2761[/optional-slug]
12
+ * - bare numeric ids: "2761"
13
+ * - absolute paths of ANY OS shape: /Users/me/Noesis/x.md,
14
+ * c:/Users/me/Noesis/x.md, C:\temp_cGit\.noesis\x.md
15
+ * - tilde / %USERPROFILE% forms: ~/Noesis/x.md, %USERPROFILE%\Noesis\x.md
16
+ * - LEGACY pre-migration absolute paths, translated through archived roots
17
+ * (vault_subfolder + '.noesis/'-collapse candidates, collapsed first)
18
+ *
19
+ * Fixes the pre-M1 footgun where a root with an empty current-OS path
20
+ * resolved to process.cwd(): empty/missing slots never produce a comparable
21
+ * form here, and local paths are composed (never path.resolve'd) from a
22
+ * verified non-empty base.
23
+ */
24
+ /** Forward slashes + lowercase drive letter (win32 canonical compare form). */
25
+ export function normalizePath(p) {
26
+ let normalized = p.replace(/\\/g, '/');
27
+ if (normalized.length >= 2 && normalized[1] === ':') {
28
+ normalized = normalized[0].toLowerCase() + normalized.slice(1);
29
+ }
30
+ return normalized;
31
+ }
32
+ /** Shape-sniff which OS family a path belongs to (mirrors the backend). */
33
+ export function detectPathOsKey(p) {
34
+ if (/^[A-Za-z]:[\\/]/.test(p))
35
+ return 'win32';
36
+ if (p.startsWith('/Users/') || p.startsWith('~/'))
37
+ return 'darwin';
38
+ if (p.startsWith('/'))
39
+ return 'linux';
40
+ return 'win32';
41
+ }
42
+ /**
43
+ * Parse an input string into a note reference. URLs and ids resolve by note
44
+ * id (the caller fetches the note to learn its relative_path); everything
45
+ * else is treated as a path.
46
+ */
47
+ export function parseNoteReference(input) {
48
+ const s = input.trim();
49
+ if (/^\d+$/.test(s))
50
+ return { kind: 'id', id: parseInt(s, 10) };
51
+ const noesisScheme = s.match(/^noesis:\/\/note\/(\d+)(?:[/#?].*)?$/i);
52
+ if (noesisScheme)
53
+ return { kind: 'id', id: parseInt(noesisScheme[1], 10) };
54
+ if (/^https?:\/\//i.test(s)) {
55
+ // Anchored: the /notes/{id} segment must end the path (fragment/query
56
+ // tolerated) — '/a/notes/12.png' is NOT a note reference.
57
+ const urlNote = s.match(/\/notes\/(\d+)(?:[/#?].*)?$/i);
58
+ if (urlNote)
59
+ return { kind: 'id', id: parseInt(urlNote[1], 10) };
60
+ }
61
+ return { kind: 'path', path: s };
62
+ }
63
+ function expandHomePrefix(p, home) {
64
+ if (p === '~' || p.startsWith('~/'))
65
+ return home + p.slice(1);
66
+ if (/^%USERPROFILE%/i.test(p))
67
+ return home + p.slice('%USERPROFILE%'.length);
68
+ return null;
69
+ }
70
+ /**
71
+ * Comparable spellings of one path under an OS family: the normalized raw
72
+ * form plus one home-expanded form per known home dir for that family (this
73
+ * machine's home when the family is the client OS, and the cloud-cached
74
+ * device home). Empty inputs produce NO forms (the cwd-footgun fix).
75
+ */
76
+ export function comparableForms(p, osKey, homes) {
77
+ const raw = (p ?? '').trim();
78
+ if (!raw)
79
+ return [];
80
+ const base = normalizePath(raw);
81
+ const variants = new Set([base]);
82
+ for (const home of homes) {
83
+ const h = normalizePath(home).replace(/\/+$/, '');
84
+ if (!h)
85
+ continue;
86
+ const expanded = expandHomePrefix(base, h);
87
+ if (expanded)
88
+ variants.add(expanded);
89
+ }
90
+ return [...variants].map((v) => {
91
+ const trimmed = v.length > 1 ? v.replace(/\/+$/, '') : v;
92
+ return { orig: trimmed, cmp: osKey === 'win32' ? trimmed.toLowerCase() : trimmed };
93
+ });
94
+ }
95
+ function homesFor(ctx, osKey) {
96
+ const homes = [];
97
+ if (osKey === ctx.clientOs && ctx.homeDir)
98
+ homes.push(ctx.homeDir);
99
+ const cloudHome = ctx.deviceHomeDirs?.[osKey];
100
+ if (typeof cloudHome === 'string' && cloudHome.trim())
101
+ homes.push(cloudHome);
102
+ return homes;
103
+ }
104
+ const NOESIS_SEG = '.noesis/';
105
+ /**
106
+ * Resolve a path-form reference against the context's roots: longest-prefix
107
+ * across active + archived roots on the sniffed OS family, tilde/home
108
+ * expansion on both sides, legacy translation into the vault for archived
109
+ * matches. Null when nothing matches (or an archived match has no
110
+ * translation target).
111
+ */
112
+ export function resolvePathReference(ctx, inputPath) {
113
+ const osKey = detectPathOsKey(inputPath);
114
+ const homes = homesFor(ctx, osKey);
115
+ const inputForms = comparableForms(inputPath, osKey, homes);
116
+ if (inputForms.length === 0)
117
+ return null;
118
+ let best = null;
119
+ for (const root of ctx.roots) {
120
+ for (const rootForm of comparableForms(root.local_paths?.[osKey], osKey, homes)) {
121
+ for (const input of inputForms) {
122
+ const isMatch = input.cmp === rootForm.cmp || input.cmp.startsWith(rootForm.cmp + '/');
123
+ if (!isMatch)
124
+ continue;
125
+ if (!best || rootForm.cmp.length > best.rootLen) {
126
+ const rel = input.orig.slice(rootForm.orig.length).replace(/^\//, '');
127
+ best = { root, rootLen: rootForm.cmp.length, rel };
128
+ }
129
+ }
130
+ }
131
+ }
132
+ if (!best)
133
+ return null;
134
+ const rel = best.rel;
135
+ if (!best.root.archived_at) {
136
+ return { rootId: best.root.id, candidates: [rel], via: 'active-root' };
137
+ }
138
+ const subfolder = best.root.vault_subfolder?.replace(/^\/+|\/+$/g, '');
139
+ if (!subfolder || ctx.vaultRootId == null)
140
+ return null;
141
+ // F8: when the input EXPLICITLY names a `.noesis/` dot path, try the
142
+ // UNCOLLAPSED candidate first. In a collapse-fallback pair (a different file
143
+ // won the collapsed slot at migration, so this note kept its `.noesis/`
144
+ // prefix), collapsed-first would wrongly return the native sibling; the
145
+ // dot-path the caller typed is the one they mean. Harmless in the common
146
+ // case: the uncollapsed candidate simply misses and falls to the collapsed.
147
+ const candidates = rel.startsWith(NOESIS_SEG)
148
+ ? [`${subfolder}/${rel}`, `${subfolder}/${rel.slice(NOESIS_SEG.length)}`]
149
+ : [`${subfolder}/${rel}`];
150
+ return { rootId: ctx.vaultRootId, candidates, via: 'legacy-translation', archivedRootId: best.root.id };
151
+ }
152
+ /**
153
+ * The local base directory (this machine, CLIENT_OS) of a root — tilde-form
154
+ * expanded against THIS machine's home dir — or null when the root has no
155
+ * usable path on this OS (never a cwd-relative guess).
156
+ */
157
+ export function rootLocalBase(ctx, rootId) {
158
+ const root = ctx.roots.find((r) => r.id === rootId);
159
+ const rawBase = root?.local_paths?.[ctx.clientOs]?.trim();
160
+ if (!rawBase)
161
+ return null;
162
+ let base = normalizePath(rawBase);
163
+ const home = normalizePath(ctx.homeDir ?? '').replace(/\/+$/, '');
164
+ const expanded = home ? expandHomePrefix(base, home) : null;
165
+ if (expanded)
166
+ base = expanded;
167
+ if (base.startsWith('~'))
168
+ return null; // home unknown — refuse to guess
169
+ // Any unexpanded %TOKEN% (e.g. a stored literal %USERNAME% template) would
170
+ // otherwise mkdir a literal '%USERNAME%' directory on materialize.
171
+ if (/%[^%/\\]+%/.test(base))
172
+ return null;
173
+ return base.length > 1 ? base.replace(/\/+$/, '') : base;
174
+ }
175
+ /**
176
+ * The local absolute path (this machine, CLIENT_OS) for a note of `rootId` at
177
+ * `relativePath` — or null when that root has no usable path on this OS.
178
+ */
179
+ export function localAbsFor(ctx, rootId, relativePath) {
180
+ const base = rootLocalBase(ctx, rootId);
181
+ if (!base)
182
+ return null;
183
+ const rel = relativePath.replace(/^\/+/, '');
184
+ return `${base}/${rel}`;
185
+ }
186
+ //# sourceMappingURL=noteRef.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noteRef.js","sourceRoot":"","sources":["../../src/resolve/noteRef.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { type ResolverContext } from './noteRef.js';
2
+ /**
3
+ * Pure renderer for the `list_roots` tool output (single-vault, M3).
4
+ *
5
+ * Shows the vault (with its per-OS tilde paths AND this machine's resolved
6
+ * absolute base), any other still-active roots, and the archived roots as the
7
+ * legacy-path translation table — old absolute paths under an archived root
8
+ * keep resolving via get_note/sync_notes, translated into the vault subfolder
9
+ * shown here.
10
+ */
11
+ export declare function formatRootsList(ctx: ResolverContext): string;
12
+ //# sourceMappingURL=rootsFormat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rootsFormat.d.ts","sourceRoot":"","sources":["../../src/resolve/rootsFormat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,eAAe,EAAc,MAAM,cAAc,CAAC;AAE/E;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CA0D5D"}
@@ -0,0 +1,63 @@
1
+ import { rootLocalBase } from './noteRef.js';
2
+ /**
3
+ * Pure renderer for the `list_roots` tool output (single-vault, M3).
4
+ *
5
+ * Shows the vault (with its per-OS tilde paths AND this machine's resolved
6
+ * absolute base), any other still-active roots, and the archived roots as the
7
+ * legacy-path translation table — old absolute paths under an archived root
8
+ * keep resolving via get_note/sync_notes, translated into the vault subfolder
9
+ * shown here.
10
+ */
11
+ export function formatRootsList(ctx) {
12
+ if (ctx.roots.length === 0) {
13
+ return 'No root directories configured. A vault (~/Noesis) is designated automatically on first use.';
14
+ }
15
+ const OS_LABELS = [
16
+ ['win32', 'Windows'],
17
+ ['darwin', 'macOS '],
18
+ ['linux', 'Linux '],
19
+ ];
20
+ const active = ctx.roots.filter((r) => !r.archived_at);
21
+ const archived = ctx.roots.filter((r) => r.archived_at);
22
+ const lines = [];
23
+ let anyMissing = false;
24
+ active.forEach((root, index) => {
25
+ const isVault = ctx.vaultRootId != null && root.id === ctx.vaultRootId;
26
+ lines.push(`${index + 1}. **${root.name}**${isVault ? ' ← YOUR VAULT' : ''}`);
27
+ for (const [key, label] of OS_LABELS) {
28
+ const v = root.local_paths?.[key];
29
+ const marker = key === ctx.clientOs ? ' [active]' : '';
30
+ if (v) {
31
+ lines.push(` ${label}: ${v}${marker}`);
32
+ }
33
+ else if (key === ctx.clientOs) {
34
+ lines.push(` ${label}: (not configured for this OS)${marker}`);
35
+ anyMissing = true;
36
+ }
37
+ }
38
+ const resolved = rootLocalBase(ctx, root.id);
39
+ if (resolved) {
40
+ lines.push(` On this machine: ${resolved}`);
41
+ }
42
+ lines.push('');
43
+ });
44
+ let text = `Watched directories (${active.length}):\n\n${lines.join('\n').trimEnd()}`;
45
+ if (archived.length > 0) {
46
+ const rows = archived.map((r) => {
47
+ const oldPaths = OS_LABELS.map(([key]) => r.local_paths?.[key])
48
+ .filter((v) => !!v)
49
+ .join(' | ');
50
+ const target = r.vault_subfolder ? `→ vault: ${r.vault_subfolder}/` : '→ (notes trashed at migration)';
51
+ return `- ${r.name}: ${oldPaths} ${target}`;
52
+ });
53
+ text +=
54
+ `\n\nArchived roots — legacy path translation (${archived.length}):\n` +
55
+ rows.join('\n') +
56
+ `\n\nOld paths under these roots still work: get_note/sync_notes translate them into the vault automatically.`;
57
+ }
58
+ if (anyMissing) {
59
+ text += `\n\nTip: an ACTIVE root missing a path for this OS (${ctx.clientOs}) can't sync on this machine — add the path in the Noesis Dashboard.`;
60
+ }
61
+ return text;
62
+ }
63
+ //# sourceMappingURL=rootsFormat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rootsFormat.js","sourceRoot":"","sources":["../../src/resolve/rootsFormat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { NoesisClient } from '../api/NoesisClient.js';
3
+ export declare function registerBacklogTools(server: McpServer, client: NoesisClient): void;
4
+ //# sourceMappingURL=backlog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backlog.d.ts","sourceRoot":"","sources":["../../src/tools/backlog.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAiB3D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAiLlF"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Backlog v2 MCP tools — the loop's view of /api/mcp/backlog. Thin wrappers:
3
+ * every rule (stage legality, OWNER_GATE, freeze, park guards) is enforced
4
+ * server-side; these tools shape requests and return compact JSON text that a
5
+ * driving loop can parse. The owner-only acts (approve / cancel, and reject of
6
+ * anything UN-parked) have NO tool here by design — they exist only on the
7
+ * owner's page. The one disposal this surface carries is the owner-instructed
8
+ * reject of a PARKED item: ambiguous|superseded -> rejected, reason required.
9
+ */
10
+ import { z } from 'zod';
11
+ // 'rejected' rides along for set_stage's parked disposal and as a backlog_get
12
+ // list filter; it is NOT a stage the loop may reach from un-parked items.
13
+ const STAGE_ENUM = z.enum(['jot', 'ambiguous', 'superseded', 'refined', 'materialized', 'approved', 'running', 'implemented', 'shipped', 'rejected']);
14
+ const KIND_ENUM = z.enum(['feature', 'bug', 'design', 'process', 'research']);
15
+ const SIZE_ENUM = z.enum(['S', 'M', 'L']);
16
+ function jsonText(data) {
17
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
18
+ }
19
+ function errText(e) {
20
+ const msg = e instanceof Error ? e.message : String(e);
21
+ return { content: [{ type: 'text', text: `Backlog tool failed: ${msg}` }], isError: true };
22
+ }
23
+ export function registerBacklogTools(server, client) {
24
+ server.tool('backlog_get', 'Read the Backlog: without a key, list issues (+ per-stage counts and open_question_count, optionally filtered by stage or a text query); with a key (TCK-<n>), the full issue including increments, open owner questions, and the bounded message thread (questions + answers linked via answered_by_message_id — what RESOLVE reads).', {
25
+ key: z.string().optional().describe('Issue key like TCK-7 (omit to list)'),
26
+ stage: STAGE_ENUM.optional().describe('List filter: only this stage'),
27
+ query: z.string().optional().describe('List filter: title/body text search'),
28
+ }, async (args) => {
29
+ try {
30
+ const data = args.key
31
+ ? await client.backlogGet(args.key)
32
+ : await client.backlogList({ stage: args.stage, q: args.query });
33
+ return jsonText(data);
34
+ }
35
+ catch (e) {
36
+ return errText(e);
37
+ }
38
+ });
39
+ server.tool('backlog_upsert_issue', 'Create a Backlog issue (no key; groom may create in jot/refined/materialized) or update an existing one (with key; content fields only before approval, bookkeeping fields until terminal).', {
40
+ key: z.string().optional().describe('Existing issue key to update; omit to create'),
41
+ title: z.string().optional(),
42
+ kind: KIND_ENUM.optional(),
43
+ size: SIZE_ENUM.optional(),
44
+ origin_jot: z.string().optional().describe('Verbatim original memo text'),
45
+ body_md: z.string().optional().describe('Requirement markdown (Problem / Current / Proposed / Acceptance criteria / NOT-do)'),
46
+ stage: z.enum(['jot', 'refined', 'materialized']).optional().describe('Create-only: initial stage'),
47
+ branch: z.string().optional(),
48
+ worktree: z.string().optional(),
49
+ pr_number: z.number().int().optional(),
50
+ }, async (args) => {
51
+ try {
52
+ if (args.key) {
53
+ const { key, stage: _ignored, ...patch } = args;
54
+ const body = Object.fromEntries(Object.entries(patch).filter(([, v]) => v !== undefined));
55
+ return jsonText(await client.backlogPatch(key, body));
56
+ }
57
+ if (!args.title)
58
+ return errText(new Error('title is required to create an issue'));
59
+ const body = Object.fromEntries(Object.entries(args).filter(([, v]) => v !== undefined));
60
+ return jsonText(await client.backlogCreate(body));
61
+ }
62
+ catch (e) {
63
+ return errText(e);
64
+ }
65
+ });
66
+ server.tool('backlog_set_stage', "Move an issue through its lifecycle on the loop surface: jot->refined, refined->materialized (send-back too), approved->running, running->approved (STOP-acknowledge), running->implemented, implemented->shipped; plus the groom detours: jot->ambiguous (reading forks; post the question FIRST), ambiguous->refined/jot (resolve/retract), and jot|refined|ambiguous|materialized->superseded (grooming believes it ALREADY SHIPPED — post the evidence question first), superseded->jot/refined (retract/keep). Parked disposal: ambiguous|superseded->rejected, ONLY on the owner's explicit instruction (in chat, or their answer to the park question) — reason REQUIRED and must record that instruction. Cannot approve/cancel, nor reject un-parked items — those are the owner's page buttons (OWNER_GATE).", {
67
+ key: z.string().describe('Issue key like TCK-7'),
68
+ to: STAGE_ENUM.describe('Target stage (approve only as the running->approved stop-ack)'),
69
+ reason: z.string().optional().describe("Reason for send-backs; REQUIRED for rejected (record the owner's instruction)"),
70
+ }, async (args) => {
71
+ try {
72
+ return jsonText(await client.backlogSetStage(args.key, args.to, args.reason));
73
+ }
74
+ catch (e) {
75
+ return errText(e);
76
+ }
77
+ });
78
+ server.tool('backlog_plan_increments', 'Register or refresh the planned increment list for an issue (upsert by sequence; rows that already started are never rewritten).', {
79
+ key: z.string(),
80
+ increments: z
81
+ .array(z.object({
82
+ sequence: z.number().int().positive(),
83
+ name: z.string(),
84
+ type: z.enum(['spike', 'implementation', 'verification', 'refactor']).optional(),
85
+ }))
86
+ .min(1),
87
+ }, async (args) => {
88
+ try {
89
+ return jsonText(await client.backlogPlanIncrements(args.key, args.increments));
90
+ }
91
+ catch (e) {
92
+ return errText(e);
93
+ }
94
+ });
95
+ server.tool('backlog_report_increment', "Report an increment lifecycle edge. phase 'start': marks it running, bumps attempts, stamps model/effort. phase 'finish': status passed|failed|parked|skipped + telemetry (verifier_verdict, gate_results, commits, evidence_md, obstacles_md); duration lands in the trace automatically.", {
96
+ key: z.string(),
97
+ sequence: z.number().int().positive(),
98
+ phase: z.enum(['start', 'finish']),
99
+ model: z.string().optional(),
100
+ effort: z.string().optional(),
101
+ status: z.enum(['passed', 'failed', 'parked', 'skipped']).optional(),
102
+ verifier_verdict: z.enum(['pass', 'fail']).optional(),
103
+ gate_results: z.record(z.string(), z.unknown()).optional(),
104
+ commits: z.array(z.object({ sha: z.string(), message: z.string() })).optional(),
105
+ evidence_md: z.string().optional(),
106
+ obstacles_md: z.string().optional(),
107
+ }, async (args) => {
108
+ try {
109
+ const { key, sequence, ...body } = args;
110
+ const clean = Object.fromEntries(Object.entries(body).filter(([, v]) => v !== undefined));
111
+ return jsonText(await client.backlogReportIncrement(key, sequence, clean));
112
+ }
113
+ catch (e) {
114
+ return errText(e);
115
+ }
116
+ });
117
+ server.tool('backlog_control', 'The iteration-boundary poll: requested control state (run/pause/stop/cancel), stage, frozen vs CURRENT spec hash (both server-computed — compare the two strings; a mismatch means the requirement changed), unanswered owner questions, and discussion since a message cursor.', {
118
+ key: z.string(),
119
+ after_message_id: z.number().int().optional().describe('Return thread messages newer than this id'),
120
+ }, async (args) => {
121
+ try {
122
+ return jsonText(await client.backlogControl(args.key, args.after_message_id));
123
+ }
124
+ catch (e) {
125
+ return errText(e);
126
+ }
127
+ });
128
+ server.tool('backlog_post_message', "The loop's voice on an issue: kind 'answer' + answer_to replies to an owner question WITHOUT stopping work; kind 'question' + parks_sequence asks the owner and parks that increment ([Q] park); kind 'question' WITHOUT parks_sequence is the issue-level groom question (pair it with set_stage to 'ambiguous' for reading forks, or 'superseded' for already-shipped evidence); kind 'comment' leaves a note.", {
129
+ key: z.string(),
130
+ body_md: z.string(),
131
+ kind: z.enum(['answer', 'question', 'comment']),
132
+ answer_to: z.number().int().optional().describe('Message id of the owner question being answered'),
133
+ parks_sequence: z.number().int().optional().describe("With kind 'question': increment sequence to park"),
134
+ }, async (args) => {
135
+ try {
136
+ const { key, ...body } = args;
137
+ const clean = Object.fromEntries(Object.entries(body).filter(([, v]) => v !== undefined));
138
+ return jsonText(await client.backlogPostMessage(key, clean));
139
+ }
140
+ catch (e) {
141
+ return errText(e);
142
+ }
143
+ });
144
+ server.tool('backlog_append_event', "Append a free-form trace event to an issue (dot-namespaced type like 'gate.result' or 'loop.note'; optional payload, actor claude|verifier|system, and increment sequence linkage).", {
145
+ key: z.string(),
146
+ type: z.string().describe('Dot-namespaced lowercase type, e.g. loop.note'),
147
+ payload: z.record(z.string(), z.unknown()).optional(),
148
+ actor: z.enum(['claude', 'verifier', 'system']).optional(),
149
+ increment_seq: z.number().int().optional(),
150
+ }, async (args) => {
151
+ try {
152
+ const { key, ...body } = args;
153
+ const clean = Object.fromEntries(Object.entries(body).filter(([, v]) => v !== undefined));
154
+ return jsonText(await client.backlogAppendEvent(key, clean));
155
+ }
156
+ catch (e) {
157
+ return errText(e);
158
+ }
159
+ });
160
+ }
161
+ //# sourceMappingURL=backlog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backlog.js","sourceRoot":"","sources":["../../src/tools/backlog.ts"],"names":[],"mappings":"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"}