ctxr-cli 0.30.0 → 0.31.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 (107) hide show
  1. package/README.md +28 -0
  2. package/dist/commands/land.d.ts.map +1 -1
  3. package/dist/commands/land.js +3 -18
  4. package/dist/commands/land.js.map +1 -1
  5. package/dist/commands/serve.d.ts.map +1 -1
  6. package/dist/commands/serve.js +10 -2
  7. package/dist/commands/serve.js.map +1 -1
  8. package/dist/config/schema.d.ts +17 -0
  9. package/dist/config/schema.d.ts.map +1 -1
  10. package/dist/config/schema.js +40 -8
  11. package/dist/config/schema.js.map +1 -1
  12. package/dist/core/browse/approve.d.ts +3 -0
  13. package/dist/core/browse/approve.d.ts.map +1 -1
  14. package/dist/core/browse/approve.js.map +1 -1
  15. package/dist/core/browse/page-url.d.ts +15 -0
  16. package/dist/core/browse/page-url.d.ts.map +1 -1
  17. package/dist/core/browse/page-url.js +8 -1
  18. package/dist/core/browse/page-url.js.map +1 -1
  19. package/dist/core/forge/credentials.d.ts +12 -1
  20. package/dist/core/forge/credentials.d.ts.map +1 -1
  21. package/dist/core/forge/credentials.js +25 -1
  22. package/dist/core/forge/credentials.js.map +1 -1
  23. package/dist/core/forge/github.d.ts +9 -0
  24. package/dist/core/forge/github.d.ts.map +1 -1
  25. package/dist/core/forge/github.js +6 -0
  26. package/dist/core/forge/github.js.map +1 -1
  27. package/dist/core/mcp/drafts.d.ts +30 -0
  28. package/dist/core/mcp/drafts.d.ts.map +1 -0
  29. package/dist/core/mcp/drafts.js +52 -0
  30. package/dist/core/mcp/drafts.js.map +1 -0
  31. package/dist/core/mcp/http.d.ts +40 -0
  32. package/dist/core/mcp/http.d.ts.map +1 -0
  33. package/dist/core/mcp/http.js +90 -0
  34. package/dist/core/mcp/http.js.map +1 -0
  35. package/dist/core/mcp/jsonrpc.d.ts +73 -0
  36. package/dist/core/mcp/jsonrpc.d.ts.map +1 -0
  37. package/dist/core/mcp/jsonrpc.js +149 -0
  38. package/dist/core/mcp/jsonrpc.js.map +1 -0
  39. package/dist/core/mcp/members.d.ts +11 -0
  40. package/dist/core/mcp/members.d.ts.map +1 -0
  41. package/dist/core/mcp/members.js +21 -0
  42. package/dist/core/mcp/members.js.map +1 -0
  43. package/dist/core/mcp/oauth.d.ts +99 -0
  44. package/dist/core/mcp/oauth.d.ts.map +1 -0
  45. package/dist/core/mcp/oauth.js +454 -0
  46. package/dist/core/mcp/oauth.js.map +1 -0
  47. package/dist/core/mcp/pages.d.ts +3 -0
  48. package/dist/core/mcp/pages.d.ts.map +1 -0
  49. package/dist/core/mcp/pages.js +41 -0
  50. package/dist/core/mcp/pages.js.map +1 -0
  51. package/dist/core/mcp/ripgrep.d.ts +48 -0
  52. package/dist/core/mcp/ripgrep.d.ts.map +1 -0
  53. package/dist/core/mcp/ripgrep.js +98 -0
  54. package/dist/core/mcp/ripgrep.js.map +1 -0
  55. package/dist/core/mcp/rwlock.d.ts +16 -0
  56. package/dist/core/mcp/rwlock.d.ts.map +1 -0
  57. package/dist/core/mcp/rwlock.js +59 -0
  58. package/dist/core/mcp/rwlock.js.map +1 -0
  59. package/dist/core/mcp/sessions.d.ts +84 -0
  60. package/dist/core/mcp/sessions.d.ts.map +1 -0
  61. package/dist/core/mcp/sessions.js +154 -0
  62. package/dist/core/mcp/sessions.js.map +1 -0
  63. package/dist/core/mcp/surface.d.ts +42 -0
  64. package/dist/core/mcp/surface.d.ts.map +1 -0
  65. package/dist/core/mcp/surface.js +141 -0
  66. package/dist/core/mcp/surface.js.map +1 -0
  67. package/dist/core/mcp/sync.d.ts +91 -0
  68. package/dist/core/mcp/sync.d.ts.map +1 -0
  69. package/dist/core/mcp/sync.js +218 -0
  70. package/dist/core/mcp/sync.js.map +1 -0
  71. package/dist/core/mcp/tools/capture.d.ts +64 -0
  72. package/dist/core/mcp/tools/capture.d.ts.map +1 -0
  73. package/dist/core/mcp/tools/capture.js +182 -0
  74. package/dist/core/mcp/tools/capture.js.map +1 -0
  75. package/dist/core/mcp/tools/edit.d.ts +79 -0
  76. package/dist/core/mcp/tools/edit.d.ts.map +1 -0
  77. package/dist/core/mcp/tools/edit.js +212 -0
  78. package/dist/core/mcp/tools/edit.js.map +1 -0
  79. package/dist/core/mcp/tools/files.d.ts +163 -0
  80. package/dist/core/mcp/tools/files.d.ts.map +1 -0
  81. package/dist/core/mcp/tools/files.js +265 -0
  82. package/dist/core/mcp/tools/files.js.map +1 -0
  83. package/dist/core/mcp/tools/find.d.ts +82 -0
  84. package/dist/core/mcp/tools/find.d.ts.map +1 -0
  85. package/dist/core/mcp/tools/find.js +136 -0
  86. package/dist/core/mcp/tools/find.js.map +1 -0
  87. package/dist/core/mcp/tools/gather.d.ts +88 -0
  88. package/dist/core/mcp/tools/gather.d.ts.map +1 -0
  89. package/dist/core/mcp/tools/gather.js +123 -0
  90. package/dist/core/mcp/tools/gather.js.map +1 -0
  91. package/dist/core/mcp/tools/index.d.ts +21 -0
  92. package/dist/core/mcp/tools/index.d.ts.map +1 -0
  93. package/dist/core/mcp/tools/index.js +129 -0
  94. package/dist/core/mcp/tools/index.js.map +1 -0
  95. package/dist/version.d.ts +1 -1
  96. package/dist/version.js +1 -1
  97. package/package.json +1 -1
  98. package/templates/mcp/Edit.md +7 -0
  99. package/templates/mcp/Glob.md +5 -0
  100. package/templates/mcp/Grep.md +10 -0
  101. package/templates/mcp/Read.md +7 -0
  102. package/templates/mcp/Write.md +7 -0
  103. package/templates/mcp/capture.md +9 -0
  104. package/templates/mcp/consent.html +40 -0
  105. package/templates/mcp/find.md +9 -0
  106. package/templates/mcp/gather.md +5 -0
  107. package/templates/mcp/server-instructions.md +17 -0
@@ -0,0 +1,98 @@
1
+ import { execFile } from 'node:child_process';
2
+ /**
3
+ * serve-the-store-over-mcp design.md D12: the one module that spawns
4
+ * ripgrep, for the MCP surface's `Grep` and `Glob`. Claude Code's own Grep
5
+ * parameters map onto ripgrep's flags one for one, so they are passed through
6
+ * as an argument vector — never a shell — with `--` ahead of the pattern, so
7
+ * no pattern is ever read as a flag. Ripgrep's engine runs in time linear in
8
+ * its input, so no pattern can hang the server; the time and output limits
9
+ * here bound everything else.
10
+ */
11
+ export const RIPGREP_TIMEOUT_MS = 5_000;
12
+ export const RIPGREP_MAX_OUTPUT_BYTES = 4 * 1024 * 1024;
13
+ function strip(p) {
14
+ return p.replace(/^\.\//, '');
15
+ }
16
+ /** `--null` output: bare paths are NUL-separated; lines with content are `path NUL rest`, newline-separated. */
17
+ function parse(stdout, barePaths) {
18
+ if (barePaths)
19
+ return stdout.split('\0').map((p) => p.trim()).filter((p) => p.length > 0).map((p) => ({ path: strip(p), rest: null }));
20
+ return stdout
21
+ .split('\n')
22
+ .filter((line) => line.length > 0)
23
+ .map((line) => {
24
+ const nul = line.indexOf('\0');
25
+ return nul === -1 ? { path: null, rest: line } : { path: strip(line.slice(0, nul)), rest: line.slice(nul + 1) };
26
+ });
27
+ }
28
+ function run(args, cwd, barePaths) {
29
+ return new Promise((resolve) => {
30
+ execFile('rg', [...args], { cwd, timeout: RIPGREP_TIMEOUT_MS, maxBuffer: RIPGREP_MAX_OUTPUT_BYTES, killSignal: 'SIGKILL' }, (err, stdout, stderr) => {
31
+ if (err === null) {
32
+ // Always searched from an explicit `.` (below), which ripgrep echoes back as a `./` prefix on every path.
33
+ resolve({ kind: 'ok', entries: parse(stdout, barePaths) });
34
+ return;
35
+ }
36
+ const e = err;
37
+ if (e.code === 1)
38
+ resolve({ kind: 'no-match' });
39
+ else if (e.code === 'ENOENT')
40
+ resolve({ kind: 'error', message: 'ripgrep (rg) is not installed where this server runs' });
41
+ else if (e.killed)
42
+ resolve({ kind: 'error', message: `the search took longer than ${RIPGREP_TIMEOUT_MS / 1000} seconds; narrow it with path or glob` });
43
+ else if (e.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER')
44
+ resolve({ kind: 'error', message: 'the search produced too much output; narrow it with path, glob or head_limit' });
45
+ else
46
+ resolve({ kind: 'error', message: stderr.trim().split('\n')[0] || `ripgrep failed (${String(e.code)})` });
47
+ });
48
+ });
49
+ }
50
+ /** `--glob '!<prefix>**'` for each path the tools must never read or return. */
51
+ function exclusionArgs(hidden) {
52
+ return hidden.flatMap((prefix) => {
53
+ const trimmed = prefix.replace(/\/+$/, '');
54
+ return ['--glob', `!${trimmed}`, '--glob', `!${trimmed}/**`];
55
+ });
56
+ }
57
+ const BASE_ARGS = ['--no-config', '--color', 'never', '--sort', 'path', '--no-messages', '--null'];
58
+ /** Claude Code's Grep, mapped flag for flag. `searchPath` is store-relative, already confined to the store. */
59
+ export function grep(cwd, params, searchPath, hidden) {
60
+ const args = [...BASE_ARGS];
61
+ const mode = params.output_mode ?? 'files_with_matches';
62
+ if (mode === 'files_with_matches')
63
+ args.push('--files-with-matches');
64
+ else if (mode === 'count')
65
+ args.push('--count');
66
+ else if (params['-n'] !== false)
67
+ args.push('--line-number');
68
+ if (params['-i'] === true)
69
+ args.push('--ignore-case');
70
+ if (mode === 'content') {
71
+ const context = params['-C'] ?? params.context;
72
+ if (context !== undefined)
73
+ args.push('--context', String(context));
74
+ if (params['-A'] !== undefined)
75
+ args.push('--after-context', String(params['-A']));
76
+ if (params['-B'] !== undefined)
77
+ args.push('--before-context', String(params['-B']));
78
+ }
79
+ if (params.multiline === true)
80
+ args.push('--multiline', '--multiline-dotall');
81
+ if (params.glob !== undefined)
82
+ args.push('--glob', params.glob);
83
+ if (params.type !== undefined)
84
+ args.push('--type', params.type);
85
+ // Last: ripgrep lets a later --glob override an earlier one, so a caller's glob must never follow an exclusion.
86
+ args.push(...exclusionArgs(hidden));
87
+ // An explicit path always: given none and a stdin that is not a terminal, ripgrep searches stdin and waits.
88
+ args.push('--', params.pattern, searchPath ?? '.');
89
+ return run(args, cwd, mode === 'files_with_matches');
90
+ }
91
+ /** Claude Code's Glob: files matching `pattern`, under `searchPath`, sorted by path. */
92
+ export function glob(cwd, pattern, searchPath, hidden) {
93
+ // Exclusions after the caller's pattern: a later --glob takes precedence in ripgrep.
94
+ const args = [...BASE_ARGS, '--files', '--glob', pattern, ...exclusionArgs(hidden)];
95
+ args.push('--', searchPath ?? '.');
96
+ return run(args, cwd, true);
97
+ }
98
+ //# sourceMappingURL=ripgrep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ripgrep.js","sourceRoot":"","sources":["../../../src/core/mcp/ripgrep.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * serve-the-store-over-mcp design.md D8: every tool call reads one commit.
3
+ * Tool calls share the lock; a sync takes it exclusively. A waiting writer
4
+ * blocks new readers, so a steady stream of calls cannot postpone a sync
5
+ * forever, and a sync waits for the calls already in flight.
6
+ */
7
+ export declare class ReadWriteLock {
8
+ private readers;
9
+ private writing;
10
+ private readonly waiting;
11
+ private canRead;
12
+ private wake;
13
+ read<T>(fn: () => Promise<T>): Promise<T>;
14
+ write<T>(fn: () => Promise<T>): Promise<T>;
15
+ }
16
+ //# sourceMappingURL=rwlock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rwlock.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/rwlock.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuD;IAE/E,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,IAAI;IAiBN,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAS9C;IAEK,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAS/C;CACF"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * serve-the-store-over-mcp design.md D8: every tool call reads one commit.
3
+ * Tool calls share the lock; a sync takes it exclusively. A waiting writer
4
+ * blocks new readers, so a steady stream of calls cannot postpone a sync
5
+ * forever, and a sync waits for the calls already in flight.
6
+ */
7
+ export class ReadWriteLock {
8
+ readers = 0;
9
+ writing = false;
10
+ waiting = [];
11
+ canRead() {
12
+ return !this.writing && !this.waiting.some((w) => w.kind === 'write');
13
+ }
14
+ wake() {
15
+ while (this.waiting.length > 0) {
16
+ const next = this.waiting[0];
17
+ if (next.kind === 'write') {
18
+ if (this.writing || this.readers > 0)
19
+ return;
20
+ this.waiting.shift();
21
+ this.writing = true;
22
+ next.grant();
23
+ return;
24
+ }
25
+ if (this.writing)
26
+ return;
27
+ this.waiting.shift();
28
+ this.readers += 1;
29
+ next.grant();
30
+ }
31
+ }
32
+ async read(fn) {
33
+ if (this.canRead())
34
+ this.readers += 1;
35
+ else
36
+ await new Promise((grant) => this.waiting.push({ kind: 'read', grant }));
37
+ try {
38
+ return await fn();
39
+ }
40
+ finally {
41
+ this.readers -= 1;
42
+ this.wake();
43
+ }
44
+ }
45
+ async write(fn) {
46
+ if (!this.writing && this.readers === 0 && this.waiting.length === 0)
47
+ this.writing = true;
48
+ else
49
+ await new Promise((grant) => this.waiting.push({ kind: 'write', grant }));
50
+ try {
51
+ return await fn();
52
+ }
53
+ finally {
54
+ this.writing = false;
55
+ this.wake();
56
+ }
57
+ }
58
+ }
59
+ //# sourceMappingURL=rwlock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rwlock.js","sourceRoot":"","sources":["../../../src/core/mcp/rwlock.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,OAAO,aAAa;IAChB,OAAO,GAAG,CAAC,CAAC;IACZ,OAAO,GAAG,KAAK,CAAC;IACP,OAAO,GAAoD,EAAE,CAAC;IAEvE,OAAO;QACb,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;IACxE,CAAC;IAEO,IAAI;QACV,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC1B,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC;oBAAE,OAAO;gBAC7C,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;gBACrB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;gBACpB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACb,OAAO;YACT,CAAC;YACD,IAAI,IAAI,CAAC,OAAO;gBAAE,OAAO;YACzB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACrB,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;YAClB,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAI,EAAoB;QAChC,IAAI,IAAI,CAAC,OAAO,EAAE;YAAE,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;;YACjC,MAAM,IAAI,OAAO,CAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACpF,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;YAClB,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAI,EAAoB;QACjC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;;YACrF,MAAM,IAAI,OAAO,CAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACrF,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;YACrB,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,84 @@
1
+ import type { StoreConfig } from '../../config/schema.js';
2
+ import { type ForgeCredentials } from '../forge/credentials.js';
3
+ import type { ForgeClient, PullRequest } from '../forge/github.js';
4
+ import type { GitRunner } from '../git/exec.js';
5
+ /**
6
+ * serve-the-store-over-mcp design.md D9, D13: the write side the MCP surface
7
+ * shares between `capture` and the member drafts. Everything it writes is an
8
+ * ordinary session — a worktree under the configured worktrees path, on a
9
+ * branch under the session prefix, with a pull request — so the approval page
10
+ * lists it and `ctxr land` lands it with no MCP-specific path anywhere.
11
+ */
12
+ export interface WriteDeps {
13
+ git: GitRunner;
14
+ /** The store root `serve` was started on: where sessions are started, as `ctxr session start` starts them. */
15
+ canonicalRoot: string;
16
+ config: StoreConfig;
17
+ credentials: ForgeCredentials;
18
+ forge: ForgeClient;
19
+ env: Readonly<Record<string, string | undefined>>;
20
+ now(): Date;
21
+ /** `change_approval.recording_login`: the committer when the host has no git identity, as `ctxr land` does. */
22
+ recordingLogin: string;
23
+ /** `serve.base_url`, for the approval page address a tool returns. */
24
+ baseUrl: string;
25
+ /** The store root's path inside its repository, when the caller has already resolved it; asked of git otherwise. */
26
+ repoPrefix?: string;
27
+ }
28
+ export interface Session {
29
+ /** The worktree directory's name: the key the approval page addresses it by. */
30
+ name: string;
31
+ path: string;
32
+ branch: string;
33
+ label: string | null;
34
+ }
35
+ export type PullState = {
36
+ kind: 'open';
37
+ pull: PullRequest;
38
+ } | {
39
+ kind: 'merged';
40
+ } | {
41
+ kind: 'closed-unmerged';
42
+ } | {
43
+ kind: 'none';
44
+ };
45
+ export declare class WriteRefusal extends Error {
46
+ readonly code: string;
47
+ readonly details?: Record<string, unknown> | undefined;
48
+ constructor(code: string, message: string, details?: Record<string, unknown> | undefined);
49
+ }
50
+ /** One write at a time across `capture`, `Edit` and `Write` (design.md D9, D13). */
51
+ export declare class Mutex {
52
+ private tail;
53
+ run<T>(fn: () => Promise<T>): Promise<T>;
54
+ }
55
+ export declare function approvalUrl(deps: WriteDeps, session: Session): string;
56
+ /** The session worktrees on disk whose label starts with `labelPrefix`, with their branches. */
57
+ export declare function sessionsLabelled(deps: WriteDeps, labelPrefix: string): Promise<Session[]>;
58
+ /** What the forge says about the pull requests for `branch`: the open one, else merged, else closed, else none. */
59
+ export declare function pullStateFor(deps: WriteDeps, branch: string): Promise<PullState>;
60
+ /** A session whose pull request was closed without merging is removed: worktree and local branch (D9, D13). */
61
+ export declare function removeSession(deps: WriteDeps, session: Session): Promise<void>;
62
+ /** A new session from the freshly fetched default branch, exactly as `ctxr session start` names and places one. */
63
+ export declare function startSession(deps: WriteDeps, label: string): Promise<Session>;
64
+ /** The store-relative path's location inside the repository, for `git show <branch>:<path>`. */
65
+ export declare function repoPathOf(deps: WriteDeps, storeRelative: string): Promise<string>;
66
+ /**
67
+ * Stages `paths` and commits them authored by the member, with the store's
68
+ * hooks running as for any commit. On a refusal the worktree is restored to
69
+ * its tip — a draft never keeps a change its pull request does not carry —
70
+ * and the hook's output is the refusal (design.md D13).
71
+ */
72
+ export declare function commitAs(deps: WriteDeps, session: Session, email: string, paths: readonly string[], subject: string, trailer: string): Promise<string>;
73
+ export declare function restore(deps: WriteDeps, session: Session): Promise<void>;
74
+ /**
75
+ * Publishes whatever the session holds that the forge does not: its branch,
76
+ * pushed when the remote lacks the local tip, and a pull request, found by
77
+ * head before one is opened. Idempotent, so a retry after any partial
78
+ * failure converges on exactly one pull request (design.md D9, D13).
79
+ */
80
+ export declare function publish(deps: WriteDeps, session: Session, pull: {
81
+ title: string;
82
+ body: string;
83
+ }): Promise<PullRequest>;
84
+ //# sourceMappingURL=sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/sessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,154 @@
1
+ import path from 'node:path';
2
+ import { runRemoteGit } from '../forge/credentials.js';
3
+ import { addWorktree, removeWorktree } from '../git/worktree.js';
4
+ import { generateSessionBranchName, listSessionWorktreeDirs, sessionLabelFromDirName, sessionLabelSlug, worktreePathFor } from '../session.js';
5
+ export class WriteRefusal extends Error {
6
+ code;
7
+ details;
8
+ constructor(code, message, details) {
9
+ super(message);
10
+ this.code = code;
11
+ this.details = details;
12
+ this.name = 'WriteRefusal';
13
+ }
14
+ }
15
+ /** One write at a time across `capture`, `Edit` and `Write` (design.md D9, D13). */
16
+ export class Mutex {
17
+ tail = Promise.resolve();
18
+ run(fn) {
19
+ const next = this.tail.then(fn, fn);
20
+ this.tail = next.catch(() => undefined);
21
+ return next;
22
+ }
23
+ }
24
+ function firstLines(text, count = 6) {
25
+ return text.trim().split('\n').slice(0, count).join('\n');
26
+ }
27
+ export function approvalUrl(deps, session) {
28
+ return `${deps.baseUrl.replace(/\/+$/, '')}/approve/${encodeURIComponent(session.name)}`;
29
+ }
30
+ /** The session worktrees on disk whose label starts with `labelPrefix`, with their branches. */
31
+ export async function sessionsLabelled(deps, labelPrefix) {
32
+ const root = path.join(deps.canonicalRoot, deps.config.session.worktrees_path);
33
+ const sessions = [];
34
+ for (const name of await listSessionWorktreeDirs(deps.canonicalRoot, deps.config)) {
35
+ const label = sessionLabelFromDirName(deps.config, name);
36
+ if (label === null || !label.startsWith(labelPrefix))
37
+ continue;
38
+ const dir = path.join(root, name);
39
+ const branch = (await deps.git.run(['symbolic-ref', '--short', 'HEAD'], { cwd: dir, allowFailure: true })).stdout.trim();
40
+ if (branch === '')
41
+ continue;
42
+ sessions.push({ name, path: dir, branch, label });
43
+ }
44
+ return sessions.sort((a, b) => (a.name < b.name ? -1 : 1));
45
+ }
46
+ /** What the forge says about the pull requests for `branch`: the open one, else merged, else closed, else none. */
47
+ export async function pullStateFor(deps, branch) {
48
+ const pulls = await deps.forge.pullsForHead(branch);
49
+ const open = pulls.find((p) => p.state === 'open');
50
+ if (open !== undefined)
51
+ return { kind: 'open', pull: open };
52
+ if (pulls.some((p) => p.merged))
53
+ return { kind: 'merged' };
54
+ if (pulls.length > 0)
55
+ return { kind: 'closed-unmerged' };
56
+ return { kind: 'none' };
57
+ }
58
+ /** A session whose pull request was closed without merging is removed: worktree and local branch (D9, D13). */
59
+ export async function removeSession(deps, session) {
60
+ await removeWorktree(deps.git, deps.canonicalRoot, session.path);
61
+ await deps.git.run(['branch', '-D', session.branch], { cwd: deps.canonicalRoot, allowFailure: true });
62
+ }
63
+ /** A new session from the freshly fetched default branch, exactly as `ctxr session start` names and places one. */
64
+ export async function startSession(deps, label) {
65
+ const slug = sessionLabelSlug(label);
66
+ const defaultBranch = deps.config.git.default_branch;
67
+ await runRemoteGit(deps.git, deps.credentials, deps.env, ['fetch', '--quiet', 'origin', `+refs/heads/${defaultBranch}:refs/remotes/origin/${defaultBranch}`], {
68
+ cwd: deps.canonicalRoot,
69
+ allowFailure: true,
70
+ });
71
+ const hasRemote = (await deps.git.run(['rev-parse', '--verify', '--quiet', `refs/remotes/origin/${defaultBranch}`], { cwd: deps.canonicalRoot, allowFailure: true })).exitCode === 0;
72
+ // Names carry a timestamp to the second, and a closed pull request's branch stays on the remote: step past any taken name.
73
+ let branch = generateSessionBranchName(deps.config, deps.now(), slug);
74
+ for (let step = 1; step <= 60 && (await branchTaken(deps, branch)); step += 1) {
75
+ branch = generateSessionBranchName(deps.config, new Date(deps.now().getTime() + step * 1000), slug);
76
+ }
77
+ const dir = worktreePathFor({ root: deps.canonicalRoot, config: deps.config }, branch);
78
+ const added = await addWorktree(deps.git, deps.canonicalRoot, dir, branch, hasRemote ? `origin/${defaultBranch}` : defaultBranch, { allowFailure: true });
79
+ if (added.exitCode !== 0)
80
+ throw new WriteRefusal('mcp.session_not_started', `A session could not be started: ${firstLines(added.stderr)}`);
81
+ return { name: path.basename(dir), path: dir, branch, label: slug };
82
+ }
83
+ async function branchTaken(deps, branch) {
84
+ const local = await deps.git.run(['rev-parse', '--verify', '--quiet', `refs/heads/${branch}`], { cwd: deps.canonicalRoot, allowFailure: true });
85
+ if (local.exitCode === 0)
86
+ return true;
87
+ const remote = await runRemoteGit(deps.git, deps.credentials, deps.env, ['ls-remote', '--exit-code', 'origin', `refs/heads/${branch}`], { cwd: deps.canonicalRoot, allowFailure: true });
88
+ return remote.exitCode === 0;
89
+ }
90
+ /** The store-relative path's location inside the repository, for `git show <branch>:<path>`. */
91
+ export async function repoPathOf(deps, storeRelative) {
92
+ const prefix = deps.repoPrefix ?? (await deps.git.run(['rev-parse', '--show-prefix'], { cwd: deps.canonicalRoot, allowFailure: true })).stdout.trim();
93
+ return `${prefix}${storeRelative}`;
94
+ }
95
+ /** The committer: the host's git identity when it has one, else the recording login's noreply address, as `ctxr land` falls back. */
96
+ async function committerEnv(deps, cwd) {
97
+ const email = (await deps.git.run(['config', '--get', 'user.email'], { cwd, allowFailure: true })).stdout.trim();
98
+ if (email !== '' || deps.env.GIT_COMMITTER_EMAIL || deps.env.EMAIL)
99
+ return {};
100
+ const address = `${deps.recordingLogin.replace(/\[bot\]$/, '')}@users.noreply.github.com`;
101
+ return { GIT_COMMITTER_NAME: deps.recordingLogin, GIT_COMMITTER_EMAIL: address };
102
+ }
103
+ /**
104
+ * Stages `paths` and commits them authored by the member, with the store's
105
+ * hooks running as for any commit. On a refusal the worktree is restored to
106
+ * its tip — a draft never keeps a change its pull request does not carry —
107
+ * and the hook's output is the refusal (design.md D13).
108
+ */
109
+ export async function commitAs(deps, session, email, paths, subject, trailer) {
110
+ const run = (args, env) => deps.git.run(args, { cwd: session.path, allowFailure: true, ...(env ? { env } : {}) });
111
+ const added = await run(['add', '--', ...paths]);
112
+ if (added.exitCode !== 0) {
113
+ await restore(deps, session);
114
+ throw new WriteRefusal('mcp.commit_refused', `The change could not be staged: ${firstLines(added.stderr)}`);
115
+ }
116
+ const name = email.split('@')[0] ?? email;
117
+ const env = { ...deps.env, ...(await committerEnv(deps, session.path)), GIT_AUTHOR_NAME: name, GIT_AUTHOR_EMAIL: email };
118
+ const committed = await run(['commit', '--quiet', '-m', subject, '-m', `${trailer}: ${email}`], env);
119
+ if (committed.exitCode !== 0) {
120
+ await restore(deps, session);
121
+ throw new WriteRefusal('mcp.commit_refused', `The store refused the change: ${firstLines(`${committed.stdout}\n${committed.stderr}`, 12)}`);
122
+ }
123
+ return (await run(['rev-parse', 'HEAD'])).stdout.trim();
124
+ }
125
+ export async function restore(deps, session) {
126
+ await deps.git.run(['reset', '--quiet', '--hard', 'HEAD'], { cwd: session.path, allowFailure: true });
127
+ await deps.git.run(['clean', '-fdq'], { cwd: session.path, allowFailure: true });
128
+ }
129
+ /**
130
+ * Publishes whatever the session holds that the forge does not: its branch,
131
+ * pushed when the remote lacks the local tip, and a pull request, found by
132
+ * head before one is opened. Idempotent, so a retry after any partial
133
+ * failure converges on exactly one pull request (design.md D9, D13).
134
+ */
135
+ export async function publish(deps, session, pull) {
136
+ const tip = (await deps.git.run(['rev-parse', 'HEAD'], { cwd: session.path })).stdout.trim();
137
+ const remote = await runRemoteGit(deps.git, deps.credentials, deps.env, ['ls-remote', 'origin', `refs/heads/${session.branch}`], { cwd: session.path, allowFailure: true });
138
+ if (remote.exitCode !== 0 || remote.stdout.split(/\s/)[0] !== tip) {
139
+ const pushed = await runRemoteGit(deps.git, deps.credentials, deps.env, ['push', '--quiet', 'origin', `HEAD:refs/heads/${session.branch}`], { cwd: session.path, allowFailure: true });
140
+ if (pushed.exitCode !== 0) {
141
+ throw new WriteRefusal('mcp.push_failed', `The change is saved in its session but could not be pushed yet (${firstLines(pushed.stderr, 2)}). Retry the same call to publish it.`, { session: session.name });
142
+ }
143
+ }
144
+ const state = await pullStateFor(deps, session.branch);
145
+ if (state.kind === 'open')
146
+ return state.pull;
147
+ try {
148
+ return await deps.forge.createPull({ head: session.branch, base: deps.config.git.default_branch, title: pull.title, body: pull.body });
149
+ }
150
+ catch (err) {
151
+ throw new WriteRefusal('mcp.pull_request_failed', `The change is pushed but its pull request could not be opened yet (${err instanceof Error ? firstLines(err.message, 2) : String(err)}). Retry the same call to open it.`, { session: session.name });
152
+ }
153
+ }
154
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../../src/core/mcp/sessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import type { ApprovalServer } from '../browse/approve.js';
3
+ import type { GitRunner } from '../git/exec.js';
4
+ import type { Store } from '../store.js';
5
+ /**
6
+ * serve-the-store-over-mcp design.md D3, D10 (remote-access spec): the MCP
7
+ * surface `ctxr serve` routes its MCP paths to. One per server, holding only
8
+ * what must outlive a configuration reload — the read view, the spent-code
9
+ * nonces, and the signer. Everything else is built per request from the
10
+ * state `serve` last loaded, so adding, changing or removing the `mcp` block
11
+ * takes effect on the next request with no restart.
12
+ */
13
+ /** Every path the MCP surface answers; `serve` routes these here ahead of its own routes. */
14
+ export declare function isMcpPath(pathname: string): boolean;
15
+ export interface McpSurfaceState {
16
+ store: Store;
17
+ approval: ApprovalServer | null;
18
+ approvalError: string | null;
19
+ }
20
+ export interface McpSurfaceOptions {
21
+ git: GitRunner;
22
+ canonicalRoot: string;
23
+ env: Readonly<Record<string, string | undefined>>;
24
+ now(): Date;
25
+ log(line: string): void;
26
+ }
27
+ export declare class McpSurface {
28
+ private readonly options;
29
+ private readonly view;
30
+ private readonly spentCodes;
31
+ private readonly mutex;
32
+ private readonly signers;
33
+ private randomKey;
34
+ /** Derived once per credential: the key never changes while a credential object lives. */
35
+ private readonly derived;
36
+ private repoPrefix;
37
+ constructor(options: McpSurfaceOptions);
38
+ /** The App-derived key when there is an App; otherwise one random key for this process, said once. */
39
+ private signerFor;
40
+ handle(state: McpSurfaceState, req: IncomingMessage, res: ServerResponse, pathname: string): Promise<void>;
41
+ }
42
+ //# sourceMappingURL=surface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/surface.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AASzC;;;;;;;GAOG;AAEH,6FAA6F;AAC7F,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,SAAS,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAClD,GAAG,IAAI,IAAI,CAAC;IACZ,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAeD,qBAAa,UAAU;IAUT,OAAO,CAAC,QAAQ,CAAC,OAAO;IATpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAU;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IACxD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,SAAS,CAAqB;IACtC,0FAA0F;IAC1F,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwC;IAChE,OAAO,CAAC,UAAU,CAAqB;IAEvC,YAA6B,OAAO,EAAE,iBAAiB,EAEtD;IAED,sGAAsG;IACtG,OAAO,CAAC,SAAS;IAmBX,MAAM,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4E/G;CACF"}