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.
- package/README.md +28 -0
- package/dist/commands/land.d.ts.map +1 -1
- package/dist/commands/land.js +3 -18
- package/dist/commands/land.js.map +1 -1
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +10 -2
- package/dist/commands/serve.js.map +1 -1
- package/dist/config/schema.d.ts +17 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +40 -8
- package/dist/config/schema.js.map +1 -1
- package/dist/core/browse/approve.d.ts +3 -0
- package/dist/core/browse/approve.d.ts.map +1 -1
- package/dist/core/browse/approve.js.map +1 -1
- package/dist/core/browse/page-url.d.ts +15 -0
- package/dist/core/browse/page-url.d.ts.map +1 -1
- package/dist/core/browse/page-url.js +8 -1
- package/dist/core/browse/page-url.js.map +1 -1
- package/dist/core/forge/credentials.d.ts +12 -1
- package/dist/core/forge/credentials.d.ts.map +1 -1
- package/dist/core/forge/credentials.js +25 -1
- package/dist/core/forge/credentials.js.map +1 -1
- package/dist/core/forge/github.d.ts +9 -0
- package/dist/core/forge/github.d.ts.map +1 -1
- package/dist/core/forge/github.js +6 -0
- package/dist/core/forge/github.js.map +1 -1
- package/dist/core/mcp/drafts.d.ts +30 -0
- package/dist/core/mcp/drafts.d.ts.map +1 -0
- package/dist/core/mcp/drafts.js +52 -0
- package/dist/core/mcp/drafts.js.map +1 -0
- package/dist/core/mcp/http.d.ts +40 -0
- package/dist/core/mcp/http.d.ts.map +1 -0
- package/dist/core/mcp/http.js +90 -0
- package/dist/core/mcp/http.js.map +1 -0
- package/dist/core/mcp/jsonrpc.d.ts +73 -0
- package/dist/core/mcp/jsonrpc.d.ts.map +1 -0
- package/dist/core/mcp/jsonrpc.js +149 -0
- package/dist/core/mcp/jsonrpc.js.map +1 -0
- package/dist/core/mcp/members.d.ts +11 -0
- package/dist/core/mcp/members.d.ts.map +1 -0
- package/dist/core/mcp/members.js +21 -0
- package/dist/core/mcp/members.js.map +1 -0
- package/dist/core/mcp/oauth.d.ts +99 -0
- package/dist/core/mcp/oauth.d.ts.map +1 -0
- package/dist/core/mcp/oauth.js +454 -0
- package/dist/core/mcp/oauth.js.map +1 -0
- package/dist/core/mcp/pages.d.ts +3 -0
- package/dist/core/mcp/pages.d.ts.map +1 -0
- package/dist/core/mcp/pages.js +41 -0
- package/dist/core/mcp/pages.js.map +1 -0
- package/dist/core/mcp/ripgrep.d.ts +48 -0
- package/dist/core/mcp/ripgrep.d.ts.map +1 -0
- package/dist/core/mcp/ripgrep.js +98 -0
- package/dist/core/mcp/ripgrep.js.map +1 -0
- package/dist/core/mcp/rwlock.d.ts +16 -0
- package/dist/core/mcp/rwlock.d.ts.map +1 -0
- package/dist/core/mcp/rwlock.js +59 -0
- package/dist/core/mcp/rwlock.js.map +1 -0
- package/dist/core/mcp/sessions.d.ts +84 -0
- package/dist/core/mcp/sessions.d.ts.map +1 -0
- package/dist/core/mcp/sessions.js +154 -0
- package/dist/core/mcp/sessions.js.map +1 -0
- package/dist/core/mcp/surface.d.ts +42 -0
- package/dist/core/mcp/surface.d.ts.map +1 -0
- package/dist/core/mcp/surface.js +141 -0
- package/dist/core/mcp/surface.js.map +1 -0
- package/dist/core/mcp/sync.d.ts +91 -0
- package/dist/core/mcp/sync.d.ts.map +1 -0
- package/dist/core/mcp/sync.js +218 -0
- package/dist/core/mcp/sync.js.map +1 -0
- package/dist/core/mcp/tools/capture.d.ts +64 -0
- package/dist/core/mcp/tools/capture.d.ts.map +1 -0
- package/dist/core/mcp/tools/capture.js +182 -0
- package/dist/core/mcp/tools/capture.js.map +1 -0
- package/dist/core/mcp/tools/edit.d.ts +79 -0
- package/dist/core/mcp/tools/edit.d.ts.map +1 -0
- package/dist/core/mcp/tools/edit.js +212 -0
- package/dist/core/mcp/tools/edit.js.map +1 -0
- package/dist/core/mcp/tools/files.d.ts +163 -0
- package/dist/core/mcp/tools/files.d.ts.map +1 -0
- package/dist/core/mcp/tools/files.js +265 -0
- package/dist/core/mcp/tools/files.js.map +1 -0
- package/dist/core/mcp/tools/find.d.ts +82 -0
- package/dist/core/mcp/tools/find.d.ts.map +1 -0
- package/dist/core/mcp/tools/find.js +136 -0
- package/dist/core/mcp/tools/find.js.map +1 -0
- package/dist/core/mcp/tools/gather.d.ts +88 -0
- package/dist/core/mcp/tools/gather.d.ts.map +1 -0
- package/dist/core/mcp/tools/gather.js +123 -0
- package/dist/core/mcp/tools/gather.js.map +1 -0
- package/dist/core/mcp/tools/index.d.ts +21 -0
- package/dist/core/mcp/tools/index.d.ts.map +1 -0
- package/dist/core/mcp/tools/index.js +129 -0
- package/dist/core/mcp/tools/index.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/templates/mcp/Edit.md +7 -0
- package/templates/mcp/Glob.md +5 -0
- package/templates/mcp/Grep.md +10 -0
- package/templates/mcp/Read.md +7 -0
- package/templates/mcp/Write.md +7 -0
- package/templates/mcp/capture.md +9 -0
- package/templates/mcp/consent.html +40 -0
- package/templates/mcp/find.md +9 -0
- package/templates/mcp/gather.md +5 -0
- package/templates/mcp/server-instructions.md +17 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { stringify as stringifyYaml } from 'yaml';
|
|
5
|
+
import { contentHash } from '../../content/canonicalize.js';
|
|
6
|
+
import { SOURCE_ID_FIELD, SOURCE_TYPE_FIELD } from '../../ingest/identity.js';
|
|
7
|
+
import { evaluateSourceCheck } from '../../ingest/model.js';
|
|
8
|
+
import { missingRequiredSection } from '../../ingest/required-sections.js';
|
|
9
|
+
import { listCaptures, listNotes } from '../../notes/list.js';
|
|
10
|
+
import { parseNoteText } from '../../notes/parse.js';
|
|
11
|
+
import { slugifySegment } from '../../slug.js';
|
|
12
|
+
import { isUnderAnyPrefix } from '../../fs/prefix.js';
|
|
13
|
+
import { ToolRefusal } from '../jsonrpc.js';
|
|
14
|
+
import { approvalUrl, commitAs, publish, pullStateFor, removeSession, repoPathOf, sessionsLabelled, startSession, WriteRefusal, } from '../sessions.js';
|
|
15
|
+
/**
|
|
16
|
+
* remote-access spec, *`capture` opens a session pull request and never
|
|
17
|
+
* writes the default branch* (design.md D9). One new inbox file, on its own
|
|
18
|
+
* session, pushed as the App, with a pull request the approval page lists. A
|
|
19
|
+
* capture is idempotent by source identity — derived from the content when
|
|
20
|
+
* the caller names none — so a call that failed after committing resumes into
|
|
21
|
+
* the same pull request instead of being refused as its own duplicate.
|
|
22
|
+
*/
|
|
23
|
+
export const MAX_CAPTURE_BODY_BYTES = 256 * 1024;
|
|
24
|
+
const MAX_TITLE_CHARS = 200;
|
|
25
|
+
const SOURCE_TYPE_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
26
|
+
export const CAPTURE_LABEL_PREFIX = 'capture-';
|
|
27
|
+
/** Plain on purpose (see FIND_INPUT_SCHEMA): `parseCaptureArgs` enforces every limit. */
|
|
28
|
+
export const CAPTURE_INPUT_SCHEMA = {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
title: { type: 'string', description: 'A short title naming the item, e.g. "Pricing review call, 14 October".' },
|
|
32
|
+
body: { type: 'string', description: 'The material itself, in its own words, as markdown.' },
|
|
33
|
+
source_type: { type: 'string', description: 'Where it came from, lowercased: email, meeting, web, document, …' },
|
|
34
|
+
source_id: { type: 'string', description: "The source system's own id for the item (a message id, a meeting id, a URL), when there is one." },
|
|
35
|
+
},
|
|
36
|
+
required: ['title', 'body', 'source_type'],
|
|
37
|
+
};
|
|
38
|
+
export function parseCaptureArgs(args) {
|
|
39
|
+
const invalid = (message) => new ToolRefusal('mcp.invalid_arguments', message);
|
|
40
|
+
if (typeof args.title !== 'string' || args.title.trim().length === 0)
|
|
41
|
+
throw invalid('"title" is required.');
|
|
42
|
+
if (args.title.length > MAX_TITLE_CHARS)
|
|
43
|
+
throw invalid(`"title" is at most ${MAX_TITLE_CHARS} characters.`);
|
|
44
|
+
if (typeof args.body !== 'string' || args.body.trim().length === 0)
|
|
45
|
+
throw invalid('"body" is required: the material itself.');
|
|
46
|
+
if (Buffer.byteLength(args.body, 'utf8') > MAX_CAPTURE_BODY_BYTES)
|
|
47
|
+
throw invalid(`"body" is at most ${MAX_CAPTURE_BODY_BYTES} bytes; split the material into separate captures.`);
|
|
48
|
+
if (typeof args.source_type !== 'string' || !SOURCE_TYPE_RE.test(args.source_type)) {
|
|
49
|
+
throw invalid('"source_type" is a lowercased word naming where the material came from, e.g. "email" or "meeting".');
|
|
50
|
+
}
|
|
51
|
+
if (args.source_id !== undefined && args.source_id !== null && (typeof args.source_id !== 'string' || args.source_id.trim().length === 0)) {
|
|
52
|
+
throw invalid('"source_id", when given, is a non-empty string.');
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
title: args.title.trim(),
|
|
56
|
+
body: args.body,
|
|
57
|
+
source_type: args.source_type,
|
|
58
|
+
...(typeof args.source_id === 'string' ? { source_id: args.source_id.trim() } : {}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** The capture file's text: frontmatter carrying the identity the inbox requires, then the material as given. */
|
|
62
|
+
function captureText(args, sourceId) {
|
|
63
|
+
const frontmatter = stringifyYaml({ title: args.title, [SOURCE_TYPE_FIELD]: args.source_type, [SOURCE_ID_FIELD]: sourceId }).trimEnd();
|
|
64
|
+
return `---\n${frontmatter}\n---\n\n${args.body.replace(/\s+$/, '')}\n`;
|
|
65
|
+
}
|
|
66
|
+
/** The inbox file a capture session's committed tip adds, as an identity record; null when it adds none. */
|
|
67
|
+
async function sessionCapture(write, session, inbox) {
|
|
68
|
+
const base = `refs/remotes/origin/${write.config.git.default_branch}`;
|
|
69
|
+
const added = await write.git.run(['diff', '--name-only', '--diff-filter=A', '--no-renames', `${base}...HEAD`], { cwd: session.path, allowFailure: true });
|
|
70
|
+
const prefix = await repoPathOf(write, '');
|
|
71
|
+
for (const repoPath of added.stdout.split('\n').filter((p) => p.length > 0)) {
|
|
72
|
+
if (!repoPath.startsWith(prefix))
|
|
73
|
+
continue;
|
|
74
|
+
const rel = repoPath.slice(prefix.length);
|
|
75
|
+
if (!isUnderAnyPrefix(rel, [inbox]) || !rel.endsWith('.md'))
|
|
76
|
+
continue;
|
|
77
|
+
const shown = await write.git.run(['show', `HEAD:${repoPath}`], { cwd: session.path, allowFailure: true });
|
|
78
|
+
if (shown.exitCode !== 0)
|
|
79
|
+
return null;
|
|
80
|
+
try {
|
|
81
|
+
return { path: rel, frontmatter: parseNoteText(shown.stdout, rel).frontmatter };
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
function pullBody(email, args, sourceId, rel) {
|
|
90
|
+
return [
|
|
91
|
+
`Captured through the store's MCP connector by ${email}.`,
|
|
92
|
+
'',
|
|
93
|
+
`- Source: ${args.source_type} (\`${sourceId}\`)`,
|
|
94
|
+
`- File: \`${rel}\``,
|
|
95
|
+
'',
|
|
96
|
+
'An inbox capture only: approving it lands the file in the inbox, where it waits to be filed into notes.',
|
|
97
|
+
].join('\n');
|
|
98
|
+
}
|
|
99
|
+
function resultFor(write, session, pull, rel, sourceId, resumed) {
|
|
100
|
+
const approval = approvalUrl(write, session);
|
|
101
|
+
return {
|
|
102
|
+
saved: true,
|
|
103
|
+
resumed,
|
|
104
|
+
path: rel,
|
|
105
|
+
source_id: sourceId,
|
|
106
|
+
pull_request: { number: pull.number, url: pull.url },
|
|
107
|
+
approval_url: approval,
|
|
108
|
+
message: `Saved for approval as pull request #${pull.number}. It lands in the inbox once someone approves it at ${approval}.`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Run under the write mutex (design.md D9). */
|
|
112
|
+
export async function capture(ctx, email, args) {
|
|
113
|
+
// A sync forced now, so the duplicate check sees what was pushed and opened a moment ago.
|
|
114
|
+
await ctx.view.refresh(ctx.syncDeps);
|
|
115
|
+
// The identity records are read under the view's lock, so a sync cannot move the view mid-read.
|
|
116
|
+
const { viewConfig, viewRecords } = await ctx.view.read(ctx.syncDeps, async (snapshot) => ({
|
|
117
|
+
viewConfig: snapshot.store.config,
|
|
118
|
+
viewRecords: [...(await listCaptures(snapshot.store.root, snapshot.store.config)), ...(await listNotes(snapshot.store.root, snapshot.store.config))],
|
|
119
|
+
}));
|
|
120
|
+
const config = viewConfig;
|
|
121
|
+
const inbox = config.ingest.inbox_path;
|
|
122
|
+
const missing = missingRequiredSection(config, args.source_type, args.body);
|
|
123
|
+
if (missing !== undefined) {
|
|
124
|
+
throw new ToolRefusal('mcp.required_section_missing', `A ${args.source_type} capture must carry a "${missing}" section with something under it; this one does not.`, { section: missing });
|
|
125
|
+
}
|
|
126
|
+
// The hash is over the canonical body, which the frontmatter does not enter, so any id gives the same hash.
|
|
127
|
+
const hash = contentHash(captureText(args, 'pending'), 'capture');
|
|
128
|
+
const sourceId = args.source_id ?? `${args.source_type}/${hash}`;
|
|
129
|
+
const pullText = (rel) => ({ title: `Capture: ${args.title}`, body: pullBody(email, args, sourceId, rel) });
|
|
130
|
+
// Capture sessions on disk: reclaim the closed, resume the unpublished, count the open as pending.
|
|
131
|
+
const pending = [];
|
|
132
|
+
for (const session of await sessionsLabelled(ctx.write, CAPTURE_LABEL_PREFIX)) {
|
|
133
|
+
const state = await pullStateFor(ctx.write, session.branch);
|
|
134
|
+
if (state.kind === 'closed-unmerged') {
|
|
135
|
+
await removeSession(ctx.write, session);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (state.kind === 'merged')
|
|
139
|
+
continue;
|
|
140
|
+
const record = await sessionCapture(ctx.write, session, inbox);
|
|
141
|
+
if (record === null)
|
|
142
|
+
continue;
|
|
143
|
+
if (state.kind === 'none' && record.frontmatter?.[SOURCE_ID_FIELD] === sourceId) {
|
|
144
|
+
const pull = await publish(ctx.write, session, pullText(record.path));
|
|
145
|
+
return resultFor(ctx.write, session, pull, record.path, sourceId, true);
|
|
146
|
+
}
|
|
147
|
+
if (state.kind === 'open')
|
|
148
|
+
pending.push({ path: `${record.path} (pull request #${state.pull.number}, awaiting approval)`, frontmatter: record.frontmatter });
|
|
149
|
+
}
|
|
150
|
+
const records = [...viewRecords, ...pending];
|
|
151
|
+
const verdict = evaluateSourceCheck(records, hash, sourceId, config.ingest.tracking_params);
|
|
152
|
+
if (verdict.verdict !== 'new') {
|
|
153
|
+
throw new ToolRefusal('mcp.already_captured', `This material is already in the store or awaiting approval (${verdict.verdict.replace(/_/g, ' ')}): ${verdict.matches.join(', ')}. Nothing was saved.`, {
|
|
154
|
+
verdict: verdict.verdict,
|
|
155
|
+
matches: verdict.matches,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
const date = ctx.write.now().toISOString().slice(0, 10);
|
|
159
|
+
const slug = slugifySegment(args.title).slice(0, 40).replace(/-+$/, '') || 'capture';
|
|
160
|
+
const id = randomBytes(3).toString('hex');
|
|
161
|
+
const session = await startSession(ctx.write, `${CAPTURE_LABEL_PREFIX}${slug.slice(0, 30)}-${id}`);
|
|
162
|
+
// posix join: the schema accepts an inbox path with or without its trailing slash.
|
|
163
|
+
const rel = path.posix.join(inbox, `${date}-${slug}-${id}.md`);
|
|
164
|
+
const repoPath = await repoPathOf(ctx.write, rel);
|
|
165
|
+
const absolute = path.join(session.path, repoPath);
|
|
166
|
+
await mkdir(path.dirname(absolute), { recursive: true });
|
|
167
|
+
await writeFile(absolute, captureText(args, sourceId));
|
|
168
|
+
try {
|
|
169
|
+
await commitAs(ctx.write, session, email, [repoPath], `Capture: ${args.title}`, 'Captured-by');
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
await removeSession(ctx.write, session);
|
|
173
|
+
throw err;
|
|
174
|
+
}
|
|
175
|
+
const pull = await publish(ctx.write, session, pullText(rel));
|
|
176
|
+
return resultFor(ctx.write, session, pull, rel, sourceId, false);
|
|
177
|
+
}
|
|
178
|
+
/** WriteRefusal → ToolRefusal, so the client reads the reason rather than a protocol error. */
|
|
179
|
+
export function asToolRefusal(err) {
|
|
180
|
+
return err instanceof WriteRefusal ? new ToolRefusal(err.code, err.message, err.details) : err;
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../../../../src/core/mcp/tools/capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type WriteDeps } from '../sessions.js';
|
|
2
|
+
import type { McpView, SyncDeps } from '../sync.js';
|
|
3
|
+
/**
|
|
4
|
+
* serve-the-store-over-mcp design.md D13 (remote-access spec, *`Edit` and
|
|
5
|
+
* `Write` collect in one draft pull request per member*): Claude Code's Edit
|
|
6
|
+
* and Write, landing as commits on the member's draft — one session, one pull
|
|
7
|
+
* request, reviewed on the approval page. A call first publishes whatever the
|
|
8
|
+
* draft left unpublished, and a retry whose effect is already in the draft is
|
|
9
|
+
* answered without a second commit, so a failure partway never strands a change.
|
|
10
|
+
*/
|
|
11
|
+
export interface EditArgs {
|
|
12
|
+
file_path: string;
|
|
13
|
+
old_string: string;
|
|
14
|
+
new_string: string;
|
|
15
|
+
replace_all?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface WriteArgs {
|
|
18
|
+
file_path: string;
|
|
19
|
+
content: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const EDIT_INPUT_SCHEMA: {
|
|
22
|
+
readonly type: 'object';
|
|
23
|
+
readonly properties: {
|
|
24
|
+
readonly file_path: {
|
|
25
|
+
readonly type: 'string';
|
|
26
|
+
readonly description: 'The note to modify, relative to the store root, e.g. "projects/kestrel.md".';
|
|
27
|
+
};
|
|
28
|
+
readonly old_string: {
|
|
29
|
+
readonly type: 'string';
|
|
30
|
+
readonly description: 'The text to replace';
|
|
31
|
+
};
|
|
32
|
+
readonly new_string: {
|
|
33
|
+
readonly type: 'string';
|
|
34
|
+
readonly description: 'The text to replace it with (must be different from old_string)';
|
|
35
|
+
};
|
|
36
|
+
readonly replace_all: {
|
|
37
|
+
readonly type: 'boolean';
|
|
38
|
+
readonly description: 'Replace all occurrences of old_string (default false)';
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
readonly required: readonly ['file_path', 'old_string', 'new_string'];
|
|
42
|
+
};
|
|
43
|
+
export declare const WRITE_INPUT_SCHEMA: {
|
|
44
|
+
readonly type: 'object';
|
|
45
|
+
readonly properties: {
|
|
46
|
+
readonly file_path: {
|
|
47
|
+
readonly type: 'string';
|
|
48
|
+
readonly description: 'The new note to create, relative to the store root, e.g. "projects/kestrel-launch.md".';
|
|
49
|
+
};
|
|
50
|
+
readonly content: {
|
|
51
|
+
readonly type: 'string';
|
|
52
|
+
readonly description: 'The content to write to the note';
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
readonly required: readonly ['file_path', 'content'];
|
|
56
|
+
};
|
|
57
|
+
export declare function parseEditArgs(args: Record<string, unknown>): EditArgs;
|
|
58
|
+
export declare function parseWriteArgs(args: Record<string, unknown>): WriteArgs;
|
|
59
|
+
export interface DraftContext {
|
|
60
|
+
view: McpView;
|
|
61
|
+
syncDeps: SyncDeps;
|
|
62
|
+
write: WriteDeps;
|
|
63
|
+
}
|
|
64
|
+
export interface DraftResult {
|
|
65
|
+
file_path: string;
|
|
66
|
+
changed: boolean;
|
|
67
|
+
/** Set when a retried call's effect was already in the draft: nothing new was committed. */
|
|
68
|
+
already_applied: boolean;
|
|
69
|
+
commit: string;
|
|
70
|
+
pull_request: {
|
|
71
|
+
number: number;
|
|
72
|
+
url: string;
|
|
73
|
+
};
|
|
74
|
+
approval_url: string;
|
|
75
|
+
message: string;
|
|
76
|
+
}
|
|
77
|
+
export declare function editTool(ctx: DraftContext, email: string, args: EditArgs): Promise<DraftResult>;
|
|
78
|
+
export declare function writeTool(ctx: DraftContext, email: string, args: WriteArgs): Promise<DraftResult>;
|
|
79
|
+
//# sourceMappingURL=edit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../../src/core/mcp/tools/edit.ts"],"names":[],"mappings":"AAUA,OAAO,EAAyF,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACvI,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGpD;;;;;;;GAOG;AAEH,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,iBAAiB;aAC5B,IAAI,EAAE,QAAQ;aACd,UAAU;iBACR,SAAS;qBAAI,IAAI,EAAE,QAAQ;qBAAE,WAAW,EAAE,6EAA6E;;iBACvH,UAAU;qBAAI,IAAI,EAAE,QAAQ;qBAAE,WAAW,EAAE,qBAAqB;;iBAChE,UAAU;qBAAI,IAAI,EAAE,QAAQ;qBAAE,WAAW,EAAE,iEAAiE;;iBAC5G,WAAW;qBAAI,IAAI,EAAE,SAAS;qBAAE,WAAW,EAAE,uDAAuD;;;aAEtG,QAAQ,YAAG,WAAW,EAAE,YAAY,EAAE,YAAY;CAC1C,CAAC;AAEX,eAAO,MAAM,kBAAkB;aAC7B,IAAI,EAAE,QAAQ;aACd,UAAU;iBACR,SAAS;qBAAI,IAAI,EAAE,QAAQ;qBAAE,WAAW,EAAE,wFAAwF;;iBAClI,OAAO;qBAAI,IAAI,EAAE,QAAQ;qBAAE,WAAW,EAAE,kCAAkC;;;aAE5E,QAAQ,YAAG,WAAW,EAAE,SAAS;CACzB,CAAC;AAIX,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ,CAQrE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAMvE;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,4FAA4F;IAC5F,eAAe,EAAE,OAAO,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AA0ID,wBAAsB,QAAQ,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,CAgBrG;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAYvG"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { readConfig } from '../../../config/load.js';
|
|
4
|
+
import { MIGRATION_STEPS } from '../../../config/migrations.js';
|
|
5
|
+
import { buildCatalog } from '../../catalog/build.js';
|
|
6
|
+
import { InvalidNoteFrontmatterError } from '../../errors.js';
|
|
7
|
+
import { admitsNotePath } from '../../notes/list.js';
|
|
8
|
+
import { parseNoteText } from '../../notes/parse.js';
|
|
9
|
+
import { draftLabel, findDraft } from '../drafts.js';
|
|
10
|
+
import { ToolRefusal } from '../jsonrpc.js';
|
|
11
|
+
import { approvalUrl, commitAs, publish, removeSession, repoPathOf, startSession } from '../sessions.js';
|
|
12
|
+
import { resolveStorePath } from './files.js';
|
|
13
|
+
export const EDIT_INPUT_SCHEMA = {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
file_path: { type: 'string', description: 'The note to modify, relative to the store root, e.g. "projects/kestrel.md".' },
|
|
17
|
+
old_string: { type: 'string', description: 'The text to replace' },
|
|
18
|
+
new_string: { type: 'string', description: 'The text to replace it with (must be different from old_string)' },
|
|
19
|
+
replace_all: { type: 'boolean', description: 'Replace all occurrences of old_string (default false)' },
|
|
20
|
+
},
|
|
21
|
+
required: ['file_path', 'old_string', 'new_string'],
|
|
22
|
+
};
|
|
23
|
+
export const WRITE_INPUT_SCHEMA = {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
file_path: { type: 'string', description: 'The new note to create, relative to the store root, e.g. "projects/kestrel-launch.md".' },
|
|
27
|
+
content: { type: 'string', description: 'The content to write to the note' },
|
|
28
|
+
},
|
|
29
|
+
required: ['file_path', 'content'],
|
|
30
|
+
};
|
|
31
|
+
const MAX_CONTENT_BYTES = 256 * 1024;
|
|
32
|
+
export function parseEditArgs(args) {
|
|
33
|
+
const invalid = (message) => new ToolRefusal('mcp.invalid_arguments', message);
|
|
34
|
+
if (typeof args.file_path !== 'string' || args.file_path.trim() === '')
|
|
35
|
+
throw invalid('"file_path" is required.');
|
|
36
|
+
if (typeof args.old_string !== 'string' || args.old_string === '')
|
|
37
|
+
throw invalid('"old_string" is required and cannot be empty.');
|
|
38
|
+
if (typeof args.new_string !== 'string')
|
|
39
|
+
throw invalid('"new_string" is required.');
|
|
40
|
+
if (args.old_string === args.new_string)
|
|
41
|
+
throw invalid('"new_string" must be different from "old_string".');
|
|
42
|
+
if (args.replace_all !== undefined && args.replace_all !== null && typeof args.replace_all !== 'boolean')
|
|
43
|
+
throw invalid('"replace_all" must be true or false.');
|
|
44
|
+
return { file_path: args.file_path, old_string: args.old_string, new_string: args.new_string, replace_all: args.replace_all === true };
|
|
45
|
+
}
|
|
46
|
+
export function parseWriteArgs(args) {
|
|
47
|
+
const invalid = (message) => new ToolRefusal('mcp.invalid_arguments', message);
|
|
48
|
+
if (typeof args.file_path !== 'string' || args.file_path.trim() === '')
|
|
49
|
+
throw invalid('"file_path" is required.');
|
|
50
|
+
if (typeof args.content !== 'string' || args.content.trim() === '')
|
|
51
|
+
throw invalid('"content" is required.');
|
|
52
|
+
if (Buffer.byteLength(args.content, 'utf8') > MAX_CONTENT_BYTES)
|
|
53
|
+
throw invalid(`"content" is at most ${MAX_CONTENT_BYTES} bytes.`);
|
|
54
|
+
return { file_path: args.file_path, content: args.content };
|
|
55
|
+
}
|
|
56
|
+
/** design.md D13: editable means the note enumeration would admit it — markdown, outside the capture tier and every tool-owned path. */
|
|
57
|
+
function editablePath(input, config) {
|
|
58
|
+
const rel = resolveStorePath(input, config);
|
|
59
|
+
if (!admitsNotePath(rel, config)) {
|
|
60
|
+
throw new ToolRefusal('mcp.path_not_editable', `"${rel}" is not a note that can be edited here: only markdown notes are, never configuration, captures, the entry document or tool-owned files.`);
|
|
61
|
+
}
|
|
62
|
+
return rel;
|
|
63
|
+
}
|
|
64
|
+
function pullText(email) {
|
|
65
|
+
return {
|
|
66
|
+
title: `Edits by ${email}`,
|
|
67
|
+
body: [
|
|
68
|
+
`Edits made through the store's MCP connector by ${email}.`,
|
|
69
|
+
'',
|
|
70
|
+
'Each commit is one Edit or Write. Changes made after an approval need approving again.',
|
|
71
|
+
].join('\n'),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async function existing(file) {
|
|
75
|
+
try {
|
|
76
|
+
return await readFile(file, 'utf8');
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
if (err.code === 'ENOENT')
|
|
80
|
+
return null;
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function pathExists(file) {
|
|
85
|
+
return stat(file).then(() => true, () => false);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The member's draft as it stands, publishing anything it left unpublished; null when there is none to extend.
|
|
89
|
+
* A draft with no commit of its own (left behind by a crash between starting it and committing) is removed: the
|
|
90
|
+
* forge would refuse a pull request for it, and every later call would be refused with it.
|
|
91
|
+
*/
|
|
92
|
+
async function currentDraft(ctx, email) {
|
|
93
|
+
const draft = await findDraft(ctx.write, email);
|
|
94
|
+
if (draft === null)
|
|
95
|
+
return null;
|
|
96
|
+
if (draft.state.kind === 'closed-unmerged' || draft.state.kind === 'merged') {
|
|
97
|
+
await removeSession(ctx.write, draft.session);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const ahead = (await ctx.write.git.run(['rev-list', '--count', `refs/remotes/origin/${ctx.write.config.git.default_branch}..HEAD`], { cwd: draft.session.path, allowFailure: true })).stdout.trim();
|
|
101
|
+
if (ahead === '0' && draft.state.kind === 'none') {
|
|
102
|
+
await removeSession(ctx.write, draft.session);
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
await publish(ctx.write, draft.session, pullText(email));
|
|
106
|
+
return draft.session;
|
|
107
|
+
}
|
|
108
|
+
async function finish(ctx, email, session, rel, commit, alreadyApplied) {
|
|
109
|
+
const pull = await publish(ctx.write, session, pullText(email));
|
|
110
|
+
const approval = approvalUrl(ctx.write, session);
|
|
111
|
+
return {
|
|
112
|
+
file_path: rel,
|
|
113
|
+
changed: !alreadyApplied,
|
|
114
|
+
already_applied: alreadyApplied,
|
|
115
|
+
commit,
|
|
116
|
+
pull_request: { number: pull.number, url: pull.url },
|
|
117
|
+
approval_url: approval,
|
|
118
|
+
message: alreadyApplied
|
|
119
|
+
? `That change is already in your draft (pull request #${pull.number}); nothing new was committed.`
|
|
120
|
+
: `Saved to your draft, pull request #${pull.number}. It lands once someone approves it at ${approval}.`,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/** Applies a computed change to `rel` in the draft (starting one if needed), rebuilds the catalog, commits as the member. Under the write mutex. */
|
|
124
|
+
async function applyToDraft(ctx, email, rel, verb, compute) {
|
|
125
|
+
await ctx.view.refresh(ctx.syncDeps);
|
|
126
|
+
// Read under the view's lock, so a sync started by another call cannot move the view mid-read.
|
|
127
|
+
const fromView = (await ctx.view.read(ctx.syncDeps, async (snapshot) => ({ text: await existing(path.join(snapshot.store.root, rel)) }))).text;
|
|
128
|
+
let session = await currentDraft(ctx, email);
|
|
129
|
+
const fresh = session === null;
|
|
130
|
+
const repoPath = await repoPathOf(ctx.write, rel);
|
|
131
|
+
// Decide against what the member is editing: the draft's copy when there is a draft, else the served commit.
|
|
132
|
+
const basis = { current: session === null ? fromView : await existing(path.join(session.path, repoPath)) };
|
|
133
|
+
if (session !== null) {
|
|
134
|
+
const subject = (await ctx.write.git.run(['log', '-1', '--format=%s'], { cwd: session.path, allowFailure: true })).stdout.trim();
|
|
135
|
+
if (subject === `${verb} ${rel}`) {
|
|
136
|
+
const parent = await ctx.write.git.run(['show', `HEAD~1:${repoPath}`], { cwd: session.path, allowFailure: true });
|
|
137
|
+
basis.beforeTip = parent.exitCode === 0 ? parent.stdout : null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const outcome = compute(basis);
|
|
141
|
+
if ('alreadyApplied' in outcome) {
|
|
142
|
+
const tip = (await ctx.write.git.run(['rev-parse', 'HEAD'], { cwd: session.path })).stdout.trim();
|
|
143
|
+
return finish(ctx, email, session, rel, tip, true);
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
parseNoteText(outcome.text, rel);
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
const reason = err instanceof InvalidNoteFrontmatterError ? err.finding.message : err instanceof Error ? err.message : String(err);
|
|
150
|
+
throw new ToolRefusal('mcp.not_a_note', `The result would not be a valid note, so nothing was saved: ${reason}`);
|
|
151
|
+
}
|
|
152
|
+
session ??= await startSession(ctx.write, draftLabel(email));
|
|
153
|
+
try {
|
|
154
|
+
const absolute = path.join(session.path, repoPath);
|
|
155
|
+
await mkdir(path.dirname(absolute), { recursive: true });
|
|
156
|
+
await writeFile(absolute, outcome.text);
|
|
157
|
+
// The catalog follows the change, as it would in an agent's session (design.md D13).
|
|
158
|
+
const draftStoreRoot = path.join(session.path, await repoPathOf(ctx.write, ''));
|
|
159
|
+
const draftConfig = await readConfig(draftStoreRoot, MIGRATION_STEPS);
|
|
160
|
+
await buildCatalog({ root: draftStoreRoot, config: draftConfig });
|
|
161
|
+
const catalogPath = await repoPathOf(ctx.write, draftConfig.catalog.path);
|
|
162
|
+
const paths = (await pathExists(path.join(session.path, catalogPath))) ? [repoPath, catalogPath] : [repoPath];
|
|
163
|
+
const commit = await commitAs(ctx.write, session, email, paths, `${verb} ${rel}`, 'Edited-by');
|
|
164
|
+
return await finish(ctx, email, session, rel, commit, false);
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
if (fresh) {
|
|
168
|
+
const commits = (await ctx.write.git.run(['rev-list', '--count', `refs/remotes/origin/${ctx.write.config.git.default_branch}..HEAD`], { cwd: session.path, allowFailure: true })).stdout.trim();
|
|
169
|
+
if (commits === '0')
|
|
170
|
+
await removeSession(ctx.write, session);
|
|
171
|
+
}
|
|
172
|
+
throw err;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function replace(text, args) {
|
|
176
|
+
return args.replace_all === true ? text.split(args.old_string).join(args.new_string) : text.replace(args.old_string, () => args.new_string);
|
|
177
|
+
}
|
|
178
|
+
export async function editTool(ctx, email, args) {
|
|
179
|
+
const rel = editablePath(args.file_path, ctx.syncDeps.config);
|
|
180
|
+
return applyToDraft(ctx, email, rel, 'Edit', ({ current, beforeTip }) => {
|
|
181
|
+
if (current === null)
|
|
182
|
+
throw new ToolRefusal('mcp.file_not_found', `"${rel}" does not exist. Use Write to create a new note.`);
|
|
183
|
+
const count = current.split(args.old_string).length - 1;
|
|
184
|
+
if (count === 0) {
|
|
185
|
+
// A retry: the draft's last commit was this very replacement on this very file (design.md D13). Exact, so an
|
|
186
|
+
// empty or common new_string can never pass for an edit that was never made.
|
|
187
|
+
if (typeof beforeTip === 'string' && beforeTip.includes(args.old_string) && replace(beforeTip, args) === current)
|
|
188
|
+
return { alreadyApplied: true };
|
|
189
|
+
throw new ToolRefusal('mcp.edit_failed', `old_string was not found in "${rel}". Read the note again and copy the text exactly.`);
|
|
190
|
+
}
|
|
191
|
+
if (count > 1 && args.replace_all !== true) {
|
|
192
|
+
throw new ToolRefusal('mcp.edit_failed', `old_string occurs ${count} times in "${rel}". Include more surrounding text to make it unique, or set replace_all.`, { occurrences: count });
|
|
193
|
+
}
|
|
194
|
+
return { text: replace(current, args) };
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
export async function writeTool(ctx, email, args) {
|
|
198
|
+
const rel = editablePath(args.file_path, ctx.syncDeps.config);
|
|
199
|
+
const inView = (await ctx.view.read(ctx.syncDeps, async (snapshot) => ({ text: await existing(path.join(snapshot.store.root, rel)) }))).text;
|
|
200
|
+
if (inView !== null) {
|
|
201
|
+
throw new ToolRefusal('mcp.file_exists', `"${rel}" already exists. Write only creates new notes; use Edit to change it.`);
|
|
202
|
+
}
|
|
203
|
+
const text = args.content.endsWith('\n') ? args.content : `${args.content}\n`;
|
|
204
|
+
return applyToDraft(ctx, email, rel, 'Write', ({ current, beforeTip }) => {
|
|
205
|
+
if (current === null)
|
|
206
|
+
return { text };
|
|
207
|
+
if (current === text && beforeTip === null)
|
|
208
|
+
return { alreadyApplied: true };
|
|
209
|
+
throw new ToolRefusal('mcp.file_exists', `"${rel}" already exists in your draft. Write only creates new notes; use Edit to change it.`);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=edit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../../src/core/mcp/tools/edit.ts"],"names":[],"mappings":"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"}
|