threadnote 0.2.2 → 0.3.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 +13 -6
- package/dist/threadnote.cjs +166 -16
- package/docs/migration.md +13 -5
- package/docs/rollout.md +1 -1
- package/docs/security.md +4 -4
- package/docs/troubleshooting.md +10 -1
- package/package.json +1 -1
- package/scripts/install.sh +1 -0
- package/docs/demo.md +0 -279
package/README.md
CHANGED
|
@@ -28,8 +28,8 @@ Threadnote keeps that memory local and searchable without editing unrelated repo
|
|
|
28
28
|
- Redaction: known config files such as `.mcp.json`, `config.toml`, and settings JSON are copied through a redactor
|
|
29
29
|
before import.
|
|
30
30
|
- Secret scanning: candidate files are skipped if common token or private-key patterns remain after redaction.
|
|
31
|
-
- User instructions: `install` upserts a managed Threadnote block in `~/.codex/AGENTS.md
|
|
32
|
-
without replacing existing personal instructions.
|
|
31
|
+
- User instructions: `install` upserts a managed Threadnote block in `~/.codex/AGENTS.md`, `~/.claude/CLAUDE.md`, and
|
|
32
|
+
`~/.cursor/rules/threadnote.md` without replacing existing personal instructions.
|
|
33
33
|
- Agent config changes are explicit: `mcp-install` prints commands and snippets by default; use `--apply` to run them.
|
|
34
34
|
|
|
35
35
|
## Install
|
|
@@ -70,13 +70,15 @@ threadnote doctor --dry-run
|
|
|
70
70
|
|
|
71
71
|
### MCP
|
|
72
72
|
|
|
73
|
-
Make the agents you use aware of Threadnote. Use only the MCP install lines for agents you actually use. Open a fresh
|
|
73
|
+
Make the agents you use aware of Threadnote. Use only the MCP install lines for agents you actually use. Open a fresh
|
|
74
|
+
agent session after installing MCP so the new server registration is loaded.
|
|
74
75
|
|
|
75
76
|
Dry-run examples:
|
|
76
77
|
|
|
77
78
|
```bash
|
|
78
79
|
threadnote mcp-install codex
|
|
79
80
|
threadnote mcp-install claude
|
|
81
|
+
threadnote mcp-install cursor
|
|
80
82
|
```
|
|
81
83
|
|
|
82
84
|
Apply after review:
|
|
@@ -84,10 +86,12 @@ Apply after review:
|
|
|
84
86
|
```bash
|
|
85
87
|
threadnote mcp-install codex --apply
|
|
86
88
|
threadnote mcp-install claude --apply
|
|
89
|
+
threadnote mcp-install cursor --apply
|
|
87
90
|
```
|
|
88
91
|
|
|
89
92
|
Claude installs at `user` scope by default so the same OpenViking MCP server is available from any repo or worktree.
|
|
90
93
|
Use `--scope local` or `--scope project` only when you intentionally want repo-scoped Claude MCP config.
|
|
94
|
+
Cursor installs by updating the global `~/.cursor/mcp.json` file.
|
|
91
95
|
|
|
92
96
|
If the package or checkout that originally installed `threadnote` has moved, run repair:
|
|
93
97
|
|
|
@@ -106,6 +110,8 @@ codex mcp add threadnote -- threadnote-mcp-server
|
|
|
106
110
|
claude mcp add threadnote -- threadnote-mcp-server
|
|
107
111
|
```
|
|
108
112
|
|
|
113
|
+
Cursor uses the equivalent entry in `~/.cursor/mcp.json`.
|
|
114
|
+
|
|
109
115
|
If a future OpenViking build exposes a healthy native endpoint, install it explicitly:
|
|
110
116
|
|
|
111
117
|
```bash
|
|
@@ -146,7 +152,7 @@ This is it! Start working with your agents as usual. The agent will automaticall
|
|
|
146
152
|
- `doctor`: checks prerequisites, the generated command shim, manifest shape, templates, and local OpenViking health.
|
|
147
153
|
- `install`: installs `openviking[local-embed]==0.3.12` if missing, creates `~/.openviking` config files if absent,
|
|
148
154
|
writes the command shim, and upserts user-level agent instructions.
|
|
149
|
-
- `repair`: fixes install/config/shim/manifest/server health issues and rewrites Codex/Claude MCP configs from the
|
|
155
|
+
- `repair`: fixes install/config/shim/manifest/server health issues and rewrites Codex/Claude/Cursor MCP configs from the
|
|
150
156
|
current checkout.
|
|
151
157
|
- `start`: starts `openviking-server` on `127.0.0.1:1933`.
|
|
152
158
|
- `stop`: stops the detached server pid or macOS LaunchAgent.
|
|
@@ -156,7 +162,7 @@ This is it! Start working with your agents as usual. The agent will automaticall
|
|
|
156
162
|
- `seed`: imports curated repo guidance and docs from the manifest.
|
|
157
163
|
- `seed-skills`: imports global and repo-local `SKILL.md` files as a searchable resource catalog. Use
|
|
158
164
|
`seed-skills --native` only after configuring a working VLM provider.
|
|
159
|
-
- `mcp-install codex|claude`: installs or prints OpenViking MCP configuration for Codex or
|
|
165
|
+
- `mcp-install codex|claude|cursor`: installs or prints OpenViking MCP configuration for Codex, Claude, or Cursor.
|
|
160
166
|
- `remember`: stores a durable memory.
|
|
161
167
|
- `recall`: searches shared OpenViking context. It infers repo or skill scope from queries like
|
|
162
168
|
`skills for api service`; use `--uri` or `--no-infer-scope` to override.
|
|
@@ -178,7 +184,8 @@ npm run threadnote -- install
|
|
|
178
184
|
```
|
|
179
185
|
|
|
180
186
|
`install` writes a small command shim to `~/.local/bin/threadnote` by default and upserts user-level agent guidance in
|
|
181
|
-
`~/.codex/AGENTS.md
|
|
187
|
+
`~/.codex/AGENTS.md`, `~/.claude/CLAUDE.md`, and `~/.cursor/rules/threadnote.md`. After that, use the short command from
|
|
188
|
+
any repo or working directory:
|
|
182
189
|
|
|
183
190
|
```bash
|
|
184
191
|
threadnote doctor --dry-run
|
package/dist/threadnote.cjs
CHANGED
|
@@ -6143,7 +6143,8 @@ var USER_INSTRUCTIONS_END_MARKER = "<!-- END THREADNOTE USER INSTRUCTIONS -->";
|
|
|
6143
6143
|
var USER_MANIFEST_NAME = "seed-manifest.yaml";
|
|
6144
6144
|
var USER_AGENT_INSTRUCTION_TARGETS = [
|
|
6145
6145
|
{ label: "codex user instructions", path: "~/.codex/AGENTS.md" },
|
|
6146
|
-
{ label: "claude user instructions", path: "~/.claude/CLAUDE.md" }
|
|
6146
|
+
{ label: "claude user instructions", path: "~/.claude/CLAUDE.md" },
|
|
6147
|
+
{ label: "cursor user rule", path: "~/.cursor/rules/threadnote.md" }
|
|
6147
6148
|
];
|
|
6148
6149
|
var DEFAULT_SEED_PATTERNS = [
|
|
6149
6150
|
"AGENTS.md",
|
|
@@ -6172,7 +6173,7 @@ async function main() {
|
|
|
6172
6173
|
});
|
|
6173
6174
|
program2.command("repair").description("Repair local OpenViking install, config files, server health, shim, manifest, and MCP config").option("--dry-run", "Print the repair actions without making changes").option(
|
|
6174
6175
|
"--mcp <clients>",
|
|
6175
|
-
"MCP clients to repair: available, all, none, codex, claude, or comma-separated list",
|
|
6176
|
+
"MCP clients to repair: available, all, none, codex, claude, cursor, or comma-separated list",
|
|
6176
6177
|
"available"
|
|
6177
6178
|
).option("--no-start", "Do not start OpenViking if health is failing").option("--package-manager <manager>", "pipx, uv, or pip", parsePackageManager).action(async (options) => {
|
|
6178
6179
|
await runRepair(getRuntimeConfig(program2), options);
|
|
@@ -6185,7 +6186,7 @@ async function main() {
|
|
|
6185
6186
|
});
|
|
6186
6187
|
program2.command("uninstall").description("Remove Threadnote setup and optionally erase local memories").option("--dry-run", "Print uninstall actions without making changes").option(
|
|
6187
6188
|
"--mcp <clients>",
|
|
6188
|
-
"MCP clients to remove: available, all, none, codex, claude, or comma-separated list",
|
|
6189
|
+
"MCP clients to remove: available, all, none, codex, claude, cursor, or comma-separated list",
|
|
6189
6190
|
"available"
|
|
6190
6191
|
).option("--preserve-memories", "Preserve THREADNOTE_HOME and OpenViking memories (default)").option("--erase-memories", "Delete THREADNOTE_HOME, including all OpenViking memories").action(async (options) => {
|
|
6191
6192
|
await runUninstall(getRuntimeConfig(program2), options);
|
|
@@ -6199,10 +6200,10 @@ async function main() {
|
|
|
6199
6200
|
program2.command("seed-skills").description("Seed Codex, Claude, and repo-local SKILL.md files as a searchable catalog").option("--dry-run", "Print skill files and ov commands without importing").option("--manifest <path>", "Manifest path for repo-local skill discovery").option("--native", "Use native OpenViking skill ingestion; requires a working VLM config").action(async (options) => {
|
|
6200
6201
|
await runSeedSkills(getRuntimeConfig(program2, options.manifest), options);
|
|
6201
6202
|
});
|
|
6202
|
-
program2.command("mcp-install").description("Install OpenViking MCP config for a supported agent").argument("<agent>", "codex or
|
|
6203
|
+
program2.command("mcp-install").description("Install OpenViking MCP config for a supported agent").argument("<agent>", "codex, claude, or cursor").option("--apply", "Actually modify the selected agent config").option("--name <name>", "MCP server name", OPENVIKING_MCP_NAME).option("--native-http", "Install OpenViking native HTTP MCP endpoint instead of the local stdio adapter").option("--scope <scope>", "Claude MCP config scope: user, local, or project", parseClaudeMcpScope, "user").option("--url <url>", "OpenViking native HTTP MCP URL").option("--bearer-token-env-var <name>", "Environment variable containing the local API key").action(async (agent, options) => {
|
|
6203
6204
|
await runMcpInstall(getRuntimeConfig(program2), parseAgentClient(agent), options);
|
|
6204
6205
|
});
|
|
6205
|
-
program2.command("remember").description("Store a durable engineering memory in OpenViking").option("--dry-run", "Print memory and ov command without storing").option("--source-agent-client <name>", "codex, claude, gemini, or another client name", "codex").option("--stdin", "Read memory text from stdin").option("--text <text>", "Memory text to store").action(async (options) => {
|
|
6206
|
+
program2.command("remember").description("Store a durable engineering memory in OpenViking").option("--dry-run", "Print memory and ov command without storing").option("--source-agent-client <name>", "codex, claude, cursor, gemini, or another client name", "codex").option("--stdin", "Read memory text from stdin").option("--text <text>", "Memory text to store").action(async (options) => {
|
|
6206
6207
|
await runRemember(getRuntimeConfig(program2), options);
|
|
6207
6208
|
});
|
|
6208
6209
|
program2.command("recall").description("Search shared OpenViking context").requiredOption("--query <query>", "Search query").option("--dry-run", "Print ov command without searching").option("-n, --node-limit <count>", "Maximum number of search results").option("--no-infer-scope", "Disable query-based scope inference").option("--uri <uri>", "Restrict search to a viking:// URI").action(async (options) => {
|
|
@@ -6214,7 +6215,7 @@ async function main() {
|
|
|
6214
6215
|
program2.command("list").alias("ls").description("List a viking:// directory").argument("[uri]", "viking:// URI to list", "viking://").option("-a, --all", "Show hidden files such as .abstract.md and .overview.md").option("--dry-run", "Print ov command without listing").option("-n, --node-limit <count>", "Maximum number of nodes to list").option("-r, --recursive", "List subdirectories recursively").option("-s, --simple", "Print only paths").action(async (uri, options) => {
|
|
6215
6216
|
await runList(getRuntimeConfig(program2), uri, options);
|
|
6216
6217
|
});
|
|
6217
|
-
program2.command("handoff").description("Capture current repo state as a durable cross-agent handoff memory").option("--blockers <text>", "Known blockers").option("--dry-run", "Print handoff without storing").option("--next-step <text>", "Suggested next step").option("--source-agent-client <name>", "codex, claude, gemini, or another client name", "codex").option("--task <text>", "Current task summary").option("--tests <text>", "Tests or checks run").action(async (options) => {
|
|
6218
|
+
program2.command("handoff").description("Capture current repo state as a durable cross-agent handoff memory").option("--blockers <text>", "Known blockers").option("--dry-run", "Print handoff without storing").option("--next-step <text>", "Suggested next step").option("--source-agent-client <name>", "codex, claude, cursor, gemini, or another client name", "codex").option("--task <text>", "Current task summary").option("--tests <text>", "Tests or checks run").action(async (options) => {
|
|
6218
6219
|
await runHandoff(getRuntimeConfig(program2), options);
|
|
6219
6220
|
});
|
|
6220
6221
|
program2.command("forget").description("Remove a viking:// URI from local OpenViking context").argument("<uri>", "viking:// URI to remove").option("--dry-run", "Print ov command without deleting").action(async (uri, options) => {
|
|
@@ -6655,13 +6656,6 @@ async function runMcpInstall(config, agent, options) {
|
|
|
6655
6656
|
const url = options.url ?? `http://${config.host}:${config.port}/mcp`;
|
|
6656
6657
|
const apply = options.apply === true;
|
|
6657
6658
|
const nativeHttp = options.nativeHttp === true;
|
|
6658
|
-
const command = buildMcpInstallCommand(config, agent, name, {
|
|
6659
|
-
bearerTokenEnvVar: options.bearerTokenEnvVar,
|
|
6660
|
-
nativeHttp,
|
|
6661
|
-
scope: options.scope,
|
|
6662
|
-
url
|
|
6663
|
-
});
|
|
6664
|
-
const removeCommand = buildMcpRemoveCommand(agent, name);
|
|
6665
6659
|
if (nativeHttp) {
|
|
6666
6660
|
const mcpStatus = await readHttpStatus(url, 1200);
|
|
6667
6661
|
const unavailable = mcpStatus === void 0 || mcpStatus === 404;
|
|
@@ -6674,6 +6668,22 @@ async function runMcpInstall(config, agent, options) {
|
|
|
6674
6668
|
console.log(`WARN OpenViking native MCP endpoint is not available at ${url}; default mcp-install uses stdio.`);
|
|
6675
6669
|
}
|
|
6676
6670
|
}
|
|
6671
|
+
if (agent === "cursor") {
|
|
6672
|
+
await runCursorMcpInstall(config, name, {
|
|
6673
|
+
apply,
|
|
6674
|
+
bearerTokenEnvVar: options.bearerTokenEnvVar,
|
|
6675
|
+
nativeHttp,
|
|
6676
|
+
url
|
|
6677
|
+
});
|
|
6678
|
+
return;
|
|
6679
|
+
}
|
|
6680
|
+
const command = buildMcpInstallCommand(config, agent, name, {
|
|
6681
|
+
bearerTokenEnvVar: options.bearerTokenEnvVar,
|
|
6682
|
+
nativeHttp,
|
|
6683
|
+
scope: options.scope,
|
|
6684
|
+
url
|
|
6685
|
+
});
|
|
6686
|
+
const removeCommand = buildMcpRemoveCommand(agent, name);
|
|
6677
6687
|
if (!apply) {
|
|
6678
6688
|
console.log("Dry run. Re-run with --apply to modify the selected agent config.");
|
|
6679
6689
|
if (removeCommand.cwd || command.cwd) {
|
|
@@ -6690,6 +6700,32 @@ async function runMcpInstall(config, agent, options) {
|
|
|
6690
6700
|
});
|
|
6691
6701
|
await maybeRun(false, command.executable, command.args, { cwd: command.cwd });
|
|
6692
6702
|
}
|
|
6703
|
+
async function runCursorMcpInstall(config, name, options) {
|
|
6704
|
+
const path = cursorMcpConfigPath();
|
|
6705
|
+
const serverConfig = buildCursorMcpServerConfig(config, {
|
|
6706
|
+
bearerTokenEnvVar: options.bearerTokenEnvVar,
|
|
6707
|
+
nativeHttp: options.nativeHttp,
|
|
6708
|
+
url: options.url
|
|
6709
|
+
});
|
|
6710
|
+
const currentContent = await readFileIfExists(path);
|
|
6711
|
+
const nextContent = renderCursorMcpConfig(currentContent, name, serverConfig);
|
|
6712
|
+
if (!options.apply) {
|
|
6713
|
+
console.log("Dry run. Re-run with --apply to modify Cursor MCP config.");
|
|
6714
|
+
printCursorMcpSnippet(config, name, {
|
|
6715
|
+
bearerTokenEnvVar: options.bearerTokenEnvVar,
|
|
6716
|
+
nativeHttp: options.nativeHttp,
|
|
6717
|
+
url: options.url
|
|
6718
|
+
});
|
|
6719
|
+
return;
|
|
6720
|
+
}
|
|
6721
|
+
if (currentContent === nextContent) {
|
|
6722
|
+
console.log(`Already configured: ${path}`);
|
|
6723
|
+
return;
|
|
6724
|
+
}
|
|
6725
|
+
await ensureDirectory((0, import_node_path2.dirname)(path), false);
|
|
6726
|
+
await (0, import_promises10.writeFile)(path, nextContent, { encoding: "utf8", mode: 420 });
|
|
6727
|
+
console.log(currentContent === void 0 ? `Wrote Cursor MCP config: ${path}` : `Updated Cursor MCP config: ${path}`);
|
|
6728
|
+
}
|
|
6693
6729
|
async function runRemember(config, options) {
|
|
6694
6730
|
const text = await getInputText(options.text, options.stdin === true);
|
|
6695
6731
|
if (!text.trim()) {
|
|
@@ -7279,6 +7315,10 @@ async function removeMcpConfigs(value, dryRun) {
|
|
|
7279
7315
|
return;
|
|
7280
7316
|
}
|
|
7281
7317
|
for (const client of clients) {
|
|
7318
|
+
if (client === "cursor") {
|
|
7319
|
+
await removeCursorMcpConfig(OPENVIKING_MCP_NAME, dryRun);
|
|
7320
|
+
continue;
|
|
7321
|
+
}
|
|
7282
7322
|
const command = buildMcpRemoveCommand(client, OPENVIKING_MCP_NAME);
|
|
7283
7323
|
await maybeRun(dryRun, command.executable, command.args, { allowFailure: true, cwd: command.cwd });
|
|
7284
7324
|
}
|
|
@@ -7286,6 +7326,7 @@ async function removeMcpConfigs(value, dryRun) {
|
|
|
7286
7326
|
async function removeMcpSnippets(config, dryRun) {
|
|
7287
7327
|
await removePathIfExists((0, import_node_path4.join)(config.agentContextHome, "mcp", `${OPENVIKING_MCP_NAME}.codex.toml`), "MCP snippet", dryRun);
|
|
7288
7328
|
await removePathIfExists((0, import_node_path4.join)(config.agentContextHome, "mcp", `${OPENVIKING_MCP_NAME}.claude.txt`), "MCP snippet", dryRun);
|
|
7329
|
+
await removePathIfExists((0, import_node_path4.join)(config.agentContextHome, "mcp", `${OPENVIKING_MCP_NAME}.cursor.json`), "MCP snippet", dryRun);
|
|
7289
7330
|
}
|
|
7290
7331
|
async function eraseThreadnoteHome(path, dryRun) {
|
|
7291
7332
|
assertSafeThreadnoteHomeForErase(path);
|
|
@@ -7406,6 +7447,9 @@ async function resolveAbsolutePattern(pattern) {
|
|
|
7406
7447
|
return files.filter((filePath) => regex.test(toPosixPath(filePath)));
|
|
7407
7448
|
}
|
|
7408
7449
|
function buildMcpInstallCommand(config, agent, name, options) {
|
|
7450
|
+
if (agent === "cursor") {
|
|
7451
|
+
throw new Error("Cursor MCP config is written directly to ~/.cursor/mcp.json.");
|
|
7452
|
+
}
|
|
7409
7453
|
const claudeCwd = getInvocationCwd();
|
|
7410
7454
|
const claudeScope = options.scope ?? "user";
|
|
7411
7455
|
if (!options.nativeHttp) {
|
|
@@ -7447,6 +7491,9 @@ function mcpAdapterCommand() {
|
|
|
7447
7491
|
return [(0, import_node_path4.join)(toolRoot(), "bin", "threadnote-mcp-server.cjs")];
|
|
7448
7492
|
}
|
|
7449
7493
|
function buildMcpRemoveCommand(agent, name) {
|
|
7494
|
+
if (agent === "cursor") {
|
|
7495
|
+
throw new Error("Cursor MCP config is removed directly from ~/.cursor/mcp.json.");
|
|
7496
|
+
}
|
|
7450
7497
|
return agent === "codex" ? { executable: "codex", args: ["mcp", "remove", name] } : { executable: "claude", args: ["mcp", "remove", name], cwd: getInvocationCwd() };
|
|
7451
7498
|
}
|
|
7452
7499
|
function mcpEnvironment(config) {
|
|
@@ -7457,6 +7504,72 @@ function mcpEnvironment(config) {
|
|
|
7457
7504
|
`THREADNOTE_AGENT_ID=${config.agentId}`
|
|
7458
7505
|
];
|
|
7459
7506
|
}
|
|
7507
|
+
function mcpEnvironmentObject(config) {
|
|
7508
|
+
return {
|
|
7509
|
+
THREADNOTE_ACCOUNT: config.account,
|
|
7510
|
+
THREADNOTE_AGENT_ID: config.agentId,
|
|
7511
|
+
THREADNOTE_HOME: config.agentContextHome,
|
|
7512
|
+
THREADNOTE_USER: config.user
|
|
7513
|
+
};
|
|
7514
|
+
}
|
|
7515
|
+
function buildCursorMcpServerConfig(config, options) {
|
|
7516
|
+
if (options.nativeHttp) {
|
|
7517
|
+
const server = { url: options.url };
|
|
7518
|
+
if (options.bearerTokenEnvVar) {
|
|
7519
|
+
server.headers = { Authorization: `Bearer \${env:${options.bearerTokenEnvVar}}` };
|
|
7520
|
+
}
|
|
7521
|
+
return server;
|
|
7522
|
+
}
|
|
7523
|
+
return {
|
|
7524
|
+
args: [mcpAdapterCommand()[0]],
|
|
7525
|
+
command: "/usr/bin/env",
|
|
7526
|
+
env: mcpEnvironmentObject(config)
|
|
7527
|
+
};
|
|
7528
|
+
}
|
|
7529
|
+
function renderCursorMcpConfig(currentContent, name, serverConfig) {
|
|
7530
|
+
const parsed = currentContent === void 0 ? {} : parseJsonConfigObject(currentContent);
|
|
7531
|
+
if (parsed === void 0) {
|
|
7532
|
+
throw new Error(`${cursorMcpConfigPath()} exists but is not a JSON object; not modifying it.`);
|
|
7533
|
+
}
|
|
7534
|
+
if (parsed.mcpServers !== void 0 && !isJsonObject(parsed.mcpServers)) {
|
|
7535
|
+
throw new Error(`${cursorMcpConfigPath()} has a non-object mcpServers field; not modifying it.`);
|
|
7536
|
+
}
|
|
7537
|
+
const nextConfig = { ...parsed };
|
|
7538
|
+
const mcpServers = isJsonObject(parsed.mcpServers) ? { ...parsed.mcpServers } : {};
|
|
7539
|
+
mcpServers[name] = serverConfig;
|
|
7540
|
+
nextConfig.mcpServers = mcpServers;
|
|
7541
|
+
return `${JSON.stringify(nextConfig, null, 2)}
|
|
7542
|
+
`;
|
|
7543
|
+
}
|
|
7544
|
+
async function removeCursorMcpConfig(name, dryRun) {
|
|
7545
|
+
const path = cursorMcpConfigPath();
|
|
7546
|
+
const currentContent = await readFileIfExists(path);
|
|
7547
|
+
if (currentContent === void 0) {
|
|
7548
|
+
console.log(`Already absent: ${path}`);
|
|
7549
|
+
return;
|
|
7550
|
+
}
|
|
7551
|
+
const parsed = parseJsonConfigObject(currentContent);
|
|
7552
|
+
if (parsed === void 0) {
|
|
7553
|
+
console.log(`WARN ${path} exists but is not a JSON object; not modifying it.`);
|
|
7554
|
+
return;
|
|
7555
|
+
}
|
|
7556
|
+
if (!isJsonObject(parsed.mcpServers) || parsed.mcpServers[name] === void 0) {
|
|
7557
|
+
console.log(`No Cursor MCP config found: ${path}`);
|
|
7558
|
+
return;
|
|
7559
|
+
}
|
|
7560
|
+
const nextConfig = { ...parsed };
|
|
7561
|
+
const mcpServers = { ...parsed.mcpServers };
|
|
7562
|
+
delete mcpServers[name];
|
|
7563
|
+
nextConfig.mcpServers = mcpServers;
|
|
7564
|
+
const nextContent = `${JSON.stringify(nextConfig, null, 2)}
|
|
7565
|
+
`;
|
|
7566
|
+
if (dryRun) {
|
|
7567
|
+
console.log(`Would update Cursor MCP config: ${path}`);
|
|
7568
|
+
return;
|
|
7569
|
+
}
|
|
7570
|
+
await (0, import_promises10.writeFile)(path, nextContent, { encoding: "utf8", mode: 420 });
|
|
7571
|
+
console.log(`Updated Cursor MCP config: ${path}`);
|
|
7572
|
+
}
|
|
7460
7573
|
function withIdentity(config, args) {
|
|
7461
7574
|
return [...args, "--account", config.account, "--user", config.user, "--agent-id", config.agentId];
|
|
7462
7575
|
}
|
|
@@ -7468,6 +7581,10 @@ function uriSegment(value) {
|
|
|
7468
7581
|
return normalized.length > 0 ? normalized : "unknown";
|
|
7469
7582
|
}
|
|
7470
7583
|
function printMcpSnippet(config, agent, name, options) {
|
|
7584
|
+
if (agent === "cursor") {
|
|
7585
|
+
printCursorMcpSnippet(config, name, { nativeHttp: options.nativeHttp, url: options.url });
|
|
7586
|
+
return;
|
|
7587
|
+
}
|
|
7471
7588
|
const snippetPath = (0, import_node_path4.join)(config.agentContextHome, "mcp", `${name}.${agent}.${agent === "codex" ? "toml" : "txt"}`);
|
|
7472
7589
|
const command = buildMcpInstallCommand(config, agent, name, {
|
|
7473
7590
|
nativeHttp: options.nativeHttp,
|
|
@@ -7480,6 +7597,20 @@ function printMcpSnippet(config, agent, name, options) {
|
|
|
7480
7597
|
Snippet (${snippetPath}):
|
|
7481
7598
|
${snippet2}`);
|
|
7482
7599
|
}
|
|
7600
|
+
function printCursorMcpSnippet(config, name, options) {
|
|
7601
|
+
const snippetPath = (0, import_node_path4.join)(config.agentContextHome, "mcp", `${name}.cursor.json`);
|
|
7602
|
+
const snippet2 = JSON.stringify(
|
|
7603
|
+
{ mcpServers: { [name]: buildCursorMcpServerConfig(config, options) } },
|
|
7604
|
+
null,
|
|
7605
|
+
2
|
|
7606
|
+
);
|
|
7607
|
+
console.log(`
|
|
7608
|
+
Snippet (${snippetPath}; merge into ${cursorMcpConfigPath()}):
|
|
7609
|
+
${snippet2}`);
|
|
7610
|
+
}
|
|
7611
|
+
function cursorMcpConfigPath() {
|
|
7612
|
+
return expandPath("~/.cursor/mcp.json");
|
|
7613
|
+
}
|
|
7483
7614
|
async function storeMemory(config, memory, dryRun) {
|
|
7484
7615
|
const ov = await openVikingCliForMode(dryRun);
|
|
7485
7616
|
const memoryPath = (0, import_node_path4.join)(config.agentContextHome, "last-memory.txt");
|
|
@@ -8053,10 +8184,10 @@ function parsePackageManager(value) {
|
|
|
8053
8184
|
throw new Error(`Invalid package manager: ${value}`);
|
|
8054
8185
|
}
|
|
8055
8186
|
function parseAgentClient(value) {
|
|
8056
|
-
if (value === "codex" || value === "claude") {
|
|
8187
|
+
if (value === "codex" || value === "claude" || value === "cursor") {
|
|
8057
8188
|
return value;
|
|
8058
8189
|
}
|
|
8059
|
-
throw new Error(`Unsupported agent: ${value}. Expected codex or
|
|
8190
|
+
throw new Error(`Unsupported agent: ${value}. Expected codex, claude, or cursor.`);
|
|
8060
8191
|
}
|
|
8061
8192
|
function parseClaudeMcpScope(value) {
|
|
8062
8193
|
if (value === "local" || value === "project" || value === "user") {
|
|
@@ -8071,12 +8202,22 @@ async function resolveMcpClients(value, action) {
|
|
|
8071
8202
|
}
|
|
8072
8203
|
let requested;
|
|
8073
8204
|
if (normalized === "available" || normalized === "all") {
|
|
8074
|
-
requested = ["codex", "claude"];
|
|
8205
|
+
requested = ["codex", "claude", "cursor"];
|
|
8075
8206
|
} else {
|
|
8076
8207
|
requested = normalized.split(",").map((part) => part.trim()).filter(Boolean).map(parseAgentClient);
|
|
8077
8208
|
}
|
|
8078
8209
|
const clients = [];
|
|
8079
8210
|
for (const client of requested) {
|
|
8211
|
+
if (client === "cursor") {
|
|
8212
|
+
if (!await isCursorAvailable()) {
|
|
8213
|
+
console.log(`WARN Cursor config not found; cannot ${action} cursor MCP config.`);
|
|
8214
|
+
continue;
|
|
8215
|
+
}
|
|
8216
|
+
if (!clients.includes(client)) {
|
|
8217
|
+
clients.push(client);
|
|
8218
|
+
}
|
|
8219
|
+
continue;
|
|
8220
|
+
}
|
|
8080
8221
|
if (!await findExecutable([client])) {
|
|
8081
8222
|
console.log(`WARN ${client} command not found; cannot ${action} ${client} MCP config.`);
|
|
8082
8223
|
continue;
|
|
@@ -8087,6 +8228,15 @@ async function resolveMcpClients(value, action) {
|
|
|
8087
8228
|
}
|
|
8088
8229
|
return clients;
|
|
8089
8230
|
}
|
|
8231
|
+
async function isCursorAvailable() {
|
|
8232
|
+
if (await exists(expandPath("~/.cursor"))) {
|
|
8233
|
+
return true;
|
|
8234
|
+
}
|
|
8235
|
+
if (await findExecutable(["cursor", "cursor-agent"])) {
|
|
8236
|
+
return true;
|
|
8237
|
+
}
|
|
8238
|
+
return (0, import_node_os2.platform)() === "darwin" && await exists("/Applications/Cursor.app");
|
|
8239
|
+
}
|
|
8090
8240
|
function assertVikingUri(uri) {
|
|
8091
8241
|
if (!uri.startsWith("viking://")) {
|
|
8092
8242
|
throw new Error(`Refusing non-viking URI: ${uri}`);
|
package/docs/migration.md
CHANGED
|
@@ -46,9 +46,9 @@ Create a handoff for the next agent before you stop.
|
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
For better continuity, run `threadnote install` so it can add the agent-side guidance from `docs/agent-instructions.md`
|
|
49
|
-
to
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
to user-level Codex, Claude, and Cursor instruction files. That guidance tells agents to recall context at task start,
|
|
50
|
+
store durable memories when explicitly asked or when a reusable workflow fact is learned, and create handoffs
|
|
51
|
+
automatically before stopping meaningful work.
|
|
52
52
|
|
|
53
53
|
## Migration Steps
|
|
54
54
|
|
|
@@ -133,6 +133,15 @@ paths.
|
|
|
133
133
|
Claude installs at user scope by default so it works from every repo/worktree. Use `--scope local` only when a
|
|
134
134
|
repo-specific Claude MCP entry is intentional.
|
|
135
135
|
|
|
136
|
+
For Cursor:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
threadnote mcp-install cursor
|
|
140
|
+
threadnote mcp-install cursor --apply
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Cursor installs by updating the global `~/.cursor/mcp.json` file.
|
|
144
|
+
|
|
136
145
|
Later, if the checkout that installed the MCP adapter is deleted or moved, repair it from any fresh checkout:
|
|
137
146
|
|
|
138
147
|
```bash
|
|
@@ -158,8 +167,7 @@ Remember this workflow note for future agents: ...
|
|
|
158
167
|
Create a handoff now.
|
|
159
168
|
```
|
|
160
169
|
|
|
161
|
-
Preferred agent behavior is automatic after `threadnote install` has updated the user-level
|
|
162
|
-
files:
|
|
170
|
+
Preferred agent behavior is automatic after `threadnote install` has updated the user-level instruction files:
|
|
163
171
|
|
|
164
172
|
- On non-trivial task start, search OpenViking for recent handoffs and relevant repo guidance.
|
|
165
173
|
- When the user says "remember", store the memory after checking that it contains no secret or customer data.
|
package/docs/rollout.md
CHANGED
|
@@ -18,7 +18,7 @@ Start with a local-only pilot.
|
|
|
18
18
|
|
|
19
19
|
- Install completes in under 10 minutes after prerequisites.
|
|
20
20
|
- `doctor` reports clear actionable checks.
|
|
21
|
-
- Codex
|
|
21
|
+
- Codex, Claude, or Cursor can store and recall a shared handoff after MCP setup.
|
|
22
22
|
- Seeding curated guidance does not import known secret patterns.
|
|
23
23
|
- Fresh agents can recall repo testing guidance and discover relevant skills.
|
|
24
24
|
- `uninstall --dry-run` previews removal, and `uninstall` leaves memories intact unless `--erase-memories` is explicit.
|
package/docs/security.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
- Credentials, access tokens, API keys, certificates, private keys, or shell history.
|
|
8
8
|
- Customer data, production data, HIPAA data, or production logs without explicit approval and scrubbing.
|
|
9
|
-
- Local auth files such as `~/.codex/auth.json
|
|
9
|
+
- Local auth files such as `~/.codex/auth.json`, `~/.claude/.credentials.json`, or Cursor account/session files.
|
|
10
10
|
- Local settings files that may contain secrets unless they go through redaction.
|
|
11
11
|
|
|
12
12
|
## Built-In Controls
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
- `.threadnoteignore` excludes common secret and build-output paths.
|
|
15
15
|
- `.mcp.json`, `config.toml`, and settings JSON are redacted before import.
|
|
16
16
|
- Files are skipped if common secret patterns remain after redaction.
|
|
17
|
-
- `mcp-install` requires `--apply` before it changes Codex or
|
|
18
|
-
- `install` updates user-level Codex and
|
|
19
|
-
instructions outside that block are preserved.
|
|
17
|
+
- `mcp-install` requires `--apply` before it changes Codex, Claude, or Cursor config.
|
|
18
|
+
- `install` updates user-level Codex, Claude, and Cursor instruction files through a managed Markdown block. Existing
|
|
19
|
+
personal instructions outside that block are preserved.
|
|
20
20
|
- `uninstall` preserves local memories by default. `--erase-memories` is required before deleting `THREADNOTE_HOME`.
|
|
21
21
|
- Config files created under `THREADNOTE_HOME` are written with user-only permissions.
|
|
22
22
|
|
package/docs/troubleshooting.md
CHANGED
|
@@ -147,7 +147,7 @@ threadnote repair
|
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
`repair` reinstalls the `threadnote` shim, repairs generated config files, starts OpenViking if needed, and rewrites
|
|
150
|
-
Codex/Claude MCP configs to point at the current checkout.
|
|
150
|
+
Codex/Claude/Cursor MCP configs to point at the current checkout.
|
|
151
151
|
|
|
152
152
|
## MCP Install Is Only Printing Commands
|
|
153
153
|
|
|
@@ -157,6 +157,15 @@ This is expected. Run with `--apply` after reviewing the command:
|
|
|
157
157
|
threadnote mcp-install codex --apply
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
+
For Cursor:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
threadnote mcp-install cursor --apply
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
This updates the global `~/.cursor/mcp.json` file. Restart Cursor or open a fresh agent session after changing MCP
|
|
167
|
+
config.
|
|
168
|
+
|
|
160
169
|
## Uninstall Without Losing Memories
|
|
161
170
|
|
|
162
171
|
Run:
|
package/package.json
CHANGED
package/scripts/install.sh
CHANGED
|
@@ -130,3 +130,4 @@ say " threadnote start"
|
|
|
130
130
|
say " threadnote doctor --dry-run"
|
|
131
131
|
say " threadnote mcp-install codex --apply # if you use Codex"
|
|
132
132
|
say " threadnote mcp-install claude --apply # if you use Claude"
|
|
133
|
+
say " threadnote mcp-install cursor --apply # if you use Cursor"
|
package/docs/demo.md
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
# Demo: Cross-Agent Development Context
|
|
2
|
-
|
|
3
|
-
This demo shows `threadnote` as a shared local memory layer for engineering work. The story is intentionally simple:
|
|
4
|
-
one agent starts a task in a worktree, stores a handoff, and another agent or worktree recalls it without the
|
|
5
|
-
developer copy-pasting chat history, PR notes, or terminal output.
|
|
6
|
-
|
|
7
|
-
## Demo Goal
|
|
8
|
-
|
|
9
|
-
Show engineers that:
|
|
10
|
-
|
|
11
|
-
- repo instructions still live in `AGENTS.md`, `CLAUDE.md`, and checked-in docs;
|
|
12
|
-
- OpenViking adds durable, searchable context across agent sessions, worktrees, and repos;
|
|
13
|
-
- agents should use MCP directly, while `threadnote` is the human-readable fallback and diagnostic path;
|
|
14
|
-
- memories survive branch merges and worktree deletion because they live under the developer's OpenViking home, not in
|
|
15
|
-
the git worktree;
|
|
16
|
-
- `repair` fixes the common stale-worktree and MCP launcher problems that show up during real local development.
|
|
17
|
-
|
|
18
|
-
## Use Case
|
|
19
|
-
|
|
20
|
-
Use "continue a PR after switching agents" as the live scenario.
|
|
21
|
-
|
|
22
|
-
The engineer has a PR that updates a local tool. Codex has already done some work and creates a handoff. The engineer
|
|
23
|
-
opens Claude, or a fresh Codex session from another repo/worktree, and asks it to continue. The new agent
|
|
24
|
-
recalls the handoff, reads the relevant `viking://` memory, discovers repo guidance, and stores an updated handoff when
|
|
25
|
-
it finishes.
|
|
26
|
-
|
|
27
|
-
This is the highest-signal demo because it exercises the behavior engineers actually feel every day: context transfer,
|
|
28
|
-
stale local setup, and "what did the previous agent already learn?"
|
|
29
|
-
|
|
30
|
-
## Prep Checklist
|
|
31
|
-
|
|
32
|
-
Run these before the meeting from this checkout:
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
npm install
|
|
36
|
-
npm run doctor -- --dry-run
|
|
37
|
-
npm run threadnote -- install
|
|
38
|
-
threadnote start
|
|
39
|
-
threadnote doctor --dry-run
|
|
40
|
-
threadnote init-manifest --repo "$(pwd)"
|
|
41
|
-
threadnote seed --dry-run
|
|
42
|
-
threadnote seed
|
|
43
|
-
threadnote seed-skills --dry-run
|
|
44
|
-
threadnote seed-skills
|
|
45
|
-
threadnote mcp-install codex --apply
|
|
46
|
-
threadnote mcp-install claude --apply
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Open a fresh Codex or Claude session after installing MCP. Existing sessions may not pick up newly registered MCP
|
|
50
|
-
servers.
|
|
51
|
-
|
|
52
|
-
Optional: create a deterministic demo handoff so the search result is easy to find.
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
threadnote handoff \
|
|
56
|
-
--task "Demo PR: continue the threadnote rollout after adding repair and docs" \
|
|
57
|
-
--tests "doctor --dry-run, mcp-install dry runs, local TypeScript checks" \
|
|
58
|
-
--next-step "Recall this handoff, read the most relevant viking URI, then summarize what changed and what to do next"
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Live Script
|
|
62
|
-
|
|
63
|
-
### 1. Open With The Problem
|
|
64
|
-
|
|
65
|
-
Say:
|
|
66
|
-
|
|
67
|
-
```text
|
|
68
|
-
When we switch from one agent session to another, we usually lose the useful middle: what was tried, what failed, what
|
|
69
|
-
the branch is about, and what the next agent should avoid re-discovering. This tool gives agents a shared local context
|
|
70
|
-
layer without replacing repo instructions or indexing entire repos.
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Show that the repo still has normal instructions:
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
ls AGENTS.md CLAUDE.md
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### 2. Show Local Health
|
|
80
|
-
|
|
81
|
-
Run:
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
threadnote doctor --dry-run
|
|
85
|
-
threadnote start
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Say:
|
|
89
|
-
|
|
90
|
-
```text
|
|
91
|
-
The server is local. The default is 127.0.0.1:1933, so it does not conflict with apps running on localhost:80 or
|
|
92
|
-
localhost:443. If the exact port is taken, we can set THREADNOTE_PORT.
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### 3. Show MCP Is Installed
|
|
96
|
-
|
|
97
|
-
Run the client-specific check you want to demo:
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
codex mcp list
|
|
101
|
-
claude mcp list
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
Say:
|
|
105
|
-
|
|
106
|
-
```text
|
|
107
|
-
MCP is the expected agent path. The CLI exists so humans can inspect, repair, and reproduce what the agent is doing.
|
|
108
|
-
Claude installs at user scope by default, so it works from other repos and worktrees.
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
If the MCP server does not appear in an already-open agent session, restart that session after `mcp-install --apply`.
|
|
112
|
-
|
|
113
|
-
### 4. Recall The Previous Handoff
|
|
114
|
-
|
|
115
|
-
In the agent UI, ask:
|
|
116
|
-
|
|
117
|
-
```text
|
|
118
|
-
Before touching code, recall recent OpenViking context for this branch and read the most relevant handoff.
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
For a terminal fallback, run:
|
|
122
|
-
|
|
123
|
-
```bash
|
|
124
|
-
threadnote recall --query "last handoff for threadnote rollout"
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
Point out that recall returns candidate `viking://` URIs and abstracts. Then read the best match:
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
threadnote read "<paste the most relevant viking:// URI>"
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
Say:
|
|
134
|
-
|
|
135
|
-
```text
|
|
136
|
-
Search is intentionally a pointer step. The agent should read or list the selected URI before treating it as context.
|
|
137
|
-
That keeps recall cheap and makes the result auditable.
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### 5. Discover Repo Guidance
|
|
141
|
-
|
|
142
|
-
Ask the agent:
|
|
143
|
-
|
|
144
|
-
```text
|
|
145
|
-
Find any repo guidance or skills that matter for this task before you continue.
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Terminal fallback:
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
threadnote recall --query "repo agent instructions testing guidance"
|
|
152
|
-
threadnote recall --query "skills for this repo"
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
Say:
|
|
156
|
-
|
|
157
|
-
```text
|
|
158
|
-
This is not source navigation. It is a curated catalog of instructions, skills, and durable notes that helps the agent
|
|
159
|
-
start in the right neighborhood.
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### 6. Show Cross-Repo Or Cross-Worktree Continuity
|
|
163
|
-
|
|
164
|
-
Switch to another repo or worktree and run the same recall:
|
|
165
|
-
|
|
166
|
-
```bash
|
|
167
|
-
cd ~/work/another-repo
|
|
168
|
-
threadnote recall --query "last handoff for threadnote rollout"
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
Or ask a fresh Claude session from another repo:
|
|
172
|
-
|
|
173
|
-
```text
|
|
174
|
-
Use OpenViking to recall the latest handoff for the threadnote rollout and summarize the next step.
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
Say:
|
|
178
|
-
|
|
179
|
-
```text
|
|
180
|
-
The repo list is developer-local and manifest-driven. Nothing assumes a fixed source path. Memories are stored in the
|
|
181
|
-
local OpenViking home, so deleting a worktree removes launcher paths, not the remembered handoff.
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
### 7. Remember A Durable Fact
|
|
185
|
-
|
|
186
|
-
Ask the agent:
|
|
187
|
-
|
|
188
|
-
```text
|
|
189
|
-
Remember this workflow fact: threadnote MCP for Claude should be installed at user scope so it works from any repo or
|
|
190
|
-
worktree.
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Terminal fallback:
|
|
194
|
-
|
|
195
|
-
```bash
|
|
196
|
-
threadnote remember \
|
|
197
|
-
--text "Workflow fact: install Claude OpenViking MCP at user scope so it works from any repo or worktree."
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Say:
|
|
201
|
-
|
|
202
|
-
```text
|
|
203
|
-
The rule is: remember durable workflow facts, not secrets, customer data, raw logs, or long diffs. Canonical repo rules
|
|
204
|
-
should still be checked in.
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### 8. Create The Next Handoff
|
|
208
|
-
|
|
209
|
-
Ask the agent:
|
|
210
|
-
|
|
211
|
-
```text
|
|
212
|
-
Create a handoff for the next agent with the current status, tests run, blockers, and next step.
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
Terminal fallback:
|
|
216
|
-
|
|
217
|
-
```bash
|
|
218
|
-
threadnote handoff \
|
|
219
|
-
--task "Demo: verified cross-agent recall for the threadnote rollout" \
|
|
220
|
-
--tests "doctor --dry-run, recall, read, remember, handoff" \
|
|
221
|
-
--next-step "Open a fresh agent session and ask it to recall this handoff before continuing"
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
Say:
|
|
225
|
-
|
|
226
|
-
```text
|
|
227
|
-
This is the part we want agents to do automatically before pausing or finishing meaningful code changes.
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
### 9. Show Repair
|
|
231
|
-
|
|
232
|
-
Run:
|
|
233
|
-
|
|
234
|
-
```bash
|
|
235
|
-
threadnote repair --dry-run
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
Say:
|
|
239
|
-
|
|
240
|
-
```text
|
|
241
|
-
This catches the ugly local cases: stale command shims, MCP launchers pointing at deleted worktrees, missing manifests,
|
|
242
|
-
and a stopped local server. Repair rewrites paths from the current checkout.
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
### 10. Close
|
|
246
|
-
|
|
247
|
-
Say:
|
|
248
|
-
|
|
249
|
-
```text
|
|
250
|
-
The important design choice is that this does not replace the repo. It makes the useful working memory around the repo
|
|
251
|
-
available to whichever agent session picks up the task next.
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## Expected Signals
|
|
255
|
-
|
|
256
|
-
- `doctor --dry-run` reports no failures or prints concrete repair steps.
|
|
257
|
-
- `recall` returns `memory`, `resource`, or `skill` rows with `viking://` URIs.
|
|
258
|
-
- `read` turns a selected URI into actual handoff or memory content.
|
|
259
|
-
- A fresh agent session can use the OpenViking MCP tools after MCP install.
|
|
260
|
-
- `repair --dry-run` explains what it would fix without mutating anything.
|
|
261
|
-
|
|
262
|
-
## Recovery Branches
|
|
263
|
-
|
|
264
|
-
- MCP is installed but the current agent says no OpenViking tools are registered: open a fresh agent session.
|
|
265
|
-
- `claude mcp list` does not show OpenViking: run `threadnote mcp-install claude --apply`; the default scope is
|
|
266
|
-
`user`.
|
|
267
|
-
- `/health` is OK but `/mcp` fails: this is expected for OpenViking `0.3.12`; use the default stdio adapter instead of
|
|
268
|
-
`--native-http`.
|
|
269
|
-
- The old checkout or worktree was deleted: run
|
|
270
|
-
`npm run threadnote -- repair` from any fresh checkout.
|
|
271
|
-
- Recall returns only overview nodes: read the URI, or list the directory with
|
|
272
|
-
`threadnote list <uri> --all --recursive`.
|
|
273
|
-
|
|
274
|
-
## What Not To Demo
|
|
275
|
-
|
|
276
|
-
- Do not seed an entire repo.
|
|
277
|
-
- Do not store secrets, tokens, customer data, raw production logs, or long diffs.
|
|
278
|
-
- Do not present CLI commands as the ideal daily workflow. For normal work, the developer should ask the agent in
|
|
279
|
-
natural language and the agent should use MCP directly.
|