threadnote 0.7.8 → 0.7.10
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 +4 -1
- package/dist/mcp_server.cjs +159 -156
- package/dist/threadnote.cjs +1381 -150
- package/docs/agent-instructions.md +2 -2
- package/docs/index.html +56 -32
- package/docs/share.md +20 -6
- package/manager/app.css +910 -0
- package/manager/app.js +36222 -0
- package/manager/index.html +14 -0
- package/package.json +15 -6
package/README.md
CHANGED
|
@@ -342,6 +342,9 @@ This is it! Start working with your agents as usual. The agent will automaticall
|
|
|
342
342
|
- `version`: prints the installed Threadnote version, latest npm version, and release notes for newer GitHub releases.
|
|
343
343
|
- `update`: updates the published Threadnote package, then runs `repair` so shims and MCP config point at the new
|
|
344
344
|
version. When an update is available, it prints release notes for the full version diff.
|
|
345
|
+
- `manage`: opens the local React web manager for browsing, recalling, reading, editing, archiving, forgetting,
|
|
346
|
+
publishing, consolidating, and diagnosing local/shared memories. It binds to `127.0.0.1` with a per-session token; use
|
|
347
|
+
`--no-open` to print the URL without opening a browser.
|
|
345
348
|
- `repair`: fixes install/config/shim/manifest/server health issues and rewrites Codex/Claude/Cursor/Copilot MCP configs
|
|
346
349
|
from the current checkout.
|
|
347
350
|
- `start`: starts `openviking-server` on `127.0.0.1:1933`.
|
|
@@ -377,7 +380,7 @@ This is it! Start working with your agents as usual. The agent will automaticall
|
|
|
377
380
|
succeeds.
|
|
378
381
|
- `forget`: removes a `viking://` URI.
|
|
379
382
|
- `export-pack` / `import-pack`: moves local context through `.ovpack` files.
|
|
380
|
-
- `share init|status|sync|publish|unpublish|list|remove`: opts a curated subset of durable memories into a team git
|
|
383
|
+
- `share init|status|sync|publish|unpublish|list|rename|set-url|remove`: opts a curated subset of durable memories into a team git
|
|
381
384
|
repo. Threadnote periodically fetches configured share repos and automatically syncs clean incoming changes before
|
|
382
385
|
agent recall/read; use `share sync` for dirty worktrees, conflicts, explicit pushes, or immediate manual sync.
|
|
383
386
|
Personal handoffs and preferences stay local. See `docs/share.md` for the full workflow and the publish-time scrubber
|
package/dist/mcp_server.cjs
CHANGED
|
@@ -33691,33 +33691,111 @@ async function runCommand(executable, args, options = {}) {
|
|
|
33691
33691
|
const child = (0, import_node_child_process.spawn)(executable, args, { cwd: options.cwd });
|
|
33692
33692
|
const stdoutChunks = [];
|
|
33693
33693
|
const stderrChunks = [];
|
|
33694
|
+
const maxOutputBytes = options.maxOutputBytes ?? defaultCommandMaxOutputBytes();
|
|
33695
|
+
let stdoutBytes = 0;
|
|
33696
|
+
let stderrBytes = 0;
|
|
33697
|
+
let finished = false;
|
|
33698
|
+
let failureResult;
|
|
33699
|
+
let killTimer;
|
|
33700
|
+
let sentTerminationSignal = false;
|
|
33701
|
+
const timeoutMs = options.timeoutMs ?? defaultCommandTimeoutMs();
|
|
33702
|
+
const finish = (result) => {
|
|
33703
|
+
if (finished) {
|
|
33704
|
+
return;
|
|
33705
|
+
}
|
|
33706
|
+
finished = true;
|
|
33707
|
+
if (killTimer) {
|
|
33708
|
+
clearTimeout(killTimer);
|
|
33709
|
+
}
|
|
33710
|
+
if (result.exitCode !== 0 && options.allowFailure !== true) {
|
|
33711
|
+
rejectPromise(new Error(`${formatShellCommand(executable, args)} failed: ${result.stderr || result.stdout}`));
|
|
33712
|
+
return;
|
|
33713
|
+
}
|
|
33714
|
+
resolvePromise(result);
|
|
33715
|
+
};
|
|
33716
|
+
const failAndKill = (message) => {
|
|
33717
|
+
if (failureResult) {
|
|
33718
|
+
return;
|
|
33719
|
+
}
|
|
33720
|
+
failureResult = { exitCode: 124, stderr: message, stdout: stdoutChunks.join("") };
|
|
33721
|
+
if (!sentTerminationSignal) {
|
|
33722
|
+
sentTerminationSignal = true;
|
|
33723
|
+
child.kill("SIGTERM");
|
|
33724
|
+
}
|
|
33725
|
+
setTimeout(() => {
|
|
33726
|
+
if (!finished) {
|
|
33727
|
+
child.kill("SIGKILL");
|
|
33728
|
+
}
|
|
33729
|
+
}, 1e3).unref?.();
|
|
33730
|
+
};
|
|
33731
|
+
if (timeoutMs > 0) {
|
|
33732
|
+
killTimer = setTimeout(() => {
|
|
33733
|
+
failAndKill(`${formatShellCommand(executable, args)} timed out after ${timeoutMs}ms`);
|
|
33734
|
+
}, timeoutMs);
|
|
33735
|
+
killTimer.unref?.();
|
|
33736
|
+
}
|
|
33694
33737
|
child.stdout.on("data", (chunk) => {
|
|
33695
|
-
|
|
33738
|
+
if (failureResult) {
|
|
33739
|
+
return;
|
|
33740
|
+
}
|
|
33741
|
+
const text = String(chunk);
|
|
33742
|
+
stdoutBytes += Buffer.byteLength(text);
|
|
33743
|
+
if (stdoutBytes + stderrBytes > maxOutputBytes) {
|
|
33744
|
+
failAndKill(`${formatShellCommand(executable, args)} exceeded output limit of ${maxOutputBytes} bytes`);
|
|
33745
|
+
return;
|
|
33746
|
+
}
|
|
33747
|
+
stdoutChunks.push(text);
|
|
33696
33748
|
});
|
|
33697
33749
|
child.stderr.on("data", (chunk) => {
|
|
33698
|
-
|
|
33750
|
+
if (failureResult) {
|
|
33751
|
+
return;
|
|
33752
|
+
}
|
|
33753
|
+
const text = String(chunk);
|
|
33754
|
+
stderrBytes += Buffer.byteLength(text);
|
|
33755
|
+
if (stdoutBytes + stderrBytes > maxOutputBytes) {
|
|
33756
|
+
failAndKill(`${formatShellCommand(executable, args)} exceeded output limit of ${maxOutputBytes} bytes`);
|
|
33757
|
+
return;
|
|
33758
|
+
}
|
|
33759
|
+
stderrChunks.push(text);
|
|
33699
33760
|
});
|
|
33700
33761
|
child.on("error", (err) => {
|
|
33762
|
+
if (finished) {
|
|
33763
|
+
return;
|
|
33764
|
+
}
|
|
33765
|
+
if (killTimer) {
|
|
33766
|
+
clearTimeout(killTimer);
|
|
33767
|
+
}
|
|
33701
33768
|
if (options.allowFailure === true) {
|
|
33702
|
-
|
|
33769
|
+
finish({ exitCode: 127, stderr: errorMessage(err), stdout: "" });
|
|
33703
33770
|
} else {
|
|
33771
|
+
finished = true;
|
|
33704
33772
|
rejectPromise(err);
|
|
33705
33773
|
}
|
|
33706
33774
|
});
|
|
33707
33775
|
child.on("close", (code) => {
|
|
33708
|
-
const result = {
|
|
33776
|
+
const result = failureResult ?? {
|
|
33709
33777
|
exitCode: code ?? 1,
|
|
33710
33778
|
stderr: stderrChunks.join(""),
|
|
33711
33779
|
stdout: stdoutChunks.join("")
|
|
33712
33780
|
};
|
|
33713
|
-
|
|
33714
|
-
rejectPromise(new Error(`${formatShellCommand(executable, args)} failed: ${result.stderr || result.stdout}`));
|
|
33715
|
-
return;
|
|
33716
|
-
}
|
|
33717
|
-
resolvePromise(result);
|
|
33781
|
+
finish(result);
|
|
33718
33782
|
});
|
|
33719
33783
|
});
|
|
33720
33784
|
}
|
|
33785
|
+
function defaultCommandTimeoutMs() {
|
|
33786
|
+
return positiveIntegerFromEnv("THREADNOTE_COMMAND_TIMEOUT_MS") ?? 10 * 60 * 1e3;
|
|
33787
|
+
}
|
|
33788
|
+
function defaultCommandMaxOutputBytes() {
|
|
33789
|
+
return positiveIntegerFromEnv("THREADNOTE_COMMAND_MAX_OUTPUT_BYTES") ?? 5 * 1024 * 1024;
|
|
33790
|
+
}
|
|
33791
|
+
function positiveIntegerFromEnv(name) {
|
|
33792
|
+
const value = process.env[name];
|
|
33793
|
+
if (value === void 0) {
|
|
33794
|
+
return void 0;
|
|
33795
|
+
}
|
|
33796
|
+
const parsed = Number.parseInt(value, 10);
|
|
33797
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
33798
|
+
}
|
|
33721
33799
|
async function gitValue(args, cwd = getInvocationCwd()) {
|
|
33722
33800
|
const result = await runCommand("git", args, { allowFailure: true, cwd });
|
|
33723
33801
|
if (result.exitCode !== 0) {
|
|
@@ -34671,6 +34749,58 @@ async function runShareSyncQuiet(config2, state, options) {
|
|
|
34671
34749
|
}
|
|
34672
34750
|
return void 0;
|
|
34673
34751
|
}
|
|
34752
|
+
async function publishShareGitChange(worktree, relativePath, commitMessage, options = {}) {
|
|
34753
|
+
const dryRun = options.dryRun === true;
|
|
34754
|
+
const push = options.push !== false;
|
|
34755
|
+
const verb = options.verb ?? "add";
|
|
34756
|
+
const git = await requiredExecutable("git");
|
|
34757
|
+
const messages = [];
|
|
34758
|
+
const stageArgs = verb === "rm" ? ["-C", worktree, "rm", relativePath] : ["-C", worktree, "add", "--", relativePath];
|
|
34759
|
+
const stageResult = await runGitCommand(dryRun, git, stageArgs, `git ${verb} failed`);
|
|
34760
|
+
if (stageResult) {
|
|
34761
|
+
messages.push(`git ${verb}: ${stageResult.stdout.trim() || "ok"}`);
|
|
34762
|
+
}
|
|
34763
|
+
if (dryRun) {
|
|
34764
|
+
console.log(`Would run: ${formatShellCommand(git, ["-C", worktree, "commit", "-m", commitMessage])}`);
|
|
34765
|
+
} else {
|
|
34766
|
+
const commitResult = await runCommand(git, ["-C", worktree, "commit", "-m", commitMessage], { allowFailure: true });
|
|
34767
|
+
if (commitResult.exitCode !== 0) {
|
|
34768
|
+
const detail = commitResult.stdout.trim() || commitResult.stderr.trim();
|
|
34769
|
+
if (/nothing to commit|no changes added/i.test(detail)) {
|
|
34770
|
+
messages.push("git commit: nothing to commit (file already in tree)");
|
|
34771
|
+
} else {
|
|
34772
|
+
throw new Error(`git commit failed: ${detail || "unknown error"}`);
|
|
34773
|
+
}
|
|
34774
|
+
} else {
|
|
34775
|
+
messages.push(`git commit: ${commitResult.stdout.trim().split("\n").slice(0, 2).join(" ")}`);
|
|
34776
|
+
}
|
|
34777
|
+
}
|
|
34778
|
+
if (!push) {
|
|
34779
|
+
messages.push("git push skipped (push=false)");
|
|
34780
|
+
return messages;
|
|
34781
|
+
}
|
|
34782
|
+
const pushResult = await runGitCommand(
|
|
34783
|
+
dryRun,
|
|
34784
|
+
git,
|
|
34785
|
+
["-C", worktree, "push", DEFAULT_GIT_REMOTE_NAME],
|
|
34786
|
+
"git push failed"
|
|
34787
|
+
);
|
|
34788
|
+
if (pushResult) {
|
|
34789
|
+
messages.push(`git push: ${pushResult.stdout.trim() || pushResult.stderr.trim() || "ok"}`);
|
|
34790
|
+
}
|
|
34791
|
+
return messages;
|
|
34792
|
+
}
|
|
34793
|
+
async function runGitCommand(dryRun, git, args, failureLabel) {
|
|
34794
|
+
if (dryRun) {
|
|
34795
|
+
console.log(`Would run: ${formatShellCommand(git, args)}`);
|
|
34796
|
+
return void 0;
|
|
34797
|
+
}
|
|
34798
|
+
const result = await runCommand(git, args, { allowFailure: true });
|
|
34799
|
+
if (result.exitCode !== 0) {
|
|
34800
|
+
throw new Error(`${failureLabel}: ${result.stderr.trim() || result.stdout.trim() || "unknown error"}`);
|
|
34801
|
+
}
|
|
34802
|
+
return result;
|
|
34803
|
+
}
|
|
34674
34804
|
function normalizeTeamName(input) {
|
|
34675
34805
|
const candidate = (input ?? "default").trim();
|
|
34676
34806
|
if (!candidate) {
|
|
@@ -34965,47 +35095,6 @@ async function ingestSingleFile(ov, config2, uri, filePath, initialMode, options
|
|
|
34965
35095
|
const content = await (0, import_promises3.readFile)(filePath, "utf8");
|
|
34966
35096
|
await writeMemoryFile(config2, ov, uri, content, initialMode, false, options);
|
|
34967
35097
|
}
|
|
34968
|
-
async function removeWithRollback(config2, ov, sourceUri, rollbackUri, worktree, dryRun, label) {
|
|
34969
|
-
try {
|
|
34970
|
-
await removeMemoryUri(config2, ov, sourceUri, dryRun);
|
|
34971
|
-
} catch (sourceErr) {
|
|
34972
|
-
if (dryRun) {
|
|
34973
|
-
throw sourceErr;
|
|
34974
|
-
}
|
|
34975
|
-
console.error(
|
|
34976
|
-
`Source removal failed during ${label}; rolling back ${rollbackUri} so the system is back to the pre-${label} state.`
|
|
34977
|
-
);
|
|
34978
|
-
try {
|
|
34979
|
-
await removeMemoryUri(config2, ov, rollbackUri, false);
|
|
34980
|
-
} catch (rollbackErr) {
|
|
34981
|
-
console.error(
|
|
34982
|
-
`Rollback of ${rollbackUri} also failed. Manual cleanup needed via: threadnote forget ${rollbackUri}
|
|
34983
|
-
Rollback error: ${rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr)}`
|
|
34984
|
-
);
|
|
34985
|
-
}
|
|
34986
|
-
await bestEffortRemoveWorktreeFile(rollbackUri, worktree, label);
|
|
34987
|
-
throw sourceErr;
|
|
34988
|
-
}
|
|
34989
|
-
}
|
|
34990
|
-
async function bestEffortRemoveWorktreeFile(rollbackUri, worktree, label) {
|
|
34991
|
-
if (label !== "publish") {
|
|
34992
|
-
return;
|
|
34993
|
-
}
|
|
34994
|
-
const prefix = "viking://";
|
|
34995
|
-
if (!rollbackUri.startsWith(prefix)) {
|
|
34996
|
-
return;
|
|
34997
|
-
}
|
|
34998
|
-
const parts = rollbackUri.slice(prefix.length).split("/");
|
|
34999
|
-
const sharedIndex = parts.indexOf("shared");
|
|
35000
|
-
if (sharedIndex === -1 || sharedIndex + 2 >= parts.length) {
|
|
35001
|
-
return;
|
|
35002
|
-
}
|
|
35003
|
-
const relative2 = parts.slice(sharedIndex + 2).join("/");
|
|
35004
|
-
if (!relative2) {
|
|
35005
|
-
return;
|
|
35006
|
-
}
|
|
35007
|
-
await (0, import_promises3.rm)((0, import_node_path3.join)(worktree, relative2), { force: true });
|
|
35008
|
-
}
|
|
35009
35098
|
async function removeMemoryUri(config2, ov, uri, dryRun, options = {}) {
|
|
35010
35099
|
const args = withIdentity(config2, ["rm", uri]);
|
|
35011
35100
|
if (dryRun) {
|
|
@@ -35167,7 +35256,7 @@ async function main() {
|
|
|
35167
35256
|
"During feature work, update durable feature knowledge when valuable implementation details, decisions, interfaces, or gotchas change.",
|
|
35168
35257
|
"When updating the same active issue, pass project/topic or replaceUri to remember_context so duplicate durable memories or handoffs do not accumulate.",
|
|
35169
35258
|
"Use compact_context with dryRun=true for scoped memory hygiene when recall surfaces overlapping active memories.",
|
|
35170
|
-
"To share a durable memory with teammates, call `share_publish` with its viking:// URI. share_publish
|
|
35259
|
+
"To share a durable memory with teammates, call `share_publish` with its viking:// URI. share_publish scrubs for secrets, writes and pushes the shared copy first, then removes the personal copy after the push succeeds. Do not publish handoffs, preferences, or anything carrying machine-local paths or in-flight task context.",
|
|
35171
35260
|
"Do not store secrets, customer data, raw production logs, or credentials."
|
|
35172
35261
|
].join("\n")
|
|
35173
35262
|
}
|
|
@@ -35351,7 +35440,7 @@ function registerTools(server, config2) {
|
|
|
35351
35440
|
"share_publish",
|
|
35352
35441
|
{
|
|
35353
35442
|
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
35354
|
-
description: "Publish a personal memory into a team's shared memories git repo. Reads the memory, strips local-only provenance frontmatter (supersedes:, archived_from:), refuses publish if it matches secret patterns,
|
|
35443
|
+
description: "Publish a personal memory into a team's shared memories git repo. Reads the memory, strips local-only provenance frontmatter (supersedes:, archived_from:), refuses publish if it matches secret patterns, writes and pushes the shared copy first, then removes the personal original. Default team is used unless team is provided. Pass preview=true to return the would-be-published bytes without writing or committing.",
|
|
35355
35444
|
inputSchema: {
|
|
35356
35445
|
message: external_exports.string().optional().describe('Commit message override; defaults to "share: publish <path>"'),
|
|
35357
35446
|
preview: external_exports.boolean().optional().describe(
|
|
@@ -35749,27 +35838,8 @@ function registerCompactTool(server, config2) {
|
|
|
35749
35838
|
const ov = await requiredOpenVikingCli2();
|
|
35750
35839
|
const appliedMessages = [];
|
|
35751
35840
|
for (const action of plan.keepUpdates) {
|
|
35752
|
-
|
|
35753
|
-
ov,
|
|
35754
|
-
config2,
|
|
35755
|
-
action.uri,
|
|
35756
|
-
withIdentity2(config2, [
|
|
35757
|
-
"write",
|
|
35758
|
-
action.uri,
|
|
35759
|
-
"--content",
|
|
35760
|
-
action.content,
|
|
35761
|
-
"--mode",
|
|
35762
|
-
"replace",
|
|
35763
|
-
"--wait",
|
|
35764
|
-
"--timeout",
|
|
35765
|
-
"120"
|
|
35766
|
-
])
|
|
35767
|
-
);
|
|
35841
|
+
await writeMemoryFile(config2, ov, action.uri, action.content, "replace", false, { quiet: true });
|
|
35768
35842
|
appliedMessages.push(`Updated kept memory: ${action.uri}`);
|
|
35769
|
-
const output = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n");
|
|
35770
|
-
if (output) {
|
|
35771
|
-
appliedMessages.push(output);
|
|
35772
|
-
}
|
|
35773
35843
|
}
|
|
35774
35844
|
for (const action of plan.archives) {
|
|
35775
35845
|
const archiveResult = await archiveMemoryForCompact(config2, ov, action);
|
|
@@ -35942,22 +36012,7 @@ async function writeDurableMemory(config2, params) {
|
|
|
35942
36012
|
const finalMetadata = isInPlaceUpdate ? { ...params.metadata, supersedes: void 0 } : candidateMetadata;
|
|
35943
36013
|
const memory = isInPlaceUpdate ? formatMemoryDocument2("MEMORY", finalMetadata, params.bodyText) : candidateMemory;
|
|
35944
36014
|
const writeMode = await memoryWriteMode(ov, config2, memoryUri, finalMetadata);
|
|
35945
|
-
|
|
35946
|
-
ov,
|
|
35947
|
-
config2,
|
|
35948
|
-
memoryUri,
|
|
35949
|
-
withIdentity2(config2, [
|
|
35950
|
-
"write",
|
|
35951
|
-
memoryUri,
|
|
35952
|
-
"--content",
|
|
35953
|
-
memory,
|
|
35954
|
-
"--mode",
|
|
35955
|
-
writeMode,
|
|
35956
|
-
"--wait",
|
|
35957
|
-
"--timeout",
|
|
35958
|
-
"120"
|
|
35959
|
-
])
|
|
35960
|
-
);
|
|
36015
|
+
await writeMemoryFile(config2, ov, memoryUri, memory, writeMode, false, { quiet: true });
|
|
35961
36016
|
const messages = [`Stored memory: ${memoryUri}`];
|
|
35962
36017
|
if (params.replaceUri && !isInPlaceUpdate) {
|
|
35963
36018
|
const removedReplacedMemory = await removeVikingResourceWithRetry(ov, config2, params.replaceUri);
|
|
@@ -35967,8 +36022,7 @@ async function writeDurableMemory(config2, params) {
|
|
|
35967
36022
|
} else if (isInPlaceUpdate) {
|
|
35968
36023
|
messages.push(`Updated existing memory in place: ${memoryUri}`);
|
|
35969
36024
|
}
|
|
35970
|
-
|
|
35971
|
-
return { content: [{ type: "text", text: [...messages, text].filter(Boolean).join("\n") }] };
|
|
36025
|
+
return { content: [{ type: "text", text: messages.join("\n") }] };
|
|
35972
36026
|
} catch (err) {
|
|
35973
36027
|
return { content: [{ type: "text", text: errorMessage(err) }], isError: true };
|
|
35974
36028
|
}
|
|
@@ -36003,27 +36057,10 @@ async function writeSharedMemoryReplacement(config2, ov, params, targetUri) {
|
|
|
36003
36057
|
messages.push(`Redacted ${redaction.count}\xD7 ${redaction.name} before shared update.`);
|
|
36004
36058
|
}
|
|
36005
36059
|
messages.push(
|
|
36006
|
-
...await
|
|
36060
|
+
...await publishShareGitChange(resolved.config.worktree, relativePath, `share: update ${relativePath}`)
|
|
36007
36061
|
);
|
|
36008
36062
|
return { content: [{ type: "text", text: messages.join("\n") }] };
|
|
36009
36063
|
}
|
|
36010
|
-
async function runOpenVikingWriteWithRetry(ov, config2, memoryUri, args) {
|
|
36011
|
-
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
36012
|
-
const result = await runCommand(ov, args, { allowFailure: true });
|
|
36013
|
-
if (result.exitCode === 0) {
|
|
36014
|
-
return result;
|
|
36015
|
-
}
|
|
36016
|
-
if (await vikingResourceExists2(ov, config2, memoryUri)) {
|
|
36017
|
-
await runCommand(ov, withIdentity2(config2, ["wait", "--timeout", "120"]), { allowFailure: true });
|
|
36018
|
-
return { exitCode: 0, stdout: "OpenViking accepted the memory and indexing wait completed.", stderr: "" };
|
|
36019
|
-
}
|
|
36020
|
-
if (!isResourceBusy(result.stderr, result.stdout) || attempt === 3) {
|
|
36021
|
-
throw new Error(`${ov} ${args.join(" ")} failed: ${result.stderr || result.stdout}`);
|
|
36022
|
-
}
|
|
36023
|
-
await sleep(1e3 * (attempt + 1));
|
|
36024
|
-
}
|
|
36025
|
-
throw new Error(`${ov} ${args.join(" ")} failed.`);
|
|
36026
|
-
}
|
|
36027
36064
|
async function vikingResourceExists2(ov, config2, uri) {
|
|
36028
36065
|
const stat2 = await runCommand(ov, withIdentity2(config2, ["stat", uri]), { allowFailure: true });
|
|
36029
36066
|
return stat2.exitCode === 0;
|
|
@@ -36251,17 +36288,27 @@ async function runSharePublishTool(config2, sourceUri, options) {
|
|
|
36251
36288
|
}
|
|
36252
36289
|
await ensureSharedDirectoryChain(config2, ov, targetUri, false);
|
|
36253
36290
|
await writeMemoryFile(config2, ov, targetUri, content, "create", false);
|
|
36291
|
+
const messages = [`Published ${sourceUri} -> ${targetUri}`];
|
|
36292
|
+
for (const redaction of scrub.redactions) {
|
|
36293
|
+
messages.push(`Redacted ${redaction.count}\xD7 ${redaction.name} before publish.`);
|
|
36294
|
+
}
|
|
36295
|
+
const relativePath = vikingUriToWorktreeRelative(config2, targetUri, resolved.name);
|
|
36296
|
+
const commitMessage = options.message ?? `share: publish ${relativePath}`;
|
|
36297
|
+
const gitMessages = await publishShareGitChange(resolved.config.worktree, relativePath, commitMessage, {
|
|
36298
|
+
push: options.push
|
|
36299
|
+
});
|
|
36254
36300
|
try {
|
|
36255
|
-
await
|
|
36301
|
+
await removeMemoryUri(config2, ov, sourceUri, false, { quiet: true });
|
|
36256
36302
|
} catch (sourceErr) {
|
|
36257
36303
|
return {
|
|
36258
36304
|
content: [
|
|
36259
36305
|
{
|
|
36260
36306
|
type: "text",
|
|
36261
36307
|
text: [
|
|
36262
|
-
|
|
36263
|
-
|
|
36264
|
-
`
|
|
36308
|
+
...messages,
|
|
36309
|
+
...gitMessages,
|
|
36310
|
+
`Could not remove the personal source after publish: ${sourceUri}.`,
|
|
36311
|
+
`Retry cleanup later with: threadnote forget ${sourceUri}`,
|
|
36265
36312
|
sourceErr instanceof Error ? sourceErr.message : String(sourceErr)
|
|
36266
36313
|
].join("\n")
|
|
36267
36314
|
}
|
|
@@ -36269,18 +36316,6 @@ async function runSharePublishTool(config2, sourceUri, options) {
|
|
|
36269
36316
|
isError: true
|
|
36270
36317
|
};
|
|
36271
36318
|
}
|
|
36272
|
-
const messages = [`Published ${sourceUri} -> ${targetUri}`];
|
|
36273
|
-
for (const redaction of scrub.redactions) {
|
|
36274
|
-
messages.push(`Redacted ${redaction.count}\xD7 ${redaction.name} before publish.`);
|
|
36275
|
-
}
|
|
36276
|
-
const relativePath = vikingUriToWorktreeRelative(config2, targetUri, resolved.name);
|
|
36277
|
-
const commitMessage = options.message ?? `share: publish ${relativePath}`;
|
|
36278
|
-
const gitMessages = await gitPublishWorkflow(
|
|
36279
|
-
resolved.config.worktree,
|
|
36280
|
-
relativePath,
|
|
36281
|
-
commitMessage,
|
|
36282
|
-
options.push !== false
|
|
36283
|
-
);
|
|
36284
36319
|
return {
|
|
36285
36320
|
content: [{ type: "text", text: [...messages, ...gitMessages].join("\n") }],
|
|
36286
36321
|
isError: false
|
|
@@ -36289,38 +36324,6 @@ async function runSharePublishTool(config2, sourceUri, options) {
|
|
|
36289
36324
|
return { content: [{ type: "text", text: errorMessage(err) }], isError: true };
|
|
36290
36325
|
}
|
|
36291
36326
|
}
|
|
36292
|
-
async function gitPublishWorkflow(worktree, relativePath, commitMessage, push) {
|
|
36293
|
-
const messages = [];
|
|
36294
|
-
const add = await runCommand("git", ["-C", worktree, "add", relativePath], { allowFailure: true });
|
|
36295
|
-
if (add.exitCode !== 0) {
|
|
36296
|
-
messages.push(`git add failed: ${add.stderr.trim() || add.stdout.trim()}`);
|
|
36297
|
-
return messages;
|
|
36298
|
-
}
|
|
36299
|
-
const commit = await runCommand("git", ["-C", worktree, "commit", "-m", commitMessage], { allowFailure: true });
|
|
36300
|
-
if (commit.exitCode !== 0) {
|
|
36301
|
-
const detail = commit.stdout.trim() || commit.stderr.trim();
|
|
36302
|
-
if (/nothing to commit|no changes added/i.test(detail)) {
|
|
36303
|
-
messages.push("git commit: nothing to commit (file already in tree)");
|
|
36304
|
-
} else {
|
|
36305
|
-
messages.push(`git commit failed: ${detail}`);
|
|
36306
|
-
return messages;
|
|
36307
|
-
}
|
|
36308
|
-
} else {
|
|
36309
|
-
messages.push(`git commit: ${commit.stdout.trim().split("\n").slice(0, 2).join(" ")}`);
|
|
36310
|
-
}
|
|
36311
|
-
if (!push) {
|
|
36312
|
-
messages.push("git push skipped (push=false)");
|
|
36313
|
-
return messages;
|
|
36314
|
-
}
|
|
36315
|
-
const pushResult = await runCommand("git", ["-C", worktree, "push", DEFAULT_GIT_REMOTE_NAME], { allowFailure: true });
|
|
36316
|
-
const pushDetail = pushResult.stdout.trim() || pushResult.stderr.trim();
|
|
36317
|
-
if (pushResult.exitCode !== 0) {
|
|
36318
|
-
messages.push(`git push failed: ${pushDetail}`);
|
|
36319
|
-
} else {
|
|
36320
|
-
messages.push(`git push: ${pushDetail || "ok"}`);
|
|
36321
|
-
}
|
|
36322
|
-
return messages;
|
|
36323
|
-
}
|
|
36324
36327
|
function withIdentity2(config2, args) {
|
|
36325
36328
|
return [...args, "--account", config2.account, "--user", config2.user, "--agent-id", config2.agentId];
|
|
36326
36329
|
}
|