threadnote 1.1.1 → 1.1.3
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/LICENSE +67 -80
- package/README.md +19 -2
- package/THIRD_PARTY.md +36 -0
- package/dist/mcp_server.cjs +587 -9
- package/dist/threadnote.cjs +528 -19
- package/docs/agent-instructions.md +22 -5
- package/docs/index.html +2 -2
- package/docs/share.md +100 -10
- package/package.json +4 -3
package/dist/threadnote.cjs
CHANGED
|
@@ -3473,6 +3473,7 @@ var {
|
|
|
3473
3473
|
var DEFAULT_HOST = "127.0.0.1";
|
|
3474
3474
|
var DEFAULT_PORT = 1933;
|
|
3475
3475
|
var DEFAULT_OPENVIKING_VERSION = "0.3.24";
|
|
3476
|
+
var OPENVIKING_TOOL_PYTHON = "3.12";
|
|
3476
3477
|
var DEFAULT_ACCOUNT = "local";
|
|
3477
3478
|
var DEFAULT_AGENT_ID = "threadnote";
|
|
3478
3479
|
var OPENVIKING_PACKAGE_NAME = "openviking[local-embed]";
|
|
@@ -4315,7 +4316,13 @@ function recallSnippet(value) {
|
|
|
4315
4316
|
const oneLine = value.replace(/\s+/g, " ").trim();
|
|
4316
4317
|
return oneLine.length > 180 ? `${oneLine.slice(0, 180)}\u2026` : oneLine;
|
|
4317
4318
|
}
|
|
4318
|
-
function
|
|
4319
|
+
function isArchivedMemoryUri(uri) {
|
|
4320
|
+
const documentUri = uri.replace(/#.*$/, "");
|
|
4321
|
+
return /^viking:\/\/user\/[^/]+\/memories\/(?:durable|handoffs|incidents|preferences|smoke)\/archived(?:\/|$)/.test(
|
|
4322
|
+
documentUri
|
|
4323
|
+
);
|
|
4324
|
+
}
|
|
4325
|
+
function parseRecallHits(output2, options = {}) {
|
|
4319
4326
|
const start = output2.search(/^\{/m);
|
|
4320
4327
|
if (start < 0) {
|
|
4321
4328
|
return [];
|
|
@@ -4336,6 +4343,9 @@ function parseRecallHits(output2) {
|
|
|
4336
4343
|
if (!isJsonObject(item) || typeof item.uri !== "string" || isSummarySidecarUri(item.uri)) {
|
|
4337
4344
|
continue;
|
|
4338
4345
|
}
|
|
4346
|
+
if (options.includeArchived !== true && isArchivedMemoryUri(item.uri)) {
|
|
4347
|
+
continue;
|
|
4348
|
+
}
|
|
4339
4349
|
hits.push({
|
|
4340
4350
|
contextType: typeof item.context_type === "string" ? item.context_type : "result",
|
|
4341
4351
|
score: typeof item.score === "number" ? item.score : 0,
|
|
@@ -8447,6 +8457,9 @@ var import_node_path6 = require("node:path");
|
|
|
8447
8457
|
var TEAMS_FILE_VERSION = 1;
|
|
8448
8458
|
var SHARED_SEGMENT = "shared";
|
|
8449
8459
|
var SHAREABLE_MEMORY_KIND_DIRS = ["durable"];
|
|
8460
|
+
var SHAREABLE_ARTIFACT_DIR = "agent-artifacts";
|
|
8461
|
+
var SHAREABLE_TOP_LEVEL_DIRS = [...SHAREABLE_MEMORY_KIND_DIRS, SHAREABLE_ARTIFACT_DIR];
|
|
8462
|
+
var ARTIFACT_INSTALL_METADATA_VERSION = 1;
|
|
8450
8463
|
var AUTO_SHARE_FETCH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
8451
8464
|
var DEFAULT_GIT_REMOTE_NAME = "origin";
|
|
8452
8465
|
var SCRUBBER_PATTERNS = [
|
|
@@ -8538,7 +8551,7 @@ async function runShareInit(config, remoteUrl, options) {
|
|
|
8538
8551
|
if (!dryRun) {
|
|
8539
8552
|
await ensureSharedGitignore(worktree, git, options.push !== false);
|
|
8540
8553
|
const ingested = await ingestWorktreeFiles(config, newConfig, "create");
|
|
8541
|
-
console.log(`Ingested ${ingested} shared
|
|
8554
|
+
console.log(`Ingested ${ingested} shared file(s) into OpenViking.`);
|
|
8542
8555
|
}
|
|
8543
8556
|
}
|
|
8544
8557
|
var SHARED_GITIGNORE_PATTERNS = ["**/.abstract.md", "**/.overview.md"];
|
|
@@ -8865,7 +8878,7 @@ async function runShareSyncQuiet(config, state, options) {
|
|
|
8865
8878
|
return void 0;
|
|
8866
8879
|
}
|
|
8867
8880
|
async function stageShareableChanges(dryRun, git, worktree) {
|
|
8868
|
-
const pathspecs = [":(top)README.md", ":(top).gitignore", ...
|
|
8881
|
+
const pathspecs = [":(top)README.md", ":(top).gitignore", ...SHAREABLE_TOP_LEVEL_DIRS.map((dir) => `:(top)${dir}`)];
|
|
8869
8882
|
await maybeRun(dryRun, git, ["-C", worktree, "add", "--", ...pathspecs], { allowFailure: true });
|
|
8870
8883
|
}
|
|
8871
8884
|
async function publishShareGitChange(worktree, relativePath, commitMessage, options = {}) {
|
|
@@ -8987,6 +9000,157 @@ ${err instanceof Error ? err.message : String(err)}`,
|
|
|
8987
9000
|
}
|
|
8988
9001
|
console.log(`Published ${sourceUri} -> ${targetUri}`);
|
|
8989
9002
|
}
|
|
9003
|
+
async function runSharePublishArtifact(config, sourcePath, options) {
|
|
9004
|
+
const result = await shareAgentArtifact(config, sourcePath, options);
|
|
9005
|
+
printShareArtifactResult(result, options.preview === true);
|
|
9006
|
+
}
|
|
9007
|
+
async function shareAgentArtifact(config, sourcePath, options) {
|
|
9008
|
+
const team = await resolveTeam(config, options.team);
|
|
9009
|
+
const dryRun = options.dryRun === true;
|
|
9010
|
+
const preview = options.preview === true;
|
|
9011
|
+
const resolvedSourcePath = expandPath(sourcePath);
|
|
9012
|
+
if (!await isRegularFileNoSymlink(resolvedSourcePath)) {
|
|
9013
|
+
throw new Error(`Agent artifact source is not a regular file: ${resolvedSourcePath}`);
|
|
9014
|
+
}
|
|
9015
|
+
const artifact = inferShareArtifact(resolvedSourcePath, options);
|
|
9016
|
+
const rawContent = await (0, import_promises5.readFile)(resolvedSourcePath, "utf8");
|
|
9017
|
+
if (!rawContent.trim()) {
|
|
9018
|
+
throw new Error(`Refusing to share empty agent artifact: ${resolvedSourcePath}`);
|
|
9019
|
+
}
|
|
9020
|
+
const scrub = applyScrubber(rawContent, { redact: options.redact === true });
|
|
9021
|
+
const relativePath = sharedArtifactRelativePath(artifact);
|
|
9022
|
+
const targetPath = (0, import_node_path6.join)(team.config.worktree, ...relativePath.split("/"));
|
|
9023
|
+
const targetUri = workfileToVikingUri(config, team.config, targetPath);
|
|
9024
|
+
const messages = [
|
|
9025
|
+
`${preview ? "Previewing" : dryRun ? "Would share" : "Sharing"} ${artifact.kind} ${artifact.agent}/${artifact.name}`,
|
|
9026
|
+
`Source: ${portablePath(resolvedSourcePath)}`,
|
|
9027
|
+
`Destination: ${targetUri}`
|
|
9028
|
+
];
|
|
9029
|
+
if (preview) {
|
|
9030
|
+
if (scrub.blocker) {
|
|
9031
|
+
messages.push(`PREVIEW BLOCKED: ${scrub.blocker}. Strip the sensitive value or pass --redact.`);
|
|
9032
|
+
return {
|
|
9033
|
+
artifact,
|
|
9034
|
+
gitMessages: [],
|
|
9035
|
+
messages,
|
|
9036
|
+
sourcePath: resolvedSourcePath,
|
|
9037
|
+
targetPath,
|
|
9038
|
+
targetUri
|
|
9039
|
+
};
|
|
9040
|
+
}
|
|
9041
|
+
for (const redaction of scrub.redactions) {
|
|
9042
|
+
messages.push(`PREVIEW redact: ${redaction.count}\xD7 ${redaction.name}`);
|
|
9043
|
+
}
|
|
9044
|
+
return {
|
|
9045
|
+
artifact,
|
|
9046
|
+
gitMessages: [],
|
|
9047
|
+
messages,
|
|
9048
|
+
previewContent: scrub.cleaned,
|
|
9049
|
+
sourcePath: resolvedSourcePath,
|
|
9050
|
+
targetPath,
|
|
9051
|
+
targetUri
|
|
9052
|
+
};
|
|
9053
|
+
}
|
|
9054
|
+
if (scrub.blocker) {
|
|
9055
|
+
throw new Error(
|
|
9056
|
+
`Refusing to share ${resolvedSourcePath}: possible ${scrub.blocker}. Strip the sensitive value or pass --redact for soft-leak patterns.`
|
|
9057
|
+
);
|
|
9058
|
+
}
|
|
9059
|
+
for (const redaction of scrub.redactions) {
|
|
9060
|
+
messages.push(`Redacted ${redaction.count}\xD7 ${redaction.name} before sharing.`);
|
|
9061
|
+
}
|
|
9062
|
+
const content = scrub.cleaned;
|
|
9063
|
+
const existingContent = await readFileIfExists(targetPath) ?? void 0;
|
|
9064
|
+
if (existingContent !== void 0 && existingContent !== content && options.force !== true) {
|
|
9065
|
+
throw new Error(
|
|
9066
|
+
`Shared artifact already exists with different content: ${portablePath(targetPath)}. Pass --force to replace it.`
|
|
9067
|
+
);
|
|
9068
|
+
}
|
|
9069
|
+
if (dryRun) {
|
|
9070
|
+
messages.push(`Would write shared artifact: ${portablePath(targetPath)}`);
|
|
9071
|
+
}
|
|
9072
|
+
const ov = await openVikingCliForMode(dryRun);
|
|
9073
|
+
const ovHasResource = !dryRun && await vikingResourceExists(ov, config, targetUri);
|
|
9074
|
+
await ensureSharedDirectoryChain(config, ov, targetUri, dryRun, { quiet: true });
|
|
9075
|
+
await writeMemoryFile(config, ov, targetUri, content, ovHasResource ? "replace" : "create", dryRun, { quiet: true });
|
|
9076
|
+
const message = options.message ?? `share: publish ${relativePath}`;
|
|
9077
|
+
const gitMessages = await publishShareGitChange(team.config.worktree, relativePath, message, {
|
|
9078
|
+
dryRun,
|
|
9079
|
+
push: options.push
|
|
9080
|
+
});
|
|
9081
|
+
return { artifact, gitMessages, messages, sourcePath: resolvedSourcePath, targetPath, targetUri };
|
|
9082
|
+
}
|
|
9083
|
+
async function runShareInstallArtifacts(config, options) {
|
|
9084
|
+
const result = await installSharedAgentArtifacts(config, options);
|
|
9085
|
+
if (result.syncedTeams.length > 0) {
|
|
9086
|
+
console.log(`Synced shared teams: ${result.syncedTeams.join(", ")}`);
|
|
9087
|
+
}
|
|
9088
|
+
for (const warning2 of result.warnings) {
|
|
9089
|
+
console.warn(`Warning: ${warning2}`);
|
|
9090
|
+
}
|
|
9091
|
+
for (const message of result.messages) {
|
|
9092
|
+
console.log(message);
|
|
9093
|
+
}
|
|
9094
|
+
}
|
|
9095
|
+
async function installSharedAgentArtifacts(config, options) {
|
|
9096
|
+
const syncResult = await maybeSyncSharedArtifacts(config, options);
|
|
9097
|
+
const team = await resolveTeam(config, options.team);
|
|
9098
|
+
const dryRun = options.dryRun === true || options.apply !== true;
|
|
9099
|
+
const allArtifacts = await collectSharedArtifacts(team.config.worktree, team.name);
|
|
9100
|
+
const artifacts = filterSharedArtifacts(allArtifacts, options);
|
|
9101
|
+
const messages = [];
|
|
9102
|
+
if (artifacts.length === 0) {
|
|
9103
|
+
const filters = sharedArtifactFilterLabel(options);
|
|
9104
|
+
if (filters) {
|
|
9105
|
+
throw new Error(`No shared agent artifacts found for team "${team.name}" matching ${filters}.`);
|
|
9106
|
+
}
|
|
9107
|
+
return {
|
|
9108
|
+
installedCount: 0,
|
|
9109
|
+
messages: [`No shared agent artifacts found for team "${team.name}".`],
|
|
9110
|
+
syncedTeams: syncResult.syncedTeams,
|
|
9111
|
+
team: team.name,
|
|
9112
|
+
warnings: syncResult.warnings
|
|
9113
|
+
};
|
|
9114
|
+
}
|
|
9115
|
+
if (options.name !== void 0 && artifacts.length > 1 && (options.agent === void 0 || options.kind === void 0)) {
|
|
9116
|
+
throw new Error(
|
|
9117
|
+
`Shared artifact "${options.name}" is ambiguous. Specify agent and kind. Matches: ${artifacts.map((artifact) => sharedArtifactLabel(artifact.artifact)).join(", ")}`
|
|
9118
|
+
);
|
|
9119
|
+
}
|
|
9120
|
+
let installedCount = 0;
|
|
9121
|
+
for (const artifact of artifacts) {
|
|
9122
|
+
const label = sharedArtifactLabel(artifact.artifact);
|
|
9123
|
+
const state = await sharedArtifactInstallState(artifact);
|
|
9124
|
+
if (dryRun) {
|
|
9125
|
+
const verb = sharedArtifactDryRunVerb(state.status, options.force === true);
|
|
9126
|
+
const suffix = sharedArtifactDryRunSuffix(state.status, options.force === true);
|
|
9127
|
+
messages.push(`${verb} ${label}: ${portablePath(artifact.installPath)}${suffix}`);
|
|
9128
|
+
continue;
|
|
9129
|
+
}
|
|
9130
|
+
if ((state.status === "local_modified" || state.status === "remote_changed_and_local_modified") && options.force !== true) {
|
|
9131
|
+
throw new Error(`Refusing to overwrite ${portablePath(artifact.installPath)}. Pass force=true or --force.`);
|
|
9132
|
+
}
|
|
9133
|
+
if (state.status === "current") {
|
|
9134
|
+
await writeSharedArtifactMetadata(artifact, state.sourceSha);
|
|
9135
|
+
messages.push(`Already installed ${label}: ${portablePath(artifact.installPath)}`);
|
|
9136
|
+
continue;
|
|
9137
|
+
}
|
|
9138
|
+
await ensureDirectory((0, import_node_path6.dirname)(artifact.installPath), false);
|
|
9139
|
+
await (0, import_promises5.writeFile)(artifact.installPath, state.sourceContent, { encoding: "utf8", mode: 384 });
|
|
9140
|
+
await writeSharedArtifactMetadata(artifact, state.sourceSha);
|
|
9141
|
+
installedCount += 1;
|
|
9142
|
+
messages.push(
|
|
9143
|
+
`${sharedArtifactInstallVerb(state.status, options.force === true)} ${label}: ${portablePath(artifact.installPath)}`
|
|
9144
|
+
);
|
|
9145
|
+
}
|
|
9146
|
+
return {
|
|
9147
|
+
installedCount,
|
|
9148
|
+
messages,
|
|
9149
|
+
syncedTeams: syncResult.syncedTeams,
|
|
9150
|
+
team: team.name,
|
|
9151
|
+
warnings: syncResult.warnings
|
|
9152
|
+
};
|
|
9153
|
+
}
|
|
8990
9154
|
async function runShareUnpublish(config, sourceUri, options) {
|
|
8991
9155
|
assertVikingUri(sourceUri);
|
|
8992
9156
|
const team = await resolveTeam(config, options.team);
|
|
@@ -9079,7 +9243,7 @@ async function runShareRename(config, options) {
|
|
|
9079
9243
|
console.log(`Would rename worktree: ${portablePath(oldTeam.config.worktree)} -> ${portablePath(newWorktree)}`);
|
|
9080
9244
|
console.log(`Would rename gitdir: ${portablePath(oldTeam.config.gitdir)} -> ${portablePath(newGitdir)}`);
|
|
9081
9245
|
console.log(`Would update git core.worktree for team "${newName}".`);
|
|
9082
|
-
console.log(`Would reindex shared
|
|
9246
|
+
console.log(`Would reindex shared context under team "${newName}" and remove old shared URI tree.`);
|
|
9083
9247
|
console.log(`Would write teams file: ${teamsFilePath(config)}`);
|
|
9084
9248
|
return;
|
|
9085
9249
|
}
|
|
@@ -9097,7 +9261,7 @@ async function runShareRename(config, options) {
|
|
|
9097
9261
|
false
|
|
9098
9262
|
);
|
|
9099
9263
|
console.log(`Renamed shared team "${oldTeam.name}" -> "${newName}".`);
|
|
9100
|
-
console.log(`Reindexed ${ingested} shared
|
|
9264
|
+
console.log(`Reindexed ${ingested} shared file(s).`);
|
|
9101
9265
|
}
|
|
9102
9266
|
async function runShareSetUrl(config, remoteUrl, options) {
|
|
9103
9267
|
if (!remoteUrl.trim()) {
|
|
@@ -9340,7 +9504,7 @@ async function walkMemoryFiles(root) {
|
|
|
9340
9504
|
}
|
|
9341
9505
|
const full = (0, import_node_path6.join)(path, entry.name);
|
|
9342
9506
|
if (entry.isDirectory()) {
|
|
9343
|
-
if (depth === 0 && !
|
|
9507
|
+
if (depth === 0 && !SHAREABLE_TOP_LEVEL_DIRS.includes(entry.name)) {
|
|
9344
9508
|
continue;
|
|
9345
9509
|
}
|
|
9346
9510
|
await visit(full, depth + 1);
|
|
@@ -9422,6 +9586,264 @@ function vikingUriToWorktreeRelative(config, uri, team) {
|
|
|
9422
9586
|
}
|
|
9423
9587
|
return uri.slice(prefix.length);
|
|
9424
9588
|
}
|
|
9589
|
+
async function isRegularFileNoSymlink(path) {
|
|
9590
|
+
try {
|
|
9591
|
+
const stat5 = await (0, import_promises5.lstat)(path);
|
|
9592
|
+
return stat5.isFile();
|
|
9593
|
+
} catch (_err) {
|
|
9594
|
+
return false;
|
|
9595
|
+
}
|
|
9596
|
+
}
|
|
9597
|
+
function inferShareArtifact(path, options) {
|
|
9598
|
+
const normalizedPath = path.split(import_node_path6.sep).join("/");
|
|
9599
|
+
const fileName = (0, import_node_path6.basename)(path);
|
|
9600
|
+
const lowerFileName = fileName.toLowerCase();
|
|
9601
|
+
const lowerPath = normalizedPath.toLowerCase();
|
|
9602
|
+
const inferredKind = lowerFileName === "skill.md" ? "skill" : lowerPath.includes("/.claude/commands/") && lowerFileName.endsWith(".md") ? "command" : void 0;
|
|
9603
|
+
const inferredAgent = lowerPath.includes("/.codex/skills/") ? "codex" : lowerPath.includes("/.claude/skills/") || lowerPath.includes("/.claude/commands/") ? "claude" : void 0;
|
|
9604
|
+
const extensionIndex = fileName.lastIndexOf(".");
|
|
9605
|
+
const stem = extensionIndex > 0 ? fileName.slice(0, extensionIndex) : fileName;
|
|
9606
|
+
const inferredName = lowerFileName === "skill.md" ? (0, import_node_path6.basename)((0, import_node_path6.dirname)(path)) : stem;
|
|
9607
|
+
const kind = options.kind ?? inferredKind;
|
|
9608
|
+
const agent = options.agent ?? inferredAgent;
|
|
9609
|
+
const name = options.name ?? inferredName;
|
|
9610
|
+
if (kind !== "skill" && kind !== "command") {
|
|
9611
|
+
throw new Error("Could not infer artifact kind. Pass --kind skill or --kind command.");
|
|
9612
|
+
}
|
|
9613
|
+
if (agent !== "codex" && agent !== "claude") {
|
|
9614
|
+
throw new Error("Could not infer artifact agent. Pass --agent codex or --agent claude.");
|
|
9615
|
+
}
|
|
9616
|
+
if (kind === "skill" && lowerFileName !== "skill.md") {
|
|
9617
|
+
throw new Error("Skill artifacts must point at a SKILL.md file.");
|
|
9618
|
+
}
|
|
9619
|
+
if (kind === "command" && !lowerFileName.endsWith(".md")) {
|
|
9620
|
+
throw new Error("Command artifacts must be Markdown files.");
|
|
9621
|
+
}
|
|
9622
|
+
if (kind === "command" && agent !== "claude") {
|
|
9623
|
+
throw new Error("Only Claude command artifacts are supported.");
|
|
9624
|
+
}
|
|
9625
|
+
if (name.trim().length === 0) {
|
|
9626
|
+
throw new Error("Artifact name cannot be empty.");
|
|
9627
|
+
}
|
|
9628
|
+
return { agent, kind, name: uriSegment(name) };
|
|
9629
|
+
}
|
|
9630
|
+
function sharedArtifactRelativePath(artifact) {
|
|
9631
|
+
if (artifact.kind === "skill") {
|
|
9632
|
+
return `${SHAREABLE_ARTIFACT_DIR}/skills/${artifact.agent}/${artifact.name}/SKILL.md`;
|
|
9633
|
+
}
|
|
9634
|
+
return `${SHAREABLE_ARTIFACT_DIR}/commands/${artifact.agent}/${artifact.name}.md`;
|
|
9635
|
+
}
|
|
9636
|
+
function sharedArtifactFromRelativePath(relativePath) {
|
|
9637
|
+
const parts = relativePath.split("/");
|
|
9638
|
+
if (parts[0] !== SHAREABLE_ARTIFACT_DIR) {
|
|
9639
|
+
return void 0;
|
|
9640
|
+
}
|
|
9641
|
+
if (parts.length === 5 && parts[1] === "skills" && (parts[2] === "codex" || parts[2] === "claude") && parts[4] === "SKILL.md") {
|
|
9642
|
+
return { agent: parts[2], kind: "skill", name: parts[3] };
|
|
9643
|
+
}
|
|
9644
|
+
if (parts.length === 4 && parts[1] === "commands" && parts[2] === "claude" && parts[3].endsWith(".md")) {
|
|
9645
|
+
return { agent: "claude", kind: "command", name: parts[3].slice(0, -".md".length) };
|
|
9646
|
+
}
|
|
9647
|
+
return void 0;
|
|
9648
|
+
}
|
|
9649
|
+
async function collectSharedArtifacts(worktree, team) {
|
|
9650
|
+
const root = (0, import_node_path6.join)(worktree, SHAREABLE_ARTIFACT_DIR);
|
|
9651
|
+
if (!await isDirectory(root)) {
|
|
9652
|
+
return [];
|
|
9653
|
+
}
|
|
9654
|
+
const out = [];
|
|
9655
|
+
async function visit(path) {
|
|
9656
|
+
const entries = await (0, import_promises5.readdir)(path, { withFileTypes: true });
|
|
9657
|
+
for (const entry of entries) {
|
|
9658
|
+
const full = (0, import_node_path6.join)(path, entry.name);
|
|
9659
|
+
if (entry.isDirectory()) {
|
|
9660
|
+
await visit(full);
|
|
9661
|
+
continue;
|
|
9662
|
+
}
|
|
9663
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
9664
|
+
continue;
|
|
9665
|
+
}
|
|
9666
|
+
const relativePath = (0, import_node_path6.relative)(worktree, full).split(import_node_path6.sep).join("/");
|
|
9667
|
+
const artifact = sharedArtifactFromRelativePath(relativePath);
|
|
9668
|
+
if (artifact === void 0) {
|
|
9669
|
+
continue;
|
|
9670
|
+
}
|
|
9671
|
+
out.push({
|
|
9672
|
+
artifact,
|
|
9673
|
+
installPath: sharedArtifactInstallPath(team, artifact),
|
|
9674
|
+
sourcePath: full,
|
|
9675
|
+
sourceRelativePath: relativePath,
|
|
9676
|
+
team
|
|
9677
|
+
});
|
|
9678
|
+
}
|
|
9679
|
+
}
|
|
9680
|
+
await visit(root);
|
|
9681
|
+
return out.sort((a, b) => a.sourcePath.localeCompare(b.sourcePath));
|
|
9682
|
+
}
|
|
9683
|
+
function filterSharedArtifacts(artifacts, options) {
|
|
9684
|
+
const name = options.name === void 0 ? void 0 : uriSegment(options.name);
|
|
9685
|
+
return artifacts.filter((artifact) => {
|
|
9686
|
+
if (options.agent !== void 0 && artifact.artifact.agent !== options.agent) {
|
|
9687
|
+
return false;
|
|
9688
|
+
}
|
|
9689
|
+
if (options.kind !== void 0 && artifact.artifact.kind !== options.kind) {
|
|
9690
|
+
return false;
|
|
9691
|
+
}
|
|
9692
|
+
if (name !== void 0 && artifact.artifact.name !== name) {
|
|
9693
|
+
return false;
|
|
9694
|
+
}
|
|
9695
|
+
return true;
|
|
9696
|
+
});
|
|
9697
|
+
}
|
|
9698
|
+
async function maybeSyncSharedArtifacts(config, options) {
|
|
9699
|
+
if (options.sync === false) {
|
|
9700
|
+
return { syncedTeams: [], warnings: [] };
|
|
9701
|
+
}
|
|
9702
|
+
return syncSharedReposBeforeAgentRead(config);
|
|
9703
|
+
}
|
|
9704
|
+
async function sharedArtifactInstallState(artifact) {
|
|
9705
|
+
const sourceContent = await (0, import_promises5.readFile)(artifact.sourcePath, "utf8");
|
|
9706
|
+
const sourceSha = sha256(sourceContent);
|
|
9707
|
+
const existingContent = await readFileIfExists(artifact.installPath) ?? void 0;
|
|
9708
|
+
if (existingContent === void 0) {
|
|
9709
|
+
return { sourceContent, sourceSha, status: "not_installed" };
|
|
9710
|
+
}
|
|
9711
|
+
const existingSha = sha256(existingContent);
|
|
9712
|
+
const metadata = await readSharedArtifactMetadata(artifact);
|
|
9713
|
+
if (existingSha === sourceSha) {
|
|
9714
|
+
return { existingContent, existingSha, metadata, sourceContent, sourceSha, status: "current" };
|
|
9715
|
+
}
|
|
9716
|
+
if (metadata === void 0) {
|
|
9717
|
+
return { existingContent, existingSha, sourceContent, sourceSha, status: "local_modified" };
|
|
9718
|
+
}
|
|
9719
|
+
const remoteChanged = metadata.sourceSha256 !== sourceSha;
|
|
9720
|
+
const localChanged = metadata.installedSha256 !== existingSha;
|
|
9721
|
+
if (remoteChanged && localChanged) {
|
|
9722
|
+
return {
|
|
9723
|
+
existingContent,
|
|
9724
|
+
existingSha,
|
|
9725
|
+
metadata,
|
|
9726
|
+
sourceContent,
|
|
9727
|
+
sourceSha,
|
|
9728
|
+
status: "remote_changed_and_local_modified"
|
|
9729
|
+
};
|
|
9730
|
+
}
|
|
9731
|
+
if (remoteChanged) {
|
|
9732
|
+
return { existingContent, existingSha, metadata, sourceContent, sourceSha, status: "update_available" };
|
|
9733
|
+
}
|
|
9734
|
+
return { existingContent, existingSha, metadata, sourceContent, sourceSha, status: "local_modified" };
|
|
9735
|
+
}
|
|
9736
|
+
async function readSharedArtifactMetadata(artifact) {
|
|
9737
|
+
const raw = await readFileIfExists(sharedArtifactMetadataPath(artifact));
|
|
9738
|
+
if (raw === void 0) {
|
|
9739
|
+
return void 0;
|
|
9740
|
+
}
|
|
9741
|
+
const parsed = parseJsonConfigObject(raw);
|
|
9742
|
+
if (parsed === void 0 || parsed.version !== ARTIFACT_INSTALL_METADATA_VERSION) {
|
|
9743
|
+
return void 0;
|
|
9744
|
+
}
|
|
9745
|
+
const artifactValue = parsed.artifact;
|
|
9746
|
+
if (typeof parsed.team !== "string" || typeof parsed.source !== "string" || typeof parsed.sourceSha256 !== "string" || typeof parsed.installedSha256 !== "string" || typeof parsed.installedAt !== "string" || typeof artifactValue !== "object" || artifactValue === null || Array.isArray(artifactValue)) {
|
|
9747
|
+
return void 0;
|
|
9748
|
+
}
|
|
9749
|
+
const metadataArtifact = artifactValue;
|
|
9750
|
+
if (metadataArtifact.agent !== artifact.artifact.agent || metadataArtifact.kind !== artifact.artifact.kind || metadataArtifact.name !== artifact.artifact.name) {
|
|
9751
|
+
return void 0;
|
|
9752
|
+
}
|
|
9753
|
+
return {
|
|
9754
|
+
artifact: artifact.artifact,
|
|
9755
|
+
installedAt: parsed.installedAt,
|
|
9756
|
+
installedSha256: parsed.installedSha256,
|
|
9757
|
+
source: parsed.source,
|
|
9758
|
+
sourceSha256: parsed.sourceSha256,
|
|
9759
|
+
team: parsed.team,
|
|
9760
|
+
version: ARTIFACT_INSTALL_METADATA_VERSION
|
|
9761
|
+
};
|
|
9762
|
+
}
|
|
9763
|
+
async function writeSharedArtifactMetadata(artifact, sourceSha) {
|
|
9764
|
+
const metadata = {
|
|
9765
|
+
artifact: artifact.artifact,
|
|
9766
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9767
|
+
installedSha256: sourceSha,
|
|
9768
|
+
source: artifact.sourceRelativePath,
|
|
9769
|
+
sourceSha256: sourceSha,
|
|
9770
|
+
team: artifact.team,
|
|
9771
|
+
version: ARTIFACT_INSTALL_METADATA_VERSION
|
|
9772
|
+
};
|
|
9773
|
+
const metadataPath = sharedArtifactMetadataPath(artifact);
|
|
9774
|
+
await ensureDirectory((0, import_node_path6.dirname)(metadataPath), false);
|
|
9775
|
+
await (0, import_promises5.writeFile)(metadataPath, `${JSON.stringify(metadata, void 0, 2)}
|
|
9776
|
+
`, { encoding: "utf8", mode: 384 });
|
|
9777
|
+
}
|
|
9778
|
+
function sharedArtifactMetadataPath(artifact) {
|
|
9779
|
+
return `${artifact.installPath}.threadnote-install.json`;
|
|
9780
|
+
}
|
|
9781
|
+
function sharedArtifactDryRunVerb(status, force) {
|
|
9782
|
+
switch (status) {
|
|
9783
|
+
case "not_installed":
|
|
9784
|
+
return "Would install";
|
|
9785
|
+
case "current":
|
|
9786
|
+
return "Already installed";
|
|
9787
|
+
case "update_available":
|
|
9788
|
+
return "Would update";
|
|
9789
|
+
case "local_modified":
|
|
9790
|
+
case "remote_changed_and_local_modified":
|
|
9791
|
+
return force ? "Would replace" : "Would skip modified";
|
|
9792
|
+
}
|
|
9793
|
+
}
|
|
9794
|
+
function sharedArtifactDryRunSuffix(status, force) {
|
|
9795
|
+
if ((status === "local_modified" || status === "remote_changed_and_local_modified") && !force) {
|
|
9796
|
+
return " (pass --force to replace local changes)";
|
|
9797
|
+
}
|
|
9798
|
+
return "";
|
|
9799
|
+
}
|
|
9800
|
+
function sharedArtifactInstallVerb(status, force) {
|
|
9801
|
+
if (force && (status === "local_modified" || status === "remote_changed_and_local_modified")) {
|
|
9802
|
+
return "Replaced";
|
|
9803
|
+
}
|
|
9804
|
+
if (status === "update_available") {
|
|
9805
|
+
return "Updated";
|
|
9806
|
+
}
|
|
9807
|
+
return "Installed";
|
|
9808
|
+
}
|
|
9809
|
+
function sharedArtifactFilterLabel(options) {
|
|
9810
|
+
const filters = [];
|
|
9811
|
+
if (options.kind !== void 0) {
|
|
9812
|
+
filters.push(`kind=${options.kind}`);
|
|
9813
|
+
}
|
|
9814
|
+
if (options.agent !== void 0) {
|
|
9815
|
+
filters.push(`agent=${options.agent}`);
|
|
9816
|
+
}
|
|
9817
|
+
if (options.name !== void 0) {
|
|
9818
|
+
filters.push(`name=${uriSegment(options.name)}`);
|
|
9819
|
+
}
|
|
9820
|
+
return filters.join(", ");
|
|
9821
|
+
}
|
|
9822
|
+
function sharedArtifactLabel(artifact) {
|
|
9823
|
+
return `${artifact.kind} ${artifact.agent}/${artifact.name}`;
|
|
9824
|
+
}
|
|
9825
|
+
function sharedArtifactInstallPath(team, artifact) {
|
|
9826
|
+
if (artifact.kind === "skill" && artifact.agent === "codex") {
|
|
9827
|
+
return (0, import_node_path6.join)((0, import_node_os4.homedir)(), ".codex", "skills", "threadnote", team, artifact.name, "SKILL.md");
|
|
9828
|
+
}
|
|
9829
|
+
if (artifact.kind === "skill" && artifact.agent === "claude") {
|
|
9830
|
+
return (0, import_node_path6.join)((0, import_node_os4.homedir)(), ".claude", "skills", "threadnote", team, artifact.name, "SKILL.md");
|
|
9831
|
+
}
|
|
9832
|
+
return (0, import_node_path6.join)((0, import_node_os4.homedir)(), ".claude", "commands", "threadnote", team, `${artifact.name}.md`);
|
|
9833
|
+
}
|
|
9834
|
+
function printShareArtifactResult(result, preview) {
|
|
9835
|
+
for (const message of result.messages) {
|
|
9836
|
+
console.log(message);
|
|
9837
|
+
}
|
|
9838
|
+
for (const gitMessage of result.gitMessages) {
|
|
9839
|
+
console.log(gitMessage);
|
|
9840
|
+
}
|
|
9841
|
+
if (preview && result.previewContent !== void 0) {
|
|
9842
|
+
console.log("-----BEGIN PREVIEW-----");
|
|
9843
|
+
console.log(result.previewContent);
|
|
9844
|
+
console.log("-----END PREVIEW-----");
|
|
9845
|
+
}
|
|
9846
|
+
}
|
|
9425
9847
|
function stripPersonalProvenance(content) {
|
|
9426
9848
|
const lines = content.split("\n");
|
|
9427
9849
|
let headerEnd = lines.length;
|
|
@@ -9470,9 +9892,9 @@ async function ensureSharedDirectoryChain(config, ov, memoryUri, dryRun, options
|
|
|
9470
9892
|
continue;
|
|
9471
9893
|
}
|
|
9472
9894
|
if (options.quiet === true) {
|
|
9473
|
-
await runCommand(ov, withIdentity(config, ["mkdir", uri, "--description", "Threadnote shared
|
|
9895
|
+
await runCommand(ov, withIdentity(config, ["mkdir", uri, "--description", "Threadnote shared context."]));
|
|
9474
9896
|
} else {
|
|
9475
|
-
await maybeRun(false, ov, withIdentity(config, ["mkdir", uri, "--description", "Threadnote shared
|
|
9897
|
+
await maybeRun(false, ov, withIdentity(config, ["mkdir", uri, "--description", "Threadnote shared context."]));
|
|
9476
9898
|
}
|
|
9477
9899
|
}
|
|
9478
9900
|
}
|
|
@@ -9701,7 +10123,7 @@ async function applyChangesToOpenViking(config, team, changes, options = {}) {
|
|
|
9701
10123
|
continue;
|
|
9702
10124
|
}
|
|
9703
10125
|
const firstSegment = change.relativePath.split("/")[0];
|
|
9704
|
-
if (!
|
|
10126
|
+
if (!SHAREABLE_TOP_LEVEL_DIRS.includes(firstSegment)) {
|
|
9705
10127
|
continue;
|
|
9706
10128
|
}
|
|
9707
10129
|
const uri = workfileToVikingUri(config, team, change.path);
|
|
@@ -9960,14 +10382,17 @@ async function runRecall(config, options) {
|
|
|
9960
10382
|
if (inferredUri) {
|
|
9961
10383
|
console.log(`Recall scope: ${inferredUri}`);
|
|
9962
10384
|
}
|
|
9963
|
-
const
|
|
10385
|
+
const includeArchived = options.includeArchived === true;
|
|
10386
|
+
const passes = [
|
|
10387
|
+
await recallSearchHits(config, ov, searchArgs(inferredUri), { dryRun, includeArchived })
|
|
10388
|
+
];
|
|
9964
10389
|
if (options.project && project) {
|
|
9965
10390
|
const projectMemoryUri = `viking://user/${uriSegment(config.user)}/memories/durable/projects/${uriSegment(project.name)}`;
|
|
9966
|
-
passes.push(await recallSearchHits(config, ov, searchArgs(projectMemoryUri), { dryRun }));
|
|
10391
|
+
passes.push(await recallSearchHits(config, ov, searchArgs(projectMemoryUri), { dryRun, includeArchived }));
|
|
9967
10392
|
}
|
|
9968
10393
|
const seededUri = project ? trimTrailingSlash(project.uri) : void 0;
|
|
9969
10394
|
if (seededUri?.startsWith("viking://") && seededUri !== inferredUri && !options.uri && options.inferScope !== false) {
|
|
9970
|
-
passes.push(await recallSearchHits(config, ov, searchArgs(seededUri), { dryRun }));
|
|
10395
|
+
passes.push(await recallSearchHits(config, ov, searchArgs(seededUri), { dryRun, includeArchived }));
|
|
9971
10396
|
}
|
|
9972
10397
|
const recallOutputs = [];
|
|
9973
10398
|
const semanticSection = formatRecallHits(mergeRecallHits(passes), nodeLimit ?? 12);
|
|
@@ -9978,7 +10403,7 @@ ${semanticSection}`);
|
|
|
9978
10403
|
}
|
|
9979
10404
|
const exactOutput = await printExactMemoryMatches(config, ov, query, {
|
|
9980
10405
|
dryRun,
|
|
9981
|
-
includeArchived
|
|
10406
|
+
includeArchived,
|
|
9982
10407
|
project
|
|
9983
10408
|
});
|
|
9984
10409
|
if (exactOutput) {
|
|
@@ -10002,7 +10427,7 @@ async function recallSearchHits(config, ov, args, options) {
|
|
|
10002
10427
|
console.log(`WARN recall search failed: ${result.stderr.trim() || result.stdout.trim() || "ov search error"}`);
|
|
10003
10428
|
return [];
|
|
10004
10429
|
}
|
|
10005
|
-
return parseRecallHits(result.stdout);
|
|
10430
|
+
return parseRecallHits(result.stdout, { includeArchived: options.includeArchived });
|
|
10006
10431
|
}
|
|
10007
10432
|
function stripAdvancedSearchFlags(args) {
|
|
10008
10433
|
const stripped = [];
|
|
@@ -12442,6 +12867,14 @@ async function runInstall(config, options) {
|
|
|
12442
12867
|
serverInstallRan = true;
|
|
12443
12868
|
}
|
|
12444
12869
|
const resolvedServerPath = serverInstallRan ? await findOpenVikingServer() : serverPath;
|
|
12870
|
+
if (serverInstallRan && !resolvedServerPath && !dryRun) {
|
|
12871
|
+
const message = `OpenViking install ran but ${OPENVIKING_SERVER_COMMAND} was not found on PATH, in the uv tool bin dir, or ~/.local/bin. Re-run \`threadnote install --force\` (it streams the full build), then \`threadnote doctor\`.`;
|
|
12872
|
+
if (options.requireServerBinary === false) {
|
|
12873
|
+
console.warn(`WARN ${message}`);
|
|
12874
|
+
} else {
|
|
12875
|
+
throw new Error(message);
|
|
12876
|
+
}
|
|
12877
|
+
}
|
|
12445
12878
|
if (resolvedServerPath && !dryRun) {
|
|
12446
12879
|
await maybePrintOpenVikingPathHint(resolvedServerPath);
|
|
12447
12880
|
}
|
|
@@ -12478,6 +12911,7 @@ async function runRepair(config, options) {
|
|
|
12478
12911
|
packageManager: options.packageManager,
|
|
12479
12912
|
printNextSteps: false,
|
|
12480
12913
|
repairInvalidConfigs: true,
|
|
12914
|
+
requireServerBinary: false,
|
|
12481
12915
|
start: false
|
|
12482
12916
|
});
|
|
12483
12917
|
await repairManifest(config, dryRun);
|
|
@@ -12832,7 +13266,11 @@ async function openVikingServerCheck() {
|
|
|
12832
13266
|
const name = OPENVIKING_SERVER_COMMAND;
|
|
12833
13267
|
const executable = await findOpenVikingServer();
|
|
12834
13268
|
if (!executable) {
|
|
12835
|
-
return {
|
|
13269
|
+
return {
|
|
13270
|
+
name,
|
|
13271
|
+
status: "fail",
|
|
13272
|
+
detail: "missing; run `threadnote install` to fetch it via uv or pipx (local-embed may compile from source on first install)"
|
|
13273
|
+
};
|
|
12836
13274
|
}
|
|
12837
13275
|
const result = await runCommand(executable, ["--help"], { allowFailure: true });
|
|
12838
13276
|
const onPath = await findExecutable([OPENVIKING_SERVER_COMMAND]);
|
|
@@ -13066,7 +13504,36 @@ async function runInstallCommands(config, preferred, force, dryRun) {
|
|
|
13066
13504
|
}
|
|
13067
13505
|
const installCommands = await getInstallCommands(config, manager, force);
|
|
13068
13506
|
for (const installCommand of installCommands) {
|
|
13069
|
-
|
|
13507
|
+
if (dryRun) {
|
|
13508
|
+
await maybeRun(true, installCommand.executable, installCommand.args);
|
|
13509
|
+
continue;
|
|
13510
|
+
}
|
|
13511
|
+
console.log(`Running: ${formatShellCommand(installCommand.executable, installCommand.args)}`);
|
|
13512
|
+
const exitCode = await runInteractive(installCommand.executable, installCommand.args);
|
|
13513
|
+
if (exitCode !== 0) {
|
|
13514
|
+
printOpenVikingInstallFailureHelp(installCommand);
|
|
13515
|
+
throw new Error(`${formatShellCommand(installCommand.executable, installCommand.args)} exited with ${exitCode}.`);
|
|
13516
|
+
}
|
|
13517
|
+
}
|
|
13518
|
+
}
|
|
13519
|
+
function printOpenVikingInstallFailureHelp(failedCommand) {
|
|
13520
|
+
console.error("");
|
|
13521
|
+
console.error("OpenViking install did not complete.");
|
|
13522
|
+
console.error(
|
|
13523
|
+
"openviking[local-embed] includes llama-cpp-python, which compiles from source when no prebuilt wheel matches your Python/platform \u2014 that build can run 10-20 minutes and is memory-heavy, so it may be killed by the OS (out of memory) or look stuck."
|
|
13524
|
+
);
|
|
13525
|
+
console.error("Re-run it directly to see full output without any wrapper timeout:");
|
|
13526
|
+
console.error(` ${formatShellCommand(failedCommand.executable, failedCommand.args)}`);
|
|
13527
|
+
console.error("Cap memory use during the compile with: CMAKE_BUILD_PARALLEL_LEVEL=2 <command above>");
|
|
13528
|
+
if (failedCommand.executable === "uv") {
|
|
13529
|
+
if (failedCommand.args.includes("--python")) {
|
|
13530
|
+
console.error(
|
|
13531
|
+
"If uv could not fetch a managed CPython (offline or restricted network), drop the version pin and retry: THREADNOTE_OPENVIKING_PYTHON= threadnote install --force"
|
|
13532
|
+
);
|
|
13533
|
+
}
|
|
13534
|
+
console.error("If it was killed mid-build, clear the partial install first, then retry:");
|
|
13535
|
+
console.error(" uv cache clean");
|
|
13536
|
+
console.error(' rm -rf "$(uv tool dir)/openviking"');
|
|
13070
13537
|
}
|
|
13071
13538
|
}
|
|
13072
13539
|
async function offerToInstallUv() {
|
|
@@ -13147,12 +13614,33 @@ async function getPythonSystemCertificatesInstallCommand(serverPath) {
|
|
|
13147
13614
|
}
|
|
13148
13615
|
return { executable: pythonPath, args: ["-m", "pip", "install", PYTHON_SYSTEM_CERTS_PACKAGE] };
|
|
13149
13616
|
}
|
|
13617
|
+
function localEmbedWheelIndexUrl() {
|
|
13618
|
+
const override = process.env.THREADNOTE_LLAMA_WHEEL_INDEX;
|
|
13619
|
+
if (override !== void 0) {
|
|
13620
|
+
return override.trim() === "" ? void 0 : override.trim();
|
|
13621
|
+
}
|
|
13622
|
+
const base = "https://abetlen.github.io/llama-cpp-python/whl";
|
|
13623
|
+
return (0, import_node_os6.platform)() === "darwin" ? `${base}/metal` : `${base}/cpu`;
|
|
13624
|
+
}
|
|
13625
|
+
function openVikingToolPython() {
|
|
13626
|
+
const override = process.env.THREADNOTE_OPENVIKING_PYTHON;
|
|
13627
|
+
if (override !== void 0) {
|
|
13628
|
+
return override.trim() === "" ? void 0 : override.trim();
|
|
13629
|
+
}
|
|
13630
|
+
return OPENVIKING_TOOL_PYTHON;
|
|
13631
|
+
}
|
|
13150
13632
|
async function getInstallCommands(config, preferred, force) {
|
|
13151
13633
|
const packageSpec = `${OPENVIKING_PACKAGE_NAME}==${config.openVikingVersion}`;
|
|
13634
|
+
const wheelIndex = localEmbedWheelIndexUrl();
|
|
13152
13635
|
const manager = preferred ?? await detectPackageManager();
|
|
13153
13636
|
if (manager === "pipx") {
|
|
13637
|
+
const installArgs = force ? ["install", "--force"] : ["install"];
|
|
13638
|
+
if (wheelIndex) {
|
|
13639
|
+
installArgs.push("--pip-args", `--extra-index-url ${wheelIndex}`);
|
|
13640
|
+
}
|
|
13641
|
+
installArgs.push(packageSpec);
|
|
13154
13642
|
return [
|
|
13155
|
-
{ executable: "pipx", args:
|
|
13643
|
+
{ executable: "pipx", args: installArgs },
|
|
13156
13644
|
{
|
|
13157
13645
|
executable: "pipx",
|
|
13158
13646
|
args: force ? ["inject", "--force", "openviking", PYTHON_SYSTEM_CERTS_PACKAGE] : ["inject", "openviking", PYTHON_SYSTEM_CERTS_PACKAGE]
|
|
@@ -13160,7 +13648,16 @@ async function getInstallCommands(config, preferred, force) {
|
|
|
13160
13648
|
];
|
|
13161
13649
|
}
|
|
13162
13650
|
if (manager === "uv") {
|
|
13163
|
-
const
|
|
13651
|
+
const toolPython = openVikingToolPython();
|
|
13652
|
+
const uvArgs = [
|
|
13653
|
+
"tool",
|
|
13654
|
+
"install",
|
|
13655
|
+
"--native-tls",
|
|
13656
|
+
...toolPython ? ["--python", toolPython] : [],
|
|
13657
|
+
"--with",
|
|
13658
|
+
PYTHON_SYSTEM_CERTS_PACKAGE,
|
|
13659
|
+
...wheelIndex ? ["--extra-index-url", wheelIndex] : []
|
|
13660
|
+
];
|
|
13164
13661
|
return [
|
|
13165
13662
|
{
|
|
13166
13663
|
executable: "uv",
|
|
@@ -13172,6 +13669,9 @@ async function getInstallCommands(config, preferred, force) {
|
|
|
13172
13669
|
if (force) {
|
|
13173
13670
|
pipArgs.push("--upgrade", "--force-reinstall");
|
|
13174
13671
|
}
|
|
13672
|
+
if (wheelIndex) {
|
|
13673
|
+
pipArgs.push("--extra-index-url", wheelIndex);
|
|
13674
|
+
}
|
|
13175
13675
|
pipArgs.push(PYTHON_SYSTEM_CERTS_PACKAGE);
|
|
13176
13676
|
pipArgs.push(packageSpec);
|
|
13177
13677
|
return [{ executable: "python3", args: pipArgs }];
|
|
@@ -14690,7 +15190,7 @@ async function main() {
|
|
|
14690
15190
|
program2.command("migrate-lifecycle").description("Move clear legacy handoff memories into lifecycle-aware archive paths").option("--apply", "Perform the migration; without this, prints a dry run").option("--dry-run", "Print migration actions without writing or removing memories").option("--limit <count>", "Maximum number of legacy handoffs to migrate").action(async (options) => {
|
|
14691
15191
|
await runMigrateLifecycle(getRuntimeConfig(program2), options);
|
|
14692
15192
|
});
|
|
14693
|
-
program2.command("recall").description("Search shared OpenViking context").requiredOption("--query <query>", "Search query").option("--dry-run", "Print ov command without searching").option("--include-archived", "Include archived memories in
|
|
15193
|
+
program2.command("recall").description("Search shared OpenViking context").requiredOption("--query <query>", "Search query").option("--dry-run", "Print ov command without searching").option("--include-archived", "Include archived memories in recall results").option("-n, --node-limit <count>", "Maximum number of search results").option("--no-infer-scope", "Disable query-based scope inference").option("--project <name>", "Prioritize a project: add a scoped pass over its memories alongside the global search").option("--threshold <score>", "Minimum relevance score 0-1 (default 0.45); lower to broaden when recall is empty").option("--uri <uri>", "Restrict search to a viking:// URI").action(async (options) => {
|
|
14694
15194
|
await runRecall(getRuntimeConfig(program2), options);
|
|
14695
15195
|
});
|
|
14696
15196
|
program2.command("compact").description("Plan or apply scoped memory hygiene for active personal memories").requiredOption("--project <name>", "Project/repo namespace to inspect").option("--apply", "Apply the compact plan; without this, prints a dry run").option("--dry-run", "Print the compact plan without changing anything").option("--kind <kind>", "durable, handoff, or incident", parseCompactKind).option("--topic <name>", "Stable topic name to inspect").action(async (options) => {
|
|
@@ -14733,6 +15233,15 @@ async function main() {
|
|
|
14733
15233
|
).action(async (uri, options) => {
|
|
14734
15234
|
await runSharePublish(getRuntimeConfig(program2), uri, options);
|
|
14735
15235
|
});
|
|
15236
|
+
share.command("publish-artifact").description("Publish a local Codex/Claude skill or Claude command into the shared team repo").argument("<path>", "Path to SKILL.md or a Claude command markdown file").option("--team <name>", "Team name; defaults to the configured default team").option("--agent <agent>", "Agent owner when path inference is ambiguous: codex or claude").option("--kind <kind>", "Artifact kind when path inference is ambiguous: skill or command").option("--name <name>", "Shared artifact name; defaults to skill directory or command file stem").option("--message <text>", "Commit message override").option("--force", "Replace an existing shared artifact with different content").option("--no-push", "Skip the push step").option("--dry-run", "Print actions without running them").option("--preview", "Print the exact artifact bytes that would land in the shared git repo").option(
|
|
15237
|
+
"--redact",
|
|
15238
|
+
"Replace soft-leak matches (local paths) with placeholders and continue; credentials still block"
|
|
15239
|
+
).action(async (path, options) => {
|
|
15240
|
+
await runSharePublishArtifact(getRuntimeConfig(program2), path, options);
|
|
15241
|
+
});
|
|
15242
|
+
share.command("install-artifacts").description("Install shared Codex/Claude skills and Claude commands from a team repo").option("--team <name>", "Team name; defaults to the configured default team").option("--agent <agent>", "Install only artifacts for this agent: codex or claude").option("--kind <kind>", "Install only artifacts of this kind: skill or command").option("--name <name>", "Install only this shared artifact name").option("--apply", "Actually write files into local agent skill/command directories").option("--force", "Replace existing installed artifacts with different content").option("--no-sync", "Skip pulling shared team updates before listing/installing artifacts").option("--dry-run", "Preview install actions without writing files").action(async (options) => {
|
|
15243
|
+
await runShareInstallArtifacts(getRuntimeConfig(program2), options);
|
|
15244
|
+
});
|
|
14736
15245
|
share.command("unpublish").description("Pull a shared memory back into the personal namespace, commit removal and push").argument("<viking-uri>", "viking:// memory URI inside a team shared subtree").option("--team <name>", "Team name; defaults to the configured default team").option("--message <text>", "Commit message override").option("--no-push", "Skip the push step").option("--dry-run", "Print actions without running them").action(async (uri, options) => {
|
|
14737
15246
|
await runShareUnpublish(getRuntimeConfig(program2), uri, options);
|
|
14738
15247
|
});
|