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/mcp_server.cjs
CHANGED
|
@@ -35953,6 +35953,13 @@ async function isFile(path) {
|
|
|
35953
35953
|
return false;
|
|
35954
35954
|
}
|
|
35955
35955
|
}
|
|
35956
|
+
async function isDirectory(path) {
|
|
35957
|
+
try {
|
|
35958
|
+
return (await (0, import_promises.stat)(path)).isDirectory();
|
|
35959
|
+
} catch (_err) {
|
|
35960
|
+
return false;
|
|
35961
|
+
}
|
|
35962
|
+
}
|
|
35956
35963
|
async function readFileIfExists(path) {
|
|
35957
35964
|
try {
|
|
35958
35965
|
return await (0, import_promises.readFile)(path, "utf8");
|
|
@@ -35976,6 +35983,17 @@ function expandPath(path) {
|
|
|
35976
35983
|
}
|
|
35977
35984
|
return (0, import_node_path.isAbsolute)(path) ? path : (0, import_node_path.resolve)(getInvocationCwd(), path);
|
|
35978
35985
|
}
|
|
35986
|
+
function portablePath(path) {
|
|
35987
|
+
const home = (0, import_node_os.homedir)();
|
|
35988
|
+
const resolvedPath = (0, import_node_path.resolve)(path);
|
|
35989
|
+
if (resolvedPath === home) {
|
|
35990
|
+
return "~";
|
|
35991
|
+
}
|
|
35992
|
+
if (resolvedPath.startsWith(`${home}${import_node_path.sep}`)) {
|
|
35993
|
+
return `~/${resolvedPath.slice(home.length + 1).split(import_node_path.sep).join("/")}`;
|
|
35994
|
+
}
|
|
35995
|
+
return resolvedPath;
|
|
35996
|
+
}
|
|
35979
35997
|
function getInvocationCwd() {
|
|
35980
35998
|
return process.env.THREADNOTE_CALLER_CWD ?? process.cwd();
|
|
35981
35999
|
}
|
|
@@ -36134,7 +36152,13 @@ function recallSnippet(value) {
|
|
|
36134
36152
|
const oneLine = value.replace(/\s+/g, " ").trim();
|
|
36135
36153
|
return oneLine.length > 180 ? `${oneLine.slice(0, 180)}\u2026` : oneLine;
|
|
36136
36154
|
}
|
|
36137
|
-
function
|
|
36155
|
+
function isArchivedMemoryUri(uri) {
|
|
36156
|
+
const documentUri = uri.replace(/#.*$/, "");
|
|
36157
|
+
return /^viking:\/\/user\/[^/]+\/memories\/(?:durable|handoffs|incidents|preferences|smoke)\/archived(?:\/|$)/.test(
|
|
36158
|
+
documentUri
|
|
36159
|
+
);
|
|
36160
|
+
}
|
|
36161
|
+
function parseRecallHits(output, options = {}) {
|
|
36138
36162
|
const start = output.search(/^\{/m);
|
|
36139
36163
|
if (start < 0) {
|
|
36140
36164
|
return [];
|
|
@@ -36155,6 +36179,9 @@ function parseRecallHits(output) {
|
|
|
36155
36179
|
if (!isJsonObject(item) || typeof item.uri !== "string" || isSummarySidecarUri(item.uri)) {
|
|
36156
36180
|
continue;
|
|
36157
36181
|
}
|
|
36182
|
+
if (options.includeArchived !== true && isArchivedMemoryUri(item.uri)) {
|
|
36183
|
+
continue;
|
|
36184
|
+
}
|
|
36158
36185
|
hits.push({
|
|
36159
36186
|
contextType: typeof item.context_type === "string" ? item.context_type : "result",
|
|
36160
36187
|
score: typeof item.score === "number" ? item.score : 0,
|
|
@@ -37073,6 +37100,9 @@ var import_node_path4 = require("node:path");
|
|
|
37073
37100
|
var TEAMS_FILE_VERSION = 1;
|
|
37074
37101
|
var SHARED_SEGMENT = "shared";
|
|
37075
37102
|
var SHAREABLE_MEMORY_KIND_DIRS = ["durable"];
|
|
37103
|
+
var SHAREABLE_ARTIFACT_DIR = "agent-artifacts";
|
|
37104
|
+
var SHAREABLE_TOP_LEVEL_DIRS = [...SHAREABLE_MEMORY_KIND_DIRS, SHAREABLE_ARTIFACT_DIR];
|
|
37105
|
+
var ARTIFACT_INSTALL_METADATA_VERSION = 1;
|
|
37076
37106
|
var AUTO_SHARE_FETCH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
37077
37107
|
var DEFAULT_GIT_REMOTE_NAME = "origin";
|
|
37078
37108
|
var SCRUBBER_PATTERNS = [
|
|
@@ -37392,6 +37422,155 @@ async function runGitCommand(dryRun, git, args, failureLabel) {
|
|
|
37392
37422
|
}
|
|
37393
37423
|
return result;
|
|
37394
37424
|
}
|
|
37425
|
+
async function shareAgentArtifact(config2, sourcePath, options) {
|
|
37426
|
+
const team = await resolveTeam(config2, options.team);
|
|
37427
|
+
const dryRun = options.dryRun === true;
|
|
37428
|
+
const preview = options.preview === true;
|
|
37429
|
+
const resolvedSourcePath = expandPath(sourcePath);
|
|
37430
|
+
if (!await isRegularFileNoSymlink(resolvedSourcePath)) {
|
|
37431
|
+
throw new Error(`Agent artifact source is not a regular file: ${resolvedSourcePath}`);
|
|
37432
|
+
}
|
|
37433
|
+
const artifact = inferShareArtifact(resolvedSourcePath, options);
|
|
37434
|
+
const rawContent = await (0, import_promises4.readFile)(resolvedSourcePath, "utf8");
|
|
37435
|
+
if (!rawContent.trim()) {
|
|
37436
|
+
throw new Error(`Refusing to share empty agent artifact: ${resolvedSourcePath}`);
|
|
37437
|
+
}
|
|
37438
|
+
const scrub = applyScrubber(rawContent, { redact: options.redact === true });
|
|
37439
|
+
const relativePath = sharedArtifactRelativePath(artifact);
|
|
37440
|
+
const targetPath = (0, import_node_path4.join)(team.config.worktree, ...relativePath.split("/"));
|
|
37441
|
+
const targetUri = workfileToVikingUri(config2, team.config, targetPath);
|
|
37442
|
+
const messages = [
|
|
37443
|
+
`${preview ? "Previewing" : dryRun ? "Would share" : "Sharing"} ${artifact.kind} ${artifact.agent}/${artifact.name}`,
|
|
37444
|
+
`Source: ${portablePath(resolvedSourcePath)}`,
|
|
37445
|
+
`Destination: ${targetUri}`
|
|
37446
|
+
];
|
|
37447
|
+
if (preview) {
|
|
37448
|
+
if (scrub.blocker) {
|
|
37449
|
+
messages.push(`PREVIEW BLOCKED: ${scrub.blocker}. Strip the sensitive value or pass --redact.`);
|
|
37450
|
+
return {
|
|
37451
|
+
artifact,
|
|
37452
|
+
gitMessages: [],
|
|
37453
|
+
messages,
|
|
37454
|
+
sourcePath: resolvedSourcePath,
|
|
37455
|
+
targetPath,
|
|
37456
|
+
targetUri
|
|
37457
|
+
};
|
|
37458
|
+
}
|
|
37459
|
+
for (const redaction of scrub.redactions) {
|
|
37460
|
+
messages.push(`PREVIEW redact: ${redaction.count}\xD7 ${redaction.name}`);
|
|
37461
|
+
}
|
|
37462
|
+
return {
|
|
37463
|
+
artifact,
|
|
37464
|
+
gitMessages: [],
|
|
37465
|
+
messages,
|
|
37466
|
+
previewContent: scrub.cleaned,
|
|
37467
|
+
sourcePath: resolvedSourcePath,
|
|
37468
|
+
targetPath,
|
|
37469
|
+
targetUri
|
|
37470
|
+
};
|
|
37471
|
+
}
|
|
37472
|
+
if (scrub.blocker) {
|
|
37473
|
+
throw new Error(
|
|
37474
|
+
`Refusing to share ${resolvedSourcePath}: possible ${scrub.blocker}. Strip the sensitive value or pass --redact for soft-leak patterns.`
|
|
37475
|
+
);
|
|
37476
|
+
}
|
|
37477
|
+
for (const redaction of scrub.redactions) {
|
|
37478
|
+
messages.push(`Redacted ${redaction.count}\xD7 ${redaction.name} before sharing.`);
|
|
37479
|
+
}
|
|
37480
|
+
const content = scrub.cleaned;
|
|
37481
|
+
const existingContent = await readFileIfExists(targetPath) ?? void 0;
|
|
37482
|
+
if (existingContent !== void 0 && existingContent !== content && options.force !== true) {
|
|
37483
|
+
throw new Error(
|
|
37484
|
+
`Shared artifact already exists with different content: ${portablePath(targetPath)}. Pass --force to replace it.`
|
|
37485
|
+
);
|
|
37486
|
+
}
|
|
37487
|
+
if (dryRun) {
|
|
37488
|
+
messages.push(`Would write shared artifact: ${portablePath(targetPath)}`);
|
|
37489
|
+
}
|
|
37490
|
+
const ov = await openVikingCliForMode(dryRun);
|
|
37491
|
+
const ovHasResource = !dryRun && await vikingResourceExists(ov, config2, targetUri);
|
|
37492
|
+
await ensureSharedDirectoryChain(config2, ov, targetUri, dryRun, { quiet: true });
|
|
37493
|
+
await writeMemoryFile(config2, ov, targetUri, content, ovHasResource ? "replace" : "create", dryRun, { quiet: true });
|
|
37494
|
+
const message = options.message ?? `share: publish ${relativePath}`;
|
|
37495
|
+
const gitMessages = await publishShareGitChange(team.config.worktree, relativePath, message, {
|
|
37496
|
+
dryRun,
|
|
37497
|
+
push: options.push
|
|
37498
|
+
});
|
|
37499
|
+
return { artifact, gitMessages, messages, sourcePath: resolvedSourcePath, targetPath, targetUri };
|
|
37500
|
+
}
|
|
37501
|
+
async function listSharedAgentArtifacts(config2, options = {}) {
|
|
37502
|
+
const syncResult = await maybeSyncSharedArtifacts(config2, options);
|
|
37503
|
+
const team = await resolveTeam(config2, options.team);
|
|
37504
|
+
const artifacts = filterSharedArtifacts(await collectSharedArtifacts(team.config.worktree, team.name), options);
|
|
37505
|
+
const summaries = [];
|
|
37506
|
+
for (const artifact of artifacts) {
|
|
37507
|
+
summaries.push({
|
|
37508
|
+
...artifact,
|
|
37509
|
+
installStatus: await sharedArtifactInstallStatus(artifact),
|
|
37510
|
+
metadataPath: sharedArtifactMetadataPath(artifact)
|
|
37511
|
+
});
|
|
37512
|
+
}
|
|
37513
|
+
return { artifacts: summaries, syncedTeams: syncResult.syncedTeams, team: team.name, warnings: syncResult.warnings };
|
|
37514
|
+
}
|
|
37515
|
+
async function installSharedAgentArtifacts(config2, options) {
|
|
37516
|
+
const syncResult = await maybeSyncSharedArtifacts(config2, options);
|
|
37517
|
+
const team = await resolveTeam(config2, options.team);
|
|
37518
|
+
const dryRun = options.dryRun === true || options.apply !== true;
|
|
37519
|
+
const allArtifacts = await collectSharedArtifacts(team.config.worktree, team.name);
|
|
37520
|
+
const artifacts = filterSharedArtifacts(allArtifacts, options);
|
|
37521
|
+
const messages = [];
|
|
37522
|
+
if (artifacts.length === 0) {
|
|
37523
|
+
const filters = sharedArtifactFilterLabel(options);
|
|
37524
|
+
if (filters) {
|
|
37525
|
+
throw new Error(`No shared agent artifacts found for team "${team.name}" matching ${filters}.`);
|
|
37526
|
+
}
|
|
37527
|
+
return {
|
|
37528
|
+
installedCount: 0,
|
|
37529
|
+
messages: [`No shared agent artifacts found for team "${team.name}".`],
|
|
37530
|
+
syncedTeams: syncResult.syncedTeams,
|
|
37531
|
+
team: team.name,
|
|
37532
|
+
warnings: syncResult.warnings
|
|
37533
|
+
};
|
|
37534
|
+
}
|
|
37535
|
+
if (options.name !== void 0 && artifacts.length > 1 && (options.agent === void 0 || options.kind === void 0)) {
|
|
37536
|
+
throw new Error(
|
|
37537
|
+
`Shared artifact "${options.name}" is ambiguous. Specify agent and kind. Matches: ${artifacts.map((artifact) => sharedArtifactLabel(artifact.artifact)).join(", ")}`
|
|
37538
|
+
);
|
|
37539
|
+
}
|
|
37540
|
+
let installedCount = 0;
|
|
37541
|
+
for (const artifact of artifacts) {
|
|
37542
|
+
const label = sharedArtifactLabel(artifact.artifact);
|
|
37543
|
+
const state = await sharedArtifactInstallState(artifact);
|
|
37544
|
+
if (dryRun) {
|
|
37545
|
+
const verb = sharedArtifactDryRunVerb(state.status, options.force === true);
|
|
37546
|
+
const suffix = sharedArtifactDryRunSuffix(state.status, options.force === true);
|
|
37547
|
+
messages.push(`${verb} ${label}: ${portablePath(artifact.installPath)}${suffix}`);
|
|
37548
|
+
continue;
|
|
37549
|
+
}
|
|
37550
|
+
if ((state.status === "local_modified" || state.status === "remote_changed_and_local_modified") && options.force !== true) {
|
|
37551
|
+
throw new Error(`Refusing to overwrite ${portablePath(artifact.installPath)}. Pass force=true or --force.`);
|
|
37552
|
+
}
|
|
37553
|
+
if (state.status === "current") {
|
|
37554
|
+
await writeSharedArtifactMetadata(artifact, state.sourceSha);
|
|
37555
|
+
messages.push(`Already installed ${label}: ${portablePath(artifact.installPath)}`);
|
|
37556
|
+
continue;
|
|
37557
|
+
}
|
|
37558
|
+
await ensureDirectory((0, import_node_path4.dirname)(artifact.installPath), false);
|
|
37559
|
+
await (0, import_promises4.writeFile)(artifact.installPath, state.sourceContent, { encoding: "utf8", mode: 384 });
|
|
37560
|
+
await writeSharedArtifactMetadata(artifact, state.sourceSha);
|
|
37561
|
+
installedCount += 1;
|
|
37562
|
+
messages.push(
|
|
37563
|
+
`${sharedArtifactInstallVerb(state.status, options.force === true)} ${label}: ${portablePath(artifact.installPath)}`
|
|
37564
|
+
);
|
|
37565
|
+
}
|
|
37566
|
+
return {
|
|
37567
|
+
installedCount,
|
|
37568
|
+
messages,
|
|
37569
|
+
syncedTeams: syncResult.syncedTeams,
|
|
37570
|
+
team: team.name,
|
|
37571
|
+
warnings: syncResult.warnings
|
|
37572
|
+
};
|
|
37573
|
+
}
|
|
37395
37574
|
function normalizeTeamName(input) {
|
|
37396
37575
|
const candidate = (input ?? "default").trim();
|
|
37397
37576
|
if (!candidate) {
|
|
@@ -37526,6 +37705,254 @@ function vikingUriToWorktreeRelative(config2, uri, team) {
|
|
|
37526
37705
|
}
|
|
37527
37706
|
return uri.slice(prefix.length);
|
|
37528
37707
|
}
|
|
37708
|
+
async function isRegularFileNoSymlink(path) {
|
|
37709
|
+
try {
|
|
37710
|
+
const stat2 = await (0, import_promises4.lstat)(path);
|
|
37711
|
+
return stat2.isFile();
|
|
37712
|
+
} catch (_err) {
|
|
37713
|
+
return false;
|
|
37714
|
+
}
|
|
37715
|
+
}
|
|
37716
|
+
function inferShareArtifact(path, options) {
|
|
37717
|
+
const normalizedPath = path.split(import_node_path4.sep).join("/");
|
|
37718
|
+
const fileName = (0, import_node_path4.basename)(path);
|
|
37719
|
+
const lowerFileName = fileName.toLowerCase();
|
|
37720
|
+
const lowerPath = normalizedPath.toLowerCase();
|
|
37721
|
+
const inferredKind = lowerFileName === "skill.md" ? "skill" : lowerPath.includes("/.claude/commands/") && lowerFileName.endsWith(".md") ? "command" : void 0;
|
|
37722
|
+
const inferredAgent = lowerPath.includes("/.codex/skills/") ? "codex" : lowerPath.includes("/.claude/skills/") || lowerPath.includes("/.claude/commands/") ? "claude" : void 0;
|
|
37723
|
+
const extensionIndex = fileName.lastIndexOf(".");
|
|
37724
|
+
const stem = extensionIndex > 0 ? fileName.slice(0, extensionIndex) : fileName;
|
|
37725
|
+
const inferredName = lowerFileName === "skill.md" ? (0, import_node_path4.basename)((0, import_node_path4.dirname)(path)) : stem;
|
|
37726
|
+
const kind = options.kind ?? inferredKind;
|
|
37727
|
+
const agent = options.agent ?? inferredAgent;
|
|
37728
|
+
const name = options.name ?? inferredName;
|
|
37729
|
+
if (kind !== "skill" && kind !== "command") {
|
|
37730
|
+
throw new Error("Could not infer artifact kind. Pass --kind skill or --kind command.");
|
|
37731
|
+
}
|
|
37732
|
+
if (agent !== "codex" && agent !== "claude") {
|
|
37733
|
+
throw new Error("Could not infer artifact agent. Pass --agent codex or --agent claude.");
|
|
37734
|
+
}
|
|
37735
|
+
if (kind === "skill" && lowerFileName !== "skill.md") {
|
|
37736
|
+
throw new Error("Skill artifacts must point at a SKILL.md file.");
|
|
37737
|
+
}
|
|
37738
|
+
if (kind === "command" && !lowerFileName.endsWith(".md")) {
|
|
37739
|
+
throw new Error("Command artifacts must be Markdown files.");
|
|
37740
|
+
}
|
|
37741
|
+
if (kind === "command" && agent !== "claude") {
|
|
37742
|
+
throw new Error("Only Claude command artifacts are supported.");
|
|
37743
|
+
}
|
|
37744
|
+
if (name.trim().length === 0) {
|
|
37745
|
+
throw new Error("Artifact name cannot be empty.");
|
|
37746
|
+
}
|
|
37747
|
+
return { agent, kind, name: uriSegment(name) };
|
|
37748
|
+
}
|
|
37749
|
+
function sharedArtifactRelativePath(artifact) {
|
|
37750
|
+
if (artifact.kind === "skill") {
|
|
37751
|
+
return `${SHAREABLE_ARTIFACT_DIR}/skills/${artifact.agent}/${artifact.name}/SKILL.md`;
|
|
37752
|
+
}
|
|
37753
|
+
return `${SHAREABLE_ARTIFACT_DIR}/commands/${artifact.agent}/${artifact.name}.md`;
|
|
37754
|
+
}
|
|
37755
|
+
function sharedArtifactFromRelativePath(relativePath) {
|
|
37756
|
+
const parts = relativePath.split("/");
|
|
37757
|
+
if (parts[0] !== SHAREABLE_ARTIFACT_DIR) {
|
|
37758
|
+
return void 0;
|
|
37759
|
+
}
|
|
37760
|
+
if (parts.length === 5 && parts[1] === "skills" && (parts[2] === "codex" || parts[2] === "claude") && parts[4] === "SKILL.md") {
|
|
37761
|
+
return { agent: parts[2], kind: "skill", name: parts[3] };
|
|
37762
|
+
}
|
|
37763
|
+
if (parts.length === 4 && parts[1] === "commands" && parts[2] === "claude" && parts[3].endsWith(".md")) {
|
|
37764
|
+
return { agent: "claude", kind: "command", name: parts[3].slice(0, -".md".length) };
|
|
37765
|
+
}
|
|
37766
|
+
return void 0;
|
|
37767
|
+
}
|
|
37768
|
+
async function collectSharedArtifacts(worktree, team) {
|
|
37769
|
+
const root = (0, import_node_path4.join)(worktree, SHAREABLE_ARTIFACT_DIR);
|
|
37770
|
+
if (!await isDirectory(root)) {
|
|
37771
|
+
return [];
|
|
37772
|
+
}
|
|
37773
|
+
const out = [];
|
|
37774
|
+
async function visit(path) {
|
|
37775
|
+
const entries = await (0, import_promises4.readdir)(path, { withFileTypes: true });
|
|
37776
|
+
for (const entry of entries) {
|
|
37777
|
+
const full = (0, import_node_path4.join)(path, entry.name);
|
|
37778
|
+
if (entry.isDirectory()) {
|
|
37779
|
+
await visit(full);
|
|
37780
|
+
continue;
|
|
37781
|
+
}
|
|
37782
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
37783
|
+
continue;
|
|
37784
|
+
}
|
|
37785
|
+
const relativePath = (0, import_node_path4.relative)(worktree, full).split(import_node_path4.sep).join("/");
|
|
37786
|
+
const artifact = sharedArtifactFromRelativePath(relativePath);
|
|
37787
|
+
if (artifact === void 0) {
|
|
37788
|
+
continue;
|
|
37789
|
+
}
|
|
37790
|
+
out.push({
|
|
37791
|
+
artifact,
|
|
37792
|
+
installPath: sharedArtifactInstallPath(team, artifact),
|
|
37793
|
+
sourcePath: full,
|
|
37794
|
+
sourceRelativePath: relativePath,
|
|
37795
|
+
team
|
|
37796
|
+
});
|
|
37797
|
+
}
|
|
37798
|
+
}
|
|
37799
|
+
await visit(root);
|
|
37800
|
+
return out.sort((a, b) => a.sourcePath.localeCompare(b.sourcePath));
|
|
37801
|
+
}
|
|
37802
|
+
function filterSharedArtifacts(artifacts, options) {
|
|
37803
|
+
const name = options.name === void 0 ? void 0 : uriSegment(options.name);
|
|
37804
|
+
return artifacts.filter((artifact) => {
|
|
37805
|
+
if (options.agent !== void 0 && artifact.artifact.agent !== options.agent) {
|
|
37806
|
+
return false;
|
|
37807
|
+
}
|
|
37808
|
+
if (options.kind !== void 0 && artifact.artifact.kind !== options.kind) {
|
|
37809
|
+
return false;
|
|
37810
|
+
}
|
|
37811
|
+
if (name !== void 0 && artifact.artifact.name !== name) {
|
|
37812
|
+
return false;
|
|
37813
|
+
}
|
|
37814
|
+
return true;
|
|
37815
|
+
});
|
|
37816
|
+
}
|
|
37817
|
+
async function maybeSyncSharedArtifacts(config2, options) {
|
|
37818
|
+
if (options.sync === false) {
|
|
37819
|
+
return { syncedTeams: [], warnings: [] };
|
|
37820
|
+
}
|
|
37821
|
+
return syncSharedReposBeforeAgentRead(config2);
|
|
37822
|
+
}
|
|
37823
|
+
async function sharedArtifactInstallStatus(artifact) {
|
|
37824
|
+
return (await sharedArtifactInstallState(artifact)).status;
|
|
37825
|
+
}
|
|
37826
|
+
async function sharedArtifactInstallState(artifact) {
|
|
37827
|
+
const sourceContent = await (0, import_promises4.readFile)(artifact.sourcePath, "utf8");
|
|
37828
|
+
const sourceSha = sha256(sourceContent);
|
|
37829
|
+
const existingContent = await readFileIfExists(artifact.installPath) ?? void 0;
|
|
37830
|
+
if (existingContent === void 0) {
|
|
37831
|
+
return { sourceContent, sourceSha, status: "not_installed" };
|
|
37832
|
+
}
|
|
37833
|
+
const existingSha = sha256(existingContent);
|
|
37834
|
+
const metadata = await readSharedArtifactMetadata(artifact);
|
|
37835
|
+
if (existingSha === sourceSha) {
|
|
37836
|
+
return { existingContent, existingSha, metadata, sourceContent, sourceSha, status: "current" };
|
|
37837
|
+
}
|
|
37838
|
+
if (metadata === void 0) {
|
|
37839
|
+
return { existingContent, existingSha, sourceContent, sourceSha, status: "local_modified" };
|
|
37840
|
+
}
|
|
37841
|
+
const remoteChanged = metadata.sourceSha256 !== sourceSha;
|
|
37842
|
+
const localChanged = metadata.installedSha256 !== existingSha;
|
|
37843
|
+
if (remoteChanged && localChanged) {
|
|
37844
|
+
return {
|
|
37845
|
+
existingContent,
|
|
37846
|
+
existingSha,
|
|
37847
|
+
metadata,
|
|
37848
|
+
sourceContent,
|
|
37849
|
+
sourceSha,
|
|
37850
|
+
status: "remote_changed_and_local_modified"
|
|
37851
|
+
};
|
|
37852
|
+
}
|
|
37853
|
+
if (remoteChanged) {
|
|
37854
|
+
return { existingContent, existingSha, metadata, sourceContent, sourceSha, status: "update_available" };
|
|
37855
|
+
}
|
|
37856
|
+
return { existingContent, existingSha, metadata, sourceContent, sourceSha, status: "local_modified" };
|
|
37857
|
+
}
|
|
37858
|
+
async function readSharedArtifactMetadata(artifact) {
|
|
37859
|
+
const raw = await readFileIfExists(sharedArtifactMetadataPath(artifact));
|
|
37860
|
+
if (raw === void 0) {
|
|
37861
|
+
return void 0;
|
|
37862
|
+
}
|
|
37863
|
+
const parsed = parseJsonConfigObject(raw);
|
|
37864
|
+
if (parsed === void 0 || parsed.version !== ARTIFACT_INSTALL_METADATA_VERSION) {
|
|
37865
|
+
return void 0;
|
|
37866
|
+
}
|
|
37867
|
+
const artifactValue = parsed.artifact;
|
|
37868
|
+
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)) {
|
|
37869
|
+
return void 0;
|
|
37870
|
+
}
|
|
37871
|
+
const metadataArtifact = artifactValue;
|
|
37872
|
+
if (metadataArtifact.agent !== artifact.artifact.agent || metadataArtifact.kind !== artifact.artifact.kind || metadataArtifact.name !== artifact.artifact.name) {
|
|
37873
|
+
return void 0;
|
|
37874
|
+
}
|
|
37875
|
+
return {
|
|
37876
|
+
artifact: artifact.artifact,
|
|
37877
|
+
installedAt: parsed.installedAt,
|
|
37878
|
+
installedSha256: parsed.installedSha256,
|
|
37879
|
+
source: parsed.source,
|
|
37880
|
+
sourceSha256: parsed.sourceSha256,
|
|
37881
|
+
team: parsed.team,
|
|
37882
|
+
version: ARTIFACT_INSTALL_METADATA_VERSION
|
|
37883
|
+
};
|
|
37884
|
+
}
|
|
37885
|
+
async function writeSharedArtifactMetadata(artifact, sourceSha) {
|
|
37886
|
+
const metadata = {
|
|
37887
|
+
artifact: artifact.artifact,
|
|
37888
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
37889
|
+
installedSha256: sourceSha,
|
|
37890
|
+
source: artifact.sourceRelativePath,
|
|
37891
|
+
sourceSha256: sourceSha,
|
|
37892
|
+
team: artifact.team,
|
|
37893
|
+
version: ARTIFACT_INSTALL_METADATA_VERSION
|
|
37894
|
+
};
|
|
37895
|
+
const metadataPath = sharedArtifactMetadataPath(artifact);
|
|
37896
|
+
await ensureDirectory((0, import_node_path4.dirname)(metadataPath), false);
|
|
37897
|
+
await (0, import_promises4.writeFile)(metadataPath, `${JSON.stringify(metadata, void 0, 2)}
|
|
37898
|
+
`, { encoding: "utf8", mode: 384 });
|
|
37899
|
+
}
|
|
37900
|
+
function sharedArtifactMetadataPath(artifact) {
|
|
37901
|
+
return `${artifact.installPath}.threadnote-install.json`;
|
|
37902
|
+
}
|
|
37903
|
+
function sharedArtifactDryRunVerb(status, force) {
|
|
37904
|
+
switch (status) {
|
|
37905
|
+
case "not_installed":
|
|
37906
|
+
return "Would install";
|
|
37907
|
+
case "current":
|
|
37908
|
+
return "Already installed";
|
|
37909
|
+
case "update_available":
|
|
37910
|
+
return "Would update";
|
|
37911
|
+
case "local_modified":
|
|
37912
|
+
case "remote_changed_and_local_modified":
|
|
37913
|
+
return force ? "Would replace" : "Would skip modified";
|
|
37914
|
+
}
|
|
37915
|
+
}
|
|
37916
|
+
function sharedArtifactDryRunSuffix(status, force) {
|
|
37917
|
+
if ((status === "local_modified" || status === "remote_changed_and_local_modified") && !force) {
|
|
37918
|
+
return " (pass --force to replace local changes)";
|
|
37919
|
+
}
|
|
37920
|
+
return "";
|
|
37921
|
+
}
|
|
37922
|
+
function sharedArtifactInstallVerb(status, force) {
|
|
37923
|
+
if (force && (status === "local_modified" || status === "remote_changed_and_local_modified")) {
|
|
37924
|
+
return "Replaced";
|
|
37925
|
+
}
|
|
37926
|
+
if (status === "update_available") {
|
|
37927
|
+
return "Updated";
|
|
37928
|
+
}
|
|
37929
|
+
return "Installed";
|
|
37930
|
+
}
|
|
37931
|
+
function sharedArtifactFilterLabel(options) {
|
|
37932
|
+
const filters = [];
|
|
37933
|
+
if (options.kind !== void 0) {
|
|
37934
|
+
filters.push(`kind=${options.kind}`);
|
|
37935
|
+
}
|
|
37936
|
+
if (options.agent !== void 0) {
|
|
37937
|
+
filters.push(`agent=${options.agent}`);
|
|
37938
|
+
}
|
|
37939
|
+
if (options.name !== void 0) {
|
|
37940
|
+
filters.push(`name=${uriSegment(options.name)}`);
|
|
37941
|
+
}
|
|
37942
|
+
return filters.join(", ");
|
|
37943
|
+
}
|
|
37944
|
+
function sharedArtifactLabel(artifact) {
|
|
37945
|
+
return `${artifact.kind} ${artifact.agent}/${artifact.name}`;
|
|
37946
|
+
}
|
|
37947
|
+
function sharedArtifactInstallPath(team, artifact) {
|
|
37948
|
+
if (artifact.kind === "skill" && artifact.agent === "codex") {
|
|
37949
|
+
return (0, import_node_path4.join)((0, import_node_os2.homedir)(), ".codex", "skills", "threadnote", team, artifact.name, "SKILL.md");
|
|
37950
|
+
}
|
|
37951
|
+
if (artifact.kind === "skill" && artifact.agent === "claude") {
|
|
37952
|
+
return (0, import_node_path4.join)((0, import_node_os2.homedir)(), ".claude", "skills", "threadnote", team, artifact.name, "SKILL.md");
|
|
37953
|
+
}
|
|
37954
|
+
return (0, import_node_path4.join)((0, import_node_os2.homedir)(), ".claude", "commands", "threadnote", team, `${artifact.name}.md`);
|
|
37955
|
+
}
|
|
37529
37956
|
function stripPersonalProvenance(content) {
|
|
37530
37957
|
const lines = content.split("\n");
|
|
37531
37958
|
let headerEnd = lines.length;
|
|
@@ -37562,9 +37989,9 @@ async function ensureSharedDirectoryChain(config2, ov, memoryUri, dryRun, option
|
|
|
37562
37989
|
continue;
|
|
37563
37990
|
}
|
|
37564
37991
|
if (options.quiet === true) {
|
|
37565
|
-
await runCommand(ov, withIdentity(config2, ["mkdir", uri, "--description", "Threadnote shared
|
|
37992
|
+
await runCommand(ov, withIdentity(config2, ["mkdir", uri, "--description", "Threadnote shared context."]));
|
|
37566
37993
|
} else {
|
|
37567
|
-
await maybeRun(false, ov, withIdentity(config2, ["mkdir", uri, "--description", "Threadnote shared
|
|
37994
|
+
await maybeRun(false, ov, withIdentity(config2, ["mkdir", uri, "--description", "Threadnote shared context."]));
|
|
37568
37995
|
}
|
|
37569
37996
|
}
|
|
37570
37997
|
}
|
|
@@ -37793,7 +38220,7 @@ async function applyChangesToOpenViking(config2, team, changes, options = {}) {
|
|
|
37793
38220
|
continue;
|
|
37794
38221
|
}
|
|
37795
38222
|
const firstSegment = change.relativePath.split("/")[0];
|
|
37796
|
-
if (!
|
|
38223
|
+
if (!SHAREABLE_TOP_LEVEL_DIRS.includes(firstSegment)) {
|
|
37797
38224
|
continue;
|
|
37798
38225
|
}
|
|
37799
38226
|
const uri = workfileToVikingUri(config2, team, change.path);
|
|
@@ -37870,6 +38297,8 @@ async function main() {
|
|
|
37870
38297
|
"When updating the same active issue, pass project/topic or replaceUri to remember_context so duplicate durable memories or handoffs do not accumulate.",
|
|
37871
38298
|
"Use compact_context with dryRun=true for scoped memory hygiene when recall surfaces overlapping active memories.",
|
|
37872
38299
|
"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.",
|
|
38300
|
+
"To share a local Codex/Claude skill or Claude command with teammates, call `share_skill` with the local file path. It publishes into the shared artifact catalog after the same scrubber checks.",
|
|
38301
|
+
"To use a team shared skill as a native local skill, call `list_shared_skills` first, then `install_shared_skill` for the selected name/agent/kind.",
|
|
37873
38302
|
"Do not store secrets, customer data, raw production logs, or credentials."
|
|
37874
38303
|
].join("\n")
|
|
37875
38304
|
}
|
|
@@ -38070,6 +38499,80 @@ function registerTools(server, config2) {
|
|
|
38070
38499
|
return runSharePublishTool(config2, checkedUri.value, { message, preview, push, redact, team });
|
|
38071
38500
|
}
|
|
38072
38501
|
);
|
|
38502
|
+
server.registerTool(
|
|
38503
|
+
"share_skill",
|
|
38504
|
+
{
|
|
38505
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
38506
|
+
description: "Publish a local Codex/Claude skill or Claude command markdown file into a team's shared artifact catalog. Path inference handles ~/.codex/skills/**/SKILL.md, ~/.claude/skills/**/SKILL.md, and ~/.claude/commands/**/*.md; pass agent/kind/name when sharing from another path. Default team is used unless team is provided. Pass preview=true to inspect bytes without writing or committing.",
|
|
38507
|
+
inputSchema: {
|
|
38508
|
+
agent: external_exports.enum(["codex", "claude"]).optional().describe("Agent owner when path inference is ambiguous"),
|
|
38509
|
+
force: external_exports.boolean().optional().describe("Replace an existing shared artifact with different content"),
|
|
38510
|
+
kind: external_exports.enum(["skill", "command"]).optional().describe("Artifact kind when path inference is ambiguous"),
|
|
38511
|
+
message: external_exports.string().optional().describe('Commit message override; defaults to "share: publish <path>"'),
|
|
38512
|
+
name: external_exports.string().optional().describe("Shared artifact name; defaults to skill directory or command file stem"),
|
|
38513
|
+
path: external_exports.string().optional().describe("Required local path to SKILL.md or a Claude command markdown file"),
|
|
38514
|
+
preview: external_exports.boolean().optional().describe("Return the bytes that would land in the shared git repo"),
|
|
38515
|
+
push: external_exports.boolean().optional().describe("Push to remote after committing; defaults to true"),
|
|
38516
|
+
redact: external_exports.boolean().optional().describe("Replace soft-leak matches (local paths) with placeholders and continue; credentials still block."),
|
|
38517
|
+
team: external_exports.string().optional().describe("Team name; defaults to the configured default team")
|
|
38518
|
+
}
|
|
38519
|
+
},
|
|
38520
|
+
async ({ agent, force, kind, message, name, path, preview, push, redact, team }) => {
|
|
38521
|
+
const checkedPath = requiredText(path, "share_skill", "path", {
|
|
38522
|
+
path: "~/.codex/skills/example/SKILL.md"
|
|
38523
|
+
});
|
|
38524
|
+
if (!checkedPath.ok) {
|
|
38525
|
+
return checkedPath.error;
|
|
38526
|
+
}
|
|
38527
|
+
return runShareSkillTool(config2, checkedPath.value, {
|
|
38528
|
+
agent,
|
|
38529
|
+
force,
|
|
38530
|
+
kind,
|
|
38531
|
+
message,
|
|
38532
|
+
name,
|
|
38533
|
+
preview,
|
|
38534
|
+
push,
|
|
38535
|
+
redact,
|
|
38536
|
+
team
|
|
38537
|
+
});
|
|
38538
|
+
}
|
|
38539
|
+
);
|
|
38540
|
+
server.registerTool(
|
|
38541
|
+
"list_shared_skills",
|
|
38542
|
+
{
|
|
38543
|
+
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
38544
|
+
description: "List shared Codex/Claude skills and Claude commands available in a configured Threadnote team repo, including whether each one is already installed locally.",
|
|
38545
|
+
inputSchema: {
|
|
38546
|
+
agent: external_exports.enum(["codex", "claude"]).optional().describe("Optional agent filter"),
|
|
38547
|
+
kind: external_exports.enum(["skill", "command"]).optional().describe("Optional kind filter"),
|
|
38548
|
+
name: external_exports.string().optional().describe("Optional shared artifact name filter"),
|
|
38549
|
+
team: external_exports.string().optional().describe("Team name; defaults to the configured default team")
|
|
38550
|
+
}
|
|
38551
|
+
},
|
|
38552
|
+
async ({ agent, kind, name, team }) => runListSharedSkillsTool(config2, { agent, kind, name, team })
|
|
38553
|
+
);
|
|
38554
|
+
server.registerTool(
|
|
38555
|
+
"install_shared_skill",
|
|
38556
|
+
{
|
|
38557
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
38558
|
+
description: "Install one shared Codex/Claude skill or Claude command from a configured Threadnote team repo into the local agent skill/command directory. Use list_shared_skills first to find names and disambiguate agent/kind.",
|
|
38559
|
+
inputSchema: {
|
|
38560
|
+
agent: external_exports.enum(["codex", "claude"]).optional().describe("Agent owner; required when name is ambiguous"),
|
|
38561
|
+
dryRun: external_exports.boolean().optional().describe("Preview install without writing local files"),
|
|
38562
|
+
force: external_exports.boolean().optional().describe("Replace an existing installed artifact with different content"),
|
|
38563
|
+
kind: external_exports.enum(["skill", "command"]).optional().describe("Artifact kind; required when name is ambiguous"),
|
|
38564
|
+
name: external_exports.string().optional().describe("Required shared artifact name to install"),
|
|
38565
|
+
team: external_exports.string().optional().describe("Team name; defaults to the configured default team")
|
|
38566
|
+
}
|
|
38567
|
+
},
|
|
38568
|
+
async ({ agent, dryRun, force, kind, name, team }) => {
|
|
38569
|
+
const checkedName = requiredText(name, "install_shared_skill", "name", { name: "reviewer" });
|
|
38570
|
+
if (!checkedName.ok) {
|
|
38571
|
+
return checkedName.error;
|
|
38572
|
+
}
|
|
38573
|
+
return runInstallSharedSkillTool(config2, checkedName.value, { agent, dryRun, force, kind, team });
|
|
38574
|
+
}
|
|
38575
|
+
);
|
|
38073
38576
|
}
|
|
38074
38577
|
function registerOpenVikingParityTools(server, config2) {
|
|
38075
38578
|
server.registerTool(
|
|
@@ -38417,7 +38920,7 @@ function registerSearchTool(server, config2, name, description) {
|
|
|
38417
38920
|
uri: external_exports.string().optional().describe("Optional viking:// subtree to search"),
|
|
38418
38921
|
callerCwd: external_exports.string().optional().describe("Optional absolute caller workspace path used to resolve this/current branch queries"),
|
|
38419
38922
|
nodeLimit: external_exports.number().int().positive().max(100).optional().describe("Maximum result count"),
|
|
38420
|
-
includeArchived: external_exports.boolean().optional().describe("Include archived memories in
|
|
38923
|
+
includeArchived: external_exports.boolean().optional().describe("Include archived memories in recall results"),
|
|
38421
38924
|
threshold: external_exports.number().min(0).max(1).optional().describe(
|
|
38422
38925
|
"Minimum relevance score 0-1 (default 0.5); lower it (toward 0) to broaden when a recall comes back empty"
|
|
38423
38926
|
)
|
|
@@ -38473,14 +38976,20 @@ async function runRecallTool(config2, params) {
|
|
|
38473
38976
|
const limitArgs = params.nodeLimit ? ["--node-limit", String(params.nodeLimit)] : [];
|
|
38474
38977
|
const threshold = params.threshold ?? RECALL_SCORE_THRESHOLD;
|
|
38475
38978
|
const pinnedArgs = params.pinnedUri ? ["--uri", params.pinnedUri] : [];
|
|
38476
|
-
const base = await recallSearchHits(
|
|
38979
|
+
const base = await recallSearchHits(
|
|
38980
|
+
config2,
|
|
38981
|
+
["search", query, ...pinnedArgs, ...limitArgs],
|
|
38982
|
+
threshold,
|
|
38983
|
+
params.includeArchived
|
|
38984
|
+
);
|
|
38477
38985
|
const passes = [base.hits];
|
|
38478
38986
|
const seededUri = project ? trimTrailingSlash(project.uri) : void 0;
|
|
38479
38987
|
if (seededUri?.startsWith("viking://") && seededUri !== params.pinnedUri) {
|
|
38480
38988
|
const seeded = await recallSearchHits(
|
|
38481
38989
|
config2,
|
|
38482
38990
|
["search", params.query, "--uri", seededUri, ...limitArgs],
|
|
38483
|
-
threshold
|
|
38991
|
+
threshold,
|
|
38992
|
+
params.includeArchived
|
|
38484
38993
|
);
|
|
38485
38994
|
passes.push(seeded.hits);
|
|
38486
38995
|
}
|
|
@@ -38514,7 +39023,7 @@ async function runRecallTool(config2, params) {
|
|
|
38514
39023
|
const onlyErrorNote = !base.ok && !semanticSection && sections.length === 1;
|
|
38515
39024
|
return { content: [{ type: "text", text: sections.join("\n\n") }], isError: onlyErrorNote || void 0 };
|
|
38516
39025
|
}
|
|
38517
|
-
async function recallSearchHits(config2, searchArgs, threshold) {
|
|
39026
|
+
async function recallSearchHits(config2, searchArgs, threshold, includeArchived) {
|
|
38518
39027
|
let result = await runOpenVikingTool(config2, [
|
|
38519
39028
|
...searchArgs,
|
|
38520
39029
|
"--threshold",
|
|
@@ -38532,7 +39041,7 @@ async function recallSearchHits(config2, searchArgs, threshold) {
|
|
|
38532
39041
|
if (result.isError === true) {
|
|
38533
39042
|
return { errorText: text.trim(), hits: [], ok: false };
|
|
38534
39043
|
}
|
|
38535
|
-
return { errorText: "", hits: parseRecallHits(text), ok: true };
|
|
39044
|
+
return { errorText: "", hits: parseRecallHits(text, { includeArchived }), ok: true };
|
|
38536
39045
|
}
|
|
38537
39046
|
async function recallHygieneHintsSection(config2, recallText) {
|
|
38538
39047
|
const uris = activePersonalMemoryUrisFromText(recallText, config2.user);
|
|
@@ -39424,6 +39933,75 @@ async function runSharePublishTool(config2, sourceUri, options) {
|
|
|
39424
39933
|
return { content: [{ type: "text", text: errorMessage(err) }], isError: true };
|
|
39425
39934
|
}
|
|
39426
39935
|
}
|
|
39936
|
+
async function runShareSkillTool(config2, sourcePath, options) {
|
|
39937
|
+
try {
|
|
39938
|
+
const result = await shareAgentArtifact(config2, sourcePath, options);
|
|
39939
|
+
const lines = [...result.messages, ...result.gitMessages];
|
|
39940
|
+
if (result.previewContent !== void 0) {
|
|
39941
|
+
lines.push("-----BEGIN PREVIEW-----");
|
|
39942
|
+
lines.push(result.previewContent);
|
|
39943
|
+
lines.push("-----END PREVIEW-----");
|
|
39944
|
+
}
|
|
39945
|
+
return { content: [{ type: "text", text: lines.join("\n") }], isError: false };
|
|
39946
|
+
} catch (err) {
|
|
39947
|
+
return { content: [{ type: "text", text: errorMessage(err) }], isError: true };
|
|
39948
|
+
}
|
|
39949
|
+
}
|
|
39950
|
+
async function runListSharedSkillsTool(config2, options) {
|
|
39951
|
+
try {
|
|
39952
|
+
const result = await listSharedAgentArtifacts(config2, options);
|
|
39953
|
+
if (result.artifacts.length === 0) {
|
|
39954
|
+
const lines2 = shareArtifactToolHeader(result.team, result.syncedTeams, result.warnings);
|
|
39955
|
+
lines2.push(`No shared skills or commands found for team "${result.team}".`);
|
|
39956
|
+
return { content: [{ type: "text", text: lines2.join("\n") }] };
|
|
39957
|
+
}
|
|
39958
|
+
const lines = shareArtifactToolHeader(result.team, result.syncedTeams, result.warnings);
|
|
39959
|
+
lines.push(`Shared skills and commands for team "${result.team}":`);
|
|
39960
|
+
for (const artifact of result.artifacts) {
|
|
39961
|
+
lines.push(
|
|
39962
|
+
`- ${artifact.artifact.kind} ${artifact.artifact.agent}/${artifact.artifact.name} (${artifact.installStatus})`
|
|
39963
|
+
);
|
|
39964
|
+
lines.push(
|
|
39965
|
+
` install: install_shared_skill({"name":"${artifact.artifact.name}","agent":"${artifact.artifact.agent}","kind":"${artifact.artifact.kind}"})`
|
|
39966
|
+
);
|
|
39967
|
+
}
|
|
39968
|
+
return { content: [{ type: "text", text: lines.join("\n") }], isError: false };
|
|
39969
|
+
} catch (err) {
|
|
39970
|
+
return { content: [{ type: "text", text: errorMessage(err) }], isError: true };
|
|
39971
|
+
}
|
|
39972
|
+
}
|
|
39973
|
+
async function runInstallSharedSkillTool(config2, name, options) {
|
|
39974
|
+
try {
|
|
39975
|
+
const result = await installSharedAgentArtifacts(config2, {
|
|
39976
|
+
...options,
|
|
39977
|
+
apply: options.dryRun !== true,
|
|
39978
|
+
name
|
|
39979
|
+
});
|
|
39980
|
+
return {
|
|
39981
|
+
content: [
|
|
39982
|
+
{
|
|
39983
|
+
type: "text",
|
|
39984
|
+
text: [...shareArtifactToolHeader(result.team, result.syncedTeams, result.warnings), ...result.messages].join(
|
|
39985
|
+
"\n"
|
|
39986
|
+
)
|
|
39987
|
+
}
|
|
39988
|
+
],
|
|
39989
|
+
isError: false
|
|
39990
|
+
};
|
|
39991
|
+
} catch (err) {
|
|
39992
|
+
return { content: [{ type: "text", text: errorMessage(err) }], isError: true };
|
|
39993
|
+
}
|
|
39994
|
+
}
|
|
39995
|
+
function shareArtifactToolHeader(team, syncedTeams, warnings) {
|
|
39996
|
+
const lines = [`Team: ${team}`];
|
|
39997
|
+
if (syncedTeams.length > 0) {
|
|
39998
|
+
lines.push(`Synced shared teams: ${syncedTeams.join(", ")}`);
|
|
39999
|
+
}
|
|
40000
|
+
for (const warning2 of warnings) {
|
|
40001
|
+
lines.push(`Warning: ${warning2}`);
|
|
40002
|
+
}
|
|
40003
|
+
return lines;
|
|
40004
|
+
}
|
|
39427
40005
|
function withIdentity2(config2, args) {
|
|
39428
40006
|
return [...args, "--account", config2.account, "--user", config2.user, "--agent-id", config2.agentId];
|
|
39429
40007
|
}
|