@appchy/jarvis 0.1.96 → 0.1.97
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/dist/bin.js +297 -199
- package/dist/bin.js.map +1 -1
- package/dist/data/backends.mjs +92 -22
- package/dist/data/mcp.mjs +62 -18
- package/package.json +4 -4
package/dist/data/backends.mjs
CHANGED
|
@@ -11,14 +11,72 @@ import {
|
|
|
11
11
|
} from "./chunk-YWSWQEJN.mjs";
|
|
12
12
|
|
|
13
13
|
// ../../packages/data/src/backends/graphify.ts
|
|
14
|
+
import { execFile as execFile2 } from "child_process";
|
|
15
|
+
import { access, readFile as readFile3, rename, rm, stat, utimes, writeFile as writeFile2 } from "fs/promises";
|
|
16
|
+
import { dirname as dirname2, resolve as resolve3 } from "path";
|
|
17
|
+
import { promisify as promisify2 } from "util";
|
|
18
|
+
|
|
19
|
+
// ../../packages/data/src/backends/changes.ts
|
|
14
20
|
import { execFile } from "child_process";
|
|
15
|
-
import {
|
|
16
|
-
import { dirname, resolve
|
|
21
|
+
import { readFile, writeFile } from "fs/promises";
|
|
22
|
+
import { dirname, resolve } from "path";
|
|
17
23
|
import { promisify } from "util";
|
|
24
|
+
var run = promisify(execFile);
|
|
25
|
+
var STAMP = "extracted-at";
|
|
26
|
+
async function git(root, argv) {
|
|
27
|
+
try {
|
|
28
|
+
const { stdout } = await run("git", argv, {
|
|
29
|
+
cwd: root,
|
|
30
|
+
timeout: 5e3,
|
|
31
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
32
|
+
});
|
|
33
|
+
return stdout.trim();
|
|
34
|
+
} catch {
|
|
35
|
+
return void 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function stampPath(graphPath) {
|
|
39
|
+
return resolve(dirname(graphPath), STAMP);
|
|
40
|
+
}
|
|
41
|
+
async function saveExtractedCommit(root, graphPath) {
|
|
42
|
+
const head = await git(root, ["rev-parse", "HEAD"]);
|
|
43
|
+
if (!head) return;
|
|
44
|
+
try {
|
|
45
|
+
await writeFile(stampPath(graphPath), `${head}
|
|
46
|
+
`, "utf8");
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function loadExtractedCommit(graphPath) {
|
|
51
|
+
try {
|
|
52
|
+
const sha = (await readFile(stampPath(graphPath), "utf8")).trim();
|
|
53
|
+
return /^[0-9a-f]{40}$/.test(sha) ? sha : void 0;
|
|
54
|
+
} catch {
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function changedSince(root, since) {
|
|
59
|
+
const committed = await git(root, ["diff", "--name-only", since, "HEAD"]);
|
|
60
|
+
if (committed === void 0) return void 0;
|
|
61
|
+
const working = await git(root, ["status", "--porcelain"]);
|
|
62
|
+
if (working === void 0) return void 0;
|
|
63
|
+
const staged = working.split("\n").filter((line) => line.trim().length > 0).flatMap((line) => line.slice(3).split(" -> "));
|
|
64
|
+
return [...committed.split("\n"), ...staged].map((path) => path.trim()).filter(Boolean);
|
|
65
|
+
}
|
|
66
|
+
async function indexedCodeChanged(root, graphPath, globs) {
|
|
67
|
+
if (!globs.include || globs.include.length === 0) return true;
|
|
68
|
+
const since = await loadExtractedCommit(graphPath);
|
|
69
|
+
if (!since) return true;
|
|
70
|
+
const changed = await changedSince(root, since);
|
|
71
|
+
if (changed === void 0) return true;
|
|
72
|
+
const included = matcher(globs.include);
|
|
73
|
+
const ignored = globs.ignore && globs.ignore.length > 0 ? matcher(globs.ignore) : () => false;
|
|
74
|
+
return changed.some((path) => included(path) && !ignored(path));
|
|
75
|
+
}
|
|
18
76
|
|
|
19
77
|
// ../../packages/data/src/backends/imports.ts
|
|
20
|
-
import { readFile } from "fs/promises";
|
|
21
|
-
import { posix, resolve } from "path";
|
|
78
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
79
|
+
import { posix, resolve as resolve2 } from "path";
|
|
22
80
|
var IMPORT_RELATION_SET = new Set(IMPORT_RELATIONS);
|
|
23
81
|
var asStr = (v) => typeof v === "string" ? v : void 0;
|
|
24
82
|
var toPosix = (p) => p.replace(/\\/g, "/");
|
|
@@ -50,7 +108,7 @@ async function packageNameOf(cache, root, file) {
|
|
|
50
108
|
return hit === NO_PACKAGE ? void 0 : hit;
|
|
51
109
|
}
|
|
52
110
|
pending.push(dir);
|
|
53
|
-
const name = await
|
|
111
|
+
const name = await readFile2(resolve2(root, dir, "package.json"), "utf8").then((text) => asStr(JSON.parse(text).name)).catch(() => void 0);
|
|
54
112
|
if (name !== void 0) {
|
|
55
113
|
for (const d of pending) cache.set(d, name);
|
|
56
114
|
return name;
|
|
@@ -65,7 +123,7 @@ async function packageNameOf(cache, root, file) {
|
|
|
65
123
|
async function readLines(cache, absPath) {
|
|
66
124
|
const hit = cache.get(absPath);
|
|
67
125
|
if (hit !== void 0) return hit;
|
|
68
|
-
const lines = await
|
|
126
|
+
const lines = await readFile2(absPath, "utf8").then((text) => text.split(/\r?\n/)).catch(() => null);
|
|
69
127
|
cache.set(absPath, lines);
|
|
70
128
|
return lines;
|
|
71
129
|
}
|
|
@@ -83,7 +141,7 @@ async function resolveImportCollisions(raw, root) {
|
|
|
83
141
|
const specsInFile = async (file) => {
|
|
84
142
|
const hit = fileSpecCache.get(file);
|
|
85
143
|
if (hit !== void 0 || fileSpecCache.has(file)) return hit;
|
|
86
|
-
const lines = await readLines(lineCache,
|
|
144
|
+
const lines = await readLines(lineCache, resolve2(root, file));
|
|
87
145
|
const specs = lines?.flatMap((l) => specifiersOf(l));
|
|
88
146
|
fileSpecCache.set(file, specs);
|
|
89
147
|
return specs;
|
|
@@ -125,7 +183,7 @@ async function resolveImportCollisions(raw, root) {
|
|
|
125
183
|
const importer = toPosix(asStr(links[0]?.source_file) ?? "");
|
|
126
184
|
const lineNo = lineNumberOf(asStr(links[0]?.source_location));
|
|
127
185
|
if (!importer || lineNo === void 0) continue;
|
|
128
|
-
const lines = await readLines(lineCache,
|
|
186
|
+
const lines = await readLines(lineCache, resolve2(root, importer));
|
|
129
187
|
const line = lines?.[lineNo - 1];
|
|
130
188
|
if (!line) continue;
|
|
131
189
|
const allSpecs = specifiersOf(line);
|
|
@@ -185,7 +243,7 @@ function communityLabelPrompt(communities) {
|
|
|
185
243
|
}
|
|
186
244
|
|
|
187
245
|
// ../../packages/data/src/backends/graphify.ts
|
|
188
|
-
var
|
|
246
|
+
var run2 = promisify2(execFile2);
|
|
189
247
|
var LABEL_TOP_K = 8;
|
|
190
248
|
var LABEL_MAX_LEN = 80;
|
|
191
249
|
var PLACEHOLDER_COMMUNITY = /^Community \d+$/;
|
|
@@ -200,8 +258,8 @@ function graphify(options = {}) {
|
|
|
200
258
|
// saying so at the moment it is produced.
|
|
201
259
|
...options.staleness ? { staleness: options.staleness } : {},
|
|
202
260
|
load: async (ctx) => {
|
|
203
|
-
const root =
|
|
204
|
-
const graphPath =
|
|
261
|
+
const root = resolve3(ctx.config.repoRoot, options.root ?? ".");
|
|
262
|
+
const graphPath = resolve3(ctx.config.repoRoot, graphRel);
|
|
205
263
|
const bin = options.bin ?? "graphify";
|
|
206
264
|
const forceExtract = process.env.JARVIS_DATA_REBUILD === "1";
|
|
207
265
|
const refresh = process.env.JARVIS_DATA_REFRESH === "1" && options.extract !== false;
|
|
@@ -209,15 +267,27 @@ function graphify(options = {}) {
|
|
|
209
267
|
const mode = forceExtract ? true : options.extract ?? "auto";
|
|
210
268
|
const present = await exists(graphPath);
|
|
211
269
|
const missing = options.missing !== void 0 ? { missing: options.missing } : {};
|
|
212
|
-
const
|
|
213
|
-
|
|
270
|
+
const unmoved = refresh && present && !await indexedCodeChanged(root, graphPath, {
|
|
271
|
+
...options.include ? { include: options.include } : {},
|
|
272
|
+
...options.ignore ? { ignore: options.ignore } : {}
|
|
273
|
+
});
|
|
274
|
+
const extracted = forceExtract && present ? await rebuildFresh(graphPath, () => extract(bin, root, ctx, graphRel, { ...missing })) : unmoved ? false : refresh ? await extract(bin, root, ctx, graphRel, { force: true, ...missing }) : mode === true || mode === "auto" && !present ? await extract(bin, root, ctx, graphRel, { ...missing }) : false;
|
|
275
|
+
if (extracted) {
|
|
276
|
+
await stampFresh(graphPath);
|
|
277
|
+
await saveExtractedCommit(root, graphPath);
|
|
278
|
+
} else if (unmoved) {
|
|
279
|
+
ctx.logger.info(
|
|
280
|
+
"graphify: nothing it indexes changed \u2014 keeping the graph, skipping extract"
|
|
281
|
+
);
|
|
282
|
+
await stampFresh(graphPath);
|
|
283
|
+
}
|
|
214
284
|
if (!await exists(graphPath)) {
|
|
215
285
|
ctx.panic(
|
|
216
286
|
`graphify graph not found at ${graphRel}. Run \`jarvis build graph\`, or set { extract: true } in the backend options.`
|
|
217
287
|
);
|
|
218
288
|
}
|
|
219
289
|
if (options.staleness) await reportIfStale(ctx, graphPath, graphRel, options.staleness);
|
|
220
|
-
const raw = JSON.parse(await
|
|
290
|
+
const raw = JSON.parse(await readFile3(graphPath, "utf8"));
|
|
221
291
|
if (options.labeler && (forceLabel || extracted && !refresh)) {
|
|
222
292
|
await labelCommunities(ctx, options.labeler, raw, graphPath, extracted);
|
|
223
293
|
}
|
|
@@ -231,7 +301,7 @@ async function loadCommunityLabels(graphPath) {
|
|
|
231
301
|
const labels = /* @__PURE__ */ new Map();
|
|
232
302
|
const labelPath = communityLabelsPath(graphPath);
|
|
233
303
|
try {
|
|
234
|
-
const raw = JSON.parse(await
|
|
304
|
+
const raw = JSON.parse(await readFile3(labelPath, "utf8"));
|
|
235
305
|
if (!raw || typeof raw !== "object") return labels;
|
|
236
306
|
for (const [rawId, rawName] of Object.entries(raw)) {
|
|
237
307
|
const name = asString(rawName);
|
|
@@ -289,7 +359,7 @@ function communityOf(n, labels) {
|
|
|
289
359
|
return key ? `Community ${key}` : void 0;
|
|
290
360
|
}
|
|
291
361
|
function graphifyOptions(ctx, graphRel) {
|
|
292
|
-
return { cwd: ctx.config.repoRoot, env: { ...process.env, GRAPHIFY_OUT:
|
|
362
|
+
return { cwd: ctx.config.repoRoot, env: { ...process.env, GRAPHIFY_OUT: dirname2(graphRel) } };
|
|
293
363
|
}
|
|
294
364
|
async function extract(bin, root, ctx, graphRel, options = {}) {
|
|
295
365
|
try {
|
|
@@ -297,7 +367,7 @@ async function extract(bin, root, ctx, graphRel, options = {}) {
|
|
|
297
367
|
`graphify: extracting ${root}${options.force ? " (incremental \u2014 prune deletions)" : ""}`
|
|
298
368
|
);
|
|
299
369
|
const args = options.force ? ["update", "--force", root] : ["update", root];
|
|
300
|
-
await
|
|
370
|
+
await run2(bin, args, graphifyOptions(ctx, graphRel));
|
|
301
371
|
return true;
|
|
302
372
|
} catch (err) {
|
|
303
373
|
const code = err.code;
|
|
@@ -337,7 +407,7 @@ async function rebuildFresh(graphPath, extract2) {
|
|
|
337
407
|
}
|
|
338
408
|
async function parseable(path) {
|
|
339
409
|
try {
|
|
340
|
-
JSON.parse(await
|
|
410
|
+
JSON.parse(await readFile3(path, "utf8"));
|
|
341
411
|
return true;
|
|
342
412
|
} catch {
|
|
343
413
|
return false;
|
|
@@ -411,12 +481,12 @@ function sampleCommunities(raw) {
|
|
|
411
481
|
return communities;
|
|
412
482
|
}
|
|
413
483
|
function communityLabelsPath(graphPath) {
|
|
414
|
-
return
|
|
484
|
+
return resolve3(dirname2(graphPath), "community-labels.json");
|
|
415
485
|
}
|
|
416
486
|
async function readCommunityLabels(graphPath) {
|
|
417
487
|
const out = /* @__PURE__ */ new Map();
|
|
418
488
|
try {
|
|
419
|
-
const existing = JSON.parse(await
|
|
489
|
+
const existing = JSON.parse(await readFile3(communityLabelsPath(graphPath), "utf8"));
|
|
420
490
|
for (const [id, value] of Object.entries(existing)) {
|
|
421
491
|
const name = asString(value);
|
|
422
492
|
if (name && !PLACEHOLDER_COMMUNITY.test(name)) out.set(id, name);
|
|
@@ -429,7 +499,7 @@ async function writeCommunityLabels(graphPath, named) {
|
|
|
429
499
|
const merged = {};
|
|
430
500
|
for (const [id, name] of await readCommunityLabels(graphPath)) merged[id] = name;
|
|
431
501
|
for (const [id, name] of named) merged[id] = name;
|
|
432
|
-
await
|
|
502
|
+
await writeFile2(communityLabelsPath(graphPath), `${JSON.stringify(merged, null, 2)}
|
|
433
503
|
`, "utf8");
|
|
434
504
|
}
|
|
435
505
|
async function reportIfStale(ctx, graphPath, graphRel, severity) {
|
|
@@ -441,7 +511,7 @@ async function reportIfStale(ctx, graphPath, graphRel, severity) {
|
|
|
441
511
|
}
|
|
442
512
|
let headMs;
|
|
443
513
|
try {
|
|
444
|
-
const { stdout } = await
|
|
514
|
+
const { stdout } = await run2("git", ["show", "-s", "--format=%ct", "HEAD"], {
|
|
445
515
|
cwd: ctx.config.repoRoot
|
|
446
516
|
});
|
|
447
517
|
const seconds = Number(stdout.trim());
|
package/dist/data/mcp.mjs
CHANGED
|
@@ -215,6 +215,14 @@ async function freshness(ctx) {
|
|
|
215
215
|
const builtMs = Date.parse(ctx.builtAt);
|
|
216
216
|
let behindHead;
|
|
217
217
|
if (head && mtimeMs) behindHead = mtimeMs < head.committedMs;
|
|
218
|
+
let behindHeadBy;
|
|
219
|
+
let behindHeadForMs;
|
|
220
|
+
if (behindHead && head && mtimeMs) {
|
|
221
|
+
behindHeadForMs = head.committedMs - mtimeMs;
|
|
222
|
+
const since = new Date(mtimeMs).toISOString();
|
|
223
|
+
const count = Number(await git(root, ["rev-list", "--count", `--since=${since}`, "HEAD"]));
|
|
224
|
+
if (Number.isFinite(count)) behindHeadBy = count;
|
|
225
|
+
}
|
|
218
226
|
const replacedMs = Math.max(mtimeMs ?? 0, await snapshotMtimeMs(ctx) ?? 0);
|
|
219
227
|
const rebuiltOnDisk = Boolean(
|
|
220
228
|
replacedMs && Number.isFinite(builtMs) && replacedMs > builtMs + 1e3
|
|
@@ -223,27 +231,47 @@ async function freshness(ctx) {
|
|
|
223
231
|
const behindOrigin = Boolean(origin?.reachable && (origin.behindBy ?? 0) > 0);
|
|
224
232
|
const originUnknown = Boolean(origin && !origin.reachable);
|
|
225
233
|
let reason;
|
|
226
|
-
|
|
234
|
+
let verdict;
|
|
235
|
+
if (behindOrigin) {
|
|
236
|
+
verdict = "stale";
|
|
227
237
|
reason = `this checkout is ${origin?.behindBy} commit(s) behind ${origin?.ref} \u2014 the map and the board both describe an older world than the origin's. Pull, then \`jarvis build graph\`.`;
|
|
228
|
-
else if (originUnknown)
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
238
|
+
} else if (originUnknown) {
|
|
239
|
+
verdict = "unknown";
|
|
240
|
+
reason = `cannot reach ${origin?.ref}, so whether this is current is UNKNOWN rather than bad \u2014 rebuilding cannot answer it${origin?.lastSyncedIso ? `. Last heard from a remote at ${origin.lastSyncedIso}` : ", and this clone has never fetched"}.`;
|
|
241
|
+
} else if (behindHead) {
|
|
242
|
+
verdict = "stale";
|
|
243
|
+
reason = `${graphRel} is ${describeBehind(behindHeadBy, behindHeadForMs)} \u2014 run \`jarvis build graph\` (the incremental update \u2014 it re-extracts in place), then traces and impact are trustworthy again. \`--rebuild\` re-extracts from scratch and relabels; it is for a graph still wrong after a plain build, not for ordinary drift.`;
|
|
244
|
+
} else if (rebuiltOnDisk) {
|
|
245
|
+
verdict = "stale";
|
|
233
246
|
reason = `${graphRel} was rebuilt after this MCP server loaded it \u2014 reloading automatically.`;
|
|
247
|
+
}
|
|
234
248
|
return {
|
|
235
249
|
graphPath: graphRel,
|
|
236
250
|
...mtimeIso ? { mtimeIso } : {},
|
|
237
251
|
...head ? { head: { commit: head.commit, dirty: head.dirty } } : {},
|
|
238
252
|
...behindHead !== void 0 ? { behindHead } : {},
|
|
253
|
+
...behindHeadBy !== void 0 ? { behindHeadBy } : {},
|
|
254
|
+
...behindHeadForMs !== void 0 ? { behindHeadForMs } : {},
|
|
239
255
|
...rebuiltOnDisk ? { rebuiltOnDisk } : {},
|
|
240
256
|
...origin ? { origin } : {},
|
|
241
257
|
...behindOrigin ? { behindOrigin } : {},
|
|
242
258
|
...originUnknown ? { originUnknown } : {},
|
|
243
259
|
stale: Boolean(behindHead) || rebuiltOnDisk || behindOrigin || originUnknown,
|
|
260
|
+
...verdict ? { verdict } : {},
|
|
244
261
|
...reason ? { reason } : {}
|
|
245
262
|
};
|
|
246
263
|
}
|
|
264
|
+
function describeBehind(commits, forMs) {
|
|
265
|
+
const age = forMs !== void 0 && forMs >= 60 * 60 * 1e3 ? describeAge(forMs) : void 0;
|
|
266
|
+
if (commits === void 0) return `older than HEAD${age ? ` by ${age}` : ""}`;
|
|
267
|
+
const plural = commits === 1 ? "commit" : "commits";
|
|
268
|
+
return `${commits} ${plural} behind HEAD${age ? `, built ${age} ago` : ""}`;
|
|
269
|
+
}
|
|
270
|
+
function describeAge(ms) {
|
|
271
|
+
const hours = Math.round(ms / (60 * 60 * 1e3));
|
|
272
|
+
if (hours < 48) return `${hours} hour(s)`;
|
|
273
|
+
return `${Math.round(hours / 24)} day(s)`;
|
|
274
|
+
}
|
|
247
275
|
async function snapshotMtimeMs(ctx) {
|
|
248
276
|
const path = ctx.config.persistence?.locate?.({ repoRoot: ctx.config.repoRoot }, "snapshot");
|
|
249
277
|
if (!path) return void 0;
|
|
@@ -874,11 +902,13 @@ async function brief(ctx, req) {
|
|
|
874
902
|
const areas = [...communityCounts.entries()].map(([name, size]) => ({ name, size })).sort((a, b) => b.size - a.size).slice(0, listCap);
|
|
875
903
|
const inProgressCount = byCategory["in-progress"] ?? 0;
|
|
876
904
|
return {
|
|
877
|
-
summary: `${leaves.length} work item(s) \u2014 ${inProgressCount} in flight, ${byCategory["open"] ?? 0} open, ${byCategory["done"] ?? 0} done. ${artifacts} knowledge artifact(s); health: ${health.counts.error} error(s), ${health.counts.warn} warning(s)${f.stale ?
|
|
905
|
+
summary: `${leaves.length} work item(s) \u2014 ${inProgressCount} in flight, ${byCategory["open"] ?? 0} open, ${byCategory["done"] ?? 0} done. ${artifacts} knowledge artifact(s); health: ${health.counts.error} error(s), ${health.counts.warn} warning(s)${f.stale ? ` \u2014 ${describeFreshness(f)}` : ""}${staleOrigins.length ? ` \u2014 ${staleOrigins.map((o) => o.origin).join(", ")} behind their own HEAD (re-run \`jarvis build graph\` there, then re-merge)` : ""}.`,
|
|
878
906
|
freshness: {
|
|
879
907
|
builtAt: ctx.builtAt,
|
|
880
908
|
...f.head ? { head: { commit: f.head.commit, dirty: f.head.dirty } } : {},
|
|
881
909
|
...f.stale !== void 0 ? { stale: f.stale } : {},
|
|
910
|
+
...f.verdict ? { verdict: f.verdict } : {},
|
|
911
|
+
...f.behindHeadBy !== void 0 ? { behindHeadBy: f.behindHeadBy } : {},
|
|
882
912
|
...originsFresh.length ? { federated: originsFresh } : {}
|
|
883
913
|
},
|
|
884
914
|
graph: { nodes: ctx.graph.nodes().length, artifacts, edges: ctx.graph.edges().length },
|
|
@@ -920,6 +950,15 @@ function renderWorkItem(ctx, item, home) {
|
|
|
920
950
|
...home !== void 0 ? { belongsTo: home } : {}
|
|
921
951
|
};
|
|
922
952
|
}
|
|
953
|
+
function describeFreshness(f) {
|
|
954
|
+
if (f.verdict === "unknown")
|
|
955
|
+
return "FRESHNESS UNKNOWN \u2014 cannot reach the origin, so nothing below is confirmed current; rebuilding cannot answer it";
|
|
956
|
+
if (f.behindOrigin)
|
|
957
|
+
return `THIS CHECKOUT IS BEHIND ITS ORIGIN by ${f.origin?.behindBy} commit(s) \u2014 pull, then run \`jarvis build graph\``;
|
|
958
|
+
if (f.behindHead)
|
|
959
|
+
return `GRAPH IS STALE by ${f.behindHeadBy ?? "an unknown number of"} commit(s), run \`jarvis build graph\` (incremental) before trusting details`;
|
|
960
|
+
return "GRAPH IS STALE, run `jarvis build graph` (incremental) before trusting details";
|
|
961
|
+
}
|
|
923
962
|
|
|
924
963
|
// ../../packages/data/src/resolve.ts
|
|
925
964
|
function resolveTarget(ctx, input) {
|
|
@@ -2562,15 +2601,11 @@ function createServer(ctx, hosted = [], prompts = []) {
|
|
|
2562
2601
|
);
|
|
2563
2602
|
}
|
|
2564
2603
|
for (const p of prompts) {
|
|
2565
|
-
server.registerPrompt(
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
{ role: "user", content: { type: "text", text: await p.read() } }
|
|
2571
|
-
]
|
|
2572
|
-
})
|
|
2573
|
-
);
|
|
2604
|
+
server.registerPrompt(p.name, { title: p.title, description: p.description }, async () => ({
|
|
2605
|
+
messages: [
|
|
2606
|
+
{ role: "user", content: { type: "text", text: await p.read() } }
|
|
2607
|
+
]
|
|
2608
|
+
}));
|
|
2574
2609
|
}
|
|
2575
2610
|
return server;
|
|
2576
2611
|
}
|
|
@@ -2601,14 +2636,23 @@ function buildInstructions(req = {}) {
|
|
|
2601
2636
|
}
|
|
2602
2637
|
async function result(ctx, structured) {
|
|
2603
2638
|
const f = await freshness(ctx);
|
|
2604
|
-
const summary = f.stale && !/stale/i.test(structured.summary) ? `${STALE_PREFIX}${structured.summary}` : structured.summary;
|
|
2639
|
+
const summary = f.stale && !/stale/i.test(structured.summary) ? `${f.verdict === "unknown" ? UNKNOWN_PREFIX : STALE_PREFIX}${structured.summary}` : structured.summary;
|
|
2605
2640
|
return {
|
|
2606
2641
|
content: [{ type: "text", text: summary }],
|
|
2607
2642
|
structuredContent: { ...structured, summary },
|
|
2608
|
-
...f.stale ? {
|
|
2643
|
+
...f.stale ? {
|
|
2644
|
+
_meta: {
|
|
2645
|
+
data: {
|
|
2646
|
+
stale: true,
|
|
2647
|
+
...f.verdict ? { verdict: f.verdict } : {},
|
|
2648
|
+
...f.reason ? { reason: f.reason } : {}
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
} : {}
|
|
2609
2652
|
};
|
|
2610
2653
|
}
|
|
2611
2654
|
var STALE_PREFIX = "\u26A0 STALE GRAPH \u2014 results may be wrong; run `jarvis build graph` (the incremental update \u2014 not --rebuild). ";
|
|
2655
|
+
var UNKNOWN_PREFIX = "\u26A0 FRESHNESS UNKNOWN \u2014 this clone cannot reach its origin, so it cannot tell whether these results are current. ";
|
|
2612
2656
|
|
|
2613
2657
|
// ../../packages/data/src/mcp/snapshot.ts
|
|
2614
2658
|
async function loadSnapshot(config) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appchy/jarvis",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.97",
|
|
4
4
|
"description": "Jarvis — local AI coding assistant CLI",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -56,15 +56,15 @@
|
|
|
56
56
|
"tsup": "^8.5.1",
|
|
57
57
|
"typescript": "^5.7.0",
|
|
58
58
|
"vitest": "^2.1.0",
|
|
59
|
-
"@jarvis/agents": "1.0.0",
|
|
60
59
|
"@jarvis/anthropic": "1.0.0",
|
|
61
|
-
"@jarvis/errors": "1.0.0",
|
|
62
60
|
"@jarvis/board": "0.1.0",
|
|
61
|
+
"@jarvis/agents": "1.0.0",
|
|
62
|
+
"@jarvis/data": "0.1.0",
|
|
63
63
|
"@jarvis/logger": "1.0.0",
|
|
64
|
+
"@jarvis/errors": "1.0.0",
|
|
64
65
|
"@jarvis/rpc": "1.0.0",
|
|
65
66
|
"@jarvis/types": "1.0.0",
|
|
66
67
|
"@jarvis/typescript-config": "1.0.0",
|
|
67
|
-
"@jarvis/data": "0.1.0",
|
|
68
68
|
"@jarvis/ui": "0.1.0",
|
|
69
69
|
"@jarvis/vitest-config": "1.0.0"
|
|
70
70
|
},
|