threadnote 0.7.9 → 0.7.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/mcp_server.cjs +0 -1
- package/dist/threadnote.cjs +71 -9
- package/docs/index.html +1 -1
- package/manager/app.css +55 -3
- package/manager/app.js +5763 -6902
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
# Threadnote
|
|
6
6
|
|
|
7
|
+
[](https://www.npmjs.com/package/threadnote) [](https://github.com/Kashkovsky/threadnote/actions/workflows/ci.yml) [](https://www.npmjs.com/package/threadnote) [](./LICENSE) 
|
|
8
|
+
|
|
7
9
|
`threadnote` is a safe local workflow for using [OpenViking](https://openviking.ai/) as shared, agent-neutral context for development work.
|
|
8
10
|
It is intentionally scoped to curated docs, memories, skills, and handoffs. It is not a source-navigation replacement,
|
|
9
11
|
and it does not index whole repositories by default.
|
package/dist/mcp_server.cjs
CHANGED
|
@@ -36148,7 +36148,6 @@ function exactMemoryScopes(config2, includeArchived) {
|
|
|
36148
36148
|
`${userBase}/durable/projects`,
|
|
36149
36149
|
`${userBase}/handoffs/active`,
|
|
36150
36150
|
`${userBase}/incidents/active`,
|
|
36151
|
-
`${userBase}/events`,
|
|
36152
36151
|
`${userBase}/shared`,
|
|
36153
36152
|
`viking://agent/${uriSegment2(config2.agentId)}/memories`,
|
|
36154
36153
|
// Seeded project resources live outside the user/memories tree. Include
|
package/dist/threadnote.cjs
CHANGED
|
@@ -3868,6 +3868,22 @@ async function gitValue(args, cwd = getInvocationCwd()) {
|
|
|
3868
3868
|
}
|
|
3869
3869
|
return result.stdout.trim();
|
|
3870
3870
|
}
|
|
3871
|
+
async function resolveRepoName(cwd = getInvocationCwd()) {
|
|
3872
|
+
const repoRoot = await gitValue(["rev-parse", "--show-toplevel"], cwd);
|
|
3873
|
+
if (!repoRoot) {
|
|
3874
|
+
return void 0;
|
|
3875
|
+
}
|
|
3876
|
+
const commonDir = await gitValue(["rev-parse", "--git-common-dir"], repoRoot);
|
|
3877
|
+
if (commonDir) {
|
|
3878
|
+
const absoluteCommonDir = (0, import_node_path.isAbsolute)(commonDir) ? commonDir : (0, import_node_path.resolve)(repoRoot, commonDir);
|
|
3879
|
+
const primaryRoot = (0, import_node_path.basename)(absoluteCommonDir) === ".git" ? (0, import_node_path.dirname)(absoluteCommonDir) : absoluteCommonDir;
|
|
3880
|
+
const name = (0, import_node_path.basename)(primaryRoot).replace(/\.git$/, "");
|
|
3881
|
+
if (name && name !== ".") {
|
|
3882
|
+
return name;
|
|
3883
|
+
}
|
|
3884
|
+
}
|
|
3885
|
+
return (0, import_node_path.basename)(repoRoot);
|
|
3886
|
+
}
|
|
3871
3887
|
async function runInteractive(executable, args) {
|
|
3872
3888
|
return new Promise((resolvePromise) => {
|
|
3873
3889
|
const child = (0, import_node_child_process.spawn)(executable, args, { stdio: "inherit" });
|
|
@@ -9547,6 +9563,40 @@ async function runCompact(config, options) {
|
|
|
9547
9563
|
await runForget(config, action.uri, { dryRun: false });
|
|
9548
9564
|
}
|
|
9549
9565
|
}
|
|
9566
|
+
async function runCompactDiagnostics(config, options) {
|
|
9567
|
+
const project = normalizeOptionalMetadata2(options.project);
|
|
9568
|
+
if (!project) {
|
|
9569
|
+
throw new Error("Provide --project for scoped memory hygiene.");
|
|
9570
|
+
}
|
|
9571
|
+
const topic = normalizeOptionalMetadata2(options.topic);
|
|
9572
|
+
const records = await scopedCompactRecords(config, {
|
|
9573
|
+
kind: options.kind,
|
|
9574
|
+
project
|
|
9575
|
+
});
|
|
9576
|
+
const activeRecords = records.filter((record) => record.metadata.status === "active");
|
|
9577
|
+
const matchingRecords = activeRecords.filter((record) => topic === void 0 || topicForRecord(record) === topic);
|
|
9578
|
+
const counts = /* @__PURE__ */ new Map();
|
|
9579
|
+
for (const record of matchingRecords) {
|
|
9580
|
+
const kind = record.metadata.kind;
|
|
9581
|
+
if (kind === "durable" || kind === "handoff" || kind === "incident") {
|
|
9582
|
+
counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
9583
|
+
}
|
|
9584
|
+
}
|
|
9585
|
+
console.log(
|
|
9586
|
+
[
|
|
9587
|
+
"Scope summary:",
|
|
9588
|
+
`- project: ${project}`,
|
|
9589
|
+
`- topic: ${topic ?? "(all)"}`,
|
|
9590
|
+
`- kind: ${options.kind ?? "(handoff, durable, incident)"}`,
|
|
9591
|
+
`- stable records read: ${records.length}`,
|
|
9592
|
+
`- active records in project: ${activeRecords.length}`,
|
|
9593
|
+
`- active records matching topic: ${matchingRecords.length}`,
|
|
9594
|
+
`- matching by kind: ${formatKindCounts(counts)}`,
|
|
9595
|
+
"- skipped by design: archived memories, shared memories, preferences, smoke records, seeded resources, and non-stable timestamped/global paths",
|
|
9596
|
+
""
|
|
9597
|
+
].join("\n")
|
|
9598
|
+
);
|
|
9599
|
+
}
|
|
9550
9600
|
async function scopedCompactRecords(config, options) {
|
|
9551
9601
|
const kinds = options.kind ? [options.kind] : ["handoff", "durable", "incident"];
|
|
9552
9602
|
const records = [];
|
|
@@ -9575,6 +9625,9 @@ async function scopedCompactRecords(config, options) {
|
|
|
9575
9625
|
}
|
|
9576
9626
|
return records;
|
|
9577
9627
|
}
|
|
9628
|
+
function formatKindCounts(counts) {
|
|
9629
|
+
return ["handoff", "durable", "incident"].map((kind) => `${kind} ${counts.get(kind) ?? 0}`).join(", ");
|
|
9630
|
+
}
|
|
9578
9631
|
async function readMemoryRecordsByUri(config, uris) {
|
|
9579
9632
|
const records = [];
|
|
9580
9633
|
for (const uri of uris) {
|
|
@@ -9975,7 +10028,6 @@ function exactMemoryScopes(config, includeArchived) {
|
|
|
9975
10028
|
`${userBase}/durable/projects`,
|
|
9976
10029
|
`${userBase}/handoffs/active`,
|
|
9977
10030
|
`${userBase}/incidents/active`,
|
|
9978
|
-
`${userBase}/events`,
|
|
9979
10031
|
`${userBase}/shared`,
|
|
9980
10032
|
`viking://agent/${uriSegment(config.agentId)}/memories`,
|
|
9981
10033
|
// Seeded project resources (READMEs, AGENTS.md, SKILL.md, docs/**) live
|
|
@@ -10278,7 +10330,7 @@ async function buildHandoff(options) {
|
|
|
10278
10330
|
const status = await gitValue(["status", "--short"], repoRoot) ?? "";
|
|
10279
10331
|
const diffStat = await gitValue(["diff", "--stat", "HEAD"], repoRoot) ?? "";
|
|
10280
10332
|
const touchedFiles = await gitTouchedFiles(repoRoot);
|
|
10281
|
-
const repoName = (0, import_node_path6.basename)(repoRoot);
|
|
10333
|
+
const repoName = await resolveRepoName(repoRoot) ?? (0, import_node_path6.basename)(repoRoot);
|
|
10282
10334
|
const topicBranch = branch && branch !== "unknown" ? branch : "current";
|
|
10283
10335
|
const metadata = {
|
|
10284
10336
|
kind: "handoff",
|
|
@@ -10607,8 +10659,7 @@ async function hasManagedClaudeHooks() {
|
|
|
10607
10659
|
}
|
|
10608
10660
|
async function runPreCompactHook(config, options = {}) {
|
|
10609
10661
|
try {
|
|
10610
|
-
const
|
|
10611
|
-
const project = repoRoot ? (0, import_node_path9.basename)(repoRoot) : "general";
|
|
10662
|
+
const project = await resolveRepoName() ?? "general";
|
|
10612
10663
|
await runHandoff(config, {
|
|
10613
10664
|
blockers: "- none recorded",
|
|
10614
10665
|
dryRun: options.dryRun === true,
|
|
@@ -10628,11 +10679,10 @@ async function runPreCompactHook(config, options = {}) {
|
|
|
10628
10679
|
}
|
|
10629
10680
|
async function runSessionStartHook(config, options = {}) {
|
|
10630
10681
|
try {
|
|
10631
|
-
const
|
|
10632
|
-
if (!
|
|
10682
|
+
const project = await resolveRepoName();
|
|
10683
|
+
if (!project) {
|
|
10633
10684
|
return;
|
|
10634
10685
|
}
|
|
10635
|
-
const project = (0, import_node_path9.basename)(repoRoot);
|
|
10636
10686
|
await emitUpdateBannerIfOutdated(config);
|
|
10637
10687
|
process.stdout.write(`## Threadnote \u2014 latest context for ${project}
|
|
10638
10688
|
|
|
@@ -12810,7 +12860,12 @@ var STATIC_FILES = {
|
|
|
12810
12860
|
"/index.html": { contentType: "text/html; charset=utf-8", path: "index.html" },
|
|
12811
12861
|
"/app.css": { contentType: "text/css; charset=utf-8", path: "app.css" },
|
|
12812
12862
|
"/app.js": { contentType: "text/javascript; charset=utf-8", path: "app.js" },
|
|
12813
|
-
"/threadnote-logo.svg": { contentType: "image/svg+xml; charset=utf-8", path: "threadnote-logo.svg", root: "docs" }
|
|
12863
|
+
"/threadnote-logo.svg": { contentType: "image/svg+xml; charset=utf-8", path: "threadnote-logo.svg", root: "docs" },
|
|
12864
|
+
"/threadnote-logo-inverted.svg": {
|
|
12865
|
+
contentType: "image/svg+xml; charset=utf-8",
|
|
12866
|
+
path: "threadnote-logo-inverted.svg",
|
|
12867
|
+
root: "docs"
|
|
12868
|
+
}
|
|
12814
12869
|
};
|
|
12815
12870
|
async function runManage(config, options) {
|
|
12816
12871
|
const token = (0, import_node_crypto2.randomBytes)(24).toString("base64url");
|
|
@@ -13018,7 +13073,14 @@ async function handleRequest(context, request, response) {
|
|
|
13018
13073
|
if (body.apply === true) {
|
|
13019
13074
|
requireConfirm(body);
|
|
13020
13075
|
}
|
|
13021
|
-
writeJson(
|
|
13076
|
+
writeJson(
|
|
13077
|
+
response,
|
|
13078
|
+
200,
|
|
13079
|
+
await runCaptured(async () => {
|
|
13080
|
+
await runCompactDiagnostics(context.config, body);
|
|
13081
|
+
await runCompact(context.config, body);
|
|
13082
|
+
})
|
|
13083
|
+
);
|
|
13022
13084
|
return;
|
|
13023
13085
|
case "/api/recall":
|
|
13024
13086
|
writeJson(
|
package/docs/index.html
CHANGED
|
@@ -1415,7 +1415,7 @@ threadnote remember \
|
|
|
1415
1415
|
<div class="v-say">Continue where we left off on this branch.</div>
|
|
1416
1416
|
<pre class="v-call">recall_context({query: "<branch> latest handoff durable feature memory"})</pre>
|
|
1417
1417
|
<pre class="v-response">
|
|
1418
|
-
→ viking://user/you/memories/handoffs/active/<
|
|
1418
|
+
→ viking://user/you/memories/handoffs/active/<repo>/auto-precompact.md
|
|
1419
1419
|
→ viking://user/you/memories/durable/projects/<repo>/<feature>.md</pre
|
|
1420
1420
|
>
|
|
1421
1421
|
<div class="v-answer">
|
package/manager/app.css
CHANGED
|
@@ -196,9 +196,9 @@ body.is-resizing-sidebar * {
|
|
|
196
196
|
|
|
197
197
|
.brand-logo {
|
|
198
198
|
flex: 0 0 auto;
|
|
199
|
-
height:
|
|
199
|
+
height: 86px;
|
|
200
200
|
object-fit: contain;
|
|
201
|
-
width:
|
|
201
|
+
width: 86px;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
.brand h1,
|
|
@@ -264,7 +264,7 @@ body.is-resizing-sidebar * {
|
|
|
264
264
|
|
|
265
265
|
.tree summary {
|
|
266
266
|
cursor: pointer;
|
|
267
|
-
grid-template-columns: 14px minmax(0, 1fr);
|
|
267
|
+
grid-template-columns: 18px 14px minmax(0, 1fr);
|
|
268
268
|
list-style: none;
|
|
269
269
|
}
|
|
270
270
|
|
|
@@ -380,6 +380,10 @@ body.is-resizing-sidebar * {
|
|
|
380
380
|
height: 100%;
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
+
.health-panel.panel.is-active {
|
|
384
|
+
overflow: auto;
|
|
385
|
+
}
|
|
386
|
+
|
|
383
387
|
.content-grid,
|
|
384
388
|
.split {
|
|
385
389
|
display: grid;
|
|
@@ -766,6 +770,34 @@ dd {
|
|
|
766
770
|
overflow: auto;
|
|
767
771
|
}
|
|
768
772
|
|
|
773
|
+
.loading-row {
|
|
774
|
+
align-items: center;
|
|
775
|
+
background: var(--panel-alt);
|
|
776
|
+
border: 1px solid var(--accent-line);
|
|
777
|
+
border-radius: 8px;
|
|
778
|
+
color: var(--text);
|
|
779
|
+
display: inline-flex;
|
|
780
|
+
gap: 8px;
|
|
781
|
+
margin-top: 12px;
|
|
782
|
+
padding: 9px 12px;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
.spinner {
|
|
786
|
+
animation: spin 0.8s linear infinite;
|
|
787
|
+
border: 2px solid var(--accent-line);
|
|
788
|
+
border-top-color: var(--accent);
|
|
789
|
+
border-radius: 50%;
|
|
790
|
+
display: inline-block;
|
|
791
|
+
height: 14px;
|
|
792
|
+
width: 14px;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
@keyframes spin {
|
|
796
|
+
to {
|
|
797
|
+
transform: rotate(360deg);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
769
801
|
.output {
|
|
770
802
|
background: var(--code-bg);
|
|
771
803
|
border-radius: var(--radius);
|
|
@@ -779,7 +811,9 @@ dd {
|
|
|
779
811
|
|
|
780
812
|
.doctor-output {
|
|
781
813
|
margin: 12px 0 0;
|
|
814
|
+
max-height: min(34dvh, 360px);
|
|
782
815
|
min-height: 120px;
|
|
816
|
+
overflow: auto;
|
|
783
817
|
}
|
|
784
818
|
|
|
785
819
|
.uri-list {
|
|
@@ -819,6 +853,24 @@ dd {
|
|
|
819
853
|
z-index: 10;
|
|
820
854
|
}
|
|
821
855
|
|
|
856
|
+
.busy-overlay {
|
|
857
|
+
align-items: center;
|
|
858
|
+
background: rgba(5, 5, 8, 0.34);
|
|
859
|
+
display: flex;
|
|
860
|
+
inset: 0;
|
|
861
|
+
justify-content: center;
|
|
862
|
+
position: fixed;
|
|
863
|
+
z-index: 20;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
.busy-panel {
|
|
867
|
+
background: #050508;
|
|
868
|
+
border: 1px solid var(--accent-line);
|
|
869
|
+
border-radius: 8px;
|
|
870
|
+
color: var(--text);
|
|
871
|
+
padding: 12px 16px;
|
|
872
|
+
}
|
|
873
|
+
|
|
822
874
|
.danger-text {
|
|
823
875
|
color: var(--danger);
|
|
824
876
|
}
|