@tomingtoming/kioq 0.8.3 → 0.9.1
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/src/engagement.js +130 -0
- package/dist/src/index.js +191 -464
- package/dist/src/noteStore.js +152 -0
- package/dist/src/signalLog.js +62 -0
- package/dist/src/storage/githubStorage.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
const HUMAN_SIGNALS = new Set([
|
|
2
|
+
"human_cited",
|
|
3
|
+
"human_discussed",
|
|
4
|
+
"human_applied",
|
|
5
|
+
"human_questioned",
|
|
6
|
+
"human_dismissed",
|
|
7
|
+
]);
|
|
8
|
+
const SIGNAL_STRENGTH = {
|
|
9
|
+
search_hit: 1,
|
|
10
|
+
context_pulled: 2,
|
|
11
|
+
read: 3,
|
|
12
|
+
orient_discovered: 4,
|
|
13
|
+
orient_referenced: 5,
|
|
14
|
+
modified: 6,
|
|
15
|
+
human_questioned: 7,
|
|
16
|
+
human_cited: 8,
|
|
17
|
+
human_discussed: 9,
|
|
18
|
+
human_dismissed: 5,
|
|
19
|
+
human_applied: 10,
|
|
20
|
+
};
|
|
21
|
+
export class EngagementIndex {
|
|
22
|
+
facetsMap = new Map();
|
|
23
|
+
build(signals, notes) {
|
|
24
|
+
this.facetsMap.clear();
|
|
25
|
+
const notesByPermalink = new Map();
|
|
26
|
+
for (const note of notes) {
|
|
27
|
+
notesByPermalink.set(note.permalink, note);
|
|
28
|
+
}
|
|
29
|
+
// Initialize facets for all notes
|
|
30
|
+
for (const note of notes) {
|
|
31
|
+
this.facetsMap.set(note.permalink, this.emptyFacets(note.frontmatter));
|
|
32
|
+
}
|
|
33
|
+
// Process signal log
|
|
34
|
+
for (const signal of signals) {
|
|
35
|
+
const facets = this.facetsMap.get(signal.note);
|
|
36
|
+
if (!facets)
|
|
37
|
+
continue;
|
|
38
|
+
facets.contactCount++;
|
|
39
|
+
if (!facets.lastContact || signal.ts > facets.lastContact) {
|
|
40
|
+
facets.lastContact = signal.ts;
|
|
41
|
+
}
|
|
42
|
+
if (HUMAN_SIGNALS.has(signal.op)) {
|
|
43
|
+
facets.humanSignalCount++;
|
|
44
|
+
if (!facets.lastHumanSignal || signal.ts > facets.lastHumanSignal) {
|
|
45
|
+
facets.lastHumanSignal = signal.ts;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (!facets.dominantSignal || SIGNAL_STRENGTH[signal.op] > SIGNAL_STRENGTH[facets.dominantSignal]) {
|
|
49
|
+
facets.dominantSignal = signal.op;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Compute ai_reference_count and synthesis_count from note content
|
|
53
|
+
const wikiLinkPattern = /\[\[([^\]\n]+)\]\]/g;
|
|
54
|
+
for (const note of notes) {
|
|
55
|
+
const origin = safeString(note.frontmatter.origin);
|
|
56
|
+
if (origin !== "ai" && origin !== "collaborative")
|
|
57
|
+
continue;
|
|
58
|
+
const links = new Set();
|
|
59
|
+
let match;
|
|
60
|
+
while ((match = wikiLinkPattern.exec(note.body)) !== null) {
|
|
61
|
+
const target = match[1].split("|")[0].trim();
|
|
62
|
+
links.add(target);
|
|
63
|
+
}
|
|
64
|
+
wikiLinkPattern.lastIndex = 0;
|
|
65
|
+
for (const target of links) {
|
|
66
|
+
// Try to resolve the target to a permalink
|
|
67
|
+
const resolved = this.resolveTarget(target, notes, notesByPermalink);
|
|
68
|
+
if (resolved) {
|
|
69
|
+
const targetFacets = this.facetsMap.get(resolved);
|
|
70
|
+
if (targetFacets) {
|
|
71
|
+
targetFacets.aiReferenceCount++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Check if this note is a synthesis (has parent or source links)
|
|
76
|
+
const parentMatch = /^-\s*(?:親|parent):\s*\[\[([^\]]+)\]\]/im.exec(note.body);
|
|
77
|
+
if (parentMatch && (origin === "ai" || origin === "collaborative")) {
|
|
78
|
+
const parentTarget = parentMatch[1].split("|")[0].trim();
|
|
79
|
+
const resolved = this.resolveTarget(parentTarget, notes, notesByPermalink);
|
|
80
|
+
if (resolved) {
|
|
81
|
+
const parentFacets = this.facetsMap.get(resolved);
|
|
82
|
+
if (parentFacets) {
|
|
83
|
+
parentFacets.synthesisCount++;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
get(permalink) {
|
|
90
|
+
return this.facetsMap.get(permalink);
|
|
91
|
+
}
|
|
92
|
+
getAll() {
|
|
93
|
+
return this.facetsMap;
|
|
94
|
+
}
|
|
95
|
+
resolveTarget(target, notes, byPermalink) {
|
|
96
|
+
// Direct permalink match
|
|
97
|
+
if (byPermalink.has(target))
|
|
98
|
+
return target;
|
|
99
|
+
// Title match (case-insensitive)
|
|
100
|
+
const lowerTarget = target.toLowerCase();
|
|
101
|
+
for (const note of notes) {
|
|
102
|
+
if (note.title.toLowerCase() === lowerTarget)
|
|
103
|
+
return note.permalink;
|
|
104
|
+
if (note.permalink.toLowerCase() === lowerTarget)
|
|
105
|
+
return note.permalink;
|
|
106
|
+
const basename = note.permalink.split("/").pop()?.toLowerCase();
|
|
107
|
+
if (basename === lowerTarget)
|
|
108
|
+
return note.permalink;
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
emptyFacets(frontmatter) {
|
|
113
|
+
return {
|
|
114
|
+
contactCount: 0,
|
|
115
|
+
lastContact: null,
|
|
116
|
+
humanSignalCount: 0,
|
|
117
|
+
lastHumanSignal: null,
|
|
118
|
+
dominantSignal: null,
|
|
119
|
+
aiReferenceCount: 0,
|
|
120
|
+
synthesisCount: 0,
|
|
121
|
+
origin: safeString(frontmatter.origin),
|
|
122
|
+
createdVia: safeString(frontmatter.created_via),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function safeString(value) {
|
|
127
|
+
if (typeof value === "string" && value.length > 0)
|
|
128
|
+
return value;
|
|
129
|
+
return null;
|
|
130
|
+
}
|
package/dist/src/index.js
CHANGED
|
@@ -3,13 +3,14 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { lintStructureResponseModeHint, lintStructureResponsePlan, summarizeLintStructureResponse, } from "./auditResponseSummary.js";
|
|
5
5
|
import { loadConfig } from "./config.js";
|
|
6
|
-
import { memoryContractResponseModeHint, memoryContractResponsePlan, summarizeMemoryContractResponse, } from "./contractResponseSummary.js";
|
|
7
6
|
import { summarizeNoConflict } from "./conflictSummary.js";
|
|
8
7
|
import { LocalNoteStore } from "./noteStore.js";
|
|
9
8
|
import { GitHubStorage, LocalFsStorage } from "./storage/index.js";
|
|
9
|
+
import { SignalLog } from "./signalLog.js";
|
|
10
|
+
import { EngagementIndex } from "./engagement.js";
|
|
10
11
|
import { normalizeDateInput, preview } from "./normalizers.js";
|
|
11
12
|
import { summarizeDeleteImpact, summarizeRenameImpact } from "./operationImpact.js";
|
|
12
|
-
import {
|
|
13
|
+
import { summarizeReadNoteResponse, summarizeSearchResponse, } from "./readResponseSummary.js";
|
|
13
14
|
import { summarizeLintChangeTrigger, summarizeReadChangeTrigger } from "./changeTriggerSummary.js";
|
|
14
15
|
import { RESPONSE_MODE_VALUES, normalizeResponseMode, responseModeAtLeast } from "./responseMode.js";
|
|
15
16
|
import { TOOL_SCOPE_LABELS, responsibilityWarningCode } from "./toolScope.js";
|
|
@@ -117,7 +118,10 @@ function appendScopeLabel(lines, scopeLabel) {
|
|
|
117
118
|
lines.push(`scope_label: ${scopeLabel}`);
|
|
118
119
|
}
|
|
119
120
|
function appendResponsibilityWarning(lines, warnings) {
|
|
120
|
-
|
|
121
|
+
const code = responsibilityWarningCode(warnings);
|
|
122
|
+
if (code === "none")
|
|
123
|
+
return;
|
|
124
|
+
lines.push(`responsibility_warning: ${code}`);
|
|
121
125
|
}
|
|
122
126
|
function appendTemplateHints(lines, hints) {
|
|
123
127
|
lines.push(`template_hint_primary: ${hints.primary}`);
|
|
@@ -262,10 +266,9 @@ function appendAuditResponseSummary(lines, summary) {
|
|
|
262
266
|
}
|
|
263
267
|
}
|
|
264
268
|
function appendChangeTriggerSummary(lines, summary) {
|
|
265
|
-
|
|
266
|
-
if (summary.code === "none") {
|
|
269
|
+
if (summary.code === "none")
|
|
267
270
|
return;
|
|
268
|
-
}
|
|
271
|
+
lines.push(`change_trigger: ${summary.code}`);
|
|
269
272
|
lines.push(`trigger_confidence: ${summary.confidence}`);
|
|
270
273
|
lines.push(`cascade_targets: ${summary.cascadeTargets?.join(", ") ?? "(none)"}`);
|
|
271
274
|
lines.push(`noise_risk: ${summary.noiseRisk}`);
|
|
@@ -276,9 +279,7 @@ function appendOperationImpact(lines, impact) {
|
|
|
276
279
|
lines.push(`impact_reasons: ${impact.reasons.join(", ")}`);
|
|
277
280
|
}
|
|
278
281
|
function appendConflictSummary(lines, summary) {
|
|
279
|
-
lines.push(`conflict_type: ${summary.conflictType}`);
|
|
280
282
|
lines.push(`server_updated: ${summary.serverUpdated}`);
|
|
281
|
-
lines.push(`retry_hint: ${summary.retryHint}`);
|
|
282
283
|
}
|
|
283
284
|
const responseModeSchema = z.enum(RESPONSE_MODE_VALUES).optional().describe("default: standard");
|
|
284
285
|
async function main() {
|
|
@@ -288,7 +289,13 @@ async function main() {
|
|
|
288
289
|
: new LocalFsStorage(config.root);
|
|
289
290
|
await storage.ensureRoot();
|
|
290
291
|
const store = new LocalNoteStore(config, storage);
|
|
292
|
+
const signalLog = new SignalLog(storage);
|
|
293
|
+
const engagementIndex = new EngagementIndex();
|
|
294
|
+
let storageContextEmitted = false;
|
|
291
295
|
const appendStorageContext = (lines) => {
|
|
296
|
+
if (storageContextEmitted)
|
|
297
|
+
return;
|
|
298
|
+
storageContextEmitted = true;
|
|
292
299
|
lines.push(`storage_backend: ${config.github ? "github" : "filesystem"}`);
|
|
293
300
|
lines.push(`storage_root: ${config.root}`);
|
|
294
301
|
if (config.github) {
|
|
@@ -296,72 +303,99 @@ async function main() {
|
|
|
296
303
|
lines.push(`storage_branch: ${config.github.branch}`);
|
|
297
304
|
}
|
|
298
305
|
};
|
|
306
|
+
/** Returns storage context lines only on first call, empty array after. */
|
|
307
|
+
const storageContextLines = () => {
|
|
308
|
+
if (storageContextEmitted)
|
|
309
|
+
return [];
|
|
310
|
+
storageContextEmitted = true;
|
|
311
|
+
return [
|
|
312
|
+
`storage_backend: ${config.github ? "github" : "filesystem"}`,
|
|
313
|
+
`storage_root: ${config.root}`,
|
|
314
|
+
...(config.github
|
|
315
|
+
? [`storage_repo: ${config.github.owner}/${config.github.repo}`, `storage_branch: ${config.github.branch}`]
|
|
316
|
+
: []),
|
|
317
|
+
];
|
|
318
|
+
};
|
|
299
319
|
const appendAutoProjectHealth = async (lines) => {
|
|
300
320
|
try {
|
|
301
321
|
const health = await store.lintStructure({
|
|
302
322
|
limit: 3,
|
|
303
323
|
});
|
|
324
|
+
const hasAttention = health.unresolvedWikiLinkCount > 0
|
|
325
|
+
|| health.duplicateWarningCount > 0
|
|
326
|
+
|| health.technicalDebtSignals.dependencyDirectionViolationCount > 0
|
|
327
|
+
|| health.orphanNoteCount > 0;
|
|
328
|
+
if (!hasAttention)
|
|
329
|
+
return;
|
|
304
330
|
appendProjectHealthSummary(lines, health);
|
|
305
331
|
}
|
|
306
|
-
catch
|
|
307
|
-
|
|
308
|
-
lines.push(`project_health: unavailable (${normalizeError(error)})`);
|
|
332
|
+
catch {
|
|
333
|
+
// silently skip if lint fails
|
|
309
334
|
}
|
|
310
335
|
};
|
|
311
336
|
const server = new McpServer({
|
|
312
337
|
name: "kioq",
|
|
313
338
|
version: "0.7.0",
|
|
314
339
|
});
|
|
315
|
-
server.tool("
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
firstIndexCandidateTitle: result.indexNoteCandidates[0]?.title,
|
|
340
|
+
server.tool("orient", "kioqを通じて思考を構造化するツール。チェインバー型パイロット支援システムとして、応答を組み立てる前に現在の思考を渡すことで、3つのスロットを返します: (1) relevant — 思考に直接関連するノート(engagement重み付け)、(2) active_context — 進行中のflowと最近のアクティブなノート、(3) discovery — 未接触だが接点がありそうなノート(セレンディピティ)。各ノートのengagement(接触回数、人間の反応、AI被参照数、origin)が付与されます。observationで人間の反応を記録でき、それがengagementに蓄積されます。対話の各ターンで呼び出すことを推奨します。", {
|
|
341
|
+
thought: z.string().min(1).describe("今何を考えているか、何について応答しようとしているか"),
|
|
342
|
+
references: z.array(z.string()).optional().describe("明示的に関連するノートの identifier(あれば)"),
|
|
343
|
+
observation: z.object({
|
|
344
|
+
note: z.string().min(1).describe("観察対象のノート identifier"),
|
|
345
|
+
signal: z.enum(["cited", "discussed", "applied", "questioned", "dismissed"]).describe("人間の反応の種類"),
|
|
346
|
+
}).optional().describe("直前の対話で人間がノートの概念に反応した場合に記録"),
|
|
347
|
+
}, async ({ thought, references, observation }) => {
|
|
348
|
+
const signals = await signalLog.load();
|
|
349
|
+
const notes = await store.loadAllNotes();
|
|
350
|
+
engagementIndex.build(signals, notes);
|
|
351
|
+
const result = await store.orient({
|
|
352
|
+
thought,
|
|
353
|
+
references,
|
|
354
|
+
observation,
|
|
355
|
+
engagementIndex,
|
|
356
|
+
signalLog,
|
|
333
357
|
});
|
|
358
|
+
await signalLog.flush();
|
|
334
359
|
const lines = [];
|
|
335
|
-
lines.push(
|
|
360
|
+
lines.push(`project: ${result.project}`);
|
|
336
361
|
appendStorageContext(lines);
|
|
337
|
-
|
|
338
|
-
lines.push(`
|
|
339
|
-
appendReadResponseSummary(lines, summary);
|
|
340
|
-
lines.push(`件数: ${result.results.length}`);
|
|
362
|
+
lines.push(`scope_label: orient`);
|
|
363
|
+
lines.push(`thought: ${thought.slice(0, 200)}`);
|
|
341
364
|
lines.push("");
|
|
342
|
-
if (result.
|
|
343
|
-
lines.push("
|
|
365
|
+
if (result.relevant.length > 0) {
|
|
366
|
+
lines.push("--- relevant ---");
|
|
367
|
+
for (const item of result.relevant) {
|
|
368
|
+
lines.push(`- ${item.title}`);
|
|
369
|
+
lines.push(` permalink: ${item.permalink}`);
|
|
370
|
+
lines.push(` reason: ${item.reason}`);
|
|
371
|
+
lines.push(` engagement: contact=${item.engagement.contactCount} human=${item.engagement.humanSignalCount} ai_ref=${item.engagement.aiReferenceCount} origin=${item.engagement.origin ?? "unknown"} created_via=${item.engagement.createdVia ?? "unknown"}`);
|
|
372
|
+
lines.push(` snippet: ${item.snippet.slice(0, 200)}`);
|
|
373
|
+
}
|
|
374
|
+
lines.push("");
|
|
344
375
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
lines.push(
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
if (responseModeAtLeast(responseMode, "verbose")) {
|
|
355
|
-
lines.push(` index_like_reasons: ${item.indexLike && item.indexReasons.length > 0 ? item.indexReasons.join(", ") : "(none)"}`);
|
|
356
|
-
}
|
|
357
|
-
});
|
|
376
|
+
if (result.activeContext.length > 0) {
|
|
377
|
+
lines.push("--- active_context ---");
|
|
378
|
+
for (const item of result.activeContext) {
|
|
379
|
+
lines.push(`- ${item.title}`);
|
|
380
|
+
lines.push(` permalink: ${item.permalink}`);
|
|
381
|
+
lines.push(` reason: ${item.reason}`);
|
|
382
|
+
lines.push(` engagement: contact=${item.engagement.contactCount} human=${item.engagement.humanSignalCount} origin=${item.engagement.origin ?? "unknown"}`);
|
|
383
|
+
}
|
|
384
|
+
lines.push("");
|
|
358
385
|
}
|
|
359
|
-
if (
|
|
386
|
+
if (result.discovery.length > 0) {
|
|
387
|
+
lines.push("--- discovery ---");
|
|
388
|
+
for (const item of result.discovery) {
|
|
389
|
+
lines.push(`- ${item.title}`);
|
|
390
|
+
lines.push(` permalink: ${item.permalink}`);
|
|
391
|
+
lines.push(` reason: ${item.reason}`);
|
|
392
|
+
lines.push(` engagement: contact=${item.engagement.contactCount} origin=${item.engagement.origin ?? "unknown"} created_via=${item.engagement.createdVia ?? "unknown"}`);
|
|
393
|
+
lines.push(` snippet: ${item.snippet.slice(0, 160)}`);
|
|
394
|
+
}
|
|
360
395
|
lines.push("");
|
|
361
|
-
appendIndexNoteCandidates(lines, result.indexNoteCandidates, "recommended_indexes");
|
|
362
396
|
}
|
|
363
|
-
if (
|
|
364
|
-
|
|
397
|
+
if (result.relevant.length === 0 && result.discovery.length === 0) {
|
|
398
|
+
lines.push("関連するノートは見つかりませんでした。");
|
|
365
399
|
}
|
|
366
400
|
return textResult(lines.join("\n"));
|
|
367
401
|
});
|
|
@@ -411,11 +445,13 @@ async function main() {
|
|
|
411
445
|
if (responseModeAtLeast(responseMode, "standard")) {
|
|
412
446
|
lines.push(` permalink: ${item.permalink}`);
|
|
413
447
|
lines.push(` file: ${item.filePath}`);
|
|
414
|
-
|
|
415
|
-
|
|
448
|
+
if (item.exactIdentifierMatch)
|
|
449
|
+
lines.push(` exact_identifier_match: yes`);
|
|
450
|
+
if (item.indexLike)
|
|
451
|
+
lines.push(` index_like: yes`);
|
|
416
452
|
}
|
|
417
|
-
if (responseModeAtLeast(responseMode, "verbose")) {
|
|
418
|
-
lines.push(` index_like_reasons: ${item.
|
|
453
|
+
if (responseModeAtLeast(responseMode, "verbose") && item.indexLike && item.indexReasons.length > 0) {
|
|
454
|
+
lines.push(` index_like_reasons: ${item.indexReasons.join(", ")}`);
|
|
419
455
|
}
|
|
420
456
|
});
|
|
421
457
|
}
|
|
@@ -454,8 +490,7 @@ async function main() {
|
|
|
454
490
|
const lines = [
|
|
455
491
|
`identifier: ${identifier}`,
|
|
456
492
|
`project: ${result.project}`,
|
|
457
|
-
|
|
458
|
-
`storage_root: ${config.root}`,
|
|
493
|
+
...storageContextLines(),
|
|
459
494
|
`scope_label: ${TOOL_SCOPE_LABELS.read_note}`,
|
|
460
495
|
`response_mode: ${responseMode}`,
|
|
461
496
|
`primary_navigation_signal: ${summary.primaryNavigationSignal}`,
|
|
@@ -464,9 +499,6 @@ async function main() {
|
|
|
464
499
|
`file: ${result.note.relativePath}`,
|
|
465
500
|
`permalink: ${result.note.permalink}`,
|
|
466
501
|
];
|
|
467
|
-
if (config.github) {
|
|
468
|
-
lines.splice(4, 0, `storage_repo: ${config.github.owner}/${config.github.repo}`, `storage_branch: ${config.github.branch}`);
|
|
469
|
-
}
|
|
470
502
|
if (summary.nextRecommendedTarget) {
|
|
471
503
|
insertBeforeLine(lines, "next_recommended_action:", `next_recommended_target: ${summary.nextRecommendedTarget}`);
|
|
472
504
|
}
|
|
@@ -476,7 +508,8 @@ async function main() {
|
|
|
476
508
|
if (responseModeAtLeast(responseMode, "standard")) {
|
|
477
509
|
lines.push(`link_health: ${result.linkHealth.resolved}/${result.linkHealth.total} resolved`);
|
|
478
510
|
lines.push(`backlinks: ${result.backlinkCount}`);
|
|
479
|
-
|
|
511
|
+
if (result.orphanWarning)
|
|
512
|
+
lines.push(`orphan_warning: yes`);
|
|
480
513
|
appendDocumentClarity(lines, result.documentClarity, responseModeAtLeast(responseMode, "verbose"));
|
|
481
514
|
appendUnresolvedWikiLinks(lines, result.unresolvedWikiLinks);
|
|
482
515
|
}
|
|
@@ -516,285 +549,14 @@ async function main() {
|
|
|
516
549
|
lines.push(` score: ${item.score}`);
|
|
517
550
|
lines.push(` permalink: ${item.permalink}`);
|
|
518
551
|
lines.push(` file: ${item.filePath}`);
|
|
519
|
-
|
|
552
|
+
if (item.exactIdentifierMatch)
|
|
553
|
+
lines.push(` exact_identifier_match: yes`);
|
|
520
554
|
});
|
|
521
555
|
}
|
|
522
556
|
return textResult(lines.join("\n"));
|
|
523
557
|
}
|
|
524
558
|
});
|
|
525
|
-
server.tool("
|
|
526
|
-
identifier: z.string().min(1),
|
|
527
|
-
response_mode: responseModeSchema,
|
|
528
|
-
}, async ({ identifier, response_mode }) => {
|
|
529
|
-
const responseMode = normalizeResponseMode(response_mode);
|
|
530
|
-
const result = await store.resolveLinks({
|
|
531
|
-
identifier,
|
|
532
|
-
});
|
|
533
|
-
const unresolvedLinkCount = result.links.filter((link) => !link.resolved).length;
|
|
534
|
-
const summary = summarizeResolveLinksResponse({
|
|
535
|
-
indexLike: result.indexLike,
|
|
536
|
-
unresolvedLinkCount,
|
|
537
|
-
boundaryWarningCount: result.boundaryWarnings.length,
|
|
538
|
-
indexCandidateTitle: result.indexNoteCandidates[0]?.title,
|
|
539
|
-
});
|
|
540
|
-
const lines = [];
|
|
541
|
-
lines.push(`identifier: ${identifier}`);
|
|
542
|
-
lines.push(`project: ${result.project}`);
|
|
543
|
-
appendStorageContext(lines);
|
|
544
|
-
appendScopeLabel(lines, TOOL_SCOPE_LABELS.resolve_links);
|
|
545
|
-
lines.push(`response_mode: ${responseMode}`);
|
|
546
|
-
appendReadResponseSummary(lines, summary);
|
|
547
|
-
appendResponsibilityWarning(lines, result.boundaryWarnings);
|
|
548
|
-
lines.push(`file: ${result.note.relativePath}`);
|
|
549
|
-
lines.push(`wikilinks: ${result.links.length}`);
|
|
550
|
-
if (responseModeAtLeast(responseMode, "verbose")) {
|
|
551
|
-
appendIndexLike(lines, result.indexLike, result.indexReasons);
|
|
552
|
-
}
|
|
553
|
-
appendBoundaryWarnings(lines, result.boundaryWarnings);
|
|
554
|
-
lines.push("");
|
|
555
|
-
if (result.links.length === 0) {
|
|
556
|
-
lines.push("WikiLink は見つかりませんでした。");
|
|
557
|
-
}
|
|
558
|
-
else {
|
|
559
|
-
result.links.forEach((link, index) => {
|
|
560
|
-
lines.push(`${index + 1}. ${link.raw}`);
|
|
561
|
-
if (link.resolved) {
|
|
562
|
-
lines.push(` resolved: yes`);
|
|
563
|
-
lines.push(` to: ${link.resolvedTitle} (${link.resolvedFilePath})`);
|
|
564
|
-
}
|
|
565
|
-
else {
|
|
566
|
-
lines.push(` resolved: no`);
|
|
567
|
-
lines.push(` reason: ${link.reason ?? "unknown"}`);
|
|
568
|
-
lines.push(` target: ${link.target}`);
|
|
569
|
-
}
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
if (responseModeAtLeast(responseMode, "verbose")) {
|
|
573
|
-
lines.push("");
|
|
574
|
-
appendIndexNoteCandidates(lines, result.indexNoteCandidates);
|
|
575
|
-
}
|
|
576
|
-
return textResult(lines.join("\n"));
|
|
577
|
-
});
|
|
578
|
-
server.tool("list_backlinks", "指定ノートへのバックリンク一覧を返します。", {
|
|
579
|
-
identifier: z.string().min(1),
|
|
580
|
-
limit: z.number().int().min(1).max(100).optional().describe("最大件数 (default: 20)"),
|
|
581
|
-
response_mode: responseModeSchema,
|
|
582
|
-
}, async ({ identifier, limit, response_mode }) => {
|
|
583
|
-
const responseMode = normalizeResponseMode(response_mode);
|
|
584
|
-
const result = await store.listBacklinks({
|
|
585
|
-
identifier,
|
|
586
|
-
limit: limit ?? 20,
|
|
587
|
-
});
|
|
588
|
-
const summary = summarizeBacklinksResponse({
|
|
589
|
-
indexLike: result.indexLike,
|
|
590
|
-
backlinkCount: result.backlinks.length,
|
|
591
|
-
boundaryWarningCount: result.boundaryWarnings.length,
|
|
592
|
-
firstBacklinkTitle: result.backlinks[0]?.title,
|
|
593
|
-
indexCandidateTitle: result.indexNoteCandidates[0]?.title,
|
|
594
|
-
});
|
|
595
|
-
const lines = [];
|
|
596
|
-
lines.push(`target: ${result.target.title}`);
|
|
597
|
-
lines.push(`project: ${result.project}`);
|
|
598
|
-
appendStorageContext(lines);
|
|
599
|
-
appendScopeLabel(lines, TOOL_SCOPE_LABELS.list_backlinks);
|
|
600
|
-
lines.push(`response_mode: ${responseMode}`);
|
|
601
|
-
appendReadResponseSummary(lines, summary);
|
|
602
|
-
appendResponsibilityWarning(lines, result.boundaryWarnings);
|
|
603
|
-
if (responseModeAtLeast(responseMode, "verbose")) {
|
|
604
|
-
appendIndexLike(lines, result.indexLike, result.indexReasons, "target");
|
|
605
|
-
}
|
|
606
|
-
appendBoundaryWarnings(lines, result.boundaryWarnings);
|
|
607
|
-
lines.push(`backlinks: ${result.backlinks.length}`);
|
|
608
|
-
lines.push("");
|
|
609
|
-
if (result.backlinks.length === 0) {
|
|
610
|
-
lines.push("バックリンクは見つかりませんでした。");
|
|
611
|
-
}
|
|
612
|
-
else {
|
|
613
|
-
result.backlinks.forEach((item, index) => {
|
|
614
|
-
lines.push(`${index + 1}. ${item.title}`);
|
|
615
|
-
lines.push(` count: ${item.count}`);
|
|
616
|
-
if (responseModeAtLeast(responseMode, "standard")) {
|
|
617
|
-
lines.push(` file: ${item.filePath}`);
|
|
618
|
-
lines.push(` updated: ${item.updated}`);
|
|
619
|
-
}
|
|
620
|
-
item.examples.forEach((example) => {
|
|
621
|
-
lines.push(` example: ${example}`);
|
|
622
|
-
});
|
|
623
|
-
});
|
|
624
|
-
}
|
|
625
|
-
if (responseModeAtLeast(responseMode, "verbose")) {
|
|
626
|
-
lines.push("");
|
|
627
|
-
appendIndexNoteCandidates(lines, result.indexNoteCandidates);
|
|
628
|
-
}
|
|
629
|
-
return textResult(lines.join("\n"));
|
|
630
|
-
});
|
|
631
|
-
server.tool("context_bundle", "指定ノートを中心に、関連ノートを優先度付きで返します。", {
|
|
632
|
-
identifier: z.string().min(1),
|
|
633
|
-
limit: z.number().int().min(1).max(30).optional().describe("最大件数 (default: 8)"),
|
|
634
|
-
response_mode: responseModeSchema,
|
|
635
|
-
}, async ({ identifier, limit, response_mode }) => {
|
|
636
|
-
const responseMode = normalizeResponseMode(response_mode);
|
|
637
|
-
const result = await store.contextBundle({
|
|
638
|
-
identifier,
|
|
639
|
-
limit: limit ?? 8,
|
|
640
|
-
});
|
|
641
|
-
const summary = summarizeContextBundleResponse({
|
|
642
|
-
sourceIndexLike: result.source.indexLike,
|
|
643
|
-
bundleItemCount: result.items.length,
|
|
644
|
-
boundaryWarningCount: result.source.boundaryWarnings.length,
|
|
645
|
-
firstBundleItemTitle: result.items[0]?.title,
|
|
646
|
-
indexCandidateTitle: result.indexNoteCandidates[0]?.title,
|
|
647
|
-
});
|
|
648
|
-
const lines = [];
|
|
649
|
-
lines.push(`source: ${result.source.title}`);
|
|
650
|
-
lines.push(`project: ${result.project}`);
|
|
651
|
-
appendStorageContext(lines);
|
|
652
|
-
appendScopeLabel(lines, TOOL_SCOPE_LABELS.context_bundle);
|
|
653
|
-
lines.push(`response_mode: ${responseMode}`);
|
|
654
|
-
appendReadResponseSummary(lines, summary);
|
|
655
|
-
appendResponsibilityWarning(lines, result.source.boundaryWarnings);
|
|
656
|
-
if (responseModeAtLeast(responseMode, "standard")) {
|
|
657
|
-
lines.push(`memory_kind: ${result.source.memoryKind}`);
|
|
658
|
-
lines.push(`file: ${result.source.filePath}`);
|
|
659
|
-
}
|
|
660
|
-
if (responseModeAtLeast(responseMode, "verbose")) {
|
|
661
|
-
appendIndexLike(lines, result.source.indexLike, result.source.indexReasons, "source");
|
|
662
|
-
}
|
|
663
|
-
appendBoundaryWarnings(lines, result.source.boundaryWarnings);
|
|
664
|
-
lines.push(`bundle_items: ${result.items.length}`);
|
|
665
|
-
lines.push("");
|
|
666
|
-
if (result.items.length === 0) {
|
|
667
|
-
lines.push("関連ノートは見つかりませんでした。");
|
|
668
|
-
}
|
|
669
|
-
else {
|
|
670
|
-
result.items.forEach((item, index) => {
|
|
671
|
-
lines.push(`${index + 1}. ${item.title}`);
|
|
672
|
-
lines.push(` score: ${item.score}`);
|
|
673
|
-
if (responseModeAtLeast(responseMode, "standard")) {
|
|
674
|
-
lines.push(` memory_kind: ${item.memoryKind}`);
|
|
675
|
-
lines.push(` file: ${item.filePath}`);
|
|
676
|
-
}
|
|
677
|
-
if (responseModeAtLeast(responseMode, "verbose")) {
|
|
678
|
-
lines.push(` reasons: ${item.reasons.join(", ")}`);
|
|
679
|
-
}
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
if (responseModeAtLeast(responseMode, "verbose")) {
|
|
683
|
-
lines.push("");
|
|
684
|
-
appendIndexNoteCandidates(lines, result.indexNoteCandidates);
|
|
685
|
-
}
|
|
686
|
-
if (responseModeAtLeast(responseMode, "standard")) {
|
|
687
|
-
appendExplorationGuidance(lines, result.explorationGuidance);
|
|
688
|
-
}
|
|
689
|
-
return textResult(lines.join("\n"));
|
|
690
|
-
});
|
|
691
|
-
server.tool("memory_contract", "kioq の Memory Contract(構造化ルール)を返します。", {
|
|
692
|
-
response_mode: responseModeSchema,
|
|
693
|
-
}, async ({ response_mode }) => {
|
|
694
|
-
const responseMode = normalizeResponseMode(response_mode);
|
|
695
|
-
const plan = memoryContractResponsePlan(responseMode);
|
|
696
|
-
const summary = {
|
|
697
|
-
...summarizeMemoryContractResponse(),
|
|
698
|
-
...memoryContractResponseModeHint(responseMode),
|
|
699
|
-
};
|
|
700
|
-
const contract = store.getMemoryContract();
|
|
701
|
-
const lines = [];
|
|
702
|
-
appendStorageContext(lines);
|
|
703
|
-
appendScopeLabel(lines, TOOL_SCOPE_LABELS.memory_contract);
|
|
704
|
-
lines.push(`response_mode: ${responseMode}`);
|
|
705
|
-
appendAuditResponseSummary(lines, summary);
|
|
706
|
-
lines.push(`memory_contract_version: ${contract.version}`);
|
|
707
|
-
lines.push(`required_frontmatter: ${contract.requiredFrontmatter.join(", ")}`);
|
|
708
|
-
lines.push(`flow_required_frontmatter: ${contract.flowRequiredFrontmatter.join(", ")}`);
|
|
709
|
-
lines.push(`parent_markers: ${contract.parentLinkMarkers.join(", ")}`);
|
|
710
|
-
lines.push(`source_metadata_canonical_field: ${contract.optionalSourceMetadata.canonicalField}`);
|
|
711
|
-
lines.push(`source_metadata_auxiliary_fields: ${contract.optionalSourceMetadata.auxiliaryFields.join(", ")}`);
|
|
712
|
-
lines.push(`index_threshold_score_at_least: ${contract.indexNavigationPolicy.thresholds.scoreAtLeast}`);
|
|
713
|
-
lines.push(`index_threshold_index_sections_at_least: ${contract.indexNavigationPolicy.thresholds.indexSectionsAtLeast}`);
|
|
714
|
-
lines.push(`technical_debt_threshold_stale_flow_days: ${contract.technicalDebtPolicy.thresholds.staleFlowDays}`);
|
|
715
|
-
lines.push("sample_templates_available: stock, flow, index");
|
|
716
|
-
if (!plan.includePolicyRules) {
|
|
717
|
-
return textResult(lines.join("\n"));
|
|
718
|
-
}
|
|
719
|
-
lines.push("requirements:");
|
|
720
|
-
lines.push(`- min_resolved_links: ${contract.requirements.minResolvedLinks}`);
|
|
721
|
-
lines.push(`- min_backlinks: ${contract.requirements.minBacklinks}`);
|
|
722
|
-
lines.push(`- min_parent_links: ${contract.requirements.minParentLinks}`);
|
|
723
|
-
lines.push(`- min_related_links: ${contract.requirements.minRelatedLinks}`);
|
|
724
|
-
lines.push(`- max_unresolved_links: ${contract.requirements.maxUnresolvedLinks}`);
|
|
725
|
-
lines.push(`- min_tags: ${contract.requirements.minTags}`);
|
|
726
|
-
lines.push("flow_requirements:");
|
|
727
|
-
lines.push(`- min_resolved_links: ${contract.flowRequirements.minResolvedLinks}`);
|
|
728
|
-
lines.push(`- min_backlinks: ${contract.flowRequirements.minBacklinks}`);
|
|
729
|
-
lines.push(`- min_parent_links: ${contract.flowRequirements.minParentLinks}`);
|
|
730
|
-
lines.push(`- min_related_links: ${contract.flowRequirements.minRelatedLinks}`);
|
|
731
|
-
lines.push(`- max_unresolved_links: ${contract.flowRequirements.maxUnresolvedLinks}`);
|
|
732
|
-
lines.push(`- min_tags: ${contract.flowRequirements.minTags}`);
|
|
733
|
-
lines.push("optional_source_metadata:");
|
|
734
|
-
lines.push(`- canonical_field: ${contract.optionalSourceMetadata.canonicalField}`);
|
|
735
|
-
lines.push(`- auxiliary_fields: ${contract.optionalSourceMetadata.auxiliaryFields.join(", ")}`);
|
|
736
|
-
contract.optionalSourceMetadata.rules.forEach((rule) => {
|
|
737
|
-
lines.push(`- rule: ${rule}`);
|
|
738
|
-
});
|
|
739
|
-
lines.push("index_navigation_policy:");
|
|
740
|
-
lines.push(`- excluded_memory_kinds: ${contract.indexNavigationPolicy.excludedMemoryKinds.join(", ")}`);
|
|
741
|
-
lines.push(`- threshold_score_at_least: ${contract.indexNavigationPolicy.thresholds.scoreAtLeast}`);
|
|
742
|
-
lines.push(`- threshold_index_sections_at_least: ${contract.indexNavigationPolicy.thresholds.indexSectionsAtLeast}`);
|
|
743
|
-
lines.push(`- configurable_via_env: ${contract.indexNavigationPolicy.configurableVia.env.join(", ")}`);
|
|
744
|
-
lines.push(`- configurable_via_cli: ${contract.indexNavigationPolicy.configurableVia.cli.join(", ")}`);
|
|
745
|
-
if (plan.includePolicySignals) {
|
|
746
|
-
contract.indexNavigationPolicy.signals.forEach((signal) => {
|
|
747
|
-
lines.push(`- signal: ${signal.code} weight=${signal.weight} description=${signal.description}`);
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
contract.indexNavigationPolicy.rules.forEach((rule) => {
|
|
751
|
-
lines.push(`- rule: ${rule}`);
|
|
752
|
-
});
|
|
753
|
-
lines.push("technical_debt_policy:");
|
|
754
|
-
lines.push(`- signals: ${contract.technicalDebtPolicy.signals.join(", ")}`);
|
|
755
|
-
lines.push(`- stale_flow_states: ${contract.technicalDebtPolicy.staleFlowStates.join(", ")}`);
|
|
756
|
-
lines.push(`- threshold_stale_flow_days: ${contract.technicalDebtPolicy.thresholds.staleFlowDays}`);
|
|
757
|
-
lines.push(`- stale_flow_age_bucket_near_threshold: ${contract.technicalDebtPolicy.staleFlowAgeBuckets.nearThreshold}`);
|
|
758
|
-
lines.push(`- stale_flow_age_bucket_aging: ${contract.technicalDebtPolicy.staleFlowAgeBuckets.aging}`);
|
|
759
|
-
lines.push(`- stale_flow_age_bucket_long_stale: ${contract.technicalDebtPolicy.staleFlowAgeBuckets.longStale}`);
|
|
760
|
-
lines.push(`- cleanup_ratio_definition: ${contract.technicalDebtPolicy.cleanupRatioDefinition}`);
|
|
761
|
-
lines.push(`- cleanup_ready_count_definition: ${contract.technicalDebtPolicy.cleanupReadyCountDefinition}`);
|
|
762
|
-
lines.push(`- attention_note_count_definition: ${contract.technicalDebtPolicy.attentionNoteCountDefinition}`);
|
|
763
|
-
lines.push(`- dependency_direction_violation_count_definition: ${contract.technicalDebtPolicy.dependencyDirectionViolationCountDefinition}`);
|
|
764
|
-
lines.push(`- single_use_tag_candidate_count_definition: ${contract.technicalDebtPolicy.singleUseTagCandidateCountDefinition}`);
|
|
765
|
-
lines.push(`- title_body_mismatch_candidate_count_definition: ${contract.technicalDebtPolicy.titleBodyMismatchCandidateCountDefinition}`);
|
|
766
|
-
lines.push(`- unresolved_wikilinks_delta_status: ${contract.technicalDebtPolicy.unresolvedWikilinksDeltaStatus}`);
|
|
767
|
-
lines.push(`- configurable_via_env: ${contract.technicalDebtPolicy.configurableVia.env.join(", ")}`);
|
|
768
|
-
lines.push(`- configurable_via_cli: ${contract.technicalDebtPolicy.configurableVia.cli.join(", ")}`);
|
|
769
|
-
lines.push("response_mode_policy:");
|
|
770
|
-
lines.push(`- supported_read_tools: ${contract.responseModePolicy.supportedTools.read.join(", ")}`);
|
|
771
|
-
lines.push(`- supported_audit_tools: ${contract.responseModePolicy.supportedTools.audit.join(", ")}`);
|
|
772
|
-
lines.push(`- minimal: ${contract.responseModePolicy.modes.minimal}`);
|
|
773
|
-
lines.push(`- standard: ${contract.responseModePolicy.modes.standard}`);
|
|
774
|
-
lines.push(`- verbose: ${contract.responseModePolicy.modes.verbose}`);
|
|
775
|
-
contract.responseModePolicy.rules.forEach((rule) => {
|
|
776
|
-
lines.push(`- rule: ${rule}`);
|
|
777
|
-
});
|
|
778
|
-
if (plan.includeSampleTemplates) {
|
|
779
|
-
lines.push("");
|
|
780
|
-
lines.push("sample_template_stock:");
|
|
781
|
-
lines.push("```md");
|
|
782
|
-
contract.sampleTemplates.stock.forEach((line) => lines.push(line));
|
|
783
|
-
lines.push("```");
|
|
784
|
-
lines.push("");
|
|
785
|
-
lines.push("sample_template_flow:");
|
|
786
|
-
lines.push("```md");
|
|
787
|
-
contract.sampleTemplates.flow.forEach((line) => lines.push(line));
|
|
788
|
-
lines.push("```");
|
|
789
|
-
lines.push("");
|
|
790
|
-
lines.push("sample_template_index:");
|
|
791
|
-
lines.push("```md");
|
|
792
|
-
contract.sampleTemplates.index.forEach((line) => lines.push(line));
|
|
793
|
-
lines.push("```");
|
|
794
|
-
}
|
|
795
|
-
return textResult(lines.join("\n"));
|
|
796
|
-
});
|
|
797
|
-
server.tool("lint_structure", "構造化を促すために、ノート群の問題を診断して優先順位付きで返します。", {
|
|
559
|
+
server.tool("lint_structure", "ノート群の構造的問題を診断し、優先順位付きで返します。", {
|
|
798
560
|
limit: z.number().int().min(1).max(200).optional().describe("返却する issue 最大件数 (default: 50)"),
|
|
799
561
|
response_mode: responseModeSchema,
|
|
800
562
|
}, async ({ limit, response_mode }) => {
|
|
@@ -914,19 +676,93 @@ async function main() {
|
|
|
914
676
|
}
|
|
915
677
|
return textResult(lines.join("\n"));
|
|
916
678
|
});
|
|
917
|
-
server.tool("write_note", "
|
|
679
|
+
server.tool("write_note", "ノートを作成/更新します。note_type='flow' で進行中の問いを記録、それ以外で知識(stock)を記録します。origin で誰が起点かを記録し、engagement追跡の基盤になります。", {
|
|
918
680
|
title: z.string().min(1),
|
|
919
|
-
content: z.string().min(1),
|
|
920
|
-
directory: z.string().min(1).optional().describe("default: KIOQ_DEFAULT_DIRECTORY
|
|
681
|
+
content: z.string().min(1).describe("note_type='flow' の場合は ## Notes セクションの本文"),
|
|
682
|
+
directory: z.string().min(1).optional().describe("default: stock→KIOQ_DEFAULT_DIRECTORY, flow→Flow"),
|
|
921
683
|
tags: z.array(z.string()).optional(),
|
|
922
|
-
note_type: z.string().optional().describe("
|
|
923
|
-
|
|
684
|
+
note_type: z.string().optional().describe("stock(default) / flow / note / decision / task"),
|
|
685
|
+
origin: z.enum(["human", "ai", "collaborative"]).optional().describe("誰が起点か (human=体験, ai=AI生成/抽出, collaborative=対話から)"),
|
|
686
|
+
created_via: z.enum(["experience", "dialogue", "extraction", "curation", "capture"]).optional().describe("生成経路"),
|
|
687
|
+
human_role: z.enum(["thinker", "architect", "curator", "reviewer"]).optional().describe("collaborative 時の人間の役割"),
|
|
688
|
+
question: z.string().optional().describe("flow 時の問い (note_type='flow' で必須)"),
|
|
689
|
+
next_action: z.string().optional().describe("flow 時の次のアクション (note_type='flow' で必須)"),
|
|
690
|
+
parent_stock: z.string().optional().describe("flow 時の親 stock identifier (note_type='flow' で必須)"),
|
|
691
|
+
related: z.array(z.string()).optional().describe("flow 時の追加関連リンク先"),
|
|
692
|
+
flow_state: z.string().optional().describe("flow 時の状態: capture / active / blocked / done / promoted / dropped"),
|
|
693
|
+
}, async ({ title, content, directory, tags, note_type, origin, created_via, human_role, question, next_action, parent_stock, related, flow_state }) => {
|
|
694
|
+
// Flow note path
|
|
695
|
+
if (note_type === "flow") {
|
|
696
|
+
if (!question || !next_action || !parent_stock) {
|
|
697
|
+
return textResult("error: note_type='flow' requires question, next_action, parent_stock");
|
|
698
|
+
}
|
|
699
|
+
const flowResult = await store.writeFlowNote({
|
|
700
|
+
title,
|
|
701
|
+
question,
|
|
702
|
+
nextAction: next_action,
|
|
703
|
+
parentStock: parent_stock,
|
|
704
|
+
related,
|
|
705
|
+
details: content,
|
|
706
|
+
directory,
|
|
707
|
+
tags,
|
|
708
|
+
flowState: flow_state,
|
|
709
|
+
origin,
|
|
710
|
+
createdVia: created_via,
|
|
711
|
+
humanRole: human_role,
|
|
712
|
+
});
|
|
713
|
+
const flowSummary = summarizeWriteFlowResponse({
|
|
714
|
+
operation: flowResult.operation,
|
|
715
|
+
unresolvedWikiLinkCount: flowResult.unresolvedWikiLinks.length,
|
|
716
|
+
boundaryWarningCount: flowResult.boundaryWarnings.length,
|
|
717
|
+
memoryContractStatus: flowResult.memoryContract.status,
|
|
718
|
+
duplicateWarningCount: flowResult.duplicateWarnings.length,
|
|
719
|
+
orphanWarning: flowResult.orphanWarning,
|
|
720
|
+
title: flowResult.title,
|
|
721
|
+
parentStockResolved: flowResult.parentStockResolved,
|
|
722
|
+
parentStock: flowResult.parentStock,
|
|
723
|
+
});
|
|
724
|
+
const flowLines = [
|
|
725
|
+
"flow ノートを作成/更新しました。",
|
|
726
|
+
`project: ${flowResult.project}`,
|
|
727
|
+
`scope_label: ${TOOL_SCOPE_LABELS.write_flow_note}`,
|
|
728
|
+
`primary_navigation_signal: ${flowSummary.primaryNavigationSignal}`,
|
|
729
|
+
`primary_quality_signal: ${flowSummary.primaryQualitySignal}`,
|
|
730
|
+
`next_recommended_action: ${flowSummary.nextRecommendedAction}`,
|
|
731
|
+
`operation: ${flowResult.operation}`,
|
|
732
|
+
`file: ${flowResult.relativePath}`,
|
|
733
|
+
`permalink: ${flowResult.permalink}`,
|
|
734
|
+
`title: ${flowResult.title}`,
|
|
735
|
+
`flow_state: ${flowResult.flowState}`,
|
|
736
|
+
`parent_stock: ${flowResult.parentStock}`,
|
|
737
|
+
`parent_stock_resolved: ${flowResult.parentStockResolved ? "yes" : "no"}`,
|
|
738
|
+
`link_health: ${flowResult.linkHealth.resolved}/${flowResult.linkHealth.total} resolved`,
|
|
739
|
+
`backlinks: ${flowResult.backlinkCount}`,
|
|
740
|
+
];
|
|
741
|
+
if (flowResult.orphanWarning)
|
|
742
|
+
flowLines.push(`orphan_warning: yes`);
|
|
743
|
+
flowLines.splice(2, 0, ...storageContextLines());
|
|
744
|
+
if (flowSummary.nextRecommendedTarget) {
|
|
745
|
+
insertBeforeLine(flowLines, "next_recommended_action:", `next_recommended_target: ${flowSummary.nextRecommendedTarget}`);
|
|
746
|
+
}
|
|
747
|
+
appendConflictSummary(flowLines, summarizeNoConflict({ serverUpdated: flowResult.serverUpdated }));
|
|
748
|
+
appendResponsibilityWarning(flowLines, flowResult.boundaryWarnings);
|
|
749
|
+
appendStructureScore(flowLines, flowResult.structureScore);
|
|
750
|
+
appendMemoryContract(flowLines, flowResult.memoryContract);
|
|
751
|
+
appendUnresolvedWikiLinks(flowLines, flowResult.unresolvedWikiLinks);
|
|
752
|
+
appendDuplicateWarnings(flowLines, flowResult.duplicateWarnings);
|
|
753
|
+
await appendAutoProjectHealth(flowLines);
|
|
754
|
+
return textResult(flowLines.join("\n"));
|
|
755
|
+
}
|
|
756
|
+
// Stock/generic note path
|
|
924
757
|
const result = await store.writeNote({
|
|
925
758
|
title,
|
|
926
759
|
content,
|
|
927
760
|
directory,
|
|
928
761
|
tags,
|
|
929
762
|
noteType: note_type,
|
|
763
|
+
origin,
|
|
764
|
+
createdVia: created_via,
|
|
765
|
+
humanRole: human_role,
|
|
930
766
|
});
|
|
931
767
|
const summary = summarizeWriteNoteResponse({
|
|
932
768
|
operation: result.operation,
|
|
@@ -950,15 +786,8 @@ async function main() {
|
|
|
950
786
|
`title: ${result.title}`,
|
|
951
787
|
`link_health: ${result.linkHealth.resolved}/${result.linkHealth.total} resolved`,
|
|
952
788
|
`backlinks: ${result.backlinkCount}`,
|
|
953
|
-
`orphan_warning: ${result.orphanWarning ? "yes" : "no"}`,
|
|
954
789
|
];
|
|
955
|
-
lines.splice(2, 0, ...
|
|
956
|
-
`storage_backend: ${config.github ? "github" : "filesystem"}`,
|
|
957
|
-
`storage_root: ${config.root}`,
|
|
958
|
-
...(config.github
|
|
959
|
-
? [`storage_repo: ${config.github.owner}/${config.github.repo}`, `storage_branch: ${config.github.branch}`]
|
|
960
|
-
: []),
|
|
961
|
-
]);
|
|
790
|
+
lines.splice(2, 0, ...storageContextLines());
|
|
962
791
|
if (summary.nextRecommendedTarget) {
|
|
963
792
|
insertBeforeLine(lines, "next_recommended_action:", `next_recommended_target: ${summary.nextRecommendedTarget}`);
|
|
964
793
|
}
|
|
@@ -967,90 +796,14 @@ async function main() {
|
|
|
967
796
|
appendStructureScore(lines, result.structureScore);
|
|
968
797
|
appendMemoryContract(lines, result.memoryContract);
|
|
969
798
|
appendBoundaryWarnings(lines, result.boundaryWarnings);
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
appendUnresolvedLinkHints(lines, result.unresolvedLinkHints);
|
|
978
|
-
appendDuplicateWarnings(lines, result.duplicateWarnings);
|
|
979
|
-
await appendAutoProjectHealth(lines);
|
|
980
|
-
return textResult(lines.join("\n"));
|
|
981
|
-
});
|
|
982
|
-
server.tool("write_flow_note", "flow ノートを構造テンプレート付きで作成/更新します。", {
|
|
983
|
-
title: z.string().min(1),
|
|
984
|
-
question: z.string().min(1),
|
|
985
|
-
next_action: z.string().min(1),
|
|
986
|
-
parent_stock: z.string().min(1),
|
|
987
|
-
related: z.array(z.string()).optional().describe("追加の関連リンク先"),
|
|
988
|
-
details: z.string().optional().describe("## Notes に入れる本文"),
|
|
989
|
-
directory: z.string().min(1).optional().describe("default: Flow"),
|
|
990
|
-
tags: z.array(z.string()).optional(),
|
|
991
|
-
flow_state: z.string().optional().describe("capture / active / blocked / done / promoted / dropped"),
|
|
992
|
-
}, async ({ title, question, next_action, parent_stock, related, details, directory, tags, flow_state }) => {
|
|
993
|
-
const result = await store.writeFlowNote({
|
|
994
|
-
title,
|
|
995
|
-
question,
|
|
996
|
-
nextAction: next_action,
|
|
997
|
-
parentStock: parent_stock,
|
|
998
|
-
related,
|
|
999
|
-
details,
|
|
1000
|
-
directory,
|
|
1001
|
-
tags,
|
|
1002
|
-
flowState: flow_state,
|
|
1003
|
-
});
|
|
1004
|
-
const summary = summarizeWriteFlowResponse({
|
|
1005
|
-
operation: result.operation,
|
|
1006
|
-
unresolvedWikiLinkCount: result.unresolvedWikiLinks.length,
|
|
1007
|
-
boundaryWarningCount: result.boundaryWarnings.length,
|
|
1008
|
-
memoryContractStatus: result.memoryContract.status,
|
|
1009
|
-
duplicateWarningCount: result.duplicateWarnings.length,
|
|
1010
|
-
orphanWarning: result.orphanWarning,
|
|
1011
|
-
title: result.title,
|
|
1012
|
-
parentStockResolved: result.parentStockResolved,
|
|
1013
|
-
parentStock: result.parentStock,
|
|
1014
|
-
});
|
|
1015
|
-
const lines = [
|
|
1016
|
-
"flow ノートを作成/更新しました。",
|
|
1017
|
-
`project: ${result.project}`,
|
|
1018
|
-
`scope_label: ${TOOL_SCOPE_LABELS.write_flow_note}`,
|
|
1019
|
-
`primary_navigation_signal: ${summary.primaryNavigationSignal}`,
|
|
1020
|
-
`primary_quality_signal: ${summary.primaryQualitySignal}`,
|
|
1021
|
-
`next_recommended_action: ${summary.nextRecommendedAction}`,
|
|
1022
|
-
`operation: ${result.operation}`,
|
|
1023
|
-
`file: ${result.relativePath}`,
|
|
1024
|
-
`permalink: ${result.permalink}`,
|
|
1025
|
-
`title: ${result.title}`,
|
|
1026
|
-
`flow_state: ${result.flowState}`,
|
|
1027
|
-
`parent_stock: ${result.parentStock}`,
|
|
1028
|
-
`parent_stock_resolved: ${result.parentStockResolved ? "yes" : "no"}`,
|
|
1029
|
-
`link_health: ${result.linkHealth.resolved}/${result.linkHealth.total} resolved`,
|
|
1030
|
-
`backlinks: ${result.backlinkCount}`,
|
|
1031
|
-
`orphan_warning: ${result.orphanWarning ? "yes" : "no"}`,
|
|
1032
|
-
];
|
|
1033
|
-
lines.splice(2, 0, ...[
|
|
1034
|
-
`storage_backend: ${config.github ? "github" : "filesystem"}`,
|
|
1035
|
-
`storage_root: ${config.root}`,
|
|
1036
|
-
...(config.github
|
|
1037
|
-
? [`storage_repo: ${config.github.owner}/${config.github.repo}`, `storage_branch: ${config.github.branch}`]
|
|
1038
|
-
: []),
|
|
1039
|
-
]);
|
|
1040
|
-
if (summary.nextRecommendedTarget) {
|
|
1041
|
-
insertBeforeLine(lines, "next_recommended_action:", `next_recommended_target: ${summary.nextRecommendedTarget}`);
|
|
799
|
+
if (result.memoryContract.status === "fail") {
|
|
800
|
+
appendTemplateHints(lines, {
|
|
801
|
+
primary: "stock",
|
|
802
|
+
alternatives: ["index"],
|
|
803
|
+
reason: "通常ノートとして知識を残すときは stock template を基準にする",
|
|
804
|
+
whenToSwitch: "再訪導線を固定したい入口ノートなら index template に切り替える",
|
|
805
|
+
});
|
|
1042
806
|
}
|
|
1043
|
-
appendConflictSummary(lines, summarizeNoConflict({ serverUpdated: result.serverUpdated }));
|
|
1044
|
-
appendResponsibilityWarning(lines, result.boundaryWarnings);
|
|
1045
|
-
appendStructureScore(lines, result.structureScore);
|
|
1046
|
-
appendMemoryContract(lines, result.memoryContract);
|
|
1047
|
-
appendBoundaryWarnings(lines, result.boundaryWarnings);
|
|
1048
|
-
appendTemplateHints(lines, {
|
|
1049
|
-
primary: "flow",
|
|
1050
|
-
alternatives: ["stock", "index"],
|
|
1051
|
-
reason: "進行中の問いと next_action を残すときは flow template を基準にする",
|
|
1052
|
-
whenToSwitch: "知識として固まったら stock、再訪入口を作るなら index template を使う",
|
|
1053
|
-
});
|
|
1054
807
|
appendUnresolvedWikiLinks(lines, result.unresolvedWikiLinks);
|
|
1055
808
|
appendUnresolvedLinkHints(lines, result.unresolvedLinkHints);
|
|
1056
809
|
appendDuplicateWarnings(lines, result.duplicateWarnings);
|
|
@@ -1090,13 +843,7 @@ async function main() {
|
|
|
1090
843
|
`stock_permalink: ${result.stock.permalink}`,
|
|
1091
844
|
`flow_state: ${result.flowState}`,
|
|
1092
845
|
];
|
|
1093
|
-
lines.splice(2, 0, ...
|
|
1094
|
-
`storage_backend: ${config.github ? "github" : "filesystem"}`,
|
|
1095
|
-
`storage_root: ${config.root}`,
|
|
1096
|
-
...(config.github
|
|
1097
|
-
? [`storage_repo: ${config.github.owner}/${config.github.repo}`, `storage_branch: ${config.github.branch}`]
|
|
1098
|
-
: []),
|
|
1099
|
-
]);
|
|
846
|
+
lines.splice(2, 0, ...storageContextLines());
|
|
1100
847
|
if (summary.nextRecommendedTarget) {
|
|
1101
848
|
insertBeforeLine(lines, "next_recommended_action:", `next_recommended_target: ${summary.nextRecommendedTarget}`);
|
|
1102
849
|
}
|
|
@@ -1147,15 +894,8 @@ async function main() {
|
|
|
1147
894
|
`title: ${result.title}`,
|
|
1148
895
|
`link_health: ${result.linkHealth.resolved}/${result.linkHealth.total} resolved`,
|
|
1149
896
|
`backlinks: ${result.backlinkCount}`,
|
|
1150
|
-
`orphan_warning: ${result.orphanWarning ? "yes" : "no"}`,
|
|
1151
897
|
];
|
|
1152
|
-
lines.splice(2, 0, ...
|
|
1153
|
-
`storage_backend: ${config.github ? "github" : "filesystem"}`,
|
|
1154
|
-
`storage_root: ${config.root}`,
|
|
1155
|
-
...(config.github
|
|
1156
|
-
? [`storage_repo: ${config.github.owner}/${config.github.repo}`, `storage_branch: ${config.github.branch}`]
|
|
1157
|
-
: []),
|
|
1158
|
-
]);
|
|
898
|
+
lines.splice(2, 0, ...storageContextLines());
|
|
1159
899
|
if (summary.nextRecommendedTarget) {
|
|
1160
900
|
insertBeforeLine(lines, "next_recommended_action:", `next_recommended_target: ${summary.nextRecommendedTarget}`);
|
|
1161
901
|
}
|
|
@@ -1164,7 +904,7 @@ async function main() {
|
|
|
1164
904
|
appendStructureScore(lines, result.structureScore);
|
|
1165
905
|
appendMemoryContract(lines, result.memoryContract);
|
|
1166
906
|
appendBoundaryWarnings(lines, result.boundaryWarnings);
|
|
1167
|
-
if (result.operation === "created") {
|
|
907
|
+
if (result.operation === "created" && result.memoryContract.status === "fail") {
|
|
1168
908
|
appendTemplateHints(lines, {
|
|
1169
909
|
primary: "stock",
|
|
1170
910
|
alternatives: ["index"],
|
|
@@ -1210,13 +950,7 @@ async function main() {
|
|
|
1210
950
|
`project_link_health_after: ${result.projectLinkHealthAfter.resolved}/${result.projectLinkHealthAfter.total}`,
|
|
1211
951
|
`project_unresolved_delta: ${projectUnresolvedDelta}`,
|
|
1212
952
|
];
|
|
1213
|
-
lines.splice(2, 0, ...
|
|
1214
|
-
`storage_backend: ${config.github ? "github" : "filesystem"}`,
|
|
1215
|
-
`storage_root: ${config.root}`,
|
|
1216
|
-
...(config.github
|
|
1217
|
-
? [`storage_repo: ${config.github.owner}/${config.github.repo}`, `storage_branch: ${config.github.branch}`]
|
|
1218
|
-
: []),
|
|
1219
|
-
]);
|
|
953
|
+
lines.splice(2, 0, ...storageContextLines());
|
|
1220
954
|
if (summary.nextRecommendedTarget) {
|
|
1221
955
|
insertBeforeLine(lines, "next_recommended_action:", `next_recommended_target: ${summary.nextRecommendedTarget}`);
|
|
1222
956
|
}
|
|
@@ -1272,18 +1006,11 @@ async function main() {
|
|
|
1272
1006
|
`files_updated: ${result.updatedFiles}`,
|
|
1273
1007
|
`renamed_note_link_health: ${result.renamedNoteLinkHealth.resolved}/${result.renamedNoteLinkHealth.total}`,
|
|
1274
1008
|
`backlinks: ${result.backlinkCount}`,
|
|
1275
|
-
`orphan_warning: ${result.orphanWarning ? "yes" : "no"}`,
|
|
1276
1009
|
`project_link_health_before: ${result.projectLinkHealthBefore.resolved}/${result.projectLinkHealthBefore.total}`,
|
|
1277
1010
|
`project_link_health_after: ${result.projectLinkHealthAfter.resolved}/${result.projectLinkHealthAfter.total}`,
|
|
1278
1011
|
`project_unresolved_delta: ${projectUnresolvedDelta}`,
|
|
1279
1012
|
];
|
|
1280
|
-
lines.splice(2, 0, ...
|
|
1281
|
-
`storage_backend: ${config.github ? "github" : "filesystem"}`,
|
|
1282
|
-
`storage_root: ${config.root}`,
|
|
1283
|
-
...(config.github
|
|
1284
|
-
? [`storage_repo: ${config.github.owner}/${config.github.repo}`, `storage_branch: ${config.github.branch}`]
|
|
1285
|
-
: []),
|
|
1286
|
-
]);
|
|
1013
|
+
lines.splice(2, 0, ...storageContextLines());
|
|
1287
1014
|
if (summary.nextRecommendedTarget) {
|
|
1288
1015
|
insertBeforeLine(lines, "next_recommended_action:", `next_recommended_target: ${summary.nextRecommendedTarget}`);
|
|
1289
1016
|
}
|
package/dist/src/noteStore.js
CHANGED
|
@@ -2048,6 +2048,9 @@ export class LocalNoteStore {
|
|
|
2048
2048
|
unresolvedLinkHints,
|
|
2049
2049
|
};
|
|
2050
2050
|
}
|
|
2051
|
+
async loadAllNotes() {
|
|
2052
|
+
return this.loadProjectNotes();
|
|
2053
|
+
}
|
|
2051
2054
|
async loadProjectNotes() {
|
|
2052
2055
|
const files = await this.storage.listMarkdownFiles();
|
|
2053
2056
|
return Promise.all(files.map((relativePath) => this.loadNote(relativePath)));
|
|
@@ -2814,6 +2817,143 @@ export class LocalNoteStore {
|
|
|
2814
2817
|
explorationGuidance: this.contextExplorationGuidance(notes, sourceNote, rankedNotes),
|
|
2815
2818
|
};
|
|
2816
2819
|
}
|
|
2820
|
+
async orient(args) {
|
|
2821
|
+
const notes = await this.loadProjectNotes();
|
|
2822
|
+
const lookup = this.buildLookup(notes);
|
|
2823
|
+
const thought = args.thought.trim();
|
|
2824
|
+
// Record observation signal if present
|
|
2825
|
+
if (args.observation && args.signalLog) {
|
|
2826
|
+
const signalType = `human_${args.observation.signal}`;
|
|
2827
|
+
const resolved = this.resolveWikiTarget(args.observation.note, lookup);
|
|
2828
|
+
const noteId = resolved.status === "resolved" && resolved.note
|
|
2829
|
+
? resolved.note.permalink
|
|
2830
|
+
: args.observation.note;
|
|
2831
|
+
args.signalLog.record(signalType, noteId, thought.slice(0, 120));
|
|
2832
|
+
}
|
|
2833
|
+
// Record orient references as signals
|
|
2834
|
+
if (args.references && args.signalLog) {
|
|
2835
|
+
for (const ref of args.references) {
|
|
2836
|
+
const resolved = this.resolveWikiTarget(ref, lookup);
|
|
2837
|
+
const noteId = resolved.status === "resolved" && resolved.note
|
|
2838
|
+
? resolved.note.permalink
|
|
2839
|
+
: ref;
|
|
2840
|
+
args.signalLog.record("orient_referenced", noteId, thought.slice(0, 120));
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
const toOrientItem = (note, reason) => {
|
|
2844
|
+
const engagement = args.engagementIndex?.get(note.permalink);
|
|
2845
|
+
const snippet = note.body.slice(0, this.config.previewLength).trim();
|
|
2846
|
+
return {
|
|
2847
|
+
identifier: note.title,
|
|
2848
|
+
title: note.title,
|
|
2849
|
+
permalink: note.permalink,
|
|
2850
|
+
snippet,
|
|
2851
|
+
reason,
|
|
2852
|
+
engagement: {
|
|
2853
|
+
contactCount: engagement?.contactCount ?? 0,
|
|
2854
|
+
lastContact: engagement?.lastContact ?? null,
|
|
2855
|
+
humanSignalCount: engagement?.humanSignalCount ?? 0,
|
|
2856
|
+
dominantSignal: engagement?.dominantSignal ?? null,
|
|
2857
|
+
aiReferenceCount: engagement?.aiReferenceCount ?? 0,
|
|
2858
|
+
origin: engagement?.origin ?? safeString(note.frontmatter.origin) ?? null,
|
|
2859
|
+
createdVia: engagement?.createdVia ?? safeString(note.frontmatter.created_via) ?? null,
|
|
2860
|
+
},
|
|
2861
|
+
};
|
|
2862
|
+
};
|
|
2863
|
+
// === Slot 1: relevant (search-based + engagement boost) ===
|
|
2864
|
+
const relevant = [];
|
|
2865
|
+
if (thought.length > 0) {
|
|
2866
|
+
for (const note of notes) {
|
|
2867
|
+
let score = this.noteScore(note, thought);
|
|
2868
|
+
if (score <= 0)
|
|
2869
|
+
continue;
|
|
2870
|
+
// Engagement boost
|
|
2871
|
+
const eng = args.engagementIndex?.get(note.permalink);
|
|
2872
|
+
if (eng) {
|
|
2873
|
+
if (eng.humanSignalCount > 0)
|
|
2874
|
+
score = Math.floor(score * 1.5);
|
|
2875
|
+
else if (eng.aiReferenceCount > 0)
|
|
2876
|
+
score = Math.floor(score * 1.2);
|
|
2877
|
+
else if (eng.contactCount === 0)
|
|
2878
|
+
score = Math.floor(score * 0.8);
|
|
2879
|
+
}
|
|
2880
|
+
relevant.push({ note, score, reason: "text_match" });
|
|
2881
|
+
}
|
|
2882
|
+
relevant.sort((a, b) => b.score - a.score);
|
|
2883
|
+
}
|
|
2884
|
+
// Record discovered notes as signals
|
|
2885
|
+
if (args.signalLog) {
|
|
2886
|
+
for (const item of relevant.slice(0, 5)) {
|
|
2887
|
+
args.signalLog.record("orient_discovered", item.note.permalink, thought.slice(0, 120));
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
// === Slot 2: active_context (active flows + recent high-engagement) ===
|
|
2891
|
+
const activeContext = [];
|
|
2892
|
+
const relevantPermalinks = new Set(relevant.slice(0, 5).map((r) => r.note.permalink));
|
|
2893
|
+
for (const note of notes) {
|
|
2894
|
+
if (relevantPermalinks.has(note.permalink))
|
|
2895
|
+
continue;
|
|
2896
|
+
const flowState = safeString(note.frontmatter.flow_state);
|
|
2897
|
+
if (flowState === "active" || flowState === "capture") {
|
|
2898
|
+
activeContext.push({ note, reason: `active_flow (${flowState})` });
|
|
2899
|
+
continue;
|
|
2900
|
+
}
|
|
2901
|
+
const eng = args.engagementIndex?.get(note.permalink);
|
|
2902
|
+
if (eng && eng.contactCount >= 3 && eng.lastContact) {
|
|
2903
|
+
const daysSinceContact = Math.floor((Date.now() - new Date(eng.lastContact).getTime()) / (1000 * 60 * 60 * 24));
|
|
2904
|
+
if (daysSinceContact <= 7) {
|
|
2905
|
+
activeContext.push({ note, reason: `recent_high_engagement (${eng.contactCount} contacts)` });
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
// === Slot 3: discovery (untouched notes with tag/directory adjacency) ===
|
|
2910
|
+
const discovery = [];
|
|
2911
|
+
const usedPermalinks = new Set([
|
|
2912
|
+
...relevantPermalinks,
|
|
2913
|
+
...activeContext.map((a) => a.note.permalink),
|
|
2914
|
+
]);
|
|
2915
|
+
// Extract tags from thought terms for tag adjacency
|
|
2916
|
+
const thoughtTerms = new Set(thought.toLowerCase().split(/[\s\p{P}]+/u).filter((t) => t.length >= 2));
|
|
2917
|
+
for (const note of notes) {
|
|
2918
|
+
if (usedPermalinks.has(note.permalink))
|
|
2919
|
+
continue;
|
|
2920
|
+
const eng = args.engagementIndex?.get(note.permalink);
|
|
2921
|
+
if (eng && eng.contactCount > 0)
|
|
2922
|
+
continue;
|
|
2923
|
+
let discoveryScore = 0;
|
|
2924
|
+
let reasons = [];
|
|
2925
|
+
// Tag adjacency
|
|
2926
|
+
const tags = safeStringArray(note.frontmatter.tags);
|
|
2927
|
+
for (const tag of tags) {
|
|
2928
|
+
if (thoughtTerms.has(tag.toLowerCase())) {
|
|
2929
|
+
discoveryScore += 10;
|
|
2930
|
+
reasons.push(`tag:${tag}`);
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
// Weak text match (lower threshold than relevant)
|
|
2934
|
+
if (thought.length > 0) {
|
|
2935
|
+
const textScore = this.noteScore(note, thought);
|
|
2936
|
+
if (textScore > 0) {
|
|
2937
|
+
discoveryScore += Math.min(textScore, 20);
|
|
2938
|
+
reasons.push("weak_text_match");
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
// Random serendipity factor
|
|
2942
|
+
discoveryScore += Math.floor(Math.random() * 5);
|
|
2943
|
+
if (discoveryScore > 0 || Math.random() < 0.02) {
|
|
2944
|
+
if (reasons.length === 0)
|
|
2945
|
+
reasons.push("serendipity");
|
|
2946
|
+
discovery.push({ note, score: discoveryScore, reason: reasons.join(", ") });
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
discovery.sort((a, b) => b.score - a.score);
|
|
2950
|
+
return {
|
|
2951
|
+
project: this.projectName,
|
|
2952
|
+
relevant: relevant.slice(0, 5).map((r) => toOrientItem(r.note, r.reason)),
|
|
2953
|
+
activeContext: activeContext.slice(0, 5).map((a) => toOrientItem(a.note, a.reason)),
|
|
2954
|
+
discovery: discovery.slice(0, 3).map((d) => toOrientItem(d.note, d.reason)),
|
|
2955
|
+
};
|
|
2956
|
+
}
|
|
2817
2957
|
async writeFlowNote(args) {
|
|
2818
2958
|
await this.storage.ensureRoot();
|
|
2819
2959
|
const flowState = (safeString(args.flowState)?.toLowerCase() ?? "capture");
|
|
@@ -2871,6 +3011,9 @@ export class LocalNoteStore {
|
|
|
2871
3011
|
next_action: nextAction,
|
|
2872
3012
|
parent_stock: parentStock,
|
|
2873
3013
|
},
|
|
3014
|
+
origin: args.origin,
|
|
3015
|
+
createdVia: args.createdVia,
|
|
3016
|
+
humanRole: args.humanRole,
|
|
2874
3017
|
});
|
|
2875
3018
|
const notes = await this.loadProjectNotes();
|
|
2876
3019
|
const lookup = this.buildLookup(notes);
|
|
@@ -3029,6 +3172,15 @@ export class LocalNoteStore {
|
|
|
3029
3172
|
created: safeString(existingFrontmatter.created) ?? date,
|
|
3030
3173
|
updated: date,
|
|
3031
3174
|
};
|
|
3175
|
+
if (input.origin && !existingFrontmatter.origin) {
|
|
3176
|
+
frontmatter.origin = input.origin;
|
|
3177
|
+
}
|
|
3178
|
+
if (input.createdVia && !existingFrontmatter.created_via) {
|
|
3179
|
+
frontmatter.created_via = input.createdVia;
|
|
3180
|
+
}
|
|
3181
|
+
if (input.humanRole && !existingFrontmatter.human_role) {
|
|
3182
|
+
frontmatter.human_role = input.humanRole;
|
|
3183
|
+
}
|
|
3032
3184
|
for (const [key, value] of Object.entries(patch)) {
|
|
3033
3185
|
if (RESERVED_FRONTMATTER_KEYS.has(key)) {
|
|
3034
3186
|
continue;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const SIGNAL_DIR = ".kioq";
|
|
2
|
+
const SIGNAL_FILE = `${SIGNAL_DIR}/signals.jsonl`;
|
|
3
|
+
export class SignalLog {
|
|
4
|
+
storage;
|
|
5
|
+
buffer = [];
|
|
6
|
+
flushPromise = null;
|
|
7
|
+
constructor(storage) {
|
|
8
|
+
this.storage = storage;
|
|
9
|
+
}
|
|
10
|
+
record(op, note, ctx) {
|
|
11
|
+
this.buffer.push({
|
|
12
|
+
ts: new Date().toISOString(),
|
|
13
|
+
op,
|
|
14
|
+
note,
|
|
15
|
+
ctx,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
recordBatch(entries) {
|
|
19
|
+
const ts = new Date().toISOString();
|
|
20
|
+
for (const entry of entries) {
|
|
21
|
+
this.buffer.push({ ts, op: entry.op, note: entry.note, ctx: entry.ctx });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async flush() {
|
|
25
|
+
if (this.buffer.length === 0)
|
|
26
|
+
return;
|
|
27
|
+
if (this.flushPromise) {
|
|
28
|
+
await this.flushPromise;
|
|
29
|
+
}
|
|
30
|
+
const entries = this.buffer.splice(0);
|
|
31
|
+
const lines = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
32
|
+
this.flushPromise = this.appendToLog(lines);
|
|
33
|
+
try {
|
|
34
|
+
await this.flushPromise;
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
this.flushPromise = null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async load() {
|
|
41
|
+
try {
|
|
42
|
+
const content = await this.storage.readFile(SIGNAL_FILE);
|
|
43
|
+
return content
|
|
44
|
+
.split("\n")
|
|
45
|
+
.filter((line) => line.trim().length > 0)
|
|
46
|
+
.map((line) => JSON.parse(line));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async appendToLog(content) {
|
|
53
|
+
let existing = "";
|
|
54
|
+
try {
|
|
55
|
+
existing = await this.storage.readFile(SIGNAL_FILE);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// file doesn't exist yet
|
|
59
|
+
}
|
|
60
|
+
await this.storage.writeFile(SIGNAL_FILE, existing + content);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -354,7 +354,7 @@ export class GitHubStorage {
|
|
|
354
354
|
}
|
|
355
355
|
async pullLocal(context) {
|
|
356
356
|
try {
|
|
357
|
-
await this.execFileAsyncFn("git", ["pull", "--ff-only"], {
|
|
357
|
+
await this.execFileAsyncFn("git", ["pull", "--ff-only", "origin", this.branch], {
|
|
358
358
|
cwd: this.rootPath,
|
|
359
359
|
timeout: 30_000,
|
|
360
360
|
});
|