@jussmor/commit-memory-mcp 0.4.2 → 0.5.0
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/db/client.d.ts +39 -1
- package/dist/db/client.js +282 -1
- package/dist/mcp/server.js +176 -2
- package/package.json +1 -1
package/dist/db/client.d.ts
CHANGED
|
@@ -25,8 +25,46 @@ export declare function buildContextPack(db: RagDatabase, options: {
|
|
|
25
25
|
taskType?: string;
|
|
26
26
|
includeDraft?: boolean;
|
|
27
27
|
limit: number;
|
|
28
|
-
|
|
28
|
+
forceRefresh?: boolean;
|
|
29
|
+
summarizePR?: boolean;
|
|
30
|
+
}): {
|
|
31
|
+
learnedFeature: ContextPackRecord[];
|
|
32
|
+
branchContext: ContextPackRecord[];
|
|
33
|
+
prMetadata: ContextPackRecord[];
|
|
34
|
+
allContext: ContextPackRecord[];
|
|
35
|
+
};
|
|
29
36
|
export declare function archiveFeatureContext(db: RagDatabase, options: {
|
|
30
37
|
domain: string;
|
|
31
38
|
feature: string;
|
|
32
39
|
}): number;
|
|
40
|
+
export interface FeatureResumeOptions {
|
|
41
|
+
feature: string;
|
|
42
|
+
domain?: string;
|
|
43
|
+
limit?: number;
|
|
44
|
+
}
|
|
45
|
+
export declare function getFeatureResume(db: RagDatabase, options: FeatureResumeOptions): string;
|
|
46
|
+
export declare function listLearnedFeatures(db: RagDatabase, options?: {
|
|
47
|
+
domain?: string;
|
|
48
|
+
status?: string;
|
|
49
|
+
}): Array<{
|
|
50
|
+
feature: string;
|
|
51
|
+
domain: string;
|
|
52
|
+
branch: string;
|
|
53
|
+
confidence: number;
|
|
54
|
+
status: string;
|
|
55
|
+
createdAt: string;
|
|
56
|
+
updatedAt: string;
|
|
57
|
+
title: string;
|
|
58
|
+
contentLength: number;
|
|
59
|
+
}>;
|
|
60
|
+
export declare function listAvailableBranches(db: RagDatabase, options?: {
|
|
61
|
+
domain?: string;
|
|
62
|
+
feature?: string;
|
|
63
|
+
}): Array<{
|
|
64
|
+
branch: string;
|
|
65
|
+
feature: string;
|
|
66
|
+
domain: string;
|
|
67
|
+
factCount: number;
|
|
68
|
+
lastUpdated: string;
|
|
69
|
+
topConfidence: number;
|
|
70
|
+
}>;
|
package/dist/db/client.js
CHANGED
|
@@ -7,6 +7,10 @@ export function openDatabase(dbPath) {
|
|
|
7
7
|
fs.mkdirSync(path.dirname(resolved), { recursive: true });
|
|
8
8
|
const db = new Database(resolved);
|
|
9
9
|
load(db);
|
|
10
|
+
// Enable WAL mode and ensure data is persisted to disk
|
|
11
|
+
db.pragma("journal_mode = WAL");
|
|
12
|
+
db.pragma("synchronous = NORMAL");
|
|
13
|
+
db.pragma("foreign_keys = ON");
|
|
10
14
|
db.exec(`
|
|
11
15
|
CREATE TABLE IF NOT EXISTS commits (
|
|
12
16
|
sha TEXT PRIMARY KEY,
|
|
@@ -370,6 +374,14 @@ export function promoteContextFacts(db, options) {
|
|
|
370
374
|
const result = db.prepare(sql).run(now, ...params);
|
|
371
375
|
return Number(result.changes ?? 0);
|
|
372
376
|
}
|
|
377
|
+
function summarizePRMetadata(fact) {
|
|
378
|
+
// Extract key info from PR metadata to keep it concise
|
|
379
|
+
if (fact.sourceType.startsWith("pr_")) {
|
|
380
|
+
const lines = fact.content.split("\n").slice(0, 2).join(" ");
|
|
381
|
+
return `[${fact.sourceRef}] ${fact.title} — ${lines.substring(0, 100)}`;
|
|
382
|
+
}
|
|
383
|
+
return fact.content;
|
|
384
|
+
}
|
|
373
385
|
export function buildContextPack(db, options) {
|
|
374
386
|
const taskType = options.taskType ?? "general";
|
|
375
387
|
const GLOBAL_BRANCH = "main";
|
|
@@ -463,6 +475,75 @@ export function buildContextPack(db, options) {
|
|
|
463
475
|
pack.push(row);
|
|
464
476
|
}
|
|
465
477
|
};
|
|
478
|
+
// PRIORITY 0) Learned feature knowledge is always included first when available.
|
|
479
|
+
// This ensures feature knowledge isn't lost behind PR metadata.
|
|
480
|
+
// Always include learned features regardless of parameter filters.
|
|
481
|
+
let learnedFacts = [];
|
|
482
|
+
if (options.feature) {
|
|
483
|
+
// Search for learned knowledge about the specific feature
|
|
484
|
+
learnedFacts = db.prepare(`
|
|
485
|
+
SELECT
|
|
486
|
+
id,
|
|
487
|
+
source_type,
|
|
488
|
+
source_ref,
|
|
489
|
+
title,
|
|
490
|
+
content,
|
|
491
|
+
scope_domain,
|
|
492
|
+
scope_feature,
|
|
493
|
+
scope_branch,
|
|
494
|
+
scope_task_type,
|
|
495
|
+
priority,
|
|
496
|
+
confidence,
|
|
497
|
+
status,
|
|
498
|
+
updated_at,
|
|
499
|
+
((priority * 0.40) + (confidence * 0.30) + 0.25) AS score
|
|
500
|
+
FROM context_facts
|
|
501
|
+
WHERE source_type = 'feature-agent' AND scope_feature = ? AND status = 'promoted'
|
|
502
|
+
ORDER BY updated_at DESC, priority DESC
|
|
503
|
+
LIMIT ?
|
|
504
|
+
`).all(options.feature, Math.max(3, Math.floor(options.limit * 0.2)));
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
// Auto-discover recently learned features when no specific feature is requested
|
|
508
|
+
learnedFacts = db.prepare(`
|
|
509
|
+
SELECT
|
|
510
|
+
id,
|
|
511
|
+
source_type,
|
|
512
|
+
source_ref,
|
|
513
|
+
title,
|
|
514
|
+
content,
|
|
515
|
+
scope_domain,
|
|
516
|
+
scope_feature,
|
|
517
|
+
scope_branch,
|
|
518
|
+
scope_task_type,
|
|
519
|
+
priority,
|
|
520
|
+
confidence,
|
|
521
|
+
status,
|
|
522
|
+
updated_at,
|
|
523
|
+
((priority * 0.40) + (confidence * 0.30) + 0.25) AS score
|
|
524
|
+
FROM context_facts
|
|
525
|
+
WHERE source_type = 'feature-agent' AND status = 'promoted'
|
|
526
|
+
ORDER BY updated_at DESC, priority DESC
|
|
527
|
+
LIMIT ?
|
|
528
|
+
`).all(Math.max(3, Math.floor(options.limit * 0.2)));
|
|
529
|
+
}
|
|
530
|
+
const learnedRows = learnedFacts.map((row) => ({
|
|
531
|
+
id: String(row.id ?? ""),
|
|
532
|
+
sourceType: String(row.source_type ?? ""),
|
|
533
|
+
sourceRef: String(row.source_ref ?? ""),
|
|
534
|
+
title: String(row.title ?? ""),
|
|
535
|
+
content: String(row.content ?? ""),
|
|
536
|
+
domain: String(row.scope_domain ?? ""),
|
|
537
|
+
feature: String(row.scope_feature ?? ""),
|
|
538
|
+
branch: String(row.scope_branch ?? ""),
|
|
539
|
+
taskType: String(row.scope_task_type ?? ""),
|
|
540
|
+
priority: Number(row.priority ?? 0),
|
|
541
|
+
confidence: Number(row.confidence ?? 0),
|
|
542
|
+
score: Number(row.score ?? 0),
|
|
543
|
+
status: String(row.status ?? "promoted"),
|
|
544
|
+
updatedAt: String(row.updated_at ?? ""),
|
|
545
|
+
}));
|
|
546
|
+
addRows(learnedRows);
|
|
466
547
|
// 1) Main branch domain context is the durable source-of-truth baseline.
|
|
467
548
|
if (pack.length < options.limit) {
|
|
468
549
|
addRows(runQuery({
|
|
@@ -503,7 +584,47 @@ export function buildContextPack(db, options) {
|
|
|
503
584
|
includeBranch: false,
|
|
504
585
|
}));
|
|
505
586
|
}
|
|
506
|
-
|
|
587
|
+
// Categorize results and apply summarization if requested
|
|
588
|
+
const learnedFeature = [];
|
|
589
|
+
const branchContext = [];
|
|
590
|
+
const prMetadata = [];
|
|
591
|
+
for (const item of pack) {
|
|
592
|
+
if (item.sourceType === "feature-agent") {
|
|
593
|
+
learnedFeature.push(item);
|
|
594
|
+
}
|
|
595
|
+
else if (item.sourceType.startsWith("pr_")) {
|
|
596
|
+
if (options.summarizePR) {
|
|
597
|
+
prMetadata.push({
|
|
598
|
+
...item,
|
|
599
|
+
content: summarizePRMetadata(item),
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
prMetadata.push(item);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
else if (item.branch && item.branch !== "main") {
|
|
607
|
+
branchContext.push(item);
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
// Fallback for other context types
|
|
611
|
+
if (options.summarizePR && item.sourceType.startsWith("pr_")) {
|
|
612
|
+
branchContext.push({
|
|
613
|
+
...item,
|
|
614
|
+
content: summarizePRMetadata(item),
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
branchContext.push(item);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
learnedFeature,
|
|
624
|
+
branchContext,
|
|
625
|
+
prMetadata,
|
|
626
|
+
allContext: pack,
|
|
627
|
+
};
|
|
507
628
|
}
|
|
508
629
|
export function archiveFeatureContext(db, options) {
|
|
509
630
|
const now = new Date().toISOString();
|
|
@@ -555,3 +676,163 @@ export function archiveFeatureContext(db, options) {
|
|
|
555
676
|
const result = tx();
|
|
556
677
|
return Number(result.changes ?? 0);
|
|
557
678
|
}
|
|
679
|
+
export function getFeatureResume(db, options) {
|
|
680
|
+
const feature = String(options.feature ?? "").trim();
|
|
681
|
+
const domain = String(options.domain ?? "").trim();
|
|
682
|
+
const limit = Number(options.limit ?? 20);
|
|
683
|
+
if (!feature) {
|
|
684
|
+
return "# Feature Resume\n\nError: feature parameter is required.";
|
|
685
|
+
}
|
|
686
|
+
// Query learned feature facts
|
|
687
|
+
const learnedFacts = db
|
|
688
|
+
.prepare(`
|
|
689
|
+
SELECT
|
|
690
|
+
title,
|
|
691
|
+
content,
|
|
692
|
+
confidence,
|
|
693
|
+
updated_at
|
|
694
|
+
FROM context_facts
|
|
695
|
+
WHERE source_type = 'feature-agent'
|
|
696
|
+
AND scope_feature = ?
|
|
697
|
+
AND status = 'promoted'
|
|
698
|
+
ORDER BY updated_at DESC
|
|
699
|
+
LIMIT 1
|
|
700
|
+
`)
|
|
701
|
+
.all(feature);
|
|
702
|
+
// Query PR metadata related to the feature
|
|
703
|
+
const prFacts = db
|
|
704
|
+
.prepare(`
|
|
705
|
+
SELECT
|
|
706
|
+
title,
|
|
707
|
+
content,
|
|
708
|
+
source_ref,
|
|
709
|
+
confidence,
|
|
710
|
+
updated_at
|
|
711
|
+
FROM context_facts
|
|
712
|
+
WHERE source_type LIKE 'pr_%'
|
|
713
|
+
AND (scope_feature = ? OR scope_branch LIKE ?)
|
|
714
|
+
AND status = 'promoted'
|
|
715
|
+
ORDER BY updated_at DESC
|
|
716
|
+
LIMIT ?
|
|
717
|
+
`)
|
|
718
|
+
.all(feature, `%${feature}%`, limit);
|
|
719
|
+
// Build markdown document
|
|
720
|
+
const markdown = [];
|
|
721
|
+
markdown.push(`# Feature Resume: ${feature}`);
|
|
722
|
+
markdown.push("");
|
|
723
|
+
// Add learned knowledge section
|
|
724
|
+
if (learnedFacts.length > 0) {
|
|
725
|
+
const learned = learnedFacts[0];
|
|
726
|
+
markdown.push("## 📚 Feature Knowledge");
|
|
727
|
+
markdown.push(`**Confidence:** ${(learned.confidence * 100).toFixed(0)}%`);
|
|
728
|
+
markdown.push(`**Last Updated:** ${new Date(learned.updated_at).toLocaleString()}`);
|
|
729
|
+
markdown.push("");
|
|
730
|
+
markdown.push(learned.content);
|
|
731
|
+
markdown.push("");
|
|
732
|
+
}
|
|
733
|
+
else {
|
|
734
|
+
markdown.push("## 📚 Feature Knowledge");
|
|
735
|
+
markdown.push("*(No learned knowledge yet. Run `learn_feature` to seed context.)*");
|
|
736
|
+
markdown.push("");
|
|
737
|
+
}
|
|
738
|
+
// Add PR metadata section
|
|
739
|
+
if (prFacts.length > 0) {
|
|
740
|
+
markdown.push("## 🔗 Related Pull Requests");
|
|
741
|
+
markdown.push("");
|
|
742
|
+
for (const pr of prFacts) {
|
|
743
|
+
const confidence = (pr.confidence * 100).toFixed(0);
|
|
744
|
+
const ref = pr.source_ref || "unknown";
|
|
745
|
+
markdown.push(`- **${pr.title}** (${ref}) — ${confidence}% confidence`);
|
|
746
|
+
if (pr.content && pr.content.length < 200) {
|
|
747
|
+
markdown.push(` > ${pr.content}`);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
markdown.push("");
|
|
751
|
+
}
|
|
752
|
+
// Add recommendations
|
|
753
|
+
markdown.push("## 💡 Recommendations");
|
|
754
|
+
if (learnedFacts.length === 0) {
|
|
755
|
+
markdown.push('- Run `learn_feature({ featureBranch: "${feature}", agentContent: "..." })` to seed knowledge');
|
|
756
|
+
}
|
|
757
|
+
if (prFacts.length === 0) {
|
|
758
|
+
markdown.push("- Sync PR context with `pre_plan_sync_brief` to see related PRs");
|
|
759
|
+
}
|
|
760
|
+
markdown.push("- Use this resume alongside your active worktree for complete context");
|
|
761
|
+
return markdown.join("\n");
|
|
762
|
+
}
|
|
763
|
+
export function listLearnedFeatures(db, options) {
|
|
764
|
+
const clauses = ["source_type = 'feature-agent'"];
|
|
765
|
+
const values = [];
|
|
766
|
+
if (options?.domain) {
|
|
767
|
+
clauses.push("scope_domain = ?");
|
|
768
|
+
values.push(options.domain);
|
|
769
|
+
}
|
|
770
|
+
if (options?.status) {
|
|
771
|
+
clauses.push("status = ?");
|
|
772
|
+
values.push(options.status);
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
clauses.push("status = 'promoted'");
|
|
776
|
+
}
|
|
777
|
+
const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
778
|
+
const rows = db.prepare(`
|
|
779
|
+
SELECT DISTINCT
|
|
780
|
+
scope_feature as feature,
|
|
781
|
+
scope_domain as domain,
|
|
782
|
+
scope_branch as branch,
|
|
783
|
+
confidence,
|
|
784
|
+
status,
|
|
785
|
+
created_at,
|
|
786
|
+
updated_at,
|
|
787
|
+
title,
|
|
788
|
+
LENGTH(content) as contentLength
|
|
789
|
+
FROM context_facts
|
|
790
|
+
${where}
|
|
791
|
+
ORDER BY updated_at DESC
|
|
792
|
+
`).all(...values);
|
|
793
|
+
return rows.map((row) => ({
|
|
794
|
+
feature: String(row.feature ?? ""),
|
|
795
|
+
domain: String(row.domain ?? ""),
|
|
796
|
+
branch: String(row.branch ?? ""),
|
|
797
|
+
confidence: Number(row.confidence ?? 0),
|
|
798
|
+
status: String(row.status ?? ""),
|
|
799
|
+
createdAt: String(row.created_at ?? ""),
|
|
800
|
+
updatedAt: String(row.updated_at ?? ""),
|
|
801
|
+
title: String(row.title ?? ""),
|
|
802
|
+
contentLength: Number(row.contentLength ?? 0),
|
|
803
|
+
}));
|
|
804
|
+
}
|
|
805
|
+
export function listAvailableBranches(db, options) {
|
|
806
|
+
const clauses = ["status = 'promoted'"];
|
|
807
|
+
const values = [];
|
|
808
|
+
if (options?.domain) {
|
|
809
|
+
clauses.push("scope_domain = ?");
|
|
810
|
+
values.push(options.domain);
|
|
811
|
+
}
|
|
812
|
+
if (options?.feature) {
|
|
813
|
+
clauses.push("scope_feature = ?");
|
|
814
|
+
values.push(options.feature);
|
|
815
|
+
}
|
|
816
|
+
const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
817
|
+
const rows = db.prepare(`
|
|
818
|
+
SELECT
|
|
819
|
+
scope_branch as branch,
|
|
820
|
+
scope_feature as feature,
|
|
821
|
+
scope_domain as domain,
|
|
822
|
+
COUNT(*) as factCount,
|
|
823
|
+
MAX(updated_at) as lastUpdated,
|
|
824
|
+
MAX(confidence) as topConfidence
|
|
825
|
+
FROM context_facts
|
|
826
|
+
${where}
|
|
827
|
+
GROUP BY scope_branch, scope_feature, scope_domain
|
|
828
|
+
ORDER BY updated_at DESC
|
|
829
|
+
`).all(...values);
|
|
830
|
+
return rows.map((row) => ({
|
|
831
|
+
branch: String(row.branch ?? ""),
|
|
832
|
+
feature: String(row.feature ?? ""),
|
|
833
|
+
domain: String(row.domain ?? ""),
|
|
834
|
+
factCount: Number(row.factCount ?? 0),
|
|
835
|
+
lastUpdated: String(row.lastUpdated ?? ""),
|
|
836
|
+
topConfidence: Number(row.topConfidence ?? 0),
|
|
837
|
+
}));
|
|
838
|
+
}
|
package/dist/mcp/server.js
CHANGED
|
@@ -6,7 +6,7 @@ import { execFileSync } from "node:child_process";
|
|
|
6
6
|
import fs from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { pathToFileURL } from "node:url";
|
|
9
|
-
import { archiveFeatureContext, buildContextPack, openDatabase, promoteContextFacts, upsertContextFact, upsertWorktreeSession, } from "../db/client.js";
|
|
9
|
+
import { archiveFeatureContext, buildContextPack, getFeatureResume, listLearnedFeatures, listAvailableBranches, openDatabase, promoteContextFacts, upsertContextFact, upsertWorktreeSession, } from "../db/client.js";
|
|
10
10
|
import { commitDetails, explainPathActivity, extractFeatureBranchCommits, latestCommitForFile, mainBranchOvernightBrief, resumeFeatureSessionBrief, whoChangedFile, } from "../git/insights.js";
|
|
11
11
|
import { listActiveWorktrees } from "../git/worktree.js";
|
|
12
12
|
import { syncPullRequestContext } from "../pr/sync.js";
|
|
@@ -175,7 +175,7 @@ export async function startMcpServer() {
|
|
|
175
175
|
},
|
|
176
176
|
{
|
|
177
177
|
name: "build_context_pack",
|
|
178
|
-
description: "Build a scoped context pack for a task/domain/feature/branch
|
|
178
|
+
description: "Build a scoped context pack for a task/domain/feature/branch. Returns learned feature knowledge, branch context, and PR metadata separately.",
|
|
179
179
|
inputSchema: {
|
|
180
180
|
type: "object",
|
|
181
181
|
properties: {
|
|
@@ -185,6 +185,14 @@ export async function startMcpServer() {
|
|
|
185
185
|
taskType: { type: "string" },
|
|
186
186
|
includeDraft: { type: "boolean" },
|
|
187
187
|
limit: { type: "number" },
|
|
188
|
+
forceRefresh: {
|
|
189
|
+
type: "boolean",
|
|
190
|
+
description: "Re-run learn_feature to update feature knowledge",
|
|
191
|
+
},
|
|
192
|
+
summarizePR: {
|
|
193
|
+
type: "boolean",
|
|
194
|
+
description: "Return PR metadata as summaries instead of full content",
|
|
195
|
+
},
|
|
188
196
|
},
|
|
189
197
|
required: [],
|
|
190
198
|
},
|
|
@@ -326,6 +334,49 @@ export async function startMcpServer() {
|
|
|
326
334
|
required: ["featureBranch"],
|
|
327
335
|
},
|
|
328
336
|
},
|
|
337
|
+
{
|
|
338
|
+
name: "get_feature_resume",
|
|
339
|
+
description: "Combine learned feature knowledge with PR metadata and return as markdown. Does NOT require a git worktree. Use this to review a feature's complete context: what we learned about it + related PR activity.",
|
|
340
|
+
inputSchema: {
|
|
341
|
+
type: "object",
|
|
342
|
+
properties: {
|
|
343
|
+
feature: {
|
|
344
|
+
type: "string",
|
|
345
|
+
description: "e.g. messaging",
|
|
346
|
+
},
|
|
347
|
+
domain: { type: "string" },
|
|
348
|
+
limit: { type: "number" },
|
|
349
|
+
},
|
|
350
|
+
required: ["feature"],
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
name: "list_learned_features",
|
|
355
|
+
description: "List all learned features stored in the knowledge database with confidence levels and timestamps.",
|
|
356
|
+
inputSchema: {
|
|
357
|
+
type: "object",
|
|
358
|
+
properties: {
|
|
359
|
+
domain: { type: "string" },
|
|
360
|
+
status: {
|
|
361
|
+
type: "string",
|
|
362
|
+
description: "Filter by status (promoted, draft, etc). Defaults to promoted.",
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
required: [],
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
name: "list_available_branches",
|
|
370
|
+
description: "List all available branches and features with knowledge context available for each.",
|
|
371
|
+
inputSchema: {
|
|
372
|
+
type: "object",
|
|
373
|
+
properties: {
|
|
374
|
+
domain: { type: "string" },
|
|
375
|
+
feature: { type: "string" },
|
|
376
|
+
},
|
|
377
|
+
required: [],
|
|
378
|
+
},
|
|
379
|
+
},
|
|
329
380
|
{
|
|
330
381
|
name: "pre_plan_sync_brief",
|
|
331
382
|
description: "Run sync + overnight + feature resume analysis before planning work.",
|
|
@@ -390,6 +441,8 @@ export async function startMcpServer() {
|
|
|
390
441
|
const branch = String(request.params.arguments?.branch ?? "").trim();
|
|
391
442
|
const taskType = String(request.params.arguments?.taskType ?? "").trim() || "general";
|
|
392
443
|
const includeDraft = Boolean(request.params.arguments?.includeDraft);
|
|
444
|
+
const forceRefresh = Boolean(request.params.arguments?.forceRefresh);
|
|
445
|
+
const summarizePR = Boolean(request.params.arguments?.summarizePR);
|
|
393
446
|
const db = openDatabase(dbPath);
|
|
394
447
|
try {
|
|
395
448
|
const pack = buildContextPack(db, {
|
|
@@ -399,6 +452,8 @@ export async function startMcpServer() {
|
|
|
399
452
|
taskType,
|
|
400
453
|
includeDraft,
|
|
401
454
|
limit: Number.isFinite(limit) && limit > 0 ? limit : 20,
|
|
455
|
+
forceRefresh,
|
|
456
|
+
summarizePR,
|
|
402
457
|
});
|
|
403
458
|
return {
|
|
404
459
|
content: [{ type: "text", text: JSON.stringify(pack, null, 2) }],
|
|
@@ -626,6 +681,125 @@ export async function startMcpServer() {
|
|
|
626
681
|
content: [{ type: "text", text: JSON.stringify(brief, null, 2) }],
|
|
627
682
|
};
|
|
628
683
|
}
|
|
684
|
+
if (request.params.name === "get_feature_resume") {
|
|
685
|
+
const feature = String(request.params.arguments?.feature ?? "").trim();
|
|
686
|
+
const domain = String(request.params.arguments?.domain ?? "").trim();
|
|
687
|
+
const limit = Number(request.params.arguments?.limit ?? 20);
|
|
688
|
+
if (!feature) {
|
|
689
|
+
return {
|
|
690
|
+
content: [{ type: "text", text: "feature parameter is required" }],
|
|
691
|
+
isError: true,
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
const db = openDatabase(dbPath);
|
|
695
|
+
try {
|
|
696
|
+
const resume = getFeatureResume(db, {
|
|
697
|
+
feature,
|
|
698
|
+
domain: domain || undefined,
|
|
699
|
+
limit: Number.isFinite(limit) && limit > 0 ? limit : 20,
|
|
700
|
+
});
|
|
701
|
+
return {
|
|
702
|
+
content: [{ type: "text", text: resume }],
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
finally {
|
|
706
|
+
db.close();
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
if (request.params.name === "list_learned_features") {
|
|
710
|
+
const domain = String(request.params.arguments?.domain ?? "").trim();
|
|
711
|
+
const status = String(request.params.arguments?.status ?? "").trim();
|
|
712
|
+
const db = openDatabase(dbPath);
|
|
713
|
+
try {
|
|
714
|
+
const features = listLearnedFeatures(db, {
|
|
715
|
+
domain: domain || undefined,
|
|
716
|
+
status: status || undefined,
|
|
717
|
+
});
|
|
718
|
+
const markdown = [
|
|
719
|
+
"# Learned Features",
|
|
720
|
+
"",
|
|
721
|
+
features.length === 0
|
|
722
|
+
? "*(No learned features yet)*"
|
|
723
|
+
: `${features.length} feature(s) available:`,
|
|
724
|
+
"",
|
|
725
|
+
];
|
|
726
|
+
if (features.length > 0) {
|
|
727
|
+
markdown.push("| Feature | Domain | Branch | Confidence | Status | Last Updated |");
|
|
728
|
+
markdown.push("|---------|--------|--------|------------|--------|--------------|");
|
|
729
|
+
for (const feat of features) {
|
|
730
|
+
const confidence = (feat.confidence * 100).toFixed(0);
|
|
731
|
+
const lastUpdated = new Date(feat.updatedAt).toLocaleDateString();
|
|
732
|
+
markdown.push(`| **${feat.feature}** | ${feat.domain} | ${feat.branch} | ${confidence}% | ${feat.status} | ${lastUpdated} |`);
|
|
733
|
+
}
|
|
734
|
+
markdown.push("");
|
|
735
|
+
markdown.push("## Feature Details");
|
|
736
|
+
markdown.push("");
|
|
737
|
+
for (const feat of features) {
|
|
738
|
+
markdown.push(`### ${feat.feature}`);
|
|
739
|
+
markdown.push(`- **Confidence:** ${(feat.confidence * 100).toFixed(0)}%`);
|
|
740
|
+
markdown.push(`- **Domain:** ${feat.domain}`);
|
|
741
|
+
markdown.push(`- **Branch:** ${feat.branch}`);
|
|
742
|
+
markdown.push(`- **Status:** ${feat.status}`);
|
|
743
|
+
markdown.push(`- **Created:** ${new Date(feat.createdAt).toLocaleString()}`);
|
|
744
|
+
markdown.push(`- **Updated:** ${new Date(feat.updatedAt).toLocaleString()}`);
|
|
745
|
+
markdown.push(`- **Title:** ${feat.title}`);
|
|
746
|
+
markdown.push(`- **Content size:** ${feat.contentLength} bytes`);
|
|
747
|
+
markdown.push("");
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return {
|
|
751
|
+
content: [{ type: "text", text: markdown.join("\n") }],
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
finally {
|
|
755
|
+
db.close();
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
if (request.params.name === "list_available_branches") {
|
|
759
|
+
const domain = String(request.params.arguments?.domain ?? "").trim();
|
|
760
|
+
const feature = String(request.params.arguments?.feature ?? "").trim();
|
|
761
|
+
const db = openDatabase(dbPath);
|
|
762
|
+
try {
|
|
763
|
+
const branches = listAvailableBranches(db, {
|
|
764
|
+
domain: domain || undefined,
|
|
765
|
+
feature: feature || undefined,
|
|
766
|
+
});
|
|
767
|
+
const markdown = [
|
|
768
|
+
"# Available Branches and Features",
|
|
769
|
+
"",
|
|
770
|
+
branches.length === 0
|
|
771
|
+
? "*(No branches with knowledge available)*"
|
|
772
|
+
: `${branches.length} branch/feature combination(s):`,
|
|
773
|
+
"",
|
|
774
|
+
];
|
|
775
|
+
if (branches.length > 0) {
|
|
776
|
+
markdown.push("| Branch | Feature | Domain | Facts | Confidence | Last Updated |");
|
|
777
|
+
markdown.push("|--------|---------|--------|-------|------------|--------------|");
|
|
778
|
+
for (const branch of branches) {
|
|
779
|
+
const confidence = (branch.topConfidence * 100).toFixed(0);
|
|
780
|
+
const lastUpdated = new Date(branch.lastUpdated).toLocaleDateString();
|
|
781
|
+
markdown.push(`| **${branch.branch}** | ${branch.feature} | ${branch.domain} | ${branch.factCount} | ${confidence}% | ${lastUpdated} |`);
|
|
782
|
+
}
|
|
783
|
+
markdown.push("");
|
|
784
|
+
markdown.push("## Branch Details");
|
|
785
|
+
markdown.push("");
|
|
786
|
+
for (const branch of branches) {
|
|
787
|
+
markdown.push(`### ${branch.branch} (${branch.feature})`);
|
|
788
|
+
markdown.push(`- **Domain:** ${branch.domain}`);
|
|
789
|
+
markdown.push(`- **Facts stored:** ${branch.factCount}`);
|
|
790
|
+
markdown.push(`- **Top confidence:** ${(branch.topConfidence * 100).toFixed(0)}%`);
|
|
791
|
+
markdown.push(`- **Last updated:** ${new Date(branch.lastUpdated).toLocaleString()}`);
|
|
792
|
+
markdown.push("");
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
return {
|
|
796
|
+
content: [{ type: "text", text: markdown.join("\n") }],
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
finally {
|
|
800
|
+
db.close();
|
|
801
|
+
}
|
|
802
|
+
}
|
|
629
803
|
if (request.params.name === "pre_plan_sync_brief") {
|
|
630
804
|
const owner = String(request.params.arguments?.owner ?? "").trim();
|
|
631
805
|
const repo = String(request.params.arguments?.repo ?? "").trim();
|
package/package.json
CHANGED