wyrm-mcp 3.3.0 → 3.4.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/README.md +50 -10
- package/dist/autoconfig.d.ts +11 -0
- package/dist/autoconfig.d.ts.map +1 -1
- package/dist/autoconfig.js +63 -0
- package/dist/autoconfig.js.map +1 -1
- package/dist/capture.d.ts +19 -0
- package/dist/capture.d.ts.map +1 -0
- package/dist/capture.js +56 -0
- package/dist/capture.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +416 -1
- package/dist/index.js.map +1 -1
- package/dist/wyrm-cli.d.ts +11 -0
- package/dist/wyrm-cli.d.ts.map +1 -0
- package/dist/wyrm-cli.js +709 -0
- package/dist/wyrm-cli.js.map +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -32,6 +32,9 @@ import { IndexingPipeline } from "./indexer.js";
|
|
|
32
32
|
import { KnowledgeGraph } from "./knowledge-graph.js";
|
|
33
33
|
import { MemoryArtifacts } from "./memory-artifacts.js";
|
|
34
34
|
import { GroundTruths, ReasoningScaffolds } from "./intelligence.js";
|
|
35
|
+
import { classifyCapture } from "./capture.js";
|
|
36
|
+
import { injectSystemPrompt } from "./autoconfig.js";
|
|
37
|
+
export { classifyCapture } from "./capture.js";
|
|
35
38
|
import { initializeLicense, getLicenseInfo, hasFeature, getTier, activateLicense } from "./license.js";
|
|
36
39
|
import { WyrmAnalytics } from "./analytics.js";
|
|
37
40
|
import { WyrmCloudBackup } from "./cloud-backup.js";
|
|
@@ -223,6 +226,7 @@ const READ_ONLY_TOOLS = new Set([
|
|
|
223
226
|
"wyrm_context_build",
|
|
224
227
|
"wyrm_truth_get",
|
|
225
228
|
"wyrm_scaffold_get",
|
|
229
|
+
"wyrm_session_prime",
|
|
226
230
|
]);
|
|
227
231
|
/** Tools that mutate data — invalidate relevant caches */
|
|
228
232
|
const WRITE_TOOLS = new Set([
|
|
@@ -252,10 +256,15 @@ const WRITE_TOOLS = new Set([
|
|
|
252
256
|
"wyrm_scaffold_save",
|
|
253
257
|
"wyrm_distill",
|
|
254
258
|
"wyrm_review",
|
|
259
|
+
"wyrm_capture",
|
|
260
|
+
"wyrm_import_git",
|
|
261
|
+
"wyrm_import_pr",
|
|
262
|
+
"wyrm_import_rules",
|
|
263
|
+
"wyrm_inject_prompt",
|
|
255
264
|
]);
|
|
256
265
|
const server = new Server({
|
|
257
266
|
name: "wyrm",
|
|
258
|
-
version: "3.
|
|
267
|
+
version: "3.4.0",
|
|
259
268
|
}, {
|
|
260
269
|
capabilities: {
|
|
261
270
|
tools: {},
|
|
@@ -983,6 +992,101 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
983
992
|
required: ["artifactId", "approved"],
|
|
984
993
|
},
|
|
985
994
|
},
|
|
995
|
+
// ==================== v3.4.0 TOOLS ====================
|
|
996
|
+
{
|
|
997
|
+
name: "wyrm_capture",
|
|
998
|
+
description: "Unified capture with auto-classification. Detects whether content is a quest, ground truth, or memory artifact and stores it automatically. Use this for any quick thought, decision, task, or lesson you want to preserve.",
|
|
999
|
+
inputSchema: {
|
|
1000
|
+
type: "object",
|
|
1001
|
+
properties: {
|
|
1002
|
+
content: { type: "string", description: "The text to classify and store" },
|
|
1003
|
+
project_id: { type: "number", description: "Project ID to associate with (optional)" },
|
|
1004
|
+
tags: { type: "array", items: { type: "string" }, description: "Optional tags" },
|
|
1005
|
+
mode: { type: "string", enum: ["auto", "quest", "truth", "memory"], description: "Override auto-classification mode" },
|
|
1006
|
+
},
|
|
1007
|
+
required: ["content"],
|
|
1008
|
+
},
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
name: "wyrm_session_prime",
|
|
1012
|
+
description: "Assemble a rich session context brief — ground truths, reasoning scaffold, memory artifacts, and active quests — in a single call. Use at the start of any complex conversation to load all relevant project knowledge.",
|
|
1013
|
+
inputSchema: {
|
|
1014
|
+
type: "object",
|
|
1015
|
+
properties: {
|
|
1016
|
+
project_id: { type: "number", description: "Project ID to prime for" },
|
|
1017
|
+
project_name: { type: "string", description: "Project name (used if project_id not given)" },
|
|
1018
|
+
task: { type: "string", description: "Current task — used to find best scaffold and relevant memories" },
|
|
1019
|
+
log_session: { type: "boolean", description: "If true, also create a session log entry (default: false)" },
|
|
1020
|
+
},
|
|
1021
|
+
},
|
|
1022
|
+
},
|
|
1023
|
+
{
|
|
1024
|
+
name: "wyrm_import_git",
|
|
1025
|
+
description: "Import a list of commits into the Wyrm memory review queue. Useful for onboarding a project's history or capturing recent work.",
|
|
1026
|
+
inputSchema: {
|
|
1027
|
+
type: "object",
|
|
1028
|
+
properties: {
|
|
1029
|
+
project_id: { type: "number", description: "Project ID" },
|
|
1030
|
+
commits: {
|
|
1031
|
+
type: "array",
|
|
1032
|
+
description: "Array of commit objects",
|
|
1033
|
+
items: {
|
|
1034
|
+
type: "object",
|
|
1035
|
+
properties: {
|
|
1036
|
+
message: { type: "string" },
|
|
1037
|
+
diff_summary: { type: "string" },
|
|
1038
|
+
author: { type: "string" },
|
|
1039
|
+
date: { type: "string" },
|
|
1040
|
+
},
|
|
1041
|
+
required: ["message"],
|
|
1042
|
+
},
|
|
1043
|
+
},
|
|
1044
|
+
auto_approve: { type: "boolean", description: "Skip review queue and activate immediately (default: false)" },
|
|
1045
|
+
},
|
|
1046
|
+
required: ["project_id", "commits"],
|
|
1047
|
+
},
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
name: "wyrm_import_pr",
|
|
1051
|
+
description: "Import a pull request (title, body, review comments) into the Wyrm memory review queue to extract patterns and heuristics.",
|
|
1052
|
+
inputSchema: {
|
|
1053
|
+
type: "object",
|
|
1054
|
+
properties: {
|
|
1055
|
+
project_id: { type: "number", description: "Project ID" },
|
|
1056
|
+
title: { type: "string", description: "PR title" },
|
|
1057
|
+
body: { type: "string", description: "PR description / body" },
|
|
1058
|
+
review_comments: { type: "array", items: { type: "string" }, description: "Review comment strings" },
|
|
1059
|
+
auto_approve: { type: "boolean", description: "Skip review queue (default: false)" },
|
|
1060
|
+
},
|
|
1061
|
+
required: ["project_id", "title", "body"],
|
|
1062
|
+
},
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
name: "wyrm_import_rules",
|
|
1066
|
+
description: "Import a .cursorrules / copilot-instructions file into Wyrm. Parses by section and creates ground truths (for constraint/rule language) or memory artifacts (for general guidelines).",
|
|
1067
|
+
inputSchema: {
|
|
1068
|
+
type: "object",
|
|
1069
|
+
properties: {
|
|
1070
|
+
project_id: { type: "number", description: "Project ID" },
|
|
1071
|
+
content: { type: "string", description: "Full text of the rules file" },
|
|
1072
|
+
format: { type: "string", enum: ["cursorrules", "copilot", "plain"], description: "Source format (default: plain)" },
|
|
1073
|
+
source_file: { type: "string", description: "Filename for provenance (optional)" },
|
|
1074
|
+
},
|
|
1075
|
+
required: ["project_id", "content"],
|
|
1076
|
+
},
|
|
1077
|
+
},
|
|
1078
|
+
{
|
|
1079
|
+
name: "wyrm_inject_prompt",
|
|
1080
|
+
description: "Opt-in: inject a Wyrm session-prime instruction block into .github/copilot-instructions.md and/or .cursor/rules for a project. Uses managed markers so re-runs are safe.",
|
|
1081
|
+
inputSchema: {
|
|
1082
|
+
type: "object",
|
|
1083
|
+
properties: {
|
|
1084
|
+
project_path: { type: "string", description: "Absolute path to the project root" },
|
|
1085
|
+
clients: { type: "array", items: { type: "string" }, description: "Clients to inject into: 'copilot', 'cursor' (default: both detected)" },
|
|
1086
|
+
},
|
|
1087
|
+
required: ["project_path"],
|
|
1088
|
+
},
|
|
1089
|
+
},
|
|
986
1090
|
],
|
|
987
1091
|
}));
|
|
988
1092
|
// Tool implementations
|
|
@@ -2454,6 +2558,317 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2454
2558
|
return { content: [{ type: "text", text: `Unknown encrypt action: ${encAction}. Use: status, enable, test` }], isError: true };
|
|
2455
2559
|
}
|
|
2456
2560
|
}
|
|
2561
|
+
// ==================== v3.4.0 TOOLS ====================
|
|
2562
|
+
case "wyrm_capture": {
|
|
2563
|
+
const { content: capContent, project_id: capProjId, tags: capTags, mode: capMode } = args;
|
|
2564
|
+
// Determine classification
|
|
2565
|
+
let classified = classifyCapture(capContent);
|
|
2566
|
+
if (capMode && capMode !== 'auto') {
|
|
2567
|
+
const subtypeMap = { quest: 'quest', truth: 'decision', memory: 'pattern' };
|
|
2568
|
+
classified = {
|
|
2569
|
+
type: capMode,
|
|
2570
|
+
subtype: subtypeMap[capMode] ?? capMode,
|
|
2571
|
+
confidence: 100,
|
|
2572
|
+
reasoning: `Mode override: ${capMode}`,
|
|
2573
|
+
};
|
|
2574
|
+
}
|
|
2575
|
+
const { type: capType, subtype: capSubtype, confidence: capConf, reasoning: capReasoning } = classified;
|
|
2576
|
+
const effectiveProjId = capProjId ?? null;
|
|
2577
|
+
let storedId = 0;
|
|
2578
|
+
let typeShort = '';
|
|
2579
|
+
let needsReviewNote = '';
|
|
2580
|
+
if (capType === 'quest') {
|
|
2581
|
+
if (effectiveProjId === null) {
|
|
2582
|
+
return { content: [{ type: "text", text: `🧠 Cannot capture quest without project_id. Please provide project_id.` }], isError: true };
|
|
2583
|
+
}
|
|
2584
|
+
const quest = db.addQuest(effectiveProjId, capContent.slice(0, 200), '', 'medium', capTags?.join(','));
|
|
2585
|
+
storedId = quest.id;
|
|
2586
|
+
typeShort = 'quest';
|
|
2587
|
+
cache.invalidate('wyrm_all_quests');
|
|
2588
|
+
cache.invalidate('wyrm_stats');
|
|
2589
|
+
}
|
|
2590
|
+
else if (capType === 'truth') {
|
|
2591
|
+
if (effectiveProjId === null) {
|
|
2592
|
+
return { content: [{ type: "text", text: `🧠 Cannot capture truth without project_id. Please provide project_id.` }], isError: true };
|
|
2593
|
+
}
|
|
2594
|
+
if (capMode !== 'truth' && capConf < 100) {
|
|
2595
|
+
// Store as artifact pending review
|
|
2596
|
+
const artifact = memory.add(effectiveProjId, {
|
|
2597
|
+
kind: 'pattern',
|
|
2598
|
+
problem: capContent,
|
|
2599
|
+
validatedFix: '',
|
|
2600
|
+
whyItWorked: '',
|
|
2601
|
+
tags: capTags ?? [],
|
|
2602
|
+
confidence: capConf / 100,
|
|
2603
|
+
needsReview: 1,
|
|
2604
|
+
});
|
|
2605
|
+
storedId = artifact.id;
|
|
2606
|
+
typeShort = 'mem';
|
|
2607
|
+
needsReviewNote = `\n⚠️ Stored for review — run \`wyrm_review\` to activate`;
|
|
2608
|
+
}
|
|
2609
|
+
else {
|
|
2610
|
+
const truth = groundTruths.set(effectiveProjId, {
|
|
2611
|
+
category: 'decision',
|
|
2612
|
+
key: capContent.slice(0, 60),
|
|
2613
|
+
value: capContent,
|
|
2614
|
+
});
|
|
2615
|
+
storedId = truth.id;
|
|
2616
|
+
typeShort = 'truth';
|
|
2617
|
+
cache.invalidate('wyrm_truth_get');
|
|
2618
|
+
cache.invalidate('wyrm_context_build');
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
else {
|
|
2622
|
+
// memory
|
|
2623
|
+
if (effectiveProjId === null) {
|
|
2624
|
+
return { content: [{ type: "text", text: `🧠 Cannot capture memory without project_id. Please provide project_id.` }], isError: true };
|
|
2625
|
+
}
|
|
2626
|
+
const shouldAutoCreate = capConf >= 75;
|
|
2627
|
+
const artifact = memory.add(effectiveProjId, {
|
|
2628
|
+
kind: capSubtype,
|
|
2629
|
+
problem: capContent,
|
|
2630
|
+
validatedFix: '',
|
|
2631
|
+
whyItWorked: '',
|
|
2632
|
+
tags: capTags ?? [],
|
|
2633
|
+
confidence: capConf / 100,
|
|
2634
|
+
needsReview: shouldAutoCreate ? 0 : 1,
|
|
2635
|
+
});
|
|
2636
|
+
storedId = artifact.id;
|
|
2637
|
+
typeShort = 'mem';
|
|
2638
|
+
if (!shouldAutoCreate) {
|
|
2639
|
+
needsReviewNote = `\n⚠️ Stored for review — run \`wyrm_review\` to activate`;
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
return {
|
|
2643
|
+
content: [{
|
|
2644
|
+
type: "text",
|
|
2645
|
+
text: `🧠 Captured as ${capType}: ${capSubtype}\nConfidence: ${capConf}% | ${capReasoning}\nID: ${typeShort}:${storedId}${needsReviewNote}`,
|
|
2646
|
+
}],
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
case "wyrm_session_prime": {
|
|
2650
|
+
const { project_id: spId, project_name: spName, task: spTask, log_session: spLog } = args;
|
|
2651
|
+
// Project resolution
|
|
2652
|
+
let spProject;
|
|
2653
|
+
if (spId) {
|
|
2654
|
+
spProject = db.getProjectById(spId);
|
|
2655
|
+
}
|
|
2656
|
+
else if (spName) {
|
|
2657
|
+
const rawDb = db.getDatabase();
|
|
2658
|
+
spProject = rawDb.prepare('SELECT * FROM projects WHERE LOWER(name) LIKE LOWER(?) LIMIT 1').get(`%${spName}%`);
|
|
2659
|
+
}
|
|
2660
|
+
else {
|
|
2661
|
+
const rawDb = db.getDatabase();
|
|
2662
|
+
spProject = rawDb.prepare(`
|
|
2663
|
+
SELECT p.* FROM projects p
|
|
2664
|
+
JOIN sessions s ON s.project_id = p.id
|
|
2665
|
+
ORDER BY s.created_at DESC LIMIT 1
|
|
2666
|
+
`).get();
|
|
2667
|
+
}
|
|
2668
|
+
if (!spProject) {
|
|
2669
|
+
const allProjects = db.getAllProjects(20);
|
|
2670
|
+
if (allProjects.length === 0) {
|
|
2671
|
+
return { content: [{ type: "text", text: `🐉 No projects found. Run \`wyrm_scan_projects\` first.` }] };
|
|
2672
|
+
}
|
|
2673
|
+
const candidates = allProjects.map(p => `- ${p.name} (ID: ${p.id})`).join('\n');
|
|
2674
|
+
return { content: [{ type: "text", text: `🐉 Project not found. Available projects:\n${candidates}` }] };
|
|
2675
|
+
}
|
|
2676
|
+
const sections = [];
|
|
2677
|
+
// Section 1 — Ground Truths (up to 1200 chars)
|
|
2678
|
+
const truthsText = groundTruths.formatForContext(spProject.id, 1200);
|
|
2679
|
+
if (truthsText)
|
|
2680
|
+
sections.push(truthsText);
|
|
2681
|
+
// Section 2 — Reasoning Scaffold (up to 1500 chars)
|
|
2682
|
+
if (spTask) {
|
|
2683
|
+
const scaffoldMatchSP = scaffoldLib.findBest(spTask, spProject.id, 0.3);
|
|
2684
|
+
if (scaffoldMatchSP) {
|
|
2685
|
+
sections.push(scaffoldLib.formatForContext(scaffoldMatchSP, 1500));
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
// Section 3 — Memory Brief (up to 2000 chars)
|
|
2689
|
+
const spBrief = memory.buildContextBrief(spProject.id, spTask ?? 'general', { maxItems: 10, minConfidence: 0.3 });
|
|
2690
|
+
if (spBrief.text)
|
|
2691
|
+
sections.push(spBrief.text.slice(0, 2000));
|
|
2692
|
+
// Section 4 — Active Quests (up to 800 chars)
|
|
2693
|
+
const spRawDb = db.getDatabase();
|
|
2694
|
+
const spActiveQuests = spRawDb.prepare(`
|
|
2695
|
+
SELECT * FROM quests
|
|
2696
|
+
WHERE project_id = ? AND status = 'pending'
|
|
2697
|
+
ORDER BY
|
|
2698
|
+
CASE priority WHEN 'critical' THEN 1 WHEN 'high' THEN 2 WHEN 'medium' THEN 3 ELSE 4 END
|
|
2699
|
+
LIMIT 5
|
|
2700
|
+
`).all(spProject.id);
|
|
2701
|
+
if (spActiveQuests.length > 0) {
|
|
2702
|
+
const priorityEmoji = { critical: '🔴', high: '🟠', medium: '🟡', low: '🟢' };
|
|
2703
|
+
let questsBlock = '### 🎯 Active Quests\n';
|
|
2704
|
+
for (const q of spActiveQuests) {
|
|
2705
|
+
questsBlock += `- ${priorityEmoji[q.priority] ?? '•'} #${q.id}: ${q.title}\n`;
|
|
2706
|
+
}
|
|
2707
|
+
sections.push(questsBlock.slice(0, 800));
|
|
2708
|
+
}
|
|
2709
|
+
// Optionally log session
|
|
2710
|
+
let sessionNote = '';
|
|
2711
|
+
if (spLog) {
|
|
2712
|
+
const session = db.createSession(spProject.id, {
|
|
2713
|
+
objectives: spTask ?? 'Session started via wyrm_session_prime',
|
|
2714
|
+
summary: '',
|
|
2715
|
+
});
|
|
2716
|
+
sessionNote = `\nSession logged: #${session.id}`;
|
|
2717
|
+
}
|
|
2718
|
+
const spTruthStats = groundTruths.getStats(spProject.id);
|
|
2719
|
+
const spMemStats = memory.getStats(spProject.id);
|
|
2720
|
+
const summaryLine = `🐉 Wyrm primed for **${spProject.name}** | ${spTruthStats.current} truth${spTruthStats.current !== 1 ? 's' : ''} · ${spActiveQuests.length} quest${spActiveQuests.length !== 1 ? 's' : ''} · ${spMemStats.total} memor${spMemStats.total !== 1 ? 'ies' : 'y'}${sessionNote}`;
|
|
2721
|
+
const spContext = sections.length > 0
|
|
2722
|
+
? sections.join('\n\n---\n\n') + '\n\n' + summaryLine
|
|
2723
|
+
: summaryLine + '\n\n_No memory stored yet — start working and Wyrm will learn._';
|
|
2724
|
+
const spResponse = cachedResponse(spContext);
|
|
2725
|
+
if (cacheKey)
|
|
2726
|
+
cache.set(cacheKey, spResponse, 30000);
|
|
2727
|
+
return spResponse;
|
|
2728
|
+
}
|
|
2729
|
+
case "wyrm_import_git": {
|
|
2730
|
+
const { project_id: gitProjId, commits: gitCommits, auto_approve: gitAutoApprove } = args;
|
|
2731
|
+
const gitProject = db.getProjectById(gitProjId);
|
|
2732
|
+
if (!gitProject)
|
|
2733
|
+
return { content: [{ type: "text", text: `Project ID ${gitProjId} not found.` }], isError: true };
|
|
2734
|
+
let captured = 0, skippedCount = 0;
|
|
2735
|
+
for (const commit of gitCommits) {
|
|
2736
|
+
const msg = commit.message ?? '';
|
|
2737
|
+
if (/^Merge /i.test(msg)) {
|
|
2738
|
+
skippedCount++;
|
|
2739
|
+
continue;
|
|
2740
|
+
}
|
|
2741
|
+
if (/^(chore|bump|release|version)/i.test(msg)) {
|
|
2742
|
+
skippedCount++;
|
|
2743
|
+
continue;
|
|
2744
|
+
}
|
|
2745
|
+
let artifactKind = 'pattern';
|
|
2746
|
+
if (/^fix(\(.+\))?:/i.test(msg))
|
|
2747
|
+
artifactKind = 'lesson';
|
|
2748
|
+
else if (/^feat(\(.+\))?:/i.test(msg))
|
|
2749
|
+
artifactKind = 'pattern';
|
|
2750
|
+
else if (/^refactor(\(.+\))?:/i.test(msg))
|
|
2751
|
+
artifactKind = 'heuristic';
|
|
2752
|
+
const typePrefix = msg.split(':')[0] ?? 'commit';
|
|
2753
|
+
memory.add(gitProjId, {
|
|
2754
|
+
kind: artifactKind,
|
|
2755
|
+
problem: msg,
|
|
2756
|
+
validatedFix: commit.diff_summary ?? '',
|
|
2757
|
+
whyItWorked: `Committed by ${commit.author ?? 'unknown'} on ${commit.date ?? 'unknown'}`,
|
|
2758
|
+
tags: ['git', 'commit', typePrefix.toLowerCase()],
|
|
2759
|
+
confidence: 0.6,
|
|
2760
|
+
needsReview: gitAutoApprove ? 0 : 1,
|
|
2761
|
+
});
|
|
2762
|
+
captured++;
|
|
2763
|
+
}
|
|
2764
|
+
return {
|
|
2765
|
+
content: [{
|
|
2766
|
+
type: "text",
|
|
2767
|
+
text: `🐉 **Git Import Complete**\n\n- **Captured:** ${captured}\n- **Skipped:** ${skippedCount} (merges/chores)\n- **Review needed:** ${gitAutoApprove ? 'No (auto-approved)' : `Yes — run \`wyrm_review\` to activate ${captured} artifact${captured !== 1 ? 's' : ''}`}`,
|
|
2768
|
+
}],
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
case "wyrm_import_pr": {
|
|
2772
|
+
const { project_id: prProjId, title: prTitle, body: prBody, review_comments: prComments, auto_approve: prAutoApprove } = args;
|
|
2773
|
+
const prProject = db.getProjectById(prProjId);
|
|
2774
|
+
if (!prProject)
|
|
2775
|
+
return { content: [{ type: "text", text: `Project ID ${prProjId} not found.` }], isError: true };
|
|
2776
|
+
let prCreated = 0;
|
|
2777
|
+
const prNeedsReview = prAutoApprove ? 0 : 1;
|
|
2778
|
+
memory.add(prProjId, {
|
|
2779
|
+
kind: 'pattern',
|
|
2780
|
+
problem: `${prTitle}\n\n${prBody}`,
|
|
2781
|
+
validatedFix: '',
|
|
2782
|
+
whyItWorked: '',
|
|
2783
|
+
tags: ['git', 'pr'],
|
|
2784
|
+
confidence: 0.65,
|
|
2785
|
+
needsReview: prNeedsReview,
|
|
2786
|
+
});
|
|
2787
|
+
prCreated++;
|
|
2788
|
+
for (const comment of (prComments ?? [])) {
|
|
2789
|
+
if (/\b(should|must|always|never|prefer|avoid|use|don't)\b/i.test(comment)) {
|
|
2790
|
+
memory.add(prProjId, {
|
|
2791
|
+
kind: 'heuristic',
|
|
2792
|
+
problem: comment,
|
|
2793
|
+
validatedFix: '',
|
|
2794
|
+
whyItWorked: '',
|
|
2795
|
+
tags: ['git', 'pr', 'review'],
|
|
2796
|
+
confidence: 0.65,
|
|
2797
|
+
needsReview: prNeedsReview,
|
|
2798
|
+
});
|
|
2799
|
+
prCreated++;
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
return {
|
|
2803
|
+
content: [{
|
|
2804
|
+
type: "text",
|
|
2805
|
+
text: `🐉 **PR Import Complete**\n\n- **Artifacts created:** ${prCreated}\n- **Review needed:** ${prAutoApprove ? 'No (auto-approved)' : `Yes — run \`wyrm_review\` to activate`}`,
|
|
2806
|
+
}],
|
|
2807
|
+
};
|
|
2808
|
+
}
|
|
2809
|
+
case "wyrm_import_rules": {
|
|
2810
|
+
const { project_id: ruleProjId, content: ruleContent, format: ruleFormat, source_file: ruleSrc } = args;
|
|
2811
|
+
const rulesProject = db.getProjectById(ruleProjId);
|
|
2812
|
+
if (!rulesProject)
|
|
2813
|
+
return { content: [{ type: "text", text: `Project ID ${ruleProjId} not found.` }], isError: true };
|
|
2814
|
+
const fmt = ruleFormat ?? 'plain';
|
|
2815
|
+
const src = ruleSrc ?? 'rules';
|
|
2816
|
+
const baseTags = ['imported', fmt, src];
|
|
2817
|
+
// Section-based parsing: prefer markdown headings, fall back to paragraph breaks
|
|
2818
|
+
const byHeadings = ruleContent.split(/\n(?=#)/);
|
|
2819
|
+
const byParagraphs = ruleContent.split(/\n\n+/);
|
|
2820
|
+
const chunks = (byHeadings.length >= byParagraphs.length ? byHeadings : byParagraphs)
|
|
2821
|
+
.map((c) => c.trim())
|
|
2822
|
+
.filter((c) => c.length >= 15);
|
|
2823
|
+
let ruleTruthsCreated = 0, ruleArtifactsCreated = 0;
|
|
2824
|
+
for (const chunk of chunks) {
|
|
2825
|
+
if (/\b(always|never|must|use|don't|avoid|prefer)\b/i.test(chunk)) {
|
|
2826
|
+
groundTruths.set(ruleProjId, {
|
|
2827
|
+
category: 'constraint',
|
|
2828
|
+
key: chunk.slice(0, 50).replace(/\n/g, ' '),
|
|
2829
|
+
value: chunk,
|
|
2830
|
+
source: src,
|
|
2831
|
+
});
|
|
2832
|
+
ruleTruthsCreated++;
|
|
2833
|
+
}
|
|
2834
|
+
else {
|
|
2835
|
+
memory.add(ruleProjId, {
|
|
2836
|
+
kind: 'heuristic',
|
|
2837
|
+
problem: chunk,
|
|
2838
|
+
validatedFix: '',
|
|
2839
|
+
whyItWorked: '',
|
|
2840
|
+
tags: baseTags,
|
|
2841
|
+
confidence: 0.7,
|
|
2842
|
+
needsReview: 1,
|
|
2843
|
+
});
|
|
2844
|
+
ruleArtifactsCreated++;
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
return {
|
|
2848
|
+
content: [{
|
|
2849
|
+
type: "text",
|
|
2850
|
+
text: `🐉 **Rules Import Complete** (${fmt})\n\n- **Ground truths created:** ${ruleTruthsCreated} (active immediately)\n- **Memory artifacts created:** ${ruleArtifactsCreated} (pending review)\n- **Source:** ${src}\n\nRun \`wyrm_review\` to approve the memory artifacts.`,
|
|
2851
|
+
}],
|
|
2852
|
+
};
|
|
2853
|
+
}
|
|
2854
|
+
case "wyrm_inject_prompt": {
|
|
2855
|
+
const { project_path: injPath, clients: injClients } = args;
|
|
2856
|
+
const injectResult = injectSystemPrompt(injPath, injClients ?? []);
|
|
2857
|
+
let injText = `🐉 **System Prompt Injection**\n\n`;
|
|
2858
|
+
if (injectResult.injected.length > 0) {
|
|
2859
|
+
injText += `✅ Injected into:\n${injectResult.injected.map((f) => `- ${f}`).join('\n')}\n\n`;
|
|
2860
|
+
}
|
|
2861
|
+
if (injectResult.skipped.length > 0) {
|
|
2862
|
+
injText += `○ Skipped (unknown client): ${injectResult.skipped.join(', ')}\n\n`;
|
|
2863
|
+
}
|
|
2864
|
+
if (injectResult.errors.length > 0) {
|
|
2865
|
+
injText += `❌ Errors:\n${injectResult.errors.map((e) => `- ${e}`).join('\n')}\n\n`;
|
|
2866
|
+
}
|
|
2867
|
+
if (injectResult.injected.length > 0) {
|
|
2868
|
+
injText += `AI models in this project will now call \`wyrm_session_prime\` at the start of each conversation.`;
|
|
2869
|
+
}
|
|
2870
|
+
return { content: [{ type: "text", text: injText }] };
|
|
2871
|
+
}
|
|
2457
2872
|
default:
|
|
2458
2873
|
return {
|
|
2459
2874
|
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|