@stackmemoryai/stackmemory 1.8.1 → 1.10.2
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/cli/commands/orchestrate.js +8 -1
- package/dist/src/cli/commands/orchestrator.js +27 -6
- package/dist/src/cli/commands/ralph.js +43 -0
- package/dist/src/cli/commands/rules.js +250 -0
- package/dist/src/cli/commands/skill.js +406 -0
- package/dist/src/cli/index.js +43 -0
- package/dist/src/core/rules/built-in-rules.js +289 -0
- package/dist/src/core/rules/pr-review-rule.js +87 -0
- package/dist/src/core/rules/rule-engine.js +85 -0
- package/dist/src/core/rules/rule-store.js +99 -0
- package/dist/src/core/rules/types.js +4 -0
- package/dist/src/core/skills/index.js +21 -0
- package/dist/src/core/skills/skill-matcher.js +178 -0
- package/dist/src/core/skills/skill-registry.js +646 -0
- package/dist/src/core/skills/types.js +1 -47
- package/dist/src/integrations/linear/client.js +66 -0
- package/dist/src/integrations/mcp/handlers/index.js +43 -1
- package/dist/src/integrations/mcp/handlers/linear-handlers.js +102 -0
- package/dist/src/integrations/mcp/handlers/provenant-handlers.js +398 -0
- package/dist/src/integrations/mcp/handlers/skill-handlers.js +67 -167
- package/dist/src/integrations/mcp/server.js +117 -6
- package/dist/src/integrations/mcp/tool-definitions.js +152 -1
- package/dist/src/integrations/ralph/loopmax.js +488 -0
- package/package.json +2 -2
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
function extractFilePaths(prompt) {
|
|
6
|
+
const paths = /* @__PURE__ */ new Set();
|
|
7
|
+
const extensionPattern = /(?:^|\s|["'`])([\w\-./]+\.(?:[tj]sx?|json|gql|ya?ml|md|sh))\b/gi;
|
|
8
|
+
let match;
|
|
9
|
+
while ((match = extensionPattern.exec(prompt)) !== null) {
|
|
10
|
+
paths.add(match[1]);
|
|
11
|
+
}
|
|
12
|
+
const dirPattern = /(?:^|\s|["'`])((?:src|app|components|screens|hooks|utils|services|navigation|graphql|localization|\.claude|\.github|\.maestro)\/[\w\-./]+)/gi;
|
|
13
|
+
while ((match = dirPattern.exec(prompt)) !== null) {
|
|
14
|
+
paths.add(match[1]);
|
|
15
|
+
}
|
|
16
|
+
const quotedPattern = /["'`]([\w\-./]+\/[\w\-./]+)["'`]/g;
|
|
17
|
+
while ((match = quotedPattern.exec(prompt)) !== null) {
|
|
18
|
+
paths.add(match[1]);
|
|
19
|
+
}
|
|
20
|
+
return Array.from(paths);
|
|
21
|
+
}
|
|
22
|
+
function matchesPattern(text, pattern, flags = "i") {
|
|
23
|
+
try {
|
|
24
|
+
return new RegExp(pattern, flags).test(text);
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function matchesGlob(filePath, globPattern) {
|
|
30
|
+
const regexPattern = globPattern.replace(/\./g, "\\.").replace(/\?/g, "<<<QUESTION>>>").replace(/\*\*\//g, "<<<DOUBLESTARSLASH>>>").replace(/\*\*/g, "<<<DOUBLESTAR>>>").replace(/\*/g, "[^/]*").replace(/<<<DOUBLESTARSLASH>>>/g, "(?:.*\\/)?").replace(/<<<DOUBLESTAR>>>/g, ".*").replace(/<<<QUESTION>>>/g, ".");
|
|
31
|
+
try {
|
|
32
|
+
return new RegExp(`^${regexPattern}$`, "i").test(filePath);
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function matchDirectoryMapping(filePath, mappings) {
|
|
38
|
+
for (const [dir, skillName] of Object.entries(mappings)) {
|
|
39
|
+
if (filePath === dir || filePath.startsWith(dir + "/")) {
|
|
40
|
+
return skillName;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
function evaluateSkill(skillName, skill, prompt, promptLower, filePaths, scoring, directoryMappings) {
|
|
46
|
+
const { triggers, excludePatterns = [], priority = 5 } = skill;
|
|
47
|
+
let score = 0;
|
|
48
|
+
const reasons = [];
|
|
49
|
+
for (const excludePattern of excludePatterns) {
|
|
50
|
+
if (matchesPattern(promptLower, excludePattern)) {
|
|
51
|
+
return void 0;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (triggers.keywords) {
|
|
55
|
+
for (const keyword of triggers.keywords) {
|
|
56
|
+
if (promptLower.includes(keyword.toLowerCase())) {
|
|
57
|
+
score += scoring.keyword;
|
|
58
|
+
reasons.push(`keyword "${keyword}"`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (triggers.keywordPatterns) {
|
|
63
|
+
for (const pattern of triggers.keywordPatterns) {
|
|
64
|
+
if (matchesPattern(promptLower, pattern)) {
|
|
65
|
+
score += scoring.keywordPattern;
|
|
66
|
+
reasons.push(`pattern /${pattern}/`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (triggers.intentPatterns) {
|
|
71
|
+
for (const pattern of triggers.intentPatterns) {
|
|
72
|
+
if (matchesPattern(promptLower, pattern)) {
|
|
73
|
+
score += scoring.intentPattern;
|
|
74
|
+
reasons.push("intent detected");
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (triggers.contextPatterns) {
|
|
80
|
+
for (const pattern of triggers.contextPatterns) {
|
|
81
|
+
if (promptLower.includes(pattern.toLowerCase())) {
|
|
82
|
+
score += scoring.contextPattern;
|
|
83
|
+
reasons.push(`context "${pattern}"`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (triggers.pathPatterns && filePaths.length > 0) {
|
|
88
|
+
for (const filePath of filePaths) {
|
|
89
|
+
for (const pattern of triggers.pathPatterns) {
|
|
90
|
+
if (matchesGlob(filePath, pattern)) {
|
|
91
|
+
score += scoring.pathPattern;
|
|
92
|
+
reasons.push(`path "${filePath}"`);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (filePaths.length > 0) {
|
|
99
|
+
for (const filePath of filePaths) {
|
|
100
|
+
const mappedSkill = matchDirectoryMapping(filePath, directoryMappings);
|
|
101
|
+
if (mappedSkill === skillName) {
|
|
102
|
+
score += scoring.directoryMatch;
|
|
103
|
+
reasons.push("directory mapping");
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (triggers.contentPatterns) {
|
|
109
|
+
for (const pattern of triggers.contentPatterns) {
|
|
110
|
+
if (matchesPattern(prompt, pattern)) {
|
|
111
|
+
score += scoring.contentPattern;
|
|
112
|
+
reasons.push("code pattern detected");
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (score > 0) {
|
|
118
|
+
return {
|
|
119
|
+
name: skillName,
|
|
120
|
+
score,
|
|
121
|
+
reasons: [...new Set(reasons)],
|
|
122
|
+
priority
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
function getRelatedSkills(matches, rules) {
|
|
128
|
+
const matchedNames = new Set(matches.map((m) => m.name));
|
|
129
|
+
const related = /* @__PURE__ */ new Set();
|
|
130
|
+
for (const m of matches) {
|
|
131
|
+
const skill = rules[m.name];
|
|
132
|
+
if (skill?.relatedSkills) {
|
|
133
|
+
for (const relatedName of skill.relatedSkills) {
|
|
134
|
+
if (!matchedNames.has(relatedName)) {
|
|
135
|
+
related.add(relatedName);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return Array.from(related);
|
|
141
|
+
}
|
|
142
|
+
function formatConfidence(score, minScore) {
|
|
143
|
+
if (score >= minScore * 3) return "HIGH";
|
|
144
|
+
if (score >= minScore * 2) return "MEDIUM";
|
|
145
|
+
return "LOW";
|
|
146
|
+
}
|
|
147
|
+
function matchPrompt(prompt, rules, config, scoring, directoryMappings = {}) {
|
|
148
|
+
const promptLower = prompt.toLowerCase();
|
|
149
|
+
const filePaths = extractFilePaths(prompt);
|
|
150
|
+
const matches = [];
|
|
151
|
+
for (const [name, skill] of Object.entries(rules)) {
|
|
152
|
+
const m = evaluateSkill(
|
|
153
|
+
name,
|
|
154
|
+
skill,
|
|
155
|
+
prompt,
|
|
156
|
+
promptLower,
|
|
157
|
+
filePaths,
|
|
158
|
+
scoring,
|
|
159
|
+
directoryMappings
|
|
160
|
+
);
|
|
161
|
+
if (m && m.score >= config.minConfidenceScore) {
|
|
162
|
+
matches.push(m);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
matches.sort((a, b) => {
|
|
166
|
+
if (b.score !== a.score) return b.score - a.score;
|
|
167
|
+
return b.priority - a.priority;
|
|
168
|
+
});
|
|
169
|
+
const topMatches = matches.slice(0, config.maxSkillsToShow);
|
|
170
|
+
const relatedSkills = getRelatedSkills(topMatches, rules);
|
|
171
|
+
return { matches: topMatches, filePaths, relatedSkills };
|
|
172
|
+
}
|
|
173
|
+
export {
|
|
174
|
+
extractFilePaths,
|
|
175
|
+
formatConfidence,
|
|
176
|
+
matchPrompt,
|
|
177
|
+
matchesGlob
|
|
178
|
+
};
|