memtrace-skills 0.8.9 → 0.8.10
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/commands/doctor.js +24 -1
- package/dist/commands/install.js +10 -4
- package/dist/index.js +1 -1
- package/dist/transformers/index.d.ts +2 -1
- package/dist/transformers/index.js +3 -1
- package/dist/transformers/pi.d.ts +5 -0
- package/dist/transformers/pi.js +152 -0
- package/dist/transformers/types.d.ts +1 -1
- package/package.json +1 -1
- package/plugins/memtrace-skills/.claude-plugin/plugin.json +1 -1
package/dist/commands/doctor.js
CHANGED
|
@@ -66,6 +66,17 @@ function hermesMcpHasMemtrace(file) {
|
|
|
66
66
|
const raw = fs.readFileSync(file, 'utf-8');
|
|
67
67
|
return /^mcp_servers:\s*$(?:[\s\S]*?)^\s{2}memtrace:\s*$/m.test(raw);
|
|
68
68
|
}
|
|
69
|
+
function piPackageRegistered(file) {
|
|
70
|
+
const { value } = safeReadJson(file);
|
|
71
|
+
if (!value?.packages?.length)
|
|
72
|
+
return false;
|
|
73
|
+
return value.packages.some(entry => {
|
|
74
|
+
const source = typeof entry === 'string' ? entry : entry.source;
|
|
75
|
+
if (!source)
|
|
76
|
+
return false;
|
|
77
|
+
return source.endsWith(`${path.sep}pi-package`) || source.endsWith('/pi-package');
|
|
78
|
+
});
|
|
79
|
+
}
|
|
69
80
|
function opencodeConfigDir() {
|
|
70
81
|
const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), '.config');
|
|
71
82
|
return path.join(xdg, 'opencode');
|
|
@@ -170,6 +181,17 @@ function checkAgent(agent) {
|
|
|
170
181
|
mcpConfigPath,
|
|
171
182
|
};
|
|
172
183
|
}
|
|
184
|
+
if (agent === 'pi') {
|
|
185
|
+
const skillsDir = path.join(os.homedir(), '.pi', 'agent', 'skills');
|
|
186
|
+
const mcpConfigPath = path.join(os.homedir(), '.pi', 'agent', 'settings.json');
|
|
187
|
+
return {
|
|
188
|
+
agent,
|
|
189
|
+
skillsFound: countMemtraceSkills(skillsDir),
|
|
190
|
+
skillsDir,
|
|
191
|
+
mcpRegistered: piPackageRegistered(mcpConfigPath),
|
|
192
|
+
mcpConfigPath,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
173
195
|
const skillsDir = path.join(os.homedir(), '.cursor', 'skills');
|
|
174
196
|
const mcpConfigPath = path.join(os.homedir(), '.cursor', 'mcp.json');
|
|
175
197
|
return {
|
|
@@ -197,7 +219,8 @@ export function formatReport(r) {
|
|
|
197
219
|
lines.push(` version: ${r.binaryVersion}`);
|
|
198
220
|
for (const a of r.agents) {
|
|
199
221
|
lines.push(`${check(a.skillsFound > 0)} ${a.agent} skills installed ${a.skillsFound} in ${a.skillsDir}`);
|
|
200
|
-
|
|
222
|
+
const integrationLabel = a.agent === 'pi' ? 'Pi package registered' : 'MCP registered';
|
|
223
|
+
lines.push(`${check(a.mcpRegistered)} ${a.agent} ${integrationLabel} ${a.mcpConfigPath}`);
|
|
201
224
|
}
|
|
202
225
|
return lines.join('\n');
|
|
203
226
|
}
|
package/dist/commands/install.js
CHANGED
|
@@ -61,12 +61,18 @@ export async function runInstall(options) {
|
|
|
61
61
|
try {
|
|
62
62
|
const r = await t.install(skills, ctx);
|
|
63
63
|
console.log(` [${t.name}] ✓ ${r.skillsWritten} skills → ${r.skillsDir}`);
|
|
64
|
-
if (r.mcpRegistered)
|
|
65
|
-
console.log(` [${t.name}] ✓ MCP registered`);
|
|
66
|
-
|
|
64
|
+
if (r.mcpRegistered) {
|
|
65
|
+
console.log(` [${t.name}] ✓ ${t.name === 'pi' ? 'Pi package registered' : 'MCP registered'}`);
|
|
66
|
+
}
|
|
67
|
+
else if (t.name === 'pi') {
|
|
68
|
+
console.log(` [${t.name}] ⚠ Pi package registration failed`);
|
|
69
|
+
}
|
|
70
|
+
else if (ctx.skipMcp) {
|
|
67
71
|
console.log(` [${t.name}] (MCP skipped)`);
|
|
68
|
-
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
69
74
|
console.log(` [${t.name}] ⚠ MCP registration failed`);
|
|
75
|
+
}
|
|
70
76
|
for (const w of r.warnings)
|
|
71
77
|
console.warn(` [${t.name}] warn: ${w}`);
|
|
72
78
|
}
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ function parseOnly(val) {
|
|
|
15
15
|
program
|
|
16
16
|
.command('install')
|
|
17
17
|
.description('Install memtrace skills and register MCP for selected agents')
|
|
18
|
-
.option('--only <agents>', 'comma-separated agent names (claude,cursor,codex,gemini,windsurf,vscode,hermes,opencode,kiro,warp)', parseOnly)
|
|
18
|
+
.option('--only <agents>', 'comma-separated agent names (claude,cursor,codex,gemini,windsurf,vscode,hermes,opencode,kiro,warp,pi)', parseOnly)
|
|
19
19
|
.option('--local', 'install into the current project where the selected agent supports project scope', false)
|
|
20
20
|
.option('--global', 'install globally (~/.claude/, ~/.cursor/, ~/.agents/) [default]', false)
|
|
21
21
|
.option('--skip-mcp', 'write skills only, skip MCP server registration', false)
|
|
@@ -9,7 +9,8 @@ import { hermesTransformer } from './hermes.js';
|
|
|
9
9
|
import { opencodeTransformer } from './opencode.js';
|
|
10
10
|
import { kiroTransformer } from './kiro.js';
|
|
11
11
|
import { warpTransformer } from './warp.js';
|
|
12
|
+
import { piTransformer } from './pi.js';
|
|
12
13
|
export declare const ALL_TRANSFORMERS: Transformer[];
|
|
13
14
|
export declare function findTransformer(name: string): Transformer | undefined;
|
|
14
|
-
export { claudeTransformer, cursorTransformer, codexTransformer, geminiTransformer, windsurfTransformer, vscodeTransformer, hermesTransformer, opencodeTransformer, kiroTransformer, warpTransformer, };
|
|
15
|
+
export { claudeTransformer, cursorTransformer, codexTransformer, geminiTransformer, windsurfTransformer, vscodeTransformer, hermesTransformer, opencodeTransformer, kiroTransformer, warpTransformer, piTransformer, };
|
|
15
16
|
export type { Transformer, InstallContext, InstallResult, TransformResult } from './types.js';
|
|
@@ -8,6 +8,7 @@ import { hermesTransformer } from './hermes.js';
|
|
|
8
8
|
import { opencodeTransformer } from './opencode.js';
|
|
9
9
|
import { kiroTransformer } from './kiro.js';
|
|
10
10
|
import { warpTransformer } from './warp.js';
|
|
11
|
+
import { piTransformer } from './pi.js';
|
|
11
12
|
export const ALL_TRANSFORMERS = [
|
|
12
13
|
claudeTransformer,
|
|
13
14
|
cursorTransformer,
|
|
@@ -19,8 +20,9 @@ export const ALL_TRANSFORMERS = [
|
|
|
19
20
|
opencodeTransformer,
|
|
20
21
|
kiroTransformer,
|
|
21
22
|
warpTransformer,
|
|
23
|
+
piTransformer,
|
|
22
24
|
];
|
|
23
25
|
export function findTransformer(name) {
|
|
24
26
|
return ALL_TRANSFORMERS.find(t => t.name === name);
|
|
25
27
|
}
|
|
26
|
-
export { claudeTransformer, cursorTransformer, codexTransformer, geminiTransformer, windsurfTransformer, vscodeTransformer, hermesTransformer, opencodeTransformer, kiroTransformer, warpTransformer, };
|
|
28
|
+
export { claudeTransformer, cursorTransformer, codexTransformer, geminiTransformer, windsurfTransformer, vscodeTransformer, hermesTransformer, opencodeTransformer, kiroTransformer, warpTransformer, piTransformer, };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Transformer, InstallContext } from './types.js';
|
|
2
|
+
export declare function piSettingsPath(ctx: InstallContext): string;
|
|
3
|
+
/** Resolve the pi-package directory shipped inside the memtrace npm bundle. */
|
|
4
|
+
export declare function resolveBundledPiPackage(fromDir?: string): string;
|
|
5
|
+
export declare const piTransformer: Transformer;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { removeMemtraceSkills, skillName } from './shared.js';
|
|
6
|
+
import { safeReadJson, writeJsonAtomic } from '../fs-safe.js';
|
|
7
|
+
import { commandExists } from '../utils.js';
|
|
8
|
+
// Pi (https://pi.dev/docs/latest/packages) uses native pi packages — not MCP JSON
|
|
9
|
+
// config like Cursor. The bundled `pi-package/` extension spawns `memtrace mcp`
|
|
10
|
+
// itself and registers every Memtrace tool as a native Pi tool (`memtrace_*`).
|
|
11
|
+
//
|
|
12
|
+
// `memtrace install --only pi` writes skills + registers this bundled package.
|
|
13
|
+
function piAgentRoot(ctx) {
|
|
14
|
+
return ctx.scope === 'global'
|
|
15
|
+
? path.join(os.homedir(), '.pi', 'agent')
|
|
16
|
+
: path.join(ctx.cwd, '.pi');
|
|
17
|
+
}
|
|
18
|
+
function skillsRoot(ctx) {
|
|
19
|
+
return path.join(piAgentRoot(ctx), 'skills');
|
|
20
|
+
}
|
|
21
|
+
export function piSettingsPath(ctx) {
|
|
22
|
+
return path.join(piAgentRoot(ctx), 'settings.json');
|
|
23
|
+
}
|
|
24
|
+
/** Resolve the pi-package directory shipped inside the memtrace npm bundle. */
|
|
25
|
+
export function resolveBundledPiPackage(fromDir = path.dirname(fileURLToPath(import.meta.url))) {
|
|
26
|
+
for (const rel of ['../../../pi-package', '../../pi-package']) {
|
|
27
|
+
const candidate = path.resolve(fromDir, rel);
|
|
28
|
+
if (fs.existsSync(path.join(candidate, 'package.json')))
|
|
29
|
+
return candidate;
|
|
30
|
+
}
|
|
31
|
+
throw new Error('bundled pi-package not found (reinstall memtrace from npm)');
|
|
32
|
+
}
|
|
33
|
+
function transformForPi(text) {
|
|
34
|
+
return text.replace(/mcp__memtrace__/g, 'memtrace_');
|
|
35
|
+
}
|
|
36
|
+
function transformAllowedTool(tool) {
|
|
37
|
+
return tool
|
|
38
|
+
.replace(/^mcp__memtrace__/, 'memtrace_')
|
|
39
|
+
.replace(/^Read$/, 'read')
|
|
40
|
+
.replace(/^Grep$/, 'grep')
|
|
41
|
+
.replace(/^Glob$/, 'find');
|
|
42
|
+
}
|
|
43
|
+
function skillMarkdownForPi(skill) {
|
|
44
|
+
const name = skillName(skill);
|
|
45
|
+
const safeDesc = skill.frontmatter.description.replace(/"/g, '\\"').trim();
|
|
46
|
+
const tools = skill.frontmatter['allowed-tools'];
|
|
47
|
+
const toolsLine = tools?.length
|
|
48
|
+
? `\nallowed-tools: ${tools.map(transformAllowedTool).join(' ')}`
|
|
49
|
+
: '';
|
|
50
|
+
const body = transformForPi(skill.body);
|
|
51
|
+
return `---\nname: ${name}\ndescription: "${safeDesc}"${toolsLine}\n---\n\n${body}`;
|
|
52
|
+
}
|
|
53
|
+
function writeSkillsForPi(skills, rootDir) {
|
|
54
|
+
removeMemtraceSkills(rootDir);
|
|
55
|
+
for (const skill of skills) {
|
|
56
|
+
const name = skillName(skill);
|
|
57
|
+
const outDir = path.join(rootDir, name);
|
|
58
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
59
|
+
fs.writeFileSync(path.join(outDir, 'SKILL.md'), skillMarkdownForPi(skill));
|
|
60
|
+
}
|
|
61
|
+
return skills.length;
|
|
62
|
+
}
|
|
63
|
+
function packageEntrySource(entry) {
|
|
64
|
+
if (typeof entry === 'string')
|
|
65
|
+
return entry;
|
|
66
|
+
return typeof entry.source === 'string' ? entry.source : undefined;
|
|
67
|
+
}
|
|
68
|
+
function isMemtracePiPackageEntry(entry, packagePath) {
|
|
69
|
+
const source = packageEntrySource(entry);
|
|
70
|
+
if (!source)
|
|
71
|
+
return false;
|
|
72
|
+
if (source === packagePath)
|
|
73
|
+
return true;
|
|
74
|
+
const base = `${path.sep}pi-package`;
|
|
75
|
+
return source.endsWith(base) || source.endsWith('/pi-package');
|
|
76
|
+
}
|
|
77
|
+
function registerPiPackageAt(settingsFile, packagePath) {
|
|
78
|
+
const { value, corrupted, backupPath } = safeReadJson(settingsFile);
|
|
79
|
+
if (corrupted)
|
|
80
|
+
return { registered: false, backupPath, packagePath };
|
|
81
|
+
const cfg = (value ?? {});
|
|
82
|
+
cfg.packages = (cfg.packages ?? []).filter(entry => !isMemtracePiPackageEntry(entry, packagePath));
|
|
83
|
+
cfg.packages.push(packagePath);
|
|
84
|
+
writeJsonAtomic(settingsFile, cfg);
|
|
85
|
+
return { registered: true, packagePath };
|
|
86
|
+
}
|
|
87
|
+
function removePiPackageAt(settingsFile, packagePath) {
|
|
88
|
+
const { value, corrupted } = safeReadJson(settingsFile);
|
|
89
|
+
if (corrupted || !value?.packages?.length)
|
|
90
|
+
return;
|
|
91
|
+
const kept = value.packages.filter(entry => !isMemtracePiPackageEntry(entry, packagePath));
|
|
92
|
+
if (kept.length === value.packages.length)
|
|
93
|
+
return;
|
|
94
|
+
if (kept.length === 0) {
|
|
95
|
+
delete value.packages;
|
|
96
|
+
if (Object.keys(value).length === 0)
|
|
97
|
+
fs.unlinkSync(settingsFile);
|
|
98
|
+
else
|
|
99
|
+
writeJsonAtomic(settingsFile, value);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
value.packages = kept;
|
|
103
|
+
writeJsonAtomic(settingsFile, value);
|
|
104
|
+
}
|
|
105
|
+
export const piTransformer = {
|
|
106
|
+
name: 'pi',
|
|
107
|
+
async install(skills, ctx) {
|
|
108
|
+
const rootDir = skillsRoot(ctx);
|
|
109
|
+
const count = writeSkillsForPi(skills, rootDir);
|
|
110
|
+
const warnings = [];
|
|
111
|
+
const settingsFile = piSettingsPath(ctx);
|
|
112
|
+
let packagePath;
|
|
113
|
+
try {
|
|
114
|
+
packagePath = resolveBundledPiPackage();
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
return {
|
|
118
|
+
agent: 'pi',
|
|
119
|
+
skillsWritten: count,
|
|
120
|
+
skillsDir: rootDir,
|
|
121
|
+
mcpConfigPath: settingsFile,
|
|
122
|
+
mcpRegistered: false,
|
|
123
|
+
warnings: [`${e.message}`],
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const result = registerPiPackageAt(settingsFile, packagePath);
|
|
127
|
+
if (!result.registered && result.backupPath) {
|
|
128
|
+
warnings.push(`Pi settings.json was malformed; backed up to ${result.backupPath}.`);
|
|
129
|
+
}
|
|
130
|
+
if (!(await commandExists('pi'))) {
|
|
131
|
+
warnings.push('Pi CLI not found on PATH — skills and package path are configured; install Pi from https://pi.dev to use them.');
|
|
132
|
+
}
|
|
133
|
+
if (ctx.skipMcp) {
|
|
134
|
+
warnings.push('memtrace binary not found — the Pi extension will connect once `memtrace` is on PATH.');
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
agent: 'pi',
|
|
138
|
+
skillsWritten: count,
|
|
139
|
+
skillsDir: rootDir,
|
|
140
|
+
mcpConfigPath: settingsFile,
|
|
141
|
+
mcpRegistered: result.registered,
|
|
142
|
+
warnings,
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
async uninstall(ctx) {
|
|
146
|
+
removeMemtraceSkills(skillsRoot(ctx));
|
|
147
|
+
try {
|
|
148
|
+
removePiPackageAt(piSettingsPath(ctx), resolveBundledPiPackage());
|
|
149
|
+
}
|
|
150
|
+
catch { /* bundled pi-package path unavailable */ }
|
|
151
|
+
},
|
|
152
|
+
};
|
|
@@ -29,7 +29,7 @@ export interface InstallResult {
|
|
|
29
29
|
mcpRegistered: boolean;
|
|
30
30
|
warnings: string[];
|
|
31
31
|
}
|
|
32
|
-
export type AgentName = 'claude' | 'cursor' | 'codex' | 'gemini' | 'windsurf' | 'vscode' | 'hermes' | 'opencode' | 'kiro' | 'warp';
|
|
32
|
+
export type AgentName = 'claude' | 'cursor' | 'codex' | 'gemini' | 'windsurf' | 'vscode' | 'hermes' | 'opencode' | 'kiro' | 'warp' | 'pi';
|
|
33
33
|
/**
|
|
34
34
|
* One transformer per supported AI coding agent.
|
|
35
35
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memtrace-skills",
|
|
3
3
|
"description": "Memtrace skills for codebase exploration, code search, relationship analysis, temporal evolution, blast radius impact, code quality, graph algorithms, API topology, GitHub PR code review, multi-agent fleet coordination (branch-scoped intents, conflict classification, and agent-judged Class C resolution), and multi-step workflows for change impact, incident investigation, refactoring, session continuity, co-change analysis, and episode replay, Cortex decision memory (decision recall, provenance, intent verification), session bookends (daily briefing, hotspots, self-audit), and single-symbol pre-edit preflight checks.",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.10",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Syncable",
|
|
7
7
|
"email": "support@syncable.dev"
|