@stackmemoryai/stackmemory 1.10.4 → 1.12.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 +104 -23
- package/dist/src/cli/claude-sm.js +266 -84
- package/dist/src/cli/codex-sm.js +185 -33
- package/dist/src/cli/commands/bench.js +209 -2
- package/dist/src/cli/commands/cache.js +126 -0
- package/dist/src/cli/commands/daemon.js +41 -0
- package/dist/src/cli/commands/handoff.js +40 -9
- package/dist/src/cli/commands/onboard.js +70 -3
- package/dist/src/cli/commands/optimize.js +117 -0
- package/dist/src/cli/commands/orchestrate.js +230 -5
- package/dist/src/cli/commands/orchestrator.js +312 -24
- package/dist/src/cli/commands/pack.js +322 -0
- package/dist/src/cli/commands/search.js +40 -1
- package/dist/src/cli/commands/setup.js +177 -7
- package/dist/src/cli/commands/skills.js +10 -1
- package/dist/src/cli/commands/state.js +265 -0
- package/dist/src/cli/commands/wiki.js +33 -0
- package/dist/src/cli/gemini-sm.js +19 -29
- package/dist/src/cli/index.js +90 -29
- package/dist/src/cli/opencode-sm.js +38 -21
- package/dist/src/cli/utils/determinism-watcher.js +66 -0
- package/dist/src/cli/utils/real-cli-bin.js +44 -0
- package/dist/src/core/cache/content-cache.js +238 -0
- package/dist/src/core/cache/index.js +11 -0
- package/dist/src/core/cache/token-estimator.js +16 -0
- package/dist/src/core/context/frame-database.js +38 -30
- package/dist/src/core/cross-search/cross-project-search.js +269 -0
- package/dist/src/core/{merge → cross-search}/index.js +6 -4
- package/dist/src/core/database/sqlite-adapter.js +0 -83
- package/dist/src/core/extensions/provider-adapter.js +5 -0
- package/dist/src/core/models/model-router.js +22 -2
- package/dist/src/core/monitoring/logger.js +2 -1
- package/dist/src/core/optimization/trace-optimizer.js +413 -0
- package/dist/src/core/provenance/confidence-scorer.js +128 -0
- package/dist/src/core/provenance/index.js +40 -0
- package/dist/src/core/provenance/provenance-store.js +194 -0
- package/dist/src/core/provenance/types.js +82 -0
- package/dist/src/core/session/project-handoff.js +64 -0
- package/dist/src/core/session/session-manager.js +28 -0
- package/dist/src/core/shared-state/canonical-store.js +564 -0
- package/dist/src/core/skill-packs/index.js +18 -0
- package/dist/src/core/skill-packs/parser.js +42 -0
- package/dist/src/core/skill-packs/registry.js +224 -0
- package/dist/src/core/skill-packs/types.js +66 -0
- package/dist/src/core/trace/trace-event-store.js +282 -0
- package/dist/src/core/trace/trace-event.js +4 -0
- package/dist/src/core/wiki/wiki-compiler.js +219 -0
- package/dist/src/daemon/daemon-config.js +7 -0
- package/dist/src/daemon/services/github-service.js +126 -0
- package/dist/src/daemon/unified-daemon.js +30 -0
- package/dist/src/features/sweep/pty-wrapper.js +13 -5
- package/dist/src/hooks/schemas.js +2 -0
- package/dist/src/integrations/claude-code/subagent-client.js +89 -0
- package/dist/src/integrations/github/pr-state.js +158 -0
- package/dist/src/integrations/linear/client.js +4 -1
- package/dist/src/integrations/mcp/handlers/cross-search-handlers.js +188 -0
- package/dist/src/integrations/mcp/handlers/index.js +40 -59
- package/dist/src/integrations/mcp/server.js +425 -311
- package/dist/src/integrations/mcp/tool-alias-registry.js +370 -0
- package/dist/src/integrations/mcp/tool-definitions.js +98 -229
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +3 -40
- package/dist/src/integrations/ralph/learning/pattern-learner.js +1 -20
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -2
- package/dist/src/mcp/stackmemory-mcp-server.js +315 -0
- package/dist/src/orchestrators/multimodal/determinism.js +243 -0
- package/dist/src/orchestrators/multimodal/harness.js +147 -77
- package/dist/src/orchestrators/multimodal/providers.js +44 -3
- package/dist/src/utils/hook-installer.js +8 -8
- package/package.json +10 -1
- package/packs/coding/python-fastapi/instructions.md +60 -0
- package/packs/coding/python-fastapi/pack.yaml +28 -0
- package/packs/coding/typescript-react/instructions.md +47 -0
- package/packs/coding/typescript-react/pack.yaml +28 -0
- package/packs/core/commands/capture.md +32 -0
- package/packs/core/commands/learn.md +73 -0
- package/packs/core/commands/next.md +36 -0
- package/packs/core/commands/restart.md +58 -0
- package/packs/core/commands/restore.md +29 -0
- package/packs/core/commands/start.md +57 -0
- package/packs/core/commands/stop.md +65 -0
- package/packs/core/commands/summary.md +40 -0
- package/packs/core/manifest.json +24 -0
- package/packs/ops/decision-recovery/instructions.md +65 -0
- package/packs/ops/decision-recovery/pack.yaml +89 -0
- package/templates/claude-hooks/doc-ingest.js +76 -0
- package/dist/src/cli/commands/team.js +0 -168
- package/dist/src/core/context/shared-context-layer.js +0 -620
- package/dist/src/core/context/stack-merge-resolver.js +0 -748
- package/dist/src/core/merge/conflict-detector.js +0 -430
- package/dist/src/core/merge/resolution-engine.js +0 -557
- package/dist/src/core/merge/stack-diff.js +0 -531
- package/dist/src/core/merge/unified-merge-resolver.js +0 -302
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
- package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
- /package/dist/src/core/{merge → cache}/types.js +0 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
3
|
+
import { dirname as __pathDirname } from 'path';
|
|
4
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = __pathDirname(__filename);
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import * as fs from "fs";
|
|
9
|
+
import * as path from "path";
|
|
10
|
+
import * as os from "os";
|
|
11
|
+
import { execSync } from "child_process";
|
|
12
|
+
import * as yaml from "js-yaml";
|
|
13
|
+
import {
|
|
14
|
+
getSkillPackRegistry,
|
|
15
|
+
loadPackFromDir
|
|
16
|
+
} from "../../core/skill-packs/index.js";
|
|
17
|
+
function resolvePackSource(source) {
|
|
18
|
+
if (fs.existsSync(source) && fs.statSync(source).isDirectory()) {
|
|
19
|
+
return source;
|
|
20
|
+
}
|
|
21
|
+
if (fs.existsSync(source) && (source.endsWith(".yaml") || source.endsWith(".yml"))) {
|
|
22
|
+
return path.dirname(source);
|
|
23
|
+
}
|
|
24
|
+
if (/^[\w-]+\/[\w-]+$/.test(source)) {
|
|
25
|
+
return cloneFromGitHub(source);
|
|
26
|
+
}
|
|
27
|
+
if (source.startsWith("https://github.com/")) {
|
|
28
|
+
return cloneFromGitHub(source);
|
|
29
|
+
}
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Cannot resolve pack source: ${source}
|
|
32
|
+
Expected: local directory, pack.yaml path, namespace/pack-name, or GitHub URL`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
function cloneFromGitHub(source) {
|
|
36
|
+
const registryOrg = process.env["STACKMEMORY_PACK_REGISTRY"] || "stackmemoryai";
|
|
37
|
+
let repoUrl;
|
|
38
|
+
let packSubdir;
|
|
39
|
+
if (source.startsWith("https://")) {
|
|
40
|
+
repoUrl = source;
|
|
41
|
+
} else {
|
|
42
|
+
repoUrl = `https://github.com/${registryOrg}/skill-packs.git`;
|
|
43
|
+
packSubdir = source.replace("/", "/");
|
|
44
|
+
}
|
|
45
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sm-pack-"));
|
|
46
|
+
try {
|
|
47
|
+
execSync(`git clone --depth 1 ${repoUrl} ${tmpDir}`, {
|
|
48
|
+
stdio: "pipe",
|
|
49
|
+
timeout: 3e4
|
|
50
|
+
});
|
|
51
|
+
} catch {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Failed to clone ${repoUrl}. Check the URL or run: git clone ${repoUrl}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
const packDir = packSubdir ? path.join(tmpDir, packSubdir) : tmpDir;
|
|
57
|
+
if (!fs.existsSync(path.join(packDir, "pack.yaml"))) {
|
|
58
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
59
|
+
throw new Error(`No pack.yaml found in ${packSubdir || "repository root"}`);
|
|
60
|
+
}
|
|
61
|
+
return packDir;
|
|
62
|
+
}
|
|
63
|
+
function formatPack(pack) {
|
|
64
|
+
const m = pack.manifest;
|
|
65
|
+
const tools = m.mcp?.tools?.length ?? 0;
|
|
66
|
+
const examples = m.examples?.length ?? 0;
|
|
67
|
+
const runtime = m.runtime?.type ?? "local";
|
|
68
|
+
const installed = pack.metadata?.installedAt ? new Date(pack.metadata.installedAt).toLocaleDateString() : "";
|
|
69
|
+
return [
|
|
70
|
+
` ${chalk.bold(m.name)} ${chalk.dim(`v${m.version}`)}`,
|
|
71
|
+
` ${chalk.dim(m.description)}`,
|
|
72
|
+
` ${chalk.dim(`runtime: ${runtime} | tools: ${tools} | examples: ${examples}`)}`,
|
|
73
|
+
installed ? ` ${chalk.dim(`installed: ${installed}`)}` : ""
|
|
74
|
+
].filter(Boolean).join("\n");
|
|
75
|
+
}
|
|
76
|
+
function createPackCommand() {
|
|
77
|
+
const cmd = new Command("pack").description(
|
|
78
|
+
"Manage skill packs \u2014 versioned, distributable agent bundles (pack.yaml)"
|
|
79
|
+
);
|
|
80
|
+
cmd.command("install <source>").description(
|
|
81
|
+
"Install a skill pack from a local dir, GitHub URL, or namespace/name"
|
|
82
|
+
).option("--force", "Overwrite existing pack").action(async (source, options) => {
|
|
83
|
+
try {
|
|
84
|
+
const dir = resolvePackSource(source);
|
|
85
|
+
const pack = await loadPackFromDir(dir);
|
|
86
|
+
const registry = getSkillPackRegistry();
|
|
87
|
+
const existing = registry.get(pack.manifest.name);
|
|
88
|
+
if (existing && !options.force) {
|
|
89
|
+
console.log(
|
|
90
|
+
chalk.yellow(
|
|
91
|
+
`Pack ${pack.manifest.name}@${existing.manifest.version} already installed. Use --force to overwrite.`
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
registry.install(pack);
|
|
97
|
+
console.log(
|
|
98
|
+
chalk.green(
|
|
99
|
+
`\u2713 Installed ${pack.manifest.name}@${pack.manifest.version}`
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
if (pack.manifest.mcp?.tools?.length) {
|
|
103
|
+
console.log(
|
|
104
|
+
chalk.dim(
|
|
105
|
+
` ${pack.manifest.mcp.tools.length} MCP tools registered`
|
|
106
|
+
)
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
} catch (err) {
|
|
110
|
+
console.error(
|
|
111
|
+
chalk.red(
|
|
112
|
+
`Failed to install: ${err instanceof Error ? err.message : err}`
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
cmd.command("uninstall <name>").description("Remove an installed skill pack").action((name) => {
|
|
119
|
+
const registry = getSkillPackRegistry();
|
|
120
|
+
if (registry.uninstall(name)) {
|
|
121
|
+
console.log(chalk.green(`\u2713 Uninstalled ${name}`));
|
|
122
|
+
} else {
|
|
123
|
+
console.log(chalk.yellow(`Pack ${name} not found`));
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
cmd.command("list").description("List installed skill packs").option("--namespace <ns>", "Filter by namespace").option("--json", "Output as JSON").action((options) => {
|
|
127
|
+
const registry = getSkillPackRegistry();
|
|
128
|
+
const packs = registry.list(
|
|
129
|
+
options.namespace ? { namespace: options.namespace } : void 0
|
|
130
|
+
);
|
|
131
|
+
if (options.json) {
|
|
132
|
+
console.log(JSON.stringify(packs, null, 2));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (packs.length === 0) {
|
|
136
|
+
console.log(
|
|
137
|
+
chalk.dim(
|
|
138
|
+
"No packs installed. Run: stackmemory pack install <source>"
|
|
139
|
+
)
|
|
140
|
+
);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
console.log(chalk.bold(`${packs.length} pack(s) installed:
|
|
144
|
+
`));
|
|
145
|
+
for (const pack of packs) {
|
|
146
|
+
console.log(formatPack(pack));
|
|
147
|
+
console.log();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
cmd.command("search <query>").description("Search installed packs by keyword").action((query) => {
|
|
151
|
+
const registry = getSkillPackRegistry();
|
|
152
|
+
const results = registry.search(query);
|
|
153
|
+
if (results.length === 0) {
|
|
154
|
+
console.log(chalk.dim(`No packs matching "${query}"`));
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
console.log(chalk.bold(`${results.length} result(s):
|
|
158
|
+
`));
|
|
159
|
+
for (const pack of results) {
|
|
160
|
+
console.log(formatPack(pack));
|
|
161
|
+
console.log();
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
cmd.command("show <name>").description("Show details of an installed pack").action((name) => {
|
|
165
|
+
const registry = getSkillPackRegistry();
|
|
166
|
+
const pack = registry.get(name);
|
|
167
|
+
if (!pack) {
|
|
168
|
+
console.log(chalk.yellow(`Pack ${name} not found`));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const m = pack.manifest;
|
|
172
|
+
console.log(chalk.bold(m.name) + " " + chalk.dim(`v${m.version}`));
|
|
173
|
+
console.log(chalk.dim(m.description));
|
|
174
|
+
console.log();
|
|
175
|
+
if (m.author) console.log(` Author: ${m.author}`);
|
|
176
|
+
if (m.license) console.log(` License: ${m.license}`);
|
|
177
|
+
if (m.runtime) console.log(` Runtime: ${m.runtime.type}`);
|
|
178
|
+
if (pack.metadata?.installedAt)
|
|
179
|
+
console.log(` Installed: ${pack.metadata.installedAt}`);
|
|
180
|
+
if (pack.metadata?.source)
|
|
181
|
+
console.log(` Source: ${pack.metadata.source}`);
|
|
182
|
+
if (m.mcp?.tools?.length) {
|
|
183
|
+
console.log(`
|
|
184
|
+
MCP Tools:`);
|
|
185
|
+
for (const tool of m.mcp.tools) {
|
|
186
|
+
console.log(` - ${tool.name}: ${tool.description}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (m.examples?.length) {
|
|
190
|
+
console.log(`
|
|
191
|
+
Examples: ${m.examples.length}`);
|
|
192
|
+
}
|
|
193
|
+
if (m.ingestion?.sources?.length) {
|
|
194
|
+
console.log(`
|
|
195
|
+
Ingestion sources: ${m.ingestion.sources.join(", ")}`);
|
|
196
|
+
}
|
|
197
|
+
if (pack.instructions) {
|
|
198
|
+
console.log(`
|
|
199
|
+
Instructions: ${pack.instructions.length} chars`);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
cmd.command("init <name>").description(
|
|
203
|
+
"Initialize a new skill pack in the current directory (creates pack.yaml)"
|
|
204
|
+
).option("--runtime <type>", "Runtime type", "local").action((name, options) => {
|
|
205
|
+
const yamlPath = path.join(process.cwd(), "pack.yaml");
|
|
206
|
+
if (fs.existsSync(yamlPath)) {
|
|
207
|
+
console.log(chalk.yellow("pack.yaml already exists in this directory"));
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const template = `name: ${name}
|
|
211
|
+
version: 0.1.0
|
|
212
|
+
description: <one-line description>
|
|
213
|
+
author: <your-name>
|
|
214
|
+
license: MIT
|
|
215
|
+
runtime:
|
|
216
|
+
type: ${options.runtime || "local"}
|
|
217
|
+
ingestion:
|
|
218
|
+
sources: []
|
|
219
|
+
ontology:
|
|
220
|
+
entities: []
|
|
221
|
+
relations: []
|
|
222
|
+
mcp:
|
|
223
|
+
tools: []
|
|
224
|
+
examples: []
|
|
225
|
+
instructions: instructions.md
|
|
226
|
+
`;
|
|
227
|
+
fs.writeFileSync(yamlPath, template, "utf-8");
|
|
228
|
+
const instrPath = path.join(process.cwd(), "instructions.md");
|
|
229
|
+
if (!fs.existsSync(instrPath)) {
|
|
230
|
+
fs.writeFileSync(
|
|
231
|
+
instrPath,
|
|
232
|
+
`# ${name}
|
|
233
|
+
|
|
234
|
+
Instructions for this skill pack.
|
|
235
|
+
`,
|
|
236
|
+
"utf-8"
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
console.log(chalk.green(`\u2713 Created pack.yaml for ${name}`));
|
|
240
|
+
console.log(chalk.dim(" Edit pack.yaml and instructions.md, then:"));
|
|
241
|
+
console.log(chalk.dim(" stackmemory pack install ."));
|
|
242
|
+
});
|
|
243
|
+
cmd.command("fork <name> <new-name>").description("Fork an installed pack under a new namespace/name").action((name, newName) => {
|
|
244
|
+
const registry = getSkillPackRegistry();
|
|
245
|
+
const existing = registry.get(name);
|
|
246
|
+
if (!existing) {
|
|
247
|
+
console.log(chalk.yellow(`Pack ${name} not found`));
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const targetDir = path.join(process.cwd(), newName.replace("/", "-"));
|
|
251
|
+
if (fs.existsSync(targetDir)) {
|
|
252
|
+
console.log(chalk.yellow(`Directory ${targetDir} already exists`));
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
256
|
+
const forked = {
|
|
257
|
+
...existing.manifest,
|
|
258
|
+
name: newName,
|
|
259
|
+
version: "0.1.0"
|
|
260
|
+
};
|
|
261
|
+
fs.writeFileSync(
|
|
262
|
+
path.join(targetDir, "pack.yaml"),
|
|
263
|
+
yaml.dump(forked),
|
|
264
|
+
"utf-8"
|
|
265
|
+
);
|
|
266
|
+
if (existing.instructions) {
|
|
267
|
+
fs.writeFileSync(
|
|
268
|
+
path.join(targetDir, "instructions.md"),
|
|
269
|
+
existing.instructions,
|
|
270
|
+
"utf-8"
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
console.log(chalk.green(`\u2713 Forked ${name} \u2192 ${newName} in ${targetDir}`));
|
|
274
|
+
console.log(
|
|
275
|
+
chalk.dim(" Edit and install: stackmemory pack install " + targetDir)
|
|
276
|
+
);
|
|
277
|
+
});
|
|
278
|
+
cmd.command("publish [dir]").description(
|
|
279
|
+
"Validate and publish a pack to the public registry (creates a GitHub PR)"
|
|
280
|
+
).option("--dry-run", "Validate only, do not publish").action(async (dir, options) => {
|
|
281
|
+
const packDir = dir || process.cwd();
|
|
282
|
+
try {
|
|
283
|
+
const pack = await loadPackFromDir(packDir);
|
|
284
|
+
console.log(
|
|
285
|
+
chalk.green(
|
|
286
|
+
`\u2713 Valid pack: ${pack.manifest.name}@${pack.manifest.version}`
|
|
287
|
+
)
|
|
288
|
+
);
|
|
289
|
+
if (options.dryRun) {
|
|
290
|
+
console.log(chalk.dim("Dry run \u2014 skipping publish"));
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const registryOrg = process.env["STACKMEMORY_PACK_REGISTRY"] || "stackmemoryai";
|
|
294
|
+
console.log();
|
|
295
|
+
console.log(chalk.bold("To publish to the public registry:"));
|
|
296
|
+
console.log(
|
|
297
|
+
chalk.dim(` 1. Fork https://github.com/${registryOrg}/skill-packs`)
|
|
298
|
+
);
|
|
299
|
+
console.log(
|
|
300
|
+
chalk.dim(
|
|
301
|
+
` 2. Add your pack to ${pack.manifest.name.replace("/", "/")}/`
|
|
302
|
+
)
|
|
303
|
+
);
|
|
304
|
+
console.log(chalk.dim(" 3. Open a pull request"));
|
|
305
|
+
console.log();
|
|
306
|
+
console.log(
|
|
307
|
+
chalk.dim("Automated publish via `gh pr create` coming soon.")
|
|
308
|
+
);
|
|
309
|
+
} catch (err) {
|
|
310
|
+
console.error(
|
|
311
|
+
chalk.red(
|
|
312
|
+
`Validation failed: ${err instanceof Error ? err.message : err}`
|
|
313
|
+
)
|
|
314
|
+
);
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
return cmd;
|
|
319
|
+
}
|
|
320
|
+
export {
|
|
321
|
+
createPackCommand
|
|
322
|
+
};
|
|
@@ -7,12 +7,16 @@ import Database from "better-sqlite3";
|
|
|
7
7
|
import { join } from "path";
|
|
8
8
|
import { existsSync } from "fs";
|
|
9
9
|
import { z } from "zod";
|
|
10
|
+
import { CrossProjectSearch } from "../../core/cross-search/cross-project-search.js";
|
|
10
11
|
const SearchQuerySchema = z.string().min(1, "Search query is required").max(500, "Search query too long (max 500 characters)").transform((val) => {
|
|
11
12
|
return val.replace(/[%_\\]/g, "\\$&");
|
|
12
13
|
});
|
|
13
14
|
const LimitSchema = z.string().transform((val) => parseInt(val, 10)).pipe(z.number().int().min(1).max(100).default(20));
|
|
14
15
|
function createSearchCommand() {
|
|
15
|
-
const search = new Command("search").alias("find").description("Search across tasks and context").argument("<query>", "Search query").option("-t, --tasks", "Search only tasks").option("-c, --context", "Search only context").option(
|
|
16
|
+
const search = new Command("search").alias("find").description("Search across tasks and context").argument("<query>", "Search query").option("-t, --tasks", "Search only tasks").option("-c, --context", "Search only context").option(
|
|
17
|
+
"-a, --all-projects",
|
|
18
|
+
"Search across all registered project databases"
|
|
19
|
+
).option("-l, --limit <n>", "Limit results", "20").action(async (rawQuery, options) => {
|
|
16
20
|
const projectRoot = process.cwd();
|
|
17
21
|
const dbPath = join(projectRoot, ".stackmemory", "context.db");
|
|
18
22
|
if (!existsSync(dbPath)) {
|
|
@@ -34,6 +38,41 @@ function createSearchCommand() {
|
|
|
34
38
|
}
|
|
35
39
|
return;
|
|
36
40
|
}
|
|
41
|
+
if (options.allProjects) {
|
|
42
|
+
console.log(
|
|
43
|
+
`
|
|
44
|
+
\u{1F50D} Searching across all projects for "${rawQuery}"...
|
|
45
|
+
`
|
|
46
|
+
);
|
|
47
|
+
const crossSearch = new CrossProjectSearch();
|
|
48
|
+
const results = await crossSearch.search({
|
|
49
|
+
query: rawQuery,
|
|
50
|
+
limit
|
|
51
|
+
});
|
|
52
|
+
if (results.length === 0) {
|
|
53
|
+
console.log("No results found across project databases.\n");
|
|
54
|
+
console.log(
|
|
55
|
+
'Tip: Run "stackmemory search --all-projects" after "stackmemory projects scan" to discover databases.'
|
|
56
|
+
);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
console.log(`\u{1F4C1} Cross-Project Results (${results.length})
|
|
60
|
+
`);
|
|
61
|
+
for (const r of results) {
|
|
62
|
+
const date = new Date(r.createdAt).toLocaleDateString();
|
|
63
|
+
console.log(
|
|
64
|
+
` [${r.projectName}] ${r.name} (${r.type}, score: ${r.score.toFixed(3)})`
|
|
65
|
+
);
|
|
66
|
+
if (r.digestText) {
|
|
67
|
+
console.log(` ${r.digestText.slice(0, 100)}`);
|
|
68
|
+
}
|
|
69
|
+
console.log(` ${date} | ${r.projectPath}`);
|
|
70
|
+
}
|
|
71
|
+
console.log(`
|
|
72
|
+
Found ${results.length} results.
|
|
73
|
+
`);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
37
76
|
const db = new Database(dbPath);
|
|
38
77
|
const searchTasks = !options.context || options.tasks;
|
|
39
78
|
const searchContext = !options.tasks || options.context;
|
|
@@ -241,13 +241,7 @@ function createDoctorCommand() {
|
|
|
241
241
|
const toolDefs = new MCPToolDefinitions();
|
|
242
242
|
const allTools = toolDefs.getAllToolDefinitions();
|
|
243
243
|
const toolNames = allTools.map((t) => t.name);
|
|
244
|
-
const expectedTools = [
|
|
245
|
-
"sm_digest",
|
|
246
|
-
"cord_spawn",
|
|
247
|
-
"team_search",
|
|
248
|
-
"get_context",
|
|
249
|
-
"create_task"
|
|
250
|
-
];
|
|
244
|
+
const expectedTools = ["sm_digest", "get_context", "create_task"];
|
|
251
245
|
const missing = expectedTools.filter((t) => !toolNames.includes(t));
|
|
252
246
|
if (missing.length === 0) {
|
|
253
247
|
results.push({
|
|
@@ -1011,14 +1005,190 @@ Logs: ${logDir}/remote-mcp.log`));
|
|
|
1011
1005
|
});
|
|
1012
1006
|
return cmd;
|
|
1013
1007
|
}
|
|
1008
|
+
function createSetupCommandsCommand() {
|
|
1009
|
+
const cmd = new Command("setup-commands");
|
|
1010
|
+
cmd.description("Install StackMemory command packs for Claude Code").argument("[pack]", "Pack name to install (default: core)", "core").option("--list", "List available packs and their commands").option("--force", "Overwrite existing command files").option("--uninstall", "Remove installed commands").option("--dry-run", "Show what would be installed without making changes").action(async (pack, options) => {
|
|
1011
|
+
const commandsDir = join(CLAUDE_DIR, "commands");
|
|
1012
|
+
const possiblePaths = [
|
|
1013
|
+
join(process.cwd(), "packs"),
|
|
1014
|
+
join(__dirname, "..", "..", "..", "packs")
|
|
1015
|
+
];
|
|
1016
|
+
try {
|
|
1017
|
+
const globalRoot = execSync("npm root -g", {
|
|
1018
|
+
encoding: "utf-8"
|
|
1019
|
+
}).trim();
|
|
1020
|
+
possiblePaths.push(
|
|
1021
|
+
join(globalRoot, "@stackmemoryai", "stackmemory", "packs")
|
|
1022
|
+
);
|
|
1023
|
+
} catch {
|
|
1024
|
+
}
|
|
1025
|
+
let packsDir;
|
|
1026
|
+
for (const p of possiblePaths) {
|
|
1027
|
+
if (existsSync(p)) {
|
|
1028
|
+
packsDir = p;
|
|
1029
|
+
break;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
if (!packsDir) {
|
|
1033
|
+
console.log(chalk.red("Could not find packs directory"));
|
|
1034
|
+
process.exit(1);
|
|
1035
|
+
}
|
|
1036
|
+
if (options.list) {
|
|
1037
|
+
console.log(chalk.cyan("\nAvailable command packs:\n"));
|
|
1038
|
+
const packs = readdirSync(packsDir).filter(
|
|
1039
|
+
(d) => existsSync(join(packsDir, d, "manifest.json"))
|
|
1040
|
+
);
|
|
1041
|
+
for (const p of packs) {
|
|
1042
|
+
const manifest2 = JSON.parse(
|
|
1043
|
+
readFileSync(join(packsDir, p, "manifest.json"), "utf8")
|
|
1044
|
+
);
|
|
1045
|
+
console.log(
|
|
1046
|
+
chalk.white(` ${manifest2.name}`) + chalk.gray(` v${manifest2.version} \u2014 ${manifest2.description}`)
|
|
1047
|
+
);
|
|
1048
|
+
if (manifest2.commands?.public) {
|
|
1049
|
+
for (const cmd2 of manifest2.commands.public) {
|
|
1050
|
+
console.log(
|
|
1051
|
+
chalk.green(` /${cmd2.name}`) + chalk.gray(` \u2014 ${cmd2.description}`)
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
if (manifest2.commands?.internal) {
|
|
1056
|
+
console.log(
|
|
1057
|
+
chalk.gray(
|
|
1058
|
+
` + ${manifest2.commands.internal.length} internal deps`
|
|
1059
|
+
)
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
console.log("");
|
|
1063
|
+
}
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
const packDir = join(packsDir, pack);
|
|
1067
|
+
const manifestPath = join(packDir, "manifest.json");
|
|
1068
|
+
if (!existsSync(manifestPath)) {
|
|
1069
|
+
console.log(chalk.red(`Pack "${pack}" not found`));
|
|
1070
|
+
console.log(chalk.gray("Run: stackmemory setup-commands --list"));
|
|
1071
|
+
process.exit(1);
|
|
1072
|
+
}
|
|
1073
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
1074
|
+
const allCommands = [
|
|
1075
|
+
...manifest.commands?.public || [],
|
|
1076
|
+
...manifest.commands?.internal || []
|
|
1077
|
+
];
|
|
1078
|
+
if (options.uninstall) {
|
|
1079
|
+
console.log(
|
|
1080
|
+
chalk.cyan(
|
|
1081
|
+
`
|
|
1082
|
+
Uninstalling pack: ${manifest.name} v${manifest.version}
|
|
1083
|
+
`
|
|
1084
|
+
)
|
|
1085
|
+
);
|
|
1086
|
+
let removed = 0;
|
|
1087
|
+
for (const cmd2 of allCommands) {
|
|
1088
|
+
const targetPath = join(commandsDir, `${cmd2.name}.md`);
|
|
1089
|
+
if (existsSync(targetPath)) {
|
|
1090
|
+
if (options.dryRun) {
|
|
1091
|
+
console.log(chalk.gray(` Would remove: /${cmd2.name}`));
|
|
1092
|
+
} else {
|
|
1093
|
+
rmSync(targetPath, { force: true });
|
|
1094
|
+
console.log(chalk.green(` [OK]`) + ` Removed /${cmd2.name}`);
|
|
1095
|
+
removed++;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
console.log(chalk.green(`
|
|
1100
|
+
Removed ${removed} command(s)`));
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
console.log(
|
|
1104
|
+
chalk.cyan(`
|
|
1105
|
+
Installing pack: ${manifest.name} v${manifest.version}
|
|
1106
|
+
`)
|
|
1107
|
+
);
|
|
1108
|
+
console.log(chalk.gray(`${manifest.description}
|
|
1109
|
+
`));
|
|
1110
|
+
if (!existsSync(commandsDir)) {
|
|
1111
|
+
if (options.dryRun) {
|
|
1112
|
+
console.log(chalk.gray(`Would create: ${commandsDir}`));
|
|
1113
|
+
} else {
|
|
1114
|
+
mkdirSync(commandsDir, { recursive: true });
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
let installed = 0;
|
|
1118
|
+
let skipped = 0;
|
|
1119
|
+
for (const cmd2 of allCommands) {
|
|
1120
|
+
const sourcePath = join(packDir, cmd2.file);
|
|
1121
|
+
const targetPath = join(commandsDir, `${cmd2.name}.md`);
|
|
1122
|
+
const isPublic = manifest.commands?.public?.some(
|
|
1123
|
+
(c) => c.name === cmd2.name
|
|
1124
|
+
);
|
|
1125
|
+
if (!existsSync(sourcePath)) {
|
|
1126
|
+
console.log(
|
|
1127
|
+
chalk.red(` [MISS] /${cmd2.name}`) + chalk.gray(` \u2014 source not found: ${cmd2.file}`)
|
|
1128
|
+
);
|
|
1129
|
+
continue;
|
|
1130
|
+
}
|
|
1131
|
+
if (existsSync(targetPath) && !options.force) {
|
|
1132
|
+
console.log(
|
|
1133
|
+
chalk.gray(` [SKIP] /${cmd2.name} \u2014 exists (use --force)`)
|
|
1134
|
+
);
|
|
1135
|
+
skipped++;
|
|
1136
|
+
continue;
|
|
1137
|
+
}
|
|
1138
|
+
if (options.dryRun) {
|
|
1139
|
+
const tag = isPublic ? chalk.green("PUBLIC") : chalk.gray("INTERNAL");
|
|
1140
|
+
console.log(
|
|
1141
|
+
chalk.gray(` Would install: `) + chalk.white(`/${cmd2.name}`) + ` [${tag}]`
|
|
1142
|
+
);
|
|
1143
|
+
installed++;
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
if (existsSync(targetPath)) {
|
|
1147
|
+
rmSync(targetPath, { force: true });
|
|
1148
|
+
}
|
|
1149
|
+
try {
|
|
1150
|
+
execSync(`ln -s "${sourcePath}" "${targetPath}"`, {
|
|
1151
|
+
encoding: "utf-8"
|
|
1152
|
+
});
|
|
1153
|
+
const tag = isPublic ? chalk.green("PUBLIC") : chalk.gray("INTERNAL");
|
|
1154
|
+
console.log(
|
|
1155
|
+
chalk.green(` [OK]`) + ` /${cmd2.name} [${tag}]` + chalk.gray(` \u2014 ${cmd2.description}`)
|
|
1156
|
+
);
|
|
1157
|
+
installed++;
|
|
1158
|
+
} catch (err) {
|
|
1159
|
+
console.log(
|
|
1160
|
+
chalk.red(` [ERROR] /${cmd2.name}`) + chalk.gray(` \u2014 ${err.message}`)
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
console.log("");
|
|
1165
|
+
if (installed > 0) {
|
|
1166
|
+
console.log(chalk.green(`Installed ${installed} command(s)`));
|
|
1167
|
+
}
|
|
1168
|
+
if (skipped > 0) {
|
|
1169
|
+
console.log(chalk.gray(`Skipped ${skipped} (already exist)`));
|
|
1170
|
+
}
|
|
1171
|
+
if (!options.dryRun && installed > 0) {
|
|
1172
|
+
console.log(chalk.cyan("\nAvailable commands in Claude Code:"));
|
|
1173
|
+
for (const cmd2 of manifest.commands?.public || []) {
|
|
1174
|
+
console.log(
|
|
1175
|
+
chalk.white(` /${cmd2.name}`) + chalk.gray(` ${cmd2.description}`)
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
});
|
|
1180
|
+
return cmd;
|
|
1181
|
+
}
|
|
1014
1182
|
function registerSetupCommands(program) {
|
|
1015
1183
|
program.addCommand(createSetupMCPCommand());
|
|
1016
1184
|
program.addCommand(createDoctorCommand());
|
|
1017
1185
|
program.addCommand(createSetupPluginsCommand());
|
|
1018
1186
|
program.addCommand(createSetupRemoteCommand());
|
|
1187
|
+
program.addCommand(createSetupCommandsCommand());
|
|
1019
1188
|
}
|
|
1020
1189
|
export {
|
|
1021
1190
|
createDoctorCommand,
|
|
1191
|
+
createSetupCommandsCommand,
|
|
1022
1192
|
createSetupMCPCommand,
|
|
1023
1193
|
createSetupPluginsCommand,
|
|
1024
1194
|
createSetupRemoteCommand,
|
|
@@ -46,6 +46,9 @@ function getVersion() {
|
|
|
46
46
|
_version = "0.0.0";
|
|
47
47
|
return _version;
|
|
48
48
|
}
|
|
49
|
+
function collectRepeatedOption(value, previous = []) {
|
|
50
|
+
return [...previous, value];
|
|
51
|
+
}
|
|
49
52
|
function _getEnv(key, defaultValue) {
|
|
50
53
|
const value = process.env[key];
|
|
51
54
|
if (value === void 0) {
|
|
@@ -309,7 +312,12 @@ function createSkillsCommand() {
|
|
|
309
312
|
"--execute",
|
|
310
313
|
"Execute implementer (codex-sm) instead of dry-run",
|
|
311
314
|
false
|
|
312
|
-
).option("--audit-dir <path>", "Persist spike results to directory").option(
|
|
315
|
+
).option("--audit-dir <path>", "Persist spike results to directory").option(
|
|
316
|
+
"--verify <cmd>",
|
|
317
|
+
"Verification command to run after each implementation attempt; repeatable",
|
|
318
|
+
collectRepeatedOption,
|
|
319
|
+
[]
|
|
320
|
+
).option("--record-frame", "Record as real frame with anchors", false).option(
|
|
313
321
|
"--record",
|
|
314
322
|
"Record plan & critique into StackMemory context",
|
|
315
323
|
false
|
|
@@ -329,6 +337,7 @@ function createSkillsCommand() {
|
|
|
329
337
|
maxIters: parseInt(options.maxIters),
|
|
330
338
|
dryRun: !options.execute,
|
|
331
339
|
auditDir: options.auditDir,
|
|
340
|
+
verificationCommands: options.verify,
|
|
332
341
|
recordFrame: Boolean(options.recordFrame),
|
|
333
342
|
record: Boolean(options.record)
|
|
334
343
|
}
|