@stackmemoryai/stackmemory 1.2.8 → 1.3.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 +2 -2
- package/dist/src/cli/commands/daemon.js +306 -2
- package/dist/src/cli/commands/desires.js +117 -0
- package/dist/src/cli/commands/setup.js +369 -39
- package/dist/src/cli/commands/symphony.js +267 -0
- package/dist/src/cli/commands/team.js +168 -0
- package/dist/src/cli/index.js +6 -0
- package/dist/src/core/context/frame-handoff-manager.js +7 -4
- package/dist/src/core/database/database-adapter.js +7 -0
- package/dist/src/core/database/migration-manager.js +6 -19
- package/dist/src/core/database/sqlite-adapter.js +18 -0
- package/dist/src/integrations/mcp/server.js +157 -12
- package/dist/src/integrations/mcp/tool-definitions.js +41 -1
- package/dist/src/utils/hook-installer.js +35 -0
- package/package.json +2 -2
- package/scripts/install-claude-hooks-auto.js +35 -0
- package/templates/claude-hooks/daemon-auto-start.js +125 -0
- package/templates/claude-hooks/desire-path-trace.js +118 -0
- package/templates/claude-hooks/team-subagent-stop.js +77 -0
- package/templates/claude-hooks/team-task-complete.js +83 -0
- package/templates/claude-hooks/team-teammate-idle.js +96 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Team Subagent Stop Hook (SubagentStop)
|
|
5
|
+
*
|
|
6
|
+
* Fires when a subagent finishes. Captures the last assistant message
|
|
7
|
+
* as shared team context so other agents can see findings without
|
|
8
|
+
* manual team_context_share calls.
|
|
9
|
+
*
|
|
10
|
+
* Fire-and-forget: exits 0 always, never blocks the agent.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { spawn } = require('child_process');
|
|
16
|
+
|
|
17
|
+
const MAX_CONTENT = 500;
|
|
18
|
+
|
|
19
|
+
function hasStackmemory(cwd) {
|
|
20
|
+
let dir = cwd || process.cwd();
|
|
21
|
+
for (let i = 0; i < 20; i++) {
|
|
22
|
+
if (fs.existsSync(path.join(dir, '.stackmemory', 'context.db'))) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
const parent = path.dirname(dir);
|
|
26
|
+
if (parent === dir) break;
|
|
27
|
+
dir = parent;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function readInput() {
|
|
33
|
+
let input = '';
|
|
34
|
+
for await (const chunk of process.stdin) {
|
|
35
|
+
input += chunk;
|
|
36
|
+
}
|
|
37
|
+
return JSON.parse(input);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function main() {
|
|
41
|
+
try {
|
|
42
|
+
const input = await readInput();
|
|
43
|
+
const { agent_id, last_assistant_message, cwd } = input;
|
|
44
|
+
|
|
45
|
+
if (!hasStackmemory(cwd || process.cwd())) return;
|
|
46
|
+
|
|
47
|
+
const message = (last_assistant_message || '').slice(0, MAX_CONTENT).trim();
|
|
48
|
+
if (!message) return;
|
|
49
|
+
|
|
50
|
+
const args = [
|
|
51
|
+
'team',
|
|
52
|
+
'share',
|
|
53
|
+
'-c',
|
|
54
|
+
message,
|
|
55
|
+
'-t',
|
|
56
|
+
'FACT',
|
|
57
|
+
'-p',
|
|
58
|
+
'7',
|
|
59
|
+
'--source',
|
|
60
|
+
'subagent',
|
|
61
|
+
];
|
|
62
|
+
if (agent_id) {
|
|
63
|
+
args.push('--agent-id', String(agent_id));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const child = spawn('stackmemory', args, {
|
|
67
|
+
stdio: 'ignore',
|
|
68
|
+
detached: true,
|
|
69
|
+
cwd: cwd || process.cwd(),
|
|
70
|
+
});
|
|
71
|
+
child.unref();
|
|
72
|
+
} catch {
|
|
73
|
+
// Silent fail — never block the agent
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
main();
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Team Task Complete Hook (TaskCompleted)
|
|
5
|
+
*
|
|
6
|
+
* Fires when a task is marked complete. Shares the completion summary
|
|
7
|
+
* as a DECISION anchor so other agents know what was accomplished.
|
|
8
|
+
*
|
|
9
|
+
* Fire-and-forget: exits 0 always, never blocks the agent.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const { spawn } = require('child_process');
|
|
15
|
+
|
|
16
|
+
const MAX_CONTENT = 800;
|
|
17
|
+
|
|
18
|
+
function hasStackmemory(cwd) {
|
|
19
|
+
let dir = cwd || process.cwd();
|
|
20
|
+
for (let i = 0; i < 20; i++) {
|
|
21
|
+
if (fs.existsSync(path.join(dir, '.stackmemory', 'context.db'))) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
const parent = path.dirname(dir);
|
|
25
|
+
if (parent === dir) break;
|
|
26
|
+
dir = parent;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function readInput() {
|
|
32
|
+
let input = '';
|
|
33
|
+
for await (const chunk of process.stdin) {
|
|
34
|
+
input += chunk;
|
|
35
|
+
}
|
|
36
|
+
return JSON.parse(input);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function main() {
|
|
40
|
+
try {
|
|
41
|
+
const input = await readInput();
|
|
42
|
+
const { task_id, task_subject, task_description, teammate_name, cwd } =
|
|
43
|
+
input;
|
|
44
|
+
|
|
45
|
+
if (!hasStackmemory(cwd || process.cwd())) return;
|
|
46
|
+
|
|
47
|
+
const who = teammate_name || 'agent';
|
|
48
|
+
const subject = task_subject || 'unknown task';
|
|
49
|
+
const desc = task_description || '';
|
|
50
|
+
const content = `Task completed by ${who}: ${subject}. ${desc}`
|
|
51
|
+
.slice(0, MAX_CONTENT)
|
|
52
|
+
.trim();
|
|
53
|
+
|
|
54
|
+
if (!content) return;
|
|
55
|
+
|
|
56
|
+
const args = [
|
|
57
|
+
'team',
|
|
58
|
+
'share',
|
|
59
|
+
'-c',
|
|
60
|
+
content,
|
|
61
|
+
'-t',
|
|
62
|
+
'DECISION',
|
|
63
|
+
'-p',
|
|
64
|
+
'8',
|
|
65
|
+
'--source',
|
|
66
|
+
'task-complete',
|
|
67
|
+
];
|
|
68
|
+
if (task_id) {
|
|
69
|
+
args.push('--task-id', String(task_id));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const child = spawn('stackmemory', args, {
|
|
73
|
+
stdio: 'ignore',
|
|
74
|
+
detached: true,
|
|
75
|
+
cwd: cwd || process.cwd(),
|
|
76
|
+
});
|
|
77
|
+
child.unref();
|
|
78
|
+
} catch {
|
|
79
|
+
// Silent fail — never block the agent
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
main();
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Team Teammate Idle Hook (TeammateIdle)
|
|
5
|
+
*
|
|
6
|
+
* Fires when a teammate is about to go idle. Logs the idle event
|
|
7
|
+
* for audit and shares a low-priority FACT anchor.
|
|
8
|
+
*
|
|
9
|
+
* Fire-and-forget: exits 0 always, never blocks the agent.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const { spawn } = require('child_process');
|
|
15
|
+
|
|
16
|
+
const HOME = process.env.HOME || '/tmp';
|
|
17
|
+
|
|
18
|
+
function hasStackmemory(cwd) {
|
|
19
|
+
let dir = cwd || process.cwd();
|
|
20
|
+
for (let i = 0; i < 20; i++) {
|
|
21
|
+
if (fs.existsSync(path.join(dir, '.stackmemory', 'context.db'))) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
const parent = path.dirname(dir);
|
|
25
|
+
if (parent === dir) break;
|
|
26
|
+
dir = parent;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function ensureDir(dir) {
|
|
32
|
+
if (!fs.existsSync(dir)) {
|
|
33
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function readInput() {
|
|
38
|
+
let input = '';
|
|
39
|
+
for await (const chunk of process.stdin) {
|
|
40
|
+
input += chunk;
|
|
41
|
+
}
|
|
42
|
+
return JSON.parse(input);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function main() {
|
|
46
|
+
try {
|
|
47
|
+
const input = await readInput();
|
|
48
|
+
const { teammate_name, team_name, cwd } = input;
|
|
49
|
+
|
|
50
|
+
if (!hasStackmemory(cwd || process.cwd())) return;
|
|
51
|
+
|
|
52
|
+
const name = teammate_name || 'unknown';
|
|
53
|
+
|
|
54
|
+
// Log idle event for audit
|
|
55
|
+
const auditDir = path.join(HOME, '.stackmemory', 'team-events');
|
|
56
|
+
ensureDir(auditDir);
|
|
57
|
+
const entry = {
|
|
58
|
+
event: 'teammate_idle',
|
|
59
|
+
teammate: name,
|
|
60
|
+
team: team_name || 'default',
|
|
61
|
+
ts: new Date().toISOString(),
|
|
62
|
+
cwd: cwd || process.cwd(),
|
|
63
|
+
};
|
|
64
|
+
const auditFile = path.join(
|
|
65
|
+
auditDir,
|
|
66
|
+
`idle-${new Date().toISOString().slice(0, 10)}.jsonl`
|
|
67
|
+
);
|
|
68
|
+
fs.appendFileSync(auditFile, JSON.stringify(entry) + '\n');
|
|
69
|
+
|
|
70
|
+
// Share low-priority anchor
|
|
71
|
+
const content = `Teammate ${name} idle`;
|
|
72
|
+
const args = [
|
|
73
|
+
'team',
|
|
74
|
+
'share',
|
|
75
|
+
'-c',
|
|
76
|
+
content,
|
|
77
|
+
'-t',
|
|
78
|
+
'FACT',
|
|
79
|
+
'-p',
|
|
80
|
+
'4',
|
|
81
|
+
'--source',
|
|
82
|
+
'teammate-idle',
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
const child = spawn('stackmemory', args, {
|
|
86
|
+
stdio: 'ignore',
|
|
87
|
+
detached: true,
|
|
88
|
+
cwd: cwd || process.cwd(),
|
|
89
|
+
});
|
|
90
|
+
child.unref();
|
|
91
|
+
} catch {
|
|
92
|
+
// Silent fail — never block the agent
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
main();
|