@tonycasey/lisa 0.5.13
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 +42 -0
- package/dist/cli.js +390 -0
- package/dist/lib/interfaces/IDockerClient.js +2 -0
- package/dist/lib/interfaces/IMcpClient.js +2 -0
- package/dist/lib/interfaces/IServices.js +2 -0
- package/dist/lib/interfaces/ITemplateCopier.js +2 -0
- package/dist/lib/mcp.js +35 -0
- package/dist/lib/services.js +57 -0
- package/dist/package.json +36 -0
- package/dist/templates/agents/.sample.env +12 -0
- package/dist/templates/agents/docs/STORAGE_SETUP.md +161 -0
- package/dist/templates/agents/skills/common/group-id.js +193 -0
- package/dist/templates/agents/skills/init-review/SKILL.md +119 -0
- package/dist/templates/agents/skills/init-review/scripts/ai-enrich.js +258 -0
- package/dist/templates/agents/skills/init-review/scripts/init-review.js +769 -0
- package/dist/templates/agents/skills/lisa/SKILL.md +92 -0
- package/dist/templates/agents/skills/lisa/cache/.gitkeep +0 -0
- package/dist/templates/agents/skills/lisa/scripts/storage.js +374 -0
- package/dist/templates/agents/skills/memory/SKILL.md +31 -0
- package/dist/templates/agents/skills/memory/scripts/memory.js +533 -0
- package/dist/templates/agents/skills/prompt/SKILL.md +19 -0
- package/dist/templates/agents/skills/prompt/scripts/prompt.js +184 -0
- package/dist/templates/agents/skills/tasks/SKILL.md +31 -0
- package/dist/templates/agents/skills/tasks/scripts/tasks.js +489 -0
- package/dist/templates/claude/config.js +40 -0
- package/dist/templates/claude/hooks/README.md +158 -0
- package/dist/templates/claude/hooks/common/complexity-rater.js +290 -0
- package/dist/templates/claude/hooks/common/context.js +263 -0
- package/dist/templates/claude/hooks/common/group-id.js +188 -0
- package/dist/templates/claude/hooks/common/mcp-client.js +131 -0
- package/dist/templates/claude/hooks/common/transcript-parser.js +256 -0
- package/dist/templates/claude/hooks/common/zep-client.js +175 -0
- package/dist/templates/claude/hooks/session-start.js +401 -0
- package/dist/templates/claude/hooks/session-stop-worker.js +341 -0
- package/dist/templates/claude/hooks/session-stop.js +122 -0
- package/dist/templates/claude/hooks/user-prompt-submit.js +256 -0
- package/dist/templates/claude/settings.json +46 -0
- package/dist/templates/docker/.env.lisa.example +17 -0
- package/dist/templates/docker/docker-compose.graphiti.yml +45 -0
- package/dist/templates/rules/shared/clean-architecture.md +333 -0
- package/dist/templates/rules/shared/code-quality-rules.md +469 -0
- package/dist/templates/rules/shared/git-rules.md +64 -0
- package/dist/templates/rules/shared/testing-principles.md +469 -0
- package/dist/templates/rules/typescript/coding-standards.md +751 -0
- package/dist/templates/rules/typescript/testing.md +629 -0
- package/dist/templates/rules/typescript/typescript-config-guide.md +465 -0
- package/package.json +64 -0
- package/scripts/postinstall.js +710 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Prompt Capture Skill
|
|
5
|
+
*
|
|
6
|
+
* Stores user prompts as episodes in Graphiti MCP.
|
|
7
|
+
* Graphiti's LLM automatically classifies entities as:
|
|
8
|
+
* - KeyDecision: Major decisions about approach/strategy
|
|
9
|
+
* - DirectionChange: Changes in strategy or pivots
|
|
10
|
+
* - ArchitecturalChoice: Technical/architectural decisions
|
|
11
|
+
* - Preference: User preferences and style choices
|
|
12
|
+
* - Requirement: Specific needs that must be fulfilled
|
|
13
|
+
*
|
|
14
|
+
* The classification happens server-side based on entity_types in config.yaml
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
const crypto = require('crypto');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const os = require('os');
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Group ID Utilities (inline to avoid import complexity in deployed skills)
|
|
23
|
+
// ============================================================================
|
|
24
|
+
const MAX_GROUP_ID_LENGTH = 128;
|
|
25
|
+
function normalizePathToGroupId(absolutePath) {
|
|
26
|
+
let normalized = absolutePath
|
|
27
|
+
.toLowerCase()
|
|
28
|
+
.replace(/^\//, '')
|
|
29
|
+
.replace(/\//g, '-')
|
|
30
|
+
.replace(/\./g, '_'); // Graphiti requires alphanumeric, dashes, underscores only
|
|
31
|
+
if (normalized.length > MAX_GROUP_ID_LENGTH) {
|
|
32
|
+
normalized = normalized.slice(-MAX_GROUP_ID_LENGTH);
|
|
33
|
+
}
|
|
34
|
+
return normalized;
|
|
35
|
+
}
|
|
36
|
+
function getCurrentGroupId(cwd = process.cwd()) {
|
|
37
|
+
return normalizePathToGroupId(cwd);
|
|
38
|
+
}
|
|
39
|
+
const args = process.argv.slice(2);
|
|
40
|
+
function popFlag(name, fallback) {
|
|
41
|
+
const idx = args.indexOf(name);
|
|
42
|
+
if (idx === -1)
|
|
43
|
+
return fallback;
|
|
44
|
+
const val = args[idx + 1];
|
|
45
|
+
args.splice(idx, 2);
|
|
46
|
+
if (typeof fallback === 'boolean')
|
|
47
|
+
return true;
|
|
48
|
+
return val ?? fallback;
|
|
49
|
+
}
|
|
50
|
+
function hasFlag(name) {
|
|
51
|
+
const idx = args.indexOf(name);
|
|
52
|
+
if (idx === -1)
|
|
53
|
+
return false;
|
|
54
|
+
args.splice(idx, 1);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
// Read from .agents/skills/.env (2 levels up from prompt/scripts/)
|
|
58
|
+
const envPath = path.join(__dirname, '..', '..', '.env');
|
|
59
|
+
const env = {};
|
|
60
|
+
try {
|
|
61
|
+
const raw = fs.readFileSync(envPath, 'utf8');
|
|
62
|
+
raw.split(/\r?\n/).forEach((line) => {
|
|
63
|
+
if (!line || line.startsWith('#'))
|
|
64
|
+
return;
|
|
65
|
+
const idx = line.indexOf('=');
|
|
66
|
+
if (idx === -1)
|
|
67
|
+
return;
|
|
68
|
+
env[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (_) {
|
|
72
|
+
/* optional */
|
|
73
|
+
}
|
|
74
|
+
const endpoint = popFlag('--endpoint', env.GRAPHITI_ENDPOINT || process.env.GRAPHITI_ENDPOINT || 'http://localhost:8010/mcp/');
|
|
75
|
+
// Group ID: explicit --group > env > folder-based (current directory)
|
|
76
|
+
const explicitGroup = popFlag('--group', false);
|
|
77
|
+
const groupId = explicitGroup || env.GRAPHITI_GROUP_ID || process.env.GRAPHITI_GROUP_ID || getCurrentGroupId();
|
|
78
|
+
const force = hasFlag('--force') || popFlag('--force', false);
|
|
79
|
+
function parseArgs() {
|
|
80
|
+
const out = { role: 'user', source: 'user-prompt' };
|
|
81
|
+
for (let i = 0; i < args.length; i++) {
|
|
82
|
+
if (args[i] === '--text' || args[i] === '-t')
|
|
83
|
+
out.text = args[++i];
|
|
84
|
+
if (args[i] === '--role' || args[i] === '-r')
|
|
85
|
+
out.role = args[++i];
|
|
86
|
+
if (args[i] === '--source' || args[i] === '-s')
|
|
87
|
+
out.source = args[++i];
|
|
88
|
+
// Legacy support for --kind (now ignored - Graphiti classifies automatically)
|
|
89
|
+
if (args[i] === '--kind' || args[i] === '-k')
|
|
90
|
+
i++; // skip value
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
function fingerprint(text) {
|
|
95
|
+
return crypto.createHash('sha1').update(text.trim()).digest('hex').slice(0, 16);
|
|
96
|
+
}
|
|
97
|
+
async function initialize() {
|
|
98
|
+
const body = {
|
|
99
|
+
jsonrpc: '2.0',
|
|
100
|
+
id: 'init',
|
|
101
|
+
method: 'initialize',
|
|
102
|
+
params: {
|
|
103
|
+
protocolVersion: '2024-11-05',
|
|
104
|
+
capabilities: { experimental: {}, prompts: { listChanged: false }, resources: { subscribe: false, listChanged: false }, tools: { listChanged: false } },
|
|
105
|
+
clientInfo: { name: 'prompt-skill', version: '0.1.0' },
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
const resp = await fetch(endpoint, {
|
|
109
|
+
method: 'POST',
|
|
110
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/event-stream' },
|
|
111
|
+
body: JSON.stringify(body),
|
|
112
|
+
});
|
|
113
|
+
if (!resp.ok)
|
|
114
|
+
throw new Error(`initialize failed: ${resp.status}`);
|
|
115
|
+
const sid = resp.headers.get('mcp-session-id');
|
|
116
|
+
if (!sid)
|
|
117
|
+
throw new Error('missing mcp-session-id');
|
|
118
|
+
return sid;
|
|
119
|
+
}
|
|
120
|
+
async function rpcCall(method, params, sessionId) {
|
|
121
|
+
const payload = method === 'initialize' || method === 'ping' || method.startsWith('tools/')
|
|
122
|
+
? { jsonrpc: '2.0', id: '1', method, params }
|
|
123
|
+
: { jsonrpc: '2.0', id: '1', method: 'tools/call', params: { name: method, arguments: params } };
|
|
124
|
+
const resp = await fetch(endpoint, {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
headers: { 'Content-Type': 'application/json', 'MCP-SESSION-ID': sessionId, Accept: 'application/json, text/event-stream' },
|
|
127
|
+
body: JSON.stringify(payload),
|
|
128
|
+
});
|
|
129
|
+
let text = await resp.text();
|
|
130
|
+
if (text.startsWith('event:')) {
|
|
131
|
+
const dataLine = text.split('\n').find((l) => l.startsWith('data:'));
|
|
132
|
+
if (dataLine)
|
|
133
|
+
text = dataLine.slice(5).trim();
|
|
134
|
+
}
|
|
135
|
+
const data = JSON.parse(text);
|
|
136
|
+
if (!resp.ok || data.error)
|
|
137
|
+
throw new Error(data?.error?.message || `HTTP ${resp.status}`);
|
|
138
|
+
return data.result?.structuredContent?.result || data.result || data;
|
|
139
|
+
}
|
|
140
|
+
async function addPrompt() {
|
|
141
|
+
const { text, role = 'user', source = 'user-prompt' } = parseArgs();
|
|
142
|
+
if (!text)
|
|
143
|
+
throw new Error('prompt requires --text');
|
|
144
|
+
const fp = fingerprint(text);
|
|
145
|
+
const fpTag = `fingerprint:${fp}`;
|
|
146
|
+
const sid = await initialize();
|
|
147
|
+
if (!force) {
|
|
148
|
+
try {
|
|
149
|
+
const searchParams = { query: fp, tags: [fpTag], max_nodes: 1, group_ids: [groupId] };
|
|
150
|
+
const existing = await rpcCall('search_nodes', searchParams, sid);
|
|
151
|
+
const nodes = existing?.result?.nodes || existing?.nodes || [];
|
|
152
|
+
if (nodes.length) {
|
|
153
|
+
console.log('Duplicate prompt; skipping (use --force to override).');
|
|
154
|
+
return { status: 'skipped', reason: 'duplicate' };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (_) {
|
|
158
|
+
// ignore dedupe errors
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Let Graphiti's LLM classify the content automatically based on entity_types config
|
|
162
|
+
// We just provide the raw text - classification happens server-side
|
|
163
|
+
const params = {
|
|
164
|
+
name: text.substring(0, 100),
|
|
165
|
+
episode_body: text, // Pass raw text for better LLM classification
|
|
166
|
+
source: source, // Track where this came from
|
|
167
|
+
group_id: groupId,
|
|
168
|
+
tags: [fpTag, `role:${role}`, `source:${source}`],
|
|
169
|
+
};
|
|
170
|
+
await rpcCall('add_memory', params, sid);
|
|
171
|
+
return { status: 'ok', action: 'add', group: groupId, role, source };
|
|
172
|
+
}
|
|
173
|
+
async function main() {
|
|
174
|
+
try {
|
|
175
|
+
const result = await addPrompt();
|
|
176
|
+
console.log(JSON.stringify(result, null, 2));
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
180
|
+
console.error(message);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
main();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tasks
|
|
3
|
+
description: "Create, load, or summarize tasks via Graphiti MCP; triggers on 'tasks', 'list tasks', 'add task', usable by any model (Claude, Gemini)."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
Model-neutral helper to add/list tasks in Graphiti MCP so any agent can keep a shared task board.
|
|
8
|
+
|
|
9
|
+
## Triggers
|
|
10
|
+
Use when the user says: "add a task", "list tasks", "load tasks", "task status".
|
|
11
|
+
|
|
12
|
+
## How to use
|
|
13
|
+
1) List: `scripts/tasks.js list --cache [--group <id>] [--limit 20]`
|
|
14
|
+
2) Add: `scripts/tasks.js add "<task text>" [--status todo|doing|done] [--tag foo] [--group <id>] --cache`
|
|
15
|
+
3) Defaults: reads ${GRAPHITI_ENDPOINT} / ${GRAPHITI_GROUP_ID} from `.agents/.env` (written by init); see root `AGENTS.md` for canonical defaults.
|
|
16
|
+
4) Cache fallback: writes/reads `cache/tasks.log` when `--cache` is passed, returning last cached result on MCP failure.
|
|
17
|
+
5) Keep prompts model-neutral; models only orchestrate script calls and summarize JSON output.
|
|
18
|
+
|
|
19
|
+
## I/O contract (examples)
|
|
20
|
+
- List: `{ status: "ok", action: "list", tasks: [...] }`
|
|
21
|
+
- Add: `{ status: "ok", action: "add", task: { text, status, group } }`
|
|
22
|
+
- Fallback: `{ status: "fallback", error, fallback: <cached object> }`
|
|
23
|
+
|
|
24
|
+
## Cross-model checklist
|
|
25
|
+
- Claude: concise instructions; avoid role tokens; keep outputs small.
|
|
26
|
+
- Gemini: explicit commands and minimal formatting.
|
|
27
|
+
|
|
28
|
+
## Notes
|
|
29
|
+
- Node.js script expects fetch (Node ≥18). Use `node --experimental-fetch` on older runtimes.
|
|
30
|
+
- Tasks are stored via Graphiti MCP `add_task`/`list_tasks` (tool names referenced in script). Adjust if server differs.
|
|
31
|
+
- Folder `.agents/skills/tasks/` keeps this decoupled from model-specific bindings.
|