iranti 0.1.1 → 0.1.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/README.md +2 -3
- package/dist/scripts/claude-code-memory-hook.js +162 -45
- package/dist/scripts/iranti-cli.js +61 -0
- package/dist/scripts/iranti-mcp.js +8 -26
- package/dist/scripts/seed.js +10 -10
- package/dist/src/api/server.js +1 -1
- package/dist/src/lib/runtimeEnv.d.ts +15 -0
- package/dist/src/lib/runtimeEnv.d.ts.map +1 -0
- package/dist/src/lib/runtimeEnv.js +91 -0
- package/dist/src/lib/runtimeEnv.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -178,11 +178,10 @@ Release guide: [`docs/guides/releasing.md`](docs/guides/releasing.md)
|
|
|
178
178
|
Iranti ships a local stdio MCP server for Claude Code and other MCP clients:
|
|
179
179
|
|
|
180
180
|
```bash
|
|
181
|
-
|
|
182
|
-
node dist/scripts/iranti-mcp.js
|
|
181
|
+
iranti mcp
|
|
183
182
|
```
|
|
184
183
|
|
|
185
|
-
Use it with a project-local `.mcp.json`, and optionally add
|
|
184
|
+
Use it with a project-local `.mcp.json`, and optionally add `iranti claude-hook` for `SessionStart` and `UserPromptSubmit`.
|
|
186
185
|
|
|
187
186
|
Guide: [`docs/guides/claude-code.md`](docs/guides/claude-code.md)
|
|
188
187
|
|
|
@@ -6,6 +6,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
require("dotenv/config");
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
8
|
const sdk_1 = require("../src/sdk");
|
|
9
|
+
const runtimeEnv_1 = require("../src/lib/runtimeEnv");
|
|
10
|
+
const MEMORY_NEED_POSITIVE_PATTERNS = [
|
|
11
|
+
/\bwhat(?:'s| is| was)?\s+my\b/i,
|
|
12
|
+
/\bdo you remember\b/i,
|
|
13
|
+
/\bremind me\b/i,
|
|
14
|
+
/\bmy\s+(?:favorite|favourite|name|email|phone|address|city|country|movie|snack|color|colour)\b/i,
|
|
15
|
+
/\bwe decided\b/i,
|
|
16
|
+
/\bearlier\b/i,
|
|
17
|
+
/\bprevious(?:ly)?\b/i,
|
|
18
|
+
/\bagain\b/i,
|
|
19
|
+
];
|
|
20
|
+
const MEMORY_NEED_NEGATIVE_PATTERNS = [
|
|
21
|
+
/^\s*(hi|hello|hey|yo|sup|good (?:morning|afternoon|evening))\b[!.?\s]*$/i,
|
|
22
|
+
/^\s*(thanks|thank you|cool|great|nice)\b[!.?\s]*$/i,
|
|
23
|
+
];
|
|
9
24
|
function printHelp() {
|
|
10
25
|
console.log([
|
|
11
26
|
'Claude Code -> Iranti hook helper',
|
|
@@ -14,6 +29,11 @@ function printHelp() {
|
|
|
14
29
|
' ts-node scripts/claude-code-memory-hook.ts --event SessionStart',
|
|
15
30
|
' ts-node scripts/claude-code-memory-hook.ts --event UserPromptSubmit',
|
|
16
31
|
'',
|
|
32
|
+
'Optional flags:',
|
|
33
|
+
' --project-env <path> Explicit .env.iranti path',
|
|
34
|
+
' --instance-env <path> Explicit instance env path',
|
|
35
|
+
' --env-file <path> Explicit base .env path',
|
|
36
|
+
'',
|
|
17
37
|
'Reads Claude Code hook JSON from stdin and returns hookSpecificOutput.additionalContext on stdout.',
|
|
18
38
|
].join('\n'));
|
|
19
39
|
}
|
|
@@ -77,6 +97,9 @@ function getDefaultAgentId(payload) {
|
|
|
77
97
|
const explicit = process.env.IRANTI_CLAUDE_AGENT_ID?.trim();
|
|
78
98
|
if (explicit)
|
|
79
99
|
return explicit;
|
|
100
|
+
const projectBindingAgent = process.env.IRANTI_AGENT_ID?.trim();
|
|
101
|
+
if (projectBindingAgent)
|
|
102
|
+
return projectBindingAgent;
|
|
80
103
|
const base = path_1.default.basename(getCwd(payload));
|
|
81
104
|
return `claude_code_${slugify(base || 'project')}`;
|
|
82
105
|
}
|
|
@@ -85,6 +108,10 @@ function getEntityHints(payload) {
|
|
|
85
108
|
const cwd = getCwd(payload);
|
|
86
109
|
const projectHint = `project/${slugify(path_1.default.basename(cwd) || 'project')}`;
|
|
87
110
|
out.add(projectHint);
|
|
111
|
+
const memoryEntity = process.env.IRANTI_MEMORY_ENTITY?.trim();
|
|
112
|
+
if (memoryEntity) {
|
|
113
|
+
out.add(memoryEntity);
|
|
114
|
+
}
|
|
88
115
|
const envHints = (process.env.IRANTI_CLAUDE_ENTITY_HINTS ?? '')
|
|
89
116
|
.split(',')
|
|
90
117
|
.map((value) => value.trim())
|
|
@@ -105,7 +132,17 @@ async function ensureHookAgent(iranti, payload) {
|
|
|
105
132
|
return agentId;
|
|
106
133
|
}
|
|
107
134
|
function getPrompt(payload) {
|
|
108
|
-
|
|
135
|
+
const candidates = [
|
|
136
|
+
payload.prompt,
|
|
137
|
+
payload.message,
|
|
138
|
+
payload.text,
|
|
139
|
+
];
|
|
140
|
+
for (const candidate of candidates) {
|
|
141
|
+
if (typeof candidate === 'string' && candidate.trim()) {
|
|
142
|
+
return candidate.trim();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return '';
|
|
109
146
|
}
|
|
110
147
|
function getMaxFacts() {
|
|
111
148
|
const raw = Number(process.env.IRANTI_CLAUDE_MAX_FACTS ?? 6);
|
|
@@ -113,41 +150,26 @@ function getMaxFacts() {
|
|
|
113
150
|
return 6;
|
|
114
151
|
return Math.min(12, Math.trunc(raw));
|
|
115
152
|
}
|
|
116
|
-
function
|
|
117
|
-
const
|
|
153
|
+
function formatSessionContext(facts, cwd) {
|
|
154
|
+
const limited = facts.slice(0, getMaxFacts());
|
|
118
155
|
const lines = [
|
|
119
156
|
'[Iranti Session Memory]',
|
|
120
157
|
`Project: ${path_1.default.basename(cwd)}`,
|
|
121
158
|
];
|
|
122
|
-
if (
|
|
123
|
-
lines.push(`Task type: ${brief.inferredTaskType}`);
|
|
124
|
-
}
|
|
125
|
-
if (facts.length > 0) {
|
|
159
|
+
if (limited.length > 0) {
|
|
126
160
|
lines.push('Relevant memory:');
|
|
127
|
-
for (const fact of
|
|
128
|
-
|
|
129
|
-
const summary = typeof fact?.summary === 'string' ? fact.summary : fact?.valueSummary;
|
|
130
|
-
if (!summary)
|
|
131
|
-
continue;
|
|
132
|
-
lines.push(`- ${label}: ${summary}`);
|
|
161
|
+
for (const fact of limited) {
|
|
162
|
+
lines.push(`- ${fact.entity}/${fact.key}: ${fact.summary}`);
|
|
133
163
|
}
|
|
134
164
|
}
|
|
135
165
|
return lines.join('\n');
|
|
136
166
|
}
|
|
137
|
-
function
|
|
138
|
-
|
|
139
|
-
if (!attend?.shouldInject || facts.length === 0)
|
|
167
|
+
function formatPromptContext(facts) {
|
|
168
|
+
if (facts.length === 0)
|
|
140
169
|
return '';
|
|
141
170
|
const lines = ['[Iranti Retrieved Memory]'];
|
|
142
|
-
if (typeof attend?.reason === 'string' && attend.reason) {
|
|
143
|
-
lines.push(`Reason: ${attend.reason}`);
|
|
144
|
-
}
|
|
145
171
|
for (const fact of facts) {
|
|
146
|
-
|
|
147
|
-
const summary = typeof fact?.summary === 'string' ? fact.summary : fact?.valueSummary;
|
|
148
|
-
if (!summary)
|
|
149
|
-
continue;
|
|
150
|
-
lines.push(`- ${label}: ${summary}`);
|
|
172
|
+
lines.push(`- ${fact.entity}/${fact.key}: ${fact.summary}`);
|
|
151
173
|
}
|
|
152
174
|
return lines.join('\n');
|
|
153
175
|
}
|
|
@@ -160,6 +182,98 @@ function emitHookContext(event, additionalContext) {
|
|
|
160
182
|
};
|
|
161
183
|
process.stdout.write(`${JSON.stringify(payload)}\n`);
|
|
162
184
|
}
|
|
185
|
+
function shouldFetchMemory(prompt) {
|
|
186
|
+
const normalized = prompt.trim();
|
|
187
|
+
if (!normalized)
|
|
188
|
+
return false;
|
|
189
|
+
if (MEMORY_NEED_NEGATIVE_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
if (MEMORY_NEED_POSITIVE_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
if (/\b(my|our|we)\b/i.test(normalized)) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
return normalized.includes('/');
|
|
199
|
+
}
|
|
200
|
+
function dedupeFacts(facts) {
|
|
201
|
+
const byKey = new Map();
|
|
202
|
+
for (const fact of facts) {
|
|
203
|
+
const identity = `${fact.entity}/${fact.key}`;
|
|
204
|
+
const existing = byKey.get(identity);
|
|
205
|
+
if (!existing || fact.confidence > existing.confidence) {
|
|
206
|
+
byKey.set(identity, fact);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return Array.from(byKey.values())
|
|
210
|
+
.sort((a, b) => b.confidence - a.confidence || a.entity.localeCompare(b.entity) || a.key.localeCompare(b.key))
|
|
211
|
+
.slice(0, getMaxFacts());
|
|
212
|
+
}
|
|
213
|
+
async function loadAttendantStateFacts(iranti, agent) {
|
|
214
|
+
const state = await iranti.query(`agent/${agent}`, 'attendant_state');
|
|
215
|
+
if (!state.found || !state.value || typeof state.value !== 'object') {
|
|
216
|
+
return [];
|
|
217
|
+
}
|
|
218
|
+
const workingMemory = Array.isArray(state.value.workingMemory)
|
|
219
|
+
? state.value.workingMemory
|
|
220
|
+
: [];
|
|
221
|
+
return workingMemory.flatMap((entry) => {
|
|
222
|
+
const entityKey = typeof entry.entityKey === 'string' ? entry.entityKey.trim() : '';
|
|
223
|
+
const summary = typeof entry.summary === 'string' ? entry.summary.trim() : '';
|
|
224
|
+
if (!entityKey || !summary)
|
|
225
|
+
return [];
|
|
226
|
+
const segments = entityKey.split('/');
|
|
227
|
+
if (segments.length < 3)
|
|
228
|
+
return [];
|
|
229
|
+
return [{
|
|
230
|
+
entity: `${segments[0]}/${segments[1]}`,
|
|
231
|
+
key: segments.slice(2).join('/'),
|
|
232
|
+
summary,
|
|
233
|
+
confidence: typeof entry.confidence === 'number' ? entry.confidence : 0,
|
|
234
|
+
source: typeof entry.source === 'string' ? entry.source : 'attendant',
|
|
235
|
+
}];
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
async function loadEntityFacts(iranti, entities) {
|
|
239
|
+
const out = [];
|
|
240
|
+
for (const entity of entities) {
|
|
241
|
+
const trimmed = entity.trim();
|
|
242
|
+
if (!trimmed)
|
|
243
|
+
continue;
|
|
244
|
+
const entries = await iranti.queryAll(trimmed).catch(() => []);
|
|
245
|
+
for (const entry of entries) {
|
|
246
|
+
out.push({
|
|
247
|
+
entity: trimmed,
|
|
248
|
+
key: entry.key,
|
|
249
|
+
summary: entry.summary,
|
|
250
|
+
confidence: entry.confidence,
|
|
251
|
+
source: entry.source,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return out;
|
|
256
|
+
}
|
|
257
|
+
async function searchPromptFacts(iranti, prompt, entityHints) {
|
|
258
|
+
if (!prompt.trim())
|
|
259
|
+
return [];
|
|
260
|
+
const results = await iranti.search({
|
|
261
|
+
query: prompt,
|
|
262
|
+
limit: getMaxFacts(),
|
|
263
|
+
minScore: Number(process.env.IRANTI_CLAUDE_MIN_SEARCH_SCORE ?? 0.05),
|
|
264
|
+
}).catch(() => []);
|
|
265
|
+
const searched = results.map((result) => ({
|
|
266
|
+
entity: result.entity,
|
|
267
|
+
key: result.key,
|
|
268
|
+
summary: result.summary,
|
|
269
|
+
confidence: result.confidence,
|
|
270
|
+
source: result.source,
|
|
271
|
+
}));
|
|
272
|
+
if (searched.length > 0) {
|
|
273
|
+
return searched;
|
|
274
|
+
}
|
|
275
|
+
return loadEntityFacts(iranti, entityHints);
|
|
276
|
+
}
|
|
163
277
|
async function main() {
|
|
164
278
|
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
165
279
|
printHelp();
|
|
@@ -171,6 +285,12 @@ async function main() {
|
|
|
171
285
|
throw new Error('--event must be SessionStart or UserPromptSubmit');
|
|
172
286
|
}
|
|
173
287
|
const payload = parsePayload(await readStdin());
|
|
288
|
+
(0, runtimeEnv_1.loadRuntimeEnv)({
|
|
289
|
+
payloadCwd: getCwd(payload),
|
|
290
|
+
projectEnvFile: args['project-env'],
|
|
291
|
+
instanceEnvFile: args['instance-env'],
|
|
292
|
+
explicitEnvFile: args['env-file'],
|
|
293
|
+
});
|
|
174
294
|
const cwd = getCwd(payload);
|
|
175
295
|
const iranti = new sdk_1.Iranti({
|
|
176
296
|
connectionString: requireConnectionString(),
|
|
@@ -179,30 +299,27 @@ async function main() {
|
|
|
179
299
|
const agent = await ensureHookAgent(iranti, payload);
|
|
180
300
|
const entityHints = getEntityHints(payload);
|
|
181
301
|
if (event === 'SessionStart') {
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
});
|
|
189
|
-
emitHookContext(event, formatHandshakeContext(brief, cwd));
|
|
190
|
-
return;
|
|
302
|
+
const persistedFacts = await loadAttendantStateFacts(iranti, agent);
|
|
303
|
+
const directFacts = persistedFacts.length > 0
|
|
304
|
+
? persistedFacts
|
|
305
|
+
: await loadEntityFacts(iranti, entityHints);
|
|
306
|
+
emitHookContext(event, formatSessionContext(dedupeFacts(directFacts), cwd));
|
|
307
|
+
process.exit(0);
|
|
191
308
|
}
|
|
192
309
|
const prompt = getPrompt(payload);
|
|
193
|
-
if (!prompt)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
return;
|
|
310
|
+
if (!prompt) {
|
|
311
|
+
process.exit(0);
|
|
312
|
+
}
|
|
313
|
+
if (!shouldFetchMemory(prompt)) {
|
|
314
|
+
process.exit(0);
|
|
315
|
+
}
|
|
316
|
+
const facts = await searchPromptFacts(iranti, prompt, entityHints);
|
|
317
|
+
const context = formatPromptContext(dedupeFacts(facts));
|
|
318
|
+
if (!context) {
|
|
319
|
+
process.exit(0);
|
|
320
|
+
}
|
|
205
321
|
emitHookContext(event, context);
|
|
322
|
+
process.exit(0);
|
|
206
323
|
}
|
|
207
324
|
main().catch((error) => {
|
|
208
325
|
console.error('[claude-code-memory-hook] fatal:', error instanceof Error ? error.message : String(error));
|
|
@@ -8,6 +8,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
8
8
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
9
|
const os_1 = __importDefault(require("os"));
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const child_process_1 = require("child_process");
|
|
11
12
|
const promises_2 = __importDefault(require("readline/promises"));
|
|
12
13
|
const stream_1 = require("stream");
|
|
13
14
|
const client_1 = require("../src/library/client");
|
|
@@ -118,6 +119,54 @@ function getPackageVersion() {
|
|
|
118
119
|
}
|
|
119
120
|
return '0.0.0';
|
|
120
121
|
}
|
|
122
|
+
function builtScriptPath(scriptName) {
|
|
123
|
+
return path_1.default.resolve(__dirname, `${scriptName}.js`);
|
|
124
|
+
}
|
|
125
|
+
async function handoffToScript(scriptName, rawArgs) {
|
|
126
|
+
const builtPath = builtScriptPath(scriptName);
|
|
127
|
+
if (fs_1.default.existsSync(builtPath)) {
|
|
128
|
+
await new Promise((resolve, reject) => {
|
|
129
|
+
const child = (0, child_process_1.spawn)(process.execPath, [builtPath, ...rawArgs], {
|
|
130
|
+
stdio: 'inherit',
|
|
131
|
+
env: process.env,
|
|
132
|
+
});
|
|
133
|
+
child.on('error', reject);
|
|
134
|
+
child.on('exit', (code, signal) => {
|
|
135
|
+
if (signal) {
|
|
136
|
+
reject(new Error(`${scriptName} terminated with signal ${signal}`));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if ((code ?? 0) !== 0) {
|
|
140
|
+
process.exit(code ?? 1);
|
|
141
|
+
}
|
|
142
|
+
resolve();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const sourcePath = path_1.default.resolve(process.cwd(), 'scripts', `${scriptName}.ts`);
|
|
148
|
+
if (!fs_1.default.existsSync(sourcePath)) {
|
|
149
|
+
throw new Error(`Unable to locate ${scriptName} implementation.`);
|
|
150
|
+
}
|
|
151
|
+
await new Promise((resolve, reject) => {
|
|
152
|
+
const child = (0, child_process_1.spawn)('npx', ['ts-node', sourcePath, ...rawArgs], {
|
|
153
|
+
stdio: 'inherit',
|
|
154
|
+
env: process.env,
|
|
155
|
+
shell: process.platform === 'win32',
|
|
156
|
+
});
|
|
157
|
+
child.on('error', reject);
|
|
158
|
+
child.on('exit', (code, signal) => {
|
|
159
|
+
if (signal) {
|
|
160
|
+
reject(new Error(`${scriptName} terminated with signal ${signal}`));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if ((code ?? 0) !== 0) {
|
|
164
|
+
process.exit(code ?? 1);
|
|
165
|
+
}
|
|
166
|
+
resolve();
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
121
170
|
async function ensureDir(dir) {
|
|
122
171
|
await promises_1.default.mkdir(dir, { recursive: true });
|
|
123
172
|
}
|
|
@@ -1077,6 +1126,10 @@ Diagnostics:
|
|
|
1077
1126
|
iranti doctor [--instance <name>] [--scope user|system] [--env <file>] [--json]
|
|
1078
1127
|
iranti status [--scope user|system] [--json]
|
|
1079
1128
|
iranti upgrade [--json]
|
|
1129
|
+
|
|
1130
|
+
Integrations:
|
|
1131
|
+
iranti mcp [--help]
|
|
1132
|
+
iranti claude-hook --event SessionStart|UserPromptSubmit [--project-env <path>] [--instance-env <path>] [--env-file <path>]
|
|
1080
1133
|
`);
|
|
1081
1134
|
}
|
|
1082
1135
|
async function main() {
|
|
@@ -1150,6 +1203,14 @@ async function main() {
|
|
|
1150
1203
|
await upgradeCommand(args);
|
|
1151
1204
|
return;
|
|
1152
1205
|
}
|
|
1206
|
+
if (args.command === 'mcp') {
|
|
1207
|
+
await handoffToScript('iranti-mcp', process.argv.slice(3));
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
if (args.command === 'claude-hook') {
|
|
1211
|
+
await handoffToScript('claude-code-memory-hook', process.argv.slice(3));
|
|
1212
|
+
return;
|
|
1213
|
+
}
|
|
1153
1214
|
throw new Error(`Unknown command '${args.command}'. Run: iranti help`);
|
|
1154
1215
|
}
|
|
1155
1216
|
main().catch((err) => {
|
|
@@ -32,35 +32,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
40
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
41
|
-
const dotenv_1 = __importDefault(require("dotenv"));
|
|
42
36
|
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
43
37
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
44
38
|
const z = __importStar(require("zod/v4"));
|
|
45
39
|
const sdk_1 = require("../src/sdk");
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const candidates = explicitPath
|
|
49
|
-
? [explicitPath]
|
|
50
|
-
: [
|
|
51
|
-
node_path_1.default.resolve(process.cwd(), '.env'),
|
|
52
|
-
node_path_1.default.resolve(__dirname, '..', '.env'),
|
|
53
|
-
node_path_1.default.resolve(__dirname, '..', '..', '.env'),
|
|
54
|
-
];
|
|
55
|
-
for (const candidate of candidates) {
|
|
56
|
-
if (candidate && node_fs_1.default.existsSync(candidate)) {
|
|
57
|
-
dotenv_1.default.config({ path: candidate });
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
dotenv_1.default.config();
|
|
62
|
-
}
|
|
63
|
-
loadEnv();
|
|
40
|
+
const runtimeEnv_1 = require("../src/lib/runtimeEnv");
|
|
41
|
+
(0, runtimeEnv_1.loadRuntimeEnv)();
|
|
64
42
|
function printHelp() {
|
|
65
43
|
console.log([
|
|
66
44
|
'Iranti MCP Server',
|
|
@@ -72,6 +50,8 @@ function printHelp() {
|
|
|
72
50
|
'Environment:',
|
|
73
51
|
' DATABASE_URL PostgreSQL connection string (required)',
|
|
74
52
|
' LLM_PROVIDER Optional Iranti provider override',
|
|
53
|
+
' IRANTI_PROJECT_ENV Optional project binding path (.env.iranti)',
|
|
54
|
+
' IRANTI_INSTANCE_ENV Optional instance env path',
|
|
75
55
|
' IRANTI_MCP_DEFAULT_AGENT Default agent id (default: claude_code)',
|
|
76
56
|
' IRANTI_MCP_AGENT_NAME Default agent display name',
|
|
77
57
|
' IRANTI_MCP_AGENT_DESCRIPTION Default agent description',
|
|
@@ -89,7 +69,9 @@ function requireConnectionString() {
|
|
|
89
69
|
return connectionString;
|
|
90
70
|
}
|
|
91
71
|
function defaultAgentId() {
|
|
92
|
-
return process.env.IRANTI_MCP_DEFAULT_AGENT?.trim()
|
|
72
|
+
return process.env.IRANTI_MCP_DEFAULT_AGENT?.trim()
|
|
73
|
+
|| process.env.IRANTI_AGENT_ID?.trim()
|
|
74
|
+
|| 'claude_code';
|
|
93
75
|
}
|
|
94
76
|
function defaultWriteSource() {
|
|
95
77
|
return process.env.IRANTI_MCP_DEFAULT_SOURCE?.trim() || 'ClaudeCode';
|
|
@@ -162,7 +144,7 @@ async function main() {
|
|
|
162
144
|
await ensureDefaultAgent(iranti);
|
|
163
145
|
const server = new mcp_js_1.McpServer({
|
|
164
146
|
name: 'iranti-mcp',
|
|
165
|
-
version: '0.1.
|
|
147
|
+
version: '0.1.2',
|
|
166
148
|
});
|
|
167
149
|
server.registerTool('iranti_handshake', {
|
|
168
150
|
description: 'Initialize or refresh Claude working memory for the current task.',
|
package/dist/scripts/seed.js
CHANGED
|
@@ -15,7 +15,7 @@ const STAFF_ENTRIES = [
|
|
|
15
15
|
entityId: 'librarian',
|
|
16
16
|
key: 'operating_rules',
|
|
17
17
|
valueRaw: {
|
|
18
|
-
version: '0.1.
|
|
18
|
+
version: '0.1.2',
|
|
19
19
|
rules: [
|
|
20
20
|
'All writes from external agents go through the Librarian — never directly to the database',
|
|
21
21
|
'Check for existing entries before every write',
|
|
@@ -39,7 +39,7 @@ const STAFF_ENTRIES = [
|
|
|
39
39
|
entityId: 'attendant',
|
|
40
40
|
key: 'operating_rules',
|
|
41
41
|
valueRaw: {
|
|
42
|
-
version: '0.1.
|
|
42
|
+
version: '0.1.2',
|
|
43
43
|
rules: [
|
|
44
44
|
'Assigned one-per-external-agent — serve the agent, not the user',
|
|
45
45
|
'On handshake: read AGENTS.md and MCP config, query Librarian for relevant rules and task context',
|
|
@@ -61,7 +61,7 @@ const STAFF_ENTRIES = [
|
|
|
61
61
|
entityId: 'archivist',
|
|
62
62
|
key: 'operating_rules',
|
|
63
63
|
valueRaw: {
|
|
64
|
-
version: '0.1.
|
|
64
|
+
version: '0.1.2',
|
|
65
65
|
rules: [
|
|
66
66
|
'Run on schedule or when conflict flags exceed threshold — not on every write',
|
|
67
67
|
'Scan for expired, low-confidence, flagged, and duplicate entries',
|
|
@@ -82,7 +82,7 @@ const STAFF_ENTRIES = [
|
|
|
82
82
|
entityType: 'system',
|
|
83
83
|
entityId: 'library',
|
|
84
84
|
key: 'schema_version',
|
|
85
|
-
valueRaw: { version: '0.1.
|
|
85
|
+
valueRaw: { version: '0.1.2' },
|
|
86
86
|
valueSummary: 'Current Library schema version.',
|
|
87
87
|
confidence: 100,
|
|
88
88
|
source: 'seed',
|
|
@@ -95,7 +95,7 @@ const STAFF_ENTRIES = [
|
|
|
95
95
|
key: 'initialization_log',
|
|
96
96
|
valueRaw: {
|
|
97
97
|
initializedAt: new Date().toISOString(),
|
|
98
|
-
seedVersion: '0.1.
|
|
98
|
+
seedVersion: '0.1.2',
|
|
99
99
|
},
|
|
100
100
|
valueSummary: 'Record of when and how this Library was initialized.',
|
|
101
101
|
confidence: 100,
|
|
@@ -108,7 +108,7 @@ const STAFF_ENTRIES = [
|
|
|
108
108
|
entityId: 'ontology',
|
|
109
109
|
key: 'core_schema',
|
|
110
110
|
valueRaw: {
|
|
111
|
-
version: '0.1.
|
|
111
|
+
version: '0.1.2',
|
|
112
112
|
states: ['candidate', 'provisional', 'canonical'],
|
|
113
113
|
coreEntityTypes: [
|
|
114
114
|
'person',
|
|
@@ -156,7 +156,7 @@ const STAFF_ENTRIES = [
|
|
|
156
156
|
entityId: 'ontology',
|
|
157
157
|
key: 'extension_registry',
|
|
158
158
|
valueRaw: {
|
|
159
|
-
version: '0.1.
|
|
159
|
+
version: '0.1.2',
|
|
160
160
|
namespaces: {
|
|
161
161
|
education: {
|
|
162
162
|
status: 'provisional',
|
|
@@ -187,7 +187,7 @@ const STAFF_ENTRIES = [
|
|
|
187
187
|
entityId: 'ontology',
|
|
188
188
|
key: 'candidate_terms',
|
|
189
189
|
valueRaw: {
|
|
190
|
-
version: '0.1.
|
|
190
|
+
version: '0.1.2',
|
|
191
191
|
terms: [],
|
|
192
192
|
},
|
|
193
193
|
valueSummary: 'Staging area for ontology terms detected repeatedly but not yet promoted.',
|
|
@@ -201,7 +201,7 @@ const STAFF_ENTRIES = [
|
|
|
201
201
|
entityId: 'ontology',
|
|
202
202
|
key: 'promotion_policy',
|
|
203
203
|
valueRaw: {
|
|
204
|
-
version: '0.1.
|
|
204
|
+
version: '0.1.2',
|
|
205
205
|
candidateToProvisional: {
|
|
206
206
|
minSeenCount: 3,
|
|
207
207
|
minDistinctAgents: 2,
|
|
@@ -237,7 +237,7 @@ const STAFF_ENTRIES = [
|
|
|
237
237
|
entityId: 'ontology',
|
|
238
238
|
key: 'change_log',
|
|
239
239
|
valueRaw: {
|
|
240
|
-
version: '0.1.
|
|
240
|
+
version: '0.1.2',
|
|
241
241
|
events: [
|
|
242
242
|
{
|
|
243
243
|
at: new Date().toISOString(),
|
package/dist/src/api/server.js
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type RuntimeEnvOptions = {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
payloadCwd?: string;
|
|
4
|
+
projectEnvFile?: string;
|
|
5
|
+
instanceEnvFile?: string;
|
|
6
|
+
explicitEnvFile?: string;
|
|
7
|
+
};
|
|
8
|
+
type RuntimeEnvLoadResult = {
|
|
9
|
+
loadedFiles: string[];
|
|
10
|
+
projectEnvFile?: string;
|
|
11
|
+
instanceEnvFile?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function loadRuntimeEnv(options?: RuntimeEnvOptions): RuntimeEnvLoadResult;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=runtimeEnv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtimeEnv.d.ts","sourceRoot":"","sources":["../../../src/lib/runtimeEnv.ts"],"names":[],"mappings":"AAIA,KAAK,iBAAiB,GAAG;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,KAAK,oBAAoB,GAAG;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAuDF,wBAAgB,cAAc,CAAC,OAAO,GAAE,iBAAsB,GAAG,oBAAoB,CAsCpF"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadRuntimeEnv = loadRuntimeEnv;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
10
|
+
function parseEnvFile(filePath) {
|
|
11
|
+
const raw = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
12
|
+
return dotenv_1.default.parse(raw);
|
|
13
|
+
}
|
|
14
|
+
function applyEnvVars(vars, initialEnvKeys) {
|
|
15
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
16
|
+
if (initialEnvKeys.has(key))
|
|
17
|
+
continue;
|
|
18
|
+
process.env[key] = value;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function dedupePaths(paths) {
|
|
22
|
+
const seen = new Set();
|
|
23
|
+
const out = [];
|
|
24
|
+
for (const candidate of paths) {
|
|
25
|
+
if (!candidate)
|
|
26
|
+
continue;
|
|
27
|
+
const resolved = path_1.default.resolve(candidate);
|
|
28
|
+
if (seen.has(resolved))
|
|
29
|
+
continue;
|
|
30
|
+
seen.add(resolved);
|
|
31
|
+
out.push(resolved);
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
function findProjectEnvFile(options) {
|
|
36
|
+
const explicit = options.projectEnvFile?.trim() || process.env.IRANTI_PROJECT_ENV?.trim();
|
|
37
|
+
if (explicit && fs_1.default.existsSync(explicit))
|
|
38
|
+
return path_1.default.resolve(explicit);
|
|
39
|
+
const candidates = dedupePaths([
|
|
40
|
+
options.payloadCwd ? path_1.default.join(options.payloadCwd, '.env.iranti') : null,
|
|
41
|
+
options.cwd ? path_1.default.join(options.cwd, '.env.iranti') : null,
|
|
42
|
+
path_1.default.join(process.cwd(), '.env.iranti'),
|
|
43
|
+
]);
|
|
44
|
+
return candidates.find((candidate) => fs_1.default.existsSync(candidate));
|
|
45
|
+
}
|
|
46
|
+
function findFallbackEnvFile(options) {
|
|
47
|
+
const explicit = options.explicitEnvFile?.trim() || process.env.IRANTI_ENV_FILE?.trim();
|
|
48
|
+
if (explicit && fs_1.default.existsSync(explicit))
|
|
49
|
+
return path_1.default.resolve(explicit);
|
|
50
|
+
const candidates = dedupePaths([
|
|
51
|
+
options.payloadCwd ? path_1.default.join(options.payloadCwd, '.env') : null,
|
|
52
|
+
options.cwd ? path_1.default.join(options.cwd, '.env') : null,
|
|
53
|
+
path_1.default.join(process.cwd(), '.env'),
|
|
54
|
+
path_1.default.resolve(__dirname, '..', '..', '.env'),
|
|
55
|
+
path_1.default.resolve(__dirname, '..', '..', '..', '.env'),
|
|
56
|
+
]);
|
|
57
|
+
return candidates.find((candidate) => fs_1.default.existsSync(candidate));
|
|
58
|
+
}
|
|
59
|
+
function loadRuntimeEnv(options = {}) {
|
|
60
|
+
const loadedFiles = [];
|
|
61
|
+
const initialEnvKeys = new Set(Object.keys(process.env));
|
|
62
|
+
const fallbackEnvFile = findFallbackEnvFile(options);
|
|
63
|
+
if (fallbackEnvFile) {
|
|
64
|
+
applyEnvVars(parseEnvFile(fallbackEnvFile), initialEnvKeys);
|
|
65
|
+
loadedFiles.push(fallbackEnvFile);
|
|
66
|
+
}
|
|
67
|
+
const projectEnvFile = findProjectEnvFile(options);
|
|
68
|
+
const projectEnv = projectEnvFile && fs_1.default.existsSync(projectEnvFile)
|
|
69
|
+
? parseEnvFile(projectEnvFile)
|
|
70
|
+
: null;
|
|
71
|
+
const instanceEnvFile = options.instanceEnvFile?.trim()
|
|
72
|
+
|| process.env.IRANTI_INSTANCE_ENV?.trim()
|
|
73
|
+
|| projectEnv?.IRANTI_INSTANCE_ENV;
|
|
74
|
+
const resolvedInstanceEnvFile = instanceEnvFile && fs_1.default.existsSync(instanceEnvFile)
|
|
75
|
+
? path_1.default.resolve(instanceEnvFile)
|
|
76
|
+
: undefined;
|
|
77
|
+
if (resolvedInstanceEnvFile) {
|
|
78
|
+
applyEnvVars(parseEnvFile(resolvedInstanceEnvFile), initialEnvKeys);
|
|
79
|
+
loadedFiles.push(resolvedInstanceEnvFile);
|
|
80
|
+
}
|
|
81
|
+
if (projectEnvFile && projectEnv) {
|
|
82
|
+
applyEnvVars(projectEnv, initialEnvKeys);
|
|
83
|
+
loadedFiles.push(projectEnvFile);
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
loadedFiles,
|
|
87
|
+
projectEnvFile,
|
|
88
|
+
instanceEnvFile: resolvedInstanceEnvFile,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=runtimeEnv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtimeEnv.js","sourceRoot":"","sources":["../../../src/lib/runtimeEnv.ts"],"names":[],"mappings":"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"}
|