@soleri/forge 4.2.2 → 5.0.1
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/domain-manager.d.ts +2 -2
- package/dist/domain-manager.js +35 -16
- package/dist/domain-manager.js.map +1 -1
- package/dist/index.js +0 -0
- package/dist/knowledge-installer.js +18 -12
- package/dist/knowledge-installer.js.map +1 -1
- package/dist/patching.d.ts +15 -6
- package/dist/patching.js +37 -12
- package/dist/patching.js.map +1 -1
- package/dist/scaffolder.js +18 -28
- package/dist/scaffolder.js.map +1 -1
- package/dist/templates/brain.d.ts +6 -0
- package/dist/templates/brain.js +478 -0
- package/dist/templates/brain.js.map +1 -0
- package/dist/templates/core-facade.js +95 -47
- package/dist/templates/core-facade.js.map +1 -1
- package/dist/templates/entry-point.d.ts +4 -0
- package/dist/templates/entry-point.js +146 -89
- package/dist/templates/entry-point.js.map +1 -1
- package/dist/templates/facade-factory.d.ts +1 -0
- package/dist/templates/facade-factory.js +63 -0
- package/dist/templates/facade-factory.js.map +1 -0
- package/dist/templates/facade-types.d.ts +1 -0
- package/dist/templates/facade-types.js +46 -0
- package/dist/templates/facade-types.js.map +1 -0
- package/dist/templates/intelligence-loader.d.ts +1 -0
- package/dist/templates/intelligence-loader.js +43 -0
- package/dist/templates/intelligence-loader.js.map +1 -0
- package/dist/templates/intelligence-types.d.ts +1 -0
- package/dist/templates/intelligence-types.js +24 -0
- package/dist/templates/intelligence-types.js.map +1 -0
- package/dist/templates/llm-key-pool.d.ts +7 -0
- package/dist/templates/llm-key-pool.js +211 -0
- package/dist/templates/llm-key-pool.js.map +1 -0
- package/dist/templates/llm-types.d.ts +5 -0
- package/dist/templates/llm-types.js +161 -0
- package/dist/templates/llm-types.js.map +1 -0
- package/dist/templates/llm-utils.d.ts +5 -0
- package/dist/templates/llm-utils.js +260 -0
- package/dist/templates/llm-utils.js.map +1 -0
- package/dist/templates/package-json.js +3 -1
- package/dist/templates/package-json.js.map +1 -1
- package/dist/templates/planner.d.ts +5 -0
- package/dist/templates/planner.js +150 -0
- package/dist/templates/planner.js.map +1 -0
- package/dist/templates/test-brain.d.ts +6 -0
- package/dist/templates/test-brain.js +474 -0
- package/dist/templates/test-brain.js.map +1 -0
- package/dist/templates/test-facades.d.ts +1 -1
- package/dist/templates/test-facades.js +182 -456
- package/dist/templates/test-facades.js.map +1 -1
- package/dist/templates/test-llm.d.ts +7 -0
- package/dist/templates/test-llm.js +574 -0
- package/dist/templates/test-llm.js.map +1 -0
- package/dist/templates/test-loader.d.ts +5 -0
- package/dist/templates/test-loader.js +146 -0
- package/dist/templates/test-loader.js.map +1 -0
- package/dist/templates/test-planner.d.ts +5 -0
- package/dist/templates/test-planner.js +271 -0
- package/dist/templates/test-planner.js.map +1 -0
- package/dist/templates/test-vault.d.ts +5 -0
- package/dist/templates/test-vault.js +380 -0
- package/dist/templates/test-vault.js.map +1 -0
- package/dist/templates/vault.d.ts +5 -0
- package/dist/templates/vault.js +263 -0
- package/dist/templates/vault.js.map +1 -0
- package/dist/types.d.ts +2 -2
- package/package.json +1 -1
- package/src/__tests__/scaffolder.test.ts +52 -109
- package/src/domain-manager.ts +34 -15
- package/src/knowledge-installer.ts +23 -15
- package/src/patching.ts +44 -12
- package/src/scaffolder.ts +18 -29
- package/src/templates/entry-point.ts +146 -91
- package/src/templates/package-json.ts +3 -1
- package/src/templates/test-facades.ts +182 -458
- package/src/templates/core-facade.ts +0 -517
- package/src/templates/llm-client.ts +0 -301
|
@@ -4,20 +4,20 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export function generateCoreFacade(config) {
|
|
6
6
|
return `import { z } from 'zod';
|
|
7
|
-
import type { FacadeConfig, Vault, IntelligenceEntry, Planner, Brain, KeyPool,
|
|
7
|
+
import type { FacadeConfig, Vault, IntelligenceEntry, Planner, Brain, KeyPool, Curator } from '@soleri/core';
|
|
8
8
|
import { PERSONA } from '../identity/persona.js';
|
|
9
9
|
import { activateAgent, deactivateAgent } from '../activation/activate.js';
|
|
10
10
|
import { injectClaudeMd, injectClaudeMdGlobal, hasAgentMarker } from '../activation/inject-claude-md.js';
|
|
11
11
|
import type { LLMClient } from '../llm/llm-client.js';
|
|
12
12
|
|
|
13
|
-
export function createCoreFacade(vault: Vault, planner: Planner, brain: Brain,
|
|
13
|
+
export function createCoreFacade(vault: Vault, planner: Planner, brain: Brain, llmClient?: LLMClient, openaiKeyPool?: KeyPool, anthropicKeyPool?: KeyPool, curator?: Curator): FacadeConfig {
|
|
14
14
|
return {
|
|
15
15
|
name: '${config.id}_core',
|
|
16
16
|
description: 'Core operations — vault stats, cross-domain search, health check, identity, and activation system.',
|
|
17
17
|
ops: [
|
|
18
18
|
{
|
|
19
19
|
name: 'search',
|
|
20
|
-
description: 'Search across all knowledge domains. Results ranked by TF-IDF + severity + recency + tag overlap + domain match.
|
|
20
|
+
description: 'Search across all knowledge domains. Results ranked by TF-IDF + severity + recency + tag overlap + domain match.',
|
|
21
21
|
auth: 'read',
|
|
22
22
|
schema: z.object({
|
|
23
23
|
query: z.string(),
|
|
@@ -28,7 +28,7 @@ export function createCoreFacade(vault: Vault, planner: Planner, brain: Brain, c
|
|
|
28
28
|
limit: z.number().optional(),
|
|
29
29
|
}),
|
|
30
30
|
handler: async (params) => {
|
|
31
|
-
return
|
|
31
|
+
return brain.intelligentSearch(params.query as string, {
|
|
32
32
|
domain: params.domain as string | undefined,
|
|
33
33
|
type: params.type as string | undefined,
|
|
34
34
|
severity: params.severity as string | undefined,
|
|
@@ -448,64 +448,112 @@ export function createCoreFacade(vault: Vault, planner: Planner, brain: Brain, c
|
|
|
448
448
|
};
|
|
449
449
|
},
|
|
450
450
|
},
|
|
451
|
+
// ─── Curator ops ───────────────────────────────────────────
|
|
451
452
|
{
|
|
452
|
-
name: '
|
|
453
|
-
description: '
|
|
453
|
+
name: 'curator_status',
|
|
454
|
+
description: 'Curator status — table row counts, last groomed timestamp.',
|
|
454
455
|
auth: 'read',
|
|
455
456
|
handler: async () => {
|
|
456
|
-
if (!
|
|
457
|
-
|
|
458
|
-
const cfg = cognee.getConfig();
|
|
459
|
-
return {
|
|
460
|
-
available: cognee.isAvailable,
|
|
461
|
-
config: {
|
|
462
|
-
baseUrl: cfg.baseUrl,
|
|
463
|
-
dataset: cfg.dataset,
|
|
464
|
-
timeoutMs: cfg.timeoutMs,
|
|
465
|
-
searchTimeoutMs: cfg.searchTimeoutMs,
|
|
466
|
-
healthTimeoutMs: cfg.healthTimeoutMs,
|
|
467
|
-
healthCacheTtlMs: cfg.healthCacheTtlMs,
|
|
468
|
-
cognifyDebounceMs: cfg.cognifyDebounceMs,
|
|
469
|
-
hasApiToken: Boolean(cfg.apiToken),
|
|
470
|
-
hasServiceCredentials: Boolean(cfg.serviceEmail && cfg.servicePassword),
|
|
471
|
-
},
|
|
472
|
-
lastHealth: status,
|
|
473
|
-
};
|
|
457
|
+
if (!curator) return { error: 'Curator not initialized' };
|
|
458
|
+
return curator.getStatus();
|
|
474
459
|
},
|
|
475
460
|
},
|
|
476
461
|
{
|
|
477
|
-
name: '
|
|
478
|
-
description: '
|
|
479
|
-
auth: '
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
return {
|
|
462
|
+
name: 'curator_detect_duplicates',
|
|
463
|
+
description: 'Detect duplicate entries using TF-IDF cosine similarity.',
|
|
464
|
+
auth: 'read',
|
|
465
|
+
schema: z.object({
|
|
466
|
+
entryId: z.string().optional().describe('Check a specific entry. Omit to scan all.'),
|
|
467
|
+
threshold: z.number().optional().describe('Similarity threshold (0-1). Default 0.45.'),
|
|
468
|
+
}),
|
|
469
|
+
handler: async (params) => {
|
|
470
|
+
if (!curator) return { error: 'Curator not initialized' };
|
|
471
|
+
return curator.detectDuplicates(
|
|
472
|
+
params.entryId as string | undefined,
|
|
473
|
+
params.threshold as number | undefined,
|
|
474
|
+
);
|
|
486
475
|
},
|
|
487
476
|
},
|
|
488
477
|
{
|
|
489
|
-
name: '
|
|
490
|
-
description: '
|
|
478
|
+
name: 'curator_contradictions',
|
|
479
|
+
description: 'List or detect contradictions between patterns and anti-patterns.',
|
|
491
480
|
auth: 'read',
|
|
492
481
|
schema: z.object({
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
limit: z.number().optional().default(10),
|
|
482
|
+
status: z.enum(['open', 'resolved', 'dismissed']).optional().describe('Filter by status.'),
|
|
483
|
+
detect: z.boolean().optional().describe('If true, run detection before listing.'),
|
|
496
484
|
}),
|
|
497
485
|
handler: async (params) => {
|
|
498
|
-
if (!
|
|
499
|
-
|
|
486
|
+
if (!curator) return { error: 'Curator not initialized' };
|
|
487
|
+
if (params.detect) {
|
|
488
|
+
curator.detectContradictions();
|
|
500
489
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
490
|
+
return curator.getContradictions(params.status as 'open' | 'resolved' | 'dismissed' | undefined);
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
name: 'curator_resolve_contradiction',
|
|
495
|
+
description: 'Resolve or dismiss a contradiction.',
|
|
496
|
+
auth: 'write',
|
|
497
|
+
schema: z.object({
|
|
498
|
+
id: z.number().describe('Contradiction ID.'),
|
|
499
|
+
resolution: z.enum(['resolved', 'dismissed']),
|
|
500
|
+
}),
|
|
501
|
+
handler: async (params) => {
|
|
502
|
+
if (!curator) return { error: 'Curator not initialized' };
|
|
503
|
+
return curator.resolveContradiction(
|
|
504
|
+
params.id as number,
|
|
505
|
+
params.resolution as 'resolved' | 'dismissed',
|
|
507
506
|
);
|
|
508
|
-
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
name: 'curator_groom',
|
|
511
|
+
description: 'Groom a single entry — normalize tags, check staleness.',
|
|
512
|
+
auth: 'write',
|
|
513
|
+
schema: z.object({
|
|
514
|
+
entryId: z.string().describe('Entry ID to groom.'),
|
|
515
|
+
}),
|
|
516
|
+
handler: async (params) => {
|
|
517
|
+
if (!curator) return { error: 'Curator not initialized' };
|
|
518
|
+
return curator.groomEntry(params.entryId as string);
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
name: 'curator_groom_all',
|
|
523
|
+
description: 'Groom all vault entries — normalize tags, detect staleness.',
|
|
524
|
+
auth: 'write',
|
|
525
|
+
handler: async () => {
|
|
526
|
+
if (!curator) return { error: 'Curator not initialized' };
|
|
527
|
+
return curator.groomAll();
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
name: 'curator_consolidate',
|
|
532
|
+
description: 'Consolidate vault — find duplicates, stale entries, contradictions. Dry-run by default.',
|
|
533
|
+
auth: 'write',
|
|
534
|
+
schema: z.object({
|
|
535
|
+
dryRun: z.boolean().optional().describe('Default true. Set false to apply mutations.'),
|
|
536
|
+
staleDaysThreshold: z.number().optional().describe('Days before entry is stale. Default 90.'),
|
|
537
|
+
duplicateThreshold: z.number().optional().describe('Cosine similarity threshold. Default 0.45.'),
|
|
538
|
+
contradictionThreshold: z.number().optional().describe('Contradiction threshold. Default 0.4.'),
|
|
539
|
+
}),
|
|
540
|
+
handler: async (params) => {
|
|
541
|
+
if (!curator) return { error: 'Curator not initialized' };
|
|
542
|
+
return curator.consolidate({
|
|
543
|
+
dryRun: params.dryRun as boolean | undefined,
|
|
544
|
+
staleDaysThreshold: params.staleDaysThreshold as number | undefined,
|
|
545
|
+
duplicateThreshold: params.duplicateThreshold as number | undefined,
|
|
546
|
+
contradictionThreshold: params.contradictionThreshold as number | undefined,
|
|
547
|
+
});
|
|
548
|
+
},
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
name: 'curator_health_audit',
|
|
552
|
+
description: 'Audit vault health — score (0-100), coverage, freshness, quality, tag health, recommendations.',
|
|
553
|
+
auth: 'read',
|
|
554
|
+
handler: async () => {
|
|
555
|
+
if (!curator) return { error: 'Curator not initialized' };
|
|
556
|
+
return curator.healthAudit();
|
|
509
557
|
},
|
|
510
558
|
},
|
|
511
559
|
],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core-facade.js","sourceRoot":"","sources":["../../src/templates/core-facade.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAmB;IACpD,OAAO;;;;;;;;;aASI,MAAM,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uGA2EiF,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCA6D9E,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gEAiCiB,MAAM,CAAC,IAAI
|
|
1
|
+
{"version":3,"file":"core-facade.js","sourceRoot":"","sources":["../../src/templates/core-facade.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAmB;IACpD,OAAO;;;;;;;;;aASI,MAAM,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uGA2EiF,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCA6D9E,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gEAiCiB,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0X1E,CAAC;AACF,CAAC"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { AgentConfig } from '../types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Generate the main index.ts entry point for the agent.
|
|
4
|
+
*
|
|
5
|
+
* v5.0: Thin shell — delegates to createAgentRuntime(), createCoreOps(),
|
|
6
|
+
* and createDomainFacades() from @soleri/core. Only agent-specific code
|
|
7
|
+
* (persona, activation) lives here.
|
|
4
8
|
*/
|
|
5
9
|
export declare function generateEntryPoint(config: AgentConfig): string;
|
|
@@ -1,119 +1,182 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generate the main index.ts entry point for the agent.
|
|
3
|
+
*
|
|
4
|
+
* v5.0: Thin shell — delegates to createAgentRuntime(), createCoreOps(),
|
|
5
|
+
* and createDomainFacades() from @soleri/core. Only agent-specific code
|
|
6
|
+
* (persona, activation) lives here.
|
|
3
7
|
*/
|
|
4
8
|
export function generateEntryPoint(config) {
|
|
5
|
-
const
|
|
6
|
-
.map((d) => {
|
|
7
|
-
const fn = `create${pascalCase(d)}Facade`;
|
|
8
|
-
const file = `${d}.facade.js`;
|
|
9
|
-
return `import { ${fn} } from './facades/${file}';`;
|
|
10
|
-
})
|
|
11
|
-
.join('\n');
|
|
12
|
-
const facadeCreations = config.domains
|
|
13
|
-
.map((d) => ` create${pascalCase(d)}Facade(vault, brain),`)
|
|
14
|
-
.join('\n');
|
|
9
|
+
const domainsLiteral = JSON.stringify(config.domains);
|
|
15
10
|
return `#!/usr/bin/env node
|
|
16
11
|
|
|
17
12
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
18
13
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
19
14
|
import { dirname, join } from 'node:path';
|
|
20
15
|
import { fileURLToPath } from 'node:url';
|
|
21
|
-
import { homedir } from 'node:os';
|
|
22
16
|
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
import {
|
|
18
|
+
createAgentRuntime,
|
|
19
|
+
createCoreOps,
|
|
20
|
+
createDomainFacades,
|
|
21
|
+
registerAllFacades,
|
|
22
|
+
} from '@soleri/core';
|
|
23
|
+
import type { OpDefinition } from '@soleri/core';
|
|
24
|
+
import { z } from 'zod';
|
|
27
25
|
import { PERSONA, getPersonaPrompt } from './identity/persona.js';
|
|
26
|
+
import { activateAgent, deactivateAgent } from './activation/activate.js';
|
|
27
|
+
import { injectClaudeMd, injectClaudeMdGlobal, hasAgentMarker } from './activation/inject-claude-md.js';
|
|
28
28
|
|
|
29
29
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
30
30
|
|
|
31
31
|
async function main(): Promise<void> {
|
|
32
|
-
//
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// Load and seed intelligence data
|
|
38
|
-
const dataDir = join(__dirname, 'intelligence', 'data');
|
|
39
|
-
const entries = loadIntelligenceData(dataDir);
|
|
40
|
-
if (entries.length > 0) {
|
|
41
|
-
const seeded = vault.seed(entries);
|
|
42
|
-
console.error(\`[\${PERSONA.name.toLowerCase()}] Seeded vault with \${seeded} intelligence entries\`);
|
|
43
|
-
} else {
|
|
44
|
-
console.error(\`[\${PERSONA.name.toLowerCase()}] Vault is empty — ready for knowledge capture\`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Initialize planner at ~/.${config.id}/plans.json
|
|
48
|
-
const plansPath = join(homedir(), '.${config.id}', 'plans.json');
|
|
49
|
-
const planner = new Planner(plansPath);
|
|
50
|
-
console.error(\`[\${PERSONA.name.toLowerCase()}] Planner: \${plansPath}\`);
|
|
51
|
-
|
|
52
|
-
// Initialize Cognee client (optional — vector search + knowledge graph)
|
|
53
|
-
// Auto-registers a service account on first use. Override with env vars.
|
|
54
|
-
const cognee = new CogneeClient({
|
|
55
|
-
baseUrl: process.env.COGNEE_URL ?? 'http://localhost:8000',
|
|
56
|
-
dataset: '${config.id}-vault',
|
|
57
|
-
...(process.env.COGNEE_API_TOKEN ? { apiToken: process.env.COGNEE_API_TOKEN } : {}),
|
|
58
|
-
...(process.env.COGNEE_EMAIL ? { serviceEmail: process.env.COGNEE_EMAIL } : {}),
|
|
59
|
-
...(process.env.COGNEE_PASSWORD ? { servicePassword: process.env.COGNEE_PASSWORD } : {}),
|
|
32
|
+
// ─── Runtime — vault, brain, planner, curator, LLM, key pools ───
|
|
33
|
+
const runtime = createAgentRuntime({
|
|
34
|
+
agentId: '${config.id}',
|
|
35
|
+
dataDir: join(__dirname, 'intelligence', 'data'),
|
|
60
36
|
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
37
|
+
|
|
38
|
+
const tag = PERSONA.name.toLowerCase();
|
|
39
|
+
const stats = runtime.vault.stats();
|
|
40
|
+
console.error(\`[\${tag}] Vault: \${stats.totalEntries} entries, Brain: \${runtime.brain.getVocabularySize()} terms\`);
|
|
41
|
+
|
|
42
|
+
const llmAvail = runtime.llmClient.isAvailable();
|
|
43
|
+
console.error(\`[\${tag}] LLM: OpenAI \${llmAvail.openai ? 'available' : 'not configured'}, Anthropic \${llmAvail.anthropic ? 'available' : 'not configured'}\`);
|
|
44
|
+
|
|
45
|
+
// ─── Agent-specific ops (reference persona + activation) ────────
|
|
46
|
+
const agentOps: OpDefinition[] = [
|
|
47
|
+
{
|
|
48
|
+
name: 'health',
|
|
49
|
+
description: 'Health check — vault status and agent info.',
|
|
50
|
+
auth: 'read',
|
|
51
|
+
handler: async () => {
|
|
52
|
+
const s = runtime.vault.stats();
|
|
53
|
+
return {
|
|
54
|
+
status: 'ok',
|
|
55
|
+
agent: { name: PERSONA.name, role: PERSONA.role },
|
|
56
|
+
vault: { entries: s.totalEntries, domains: Object.keys(s.byDomain) },
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'identity',
|
|
62
|
+
description: 'Get agent identity — name, role, principles.',
|
|
63
|
+
auth: 'read',
|
|
64
|
+
handler: async () => PERSONA,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'activate',
|
|
68
|
+
description: 'Activate agent persona — returns full context for Claude to adopt. Say "Hello, ${config.name}!" to trigger.',
|
|
69
|
+
auth: 'read',
|
|
70
|
+
schema: z.object({
|
|
71
|
+
projectPath: z.string().optional().default('.'),
|
|
72
|
+
deactivate: z.boolean().optional(),
|
|
73
|
+
}),
|
|
74
|
+
handler: async (params) => {
|
|
75
|
+
if (params.deactivate) {
|
|
76
|
+
return deactivateAgent();
|
|
77
|
+
}
|
|
78
|
+
return activateAgent(runtime.vault, (params.projectPath as string) ?? '.', runtime.planner);
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'inject_claude_md',
|
|
83
|
+
description: 'Inject agent sections into CLAUDE.md — project-level or global (~/.claude/CLAUDE.md). Idempotent.',
|
|
84
|
+
auth: 'write',
|
|
85
|
+
schema: z.object({
|
|
86
|
+
projectPath: z.string().optional().default('.'),
|
|
87
|
+
global: z.boolean().optional().describe('If true, inject into ~/.claude/CLAUDE.md instead of project-level'),
|
|
88
|
+
}),
|
|
89
|
+
handler: async (params) => {
|
|
90
|
+
if (params.global) {
|
|
91
|
+
return injectClaudeMdGlobal();
|
|
92
|
+
}
|
|
93
|
+
return injectClaudeMd((params.projectPath as string) ?? '.');
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'setup',
|
|
98
|
+
description: 'Check setup status — CLAUDE.md configured? Vault has entries? What to do next?',
|
|
99
|
+
auth: 'read',
|
|
100
|
+
schema: z.object({
|
|
101
|
+
projectPath: z.string().optional().default('.'),
|
|
102
|
+
}),
|
|
103
|
+
handler: async (params) => {
|
|
104
|
+
const { existsSync } = await import('node:fs');
|
|
105
|
+
const { join: joinPath } = await import('node:path');
|
|
106
|
+
const { homedir } = await import('node:os');
|
|
107
|
+
const projectPath = (params.projectPath as string) ?? '.';
|
|
108
|
+
|
|
109
|
+
const projectClaudeMd = joinPath(projectPath, 'CLAUDE.md');
|
|
110
|
+
const globalClaudeMd = joinPath(homedir(), '.claude', 'CLAUDE.md');
|
|
111
|
+
|
|
112
|
+
const projectExists = existsSync(projectClaudeMd);
|
|
113
|
+
const projectHasAgent = hasAgentMarker(projectClaudeMd);
|
|
114
|
+
const globalExists = existsSync(globalClaudeMd);
|
|
115
|
+
const globalHasAgent = hasAgentMarker(globalClaudeMd);
|
|
116
|
+
|
|
117
|
+
const s = runtime.vault.stats();
|
|
118
|
+
|
|
119
|
+
const recommendations: string[] = [];
|
|
120
|
+
if (!globalHasAgent && !projectHasAgent) {
|
|
121
|
+
recommendations.push('No CLAUDE.md configured — run inject_claude_md with global: true for all projects, or without for this project');
|
|
122
|
+
} else if (!globalHasAgent) {
|
|
123
|
+
recommendations.push('Global ~/.claude/CLAUDE.md not configured — run inject_claude_md with global: true to enable in all projects');
|
|
124
|
+
}
|
|
125
|
+
if (s.totalEntries === 0) {
|
|
126
|
+
recommendations.push('Vault is empty — add intelligence data or capture knowledge via domain facades');
|
|
127
|
+
}
|
|
128
|
+
if (recommendations.length === 0) {
|
|
129
|
+
recommendations.push('${config.name} is fully set up and ready!');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
agent: { name: PERSONA.name, role: PERSONA.role },
|
|
134
|
+
claude_md: {
|
|
135
|
+
project: { exists: projectExists, has_agent_section: projectHasAgent },
|
|
136
|
+
global: { exists: globalExists, has_agent_section: globalHasAgent },
|
|
137
|
+
},
|
|
138
|
+
vault: { entries: s.totalEntries, domains: Object.keys(s.byDomain) },
|
|
139
|
+
recommendations,
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
// ─── Assemble facades ──────────────────────────────────────────
|
|
146
|
+
const coreOps = createCoreOps(runtime);
|
|
147
|
+
const coreFacade = {
|
|
148
|
+
name: '${config.id}_core',
|
|
149
|
+
description: 'Core operations — vault stats, cross-domain search, health check, identity, and activation system.',
|
|
150
|
+
ops: [...coreOps, ...agentOps],
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const domainFacades = createDomainFacades(runtime, '${config.id}', ${domainsLiteral});
|
|
154
|
+
|
|
155
|
+
// ─── MCP server ────────────────────────────────────────────────
|
|
84
156
|
const server = new McpServer({
|
|
85
157
|
name: '${config.id}-mcp',
|
|
86
158
|
version: '1.0.0',
|
|
87
159
|
});
|
|
88
160
|
|
|
89
|
-
// Register persona prompt
|
|
90
161
|
server.prompt('persona', 'Get agent persona and principles', async () => ({
|
|
91
162
|
messages: [{ role: 'assistant' as const, content: { type: 'text' as const, text: getPersonaPrompt() } }],
|
|
92
163
|
}));
|
|
93
164
|
|
|
94
|
-
|
|
95
|
-
const facades = [
|
|
96
|
-
${facadeCreations}
|
|
97
|
-
createCoreFacade(vault, planner, brain, cognee.isAvailable ? cognee : undefined, llmClient, openaiKeyPool, anthropicKeyPool),
|
|
98
|
-
];
|
|
99
|
-
|
|
165
|
+
const facades = [coreFacade, ...domainFacades];
|
|
100
166
|
registerAllFacades(server, facades);
|
|
101
167
|
|
|
102
|
-
|
|
103
|
-
console.error(\`[\${
|
|
104
|
-
console.error(\`[\${PERSONA.name.toLowerCase()}] Vault: \${stats.totalEntries} entries across \${Object.keys(stats.byDomain).length} domains\`);
|
|
105
|
-
console.error(\`[\${PERSONA.name.toLowerCase()}] Registered \${facades.length} facades with \${facades.reduce((sum, f) => sum + f.ops.length, 0)} operations\`);
|
|
168
|
+
console.error(\`[\${tag}] \${PERSONA.name} — \${PERSONA.role}\`);
|
|
169
|
+
console.error(\`[\${tag}] Registered \${facades.length} facades with \${facades.reduce((sum, f) => sum + f.ops.length, 0)} operations\`);
|
|
106
170
|
|
|
107
|
-
//
|
|
171
|
+
// ─── Transport + shutdown ──────────────────────────────────────
|
|
108
172
|
const transport = new StdioServerTransport();
|
|
109
173
|
await server.connect(transport);
|
|
110
|
-
console.error(\`[\${
|
|
111
|
-
console.error(\`[\${
|
|
174
|
+
console.error(\`[\${tag}] Connected via stdio transport\`);
|
|
175
|
+
console.error(\`[\${tag}] Say "Hello, \${PERSONA.name}!" to activate\`);
|
|
112
176
|
|
|
113
|
-
// Graceful shutdown
|
|
114
177
|
const shutdown = async (): Promise<void> => {
|
|
115
|
-
console.error(\`[\${
|
|
116
|
-
|
|
178
|
+
console.error(\`[\${tag}] Shutting down...\`);
|
|
179
|
+
runtime.close();
|
|
117
180
|
process.exit(0);
|
|
118
181
|
};
|
|
119
182
|
process.on('SIGTERM', shutdown);
|
|
@@ -126,10 +189,4 @@ main().catch((err) => {
|
|
|
126
189
|
});
|
|
127
190
|
`;
|
|
128
191
|
}
|
|
129
|
-
function pascalCase(s) {
|
|
130
|
-
return s
|
|
131
|
-
.split(/[-_\s]+/)
|
|
132
|
-
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
133
|
-
.join('');
|
|
134
|
-
}
|
|
135
192
|
//# sourceMappingURL=entry-point.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry-point.js","sourceRoot":"","sources":["../../src/templates/entry-point.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"entry-point.js","sourceRoot":"","sources":["../../src/templates/entry-point.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAmB;IACpD,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAEtD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;gBAwBO,MAAM,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qGAkC4E,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCA6D9E,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;;aAmBhC,MAAM,CAAC,EAAE;;;;;wDAKkC,MAAM,CAAC,EAAE,MAAM,cAAc;;;;aAIxE,MAAM,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBA8BF,MAAM,CAAC,EAAE;;;CAG5B,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function generateFacadeFactory(): string;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export function generateFacadeFactory() {
|
|
2
|
+
return `import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import type { FacadeConfig, FacadeResponse } from './types.js';
|
|
5
|
+
|
|
6
|
+
export function registerFacade(server: McpServer, facade: FacadeConfig): void {
|
|
7
|
+
const opNames = facade.ops.map((o) => o.name);
|
|
8
|
+
|
|
9
|
+
server.tool(
|
|
10
|
+
facade.name,
|
|
11
|
+
\`\${facade.description}\\n\\nOperations: \${opNames.join(', ')}\`,
|
|
12
|
+
{
|
|
13
|
+
op: z.string().describe(\`Operation: \${opNames.join(' | ')}\`),
|
|
14
|
+
params: z.record(z.unknown()).optional().default({}).describe('Operation parameters'),
|
|
15
|
+
},
|
|
16
|
+
async ({ op, params }): Promise<{ content: Array<{ type: 'text'; text: string }> }> => {
|
|
17
|
+
const response = await dispatchOp(facade, op, params);
|
|
18
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }] };
|
|
19
|
+
},
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function dispatchOp(
|
|
24
|
+
facade: FacadeConfig,
|
|
25
|
+
opName: string,
|
|
26
|
+
params: Record<string, unknown>,
|
|
27
|
+
): Promise<FacadeResponse> {
|
|
28
|
+
const op = facade.ops.find((o) => o.name === opName);
|
|
29
|
+
if (!op) {
|
|
30
|
+
return {
|
|
31
|
+
success: false,
|
|
32
|
+
error: \`Unknown operation "\${opName}" on \${facade.name}. Available: \${facade.ops.map((o) => o.name).join(', ')}\`,
|
|
33
|
+
op: opName,
|
|
34
|
+
facade: facade.name,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
let validatedParams = params;
|
|
40
|
+
if (op.schema) {
|
|
41
|
+
const result = op.schema.safeParse(params);
|
|
42
|
+
if (!result.success) {
|
|
43
|
+
return { success: false, error: \`Invalid params for \${opName}: \${result.error.message}\`, op: opName, facade: facade.name };
|
|
44
|
+
}
|
|
45
|
+
validatedParams = result.data as Record<string, unknown>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const data = await op.handler(validatedParams);
|
|
49
|
+
return { success: true, data, op: opName, facade: facade.name };
|
|
50
|
+
} catch (err) {
|
|
51
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
52
|
+
return { success: false, error: message, op: opName, facade: facade.name };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function registerAllFacades(server: McpServer, facades: FacadeConfig[]): void {
|
|
57
|
+
for (const facade of facades) {
|
|
58
|
+
registerFacade(server, facade);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=facade-factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facade-factory.js","sourceRoot":"","sources":["../../src/templates/facade-factory.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,qBAAqB;IACnC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2DR,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function generateFacadeTypes(): string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export function generateFacadeTypes() {
|
|
2
|
+
return `import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
/** Handler function for a single facade operation */
|
|
5
|
+
export type OpHandler = (params: Record<string, unknown>) => Promise<unknown>;
|
|
6
|
+
|
|
7
|
+
/** Auth level required for an operation */
|
|
8
|
+
export type AuthLevel = 'read' | 'write' | 'admin';
|
|
9
|
+
|
|
10
|
+
/** Operation definition within a facade */
|
|
11
|
+
export interface OpDefinition {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
auth: AuthLevel;
|
|
15
|
+
handler: OpHandler;
|
|
16
|
+
schema?: z.ZodType;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Facade configuration — one MCP tool */
|
|
20
|
+
export interface FacadeConfig {
|
|
21
|
+
/** MCP tool name */
|
|
22
|
+
name: string;
|
|
23
|
+
/** Human-readable description */
|
|
24
|
+
description: string;
|
|
25
|
+
/** Domain operations */
|
|
26
|
+
ops: OpDefinition[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Standard facade response envelope */
|
|
30
|
+
export interface FacadeResponse {
|
|
31
|
+
success: boolean;
|
|
32
|
+
data?: unknown;
|
|
33
|
+
error?: string;
|
|
34
|
+
op?: string;
|
|
35
|
+
facade?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const facadeInputSchema = z.object({
|
|
39
|
+
op: z.string().describe('Operation name'),
|
|
40
|
+
params: z.record(z.unknown()).optional().default({}),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export type FacadeInput = z.infer<typeof facadeInputSchema>;
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=facade-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facade-types.js","sourceRoot":"","sources":["../../src/templates/facade-types.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,mBAAmB;IACjC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CR,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function generateIntelligenceLoader(): string;
|