@soleri/forge 0.0.1 → 4.0.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.
Files changed (128) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/README.md +199 -0
  3. package/dist/facades/forge.facade.d.ts +9 -0
  4. package/dist/facades/forge.facade.js +134 -0
  5. package/dist/facades/forge.facade.js.map +1 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +81 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/knowledge-installer.d.ts +31 -0
  10. package/dist/knowledge-installer.js +437 -0
  11. package/dist/knowledge-installer.js.map +1 -0
  12. package/dist/scaffolder.d.ts +13 -0
  13. package/dist/scaffolder.js +330 -0
  14. package/dist/scaffolder.js.map +1 -0
  15. package/dist/templates/activate.d.ts +9 -0
  16. package/dist/templates/activate.js +139 -0
  17. package/dist/templates/activate.js.map +1 -0
  18. package/dist/templates/brain.d.ts +6 -0
  19. package/dist/templates/brain.js +478 -0
  20. package/dist/templates/brain.js.map +1 -0
  21. package/dist/templates/claude-md-template.d.ts +11 -0
  22. package/dist/templates/claude-md-template.js +73 -0
  23. package/dist/templates/claude-md-template.js.map +1 -0
  24. package/dist/templates/core-facade.d.ts +6 -0
  25. package/dist/templates/core-facade.js +456 -0
  26. package/dist/templates/core-facade.js.map +1 -0
  27. package/dist/templates/domain-facade.d.ts +7 -0
  28. package/dist/templates/domain-facade.js +119 -0
  29. package/dist/templates/domain-facade.js.map +1 -0
  30. package/dist/templates/entry-point.d.ts +5 -0
  31. package/dist/templates/entry-point.js +116 -0
  32. package/dist/templates/entry-point.js.map +1 -0
  33. package/dist/templates/facade-factory.d.ts +1 -0
  34. package/dist/templates/facade-factory.js +63 -0
  35. package/dist/templates/facade-factory.js.map +1 -0
  36. package/dist/templates/facade-types.d.ts +1 -0
  37. package/dist/templates/facade-types.js +46 -0
  38. package/dist/templates/facade-types.js.map +1 -0
  39. package/dist/templates/inject-claude-md.d.ts +11 -0
  40. package/dist/templates/inject-claude-md.js +92 -0
  41. package/dist/templates/inject-claude-md.js.map +1 -0
  42. package/dist/templates/intelligence-loader.d.ts +1 -0
  43. package/dist/templates/intelligence-loader.js +43 -0
  44. package/dist/templates/intelligence-loader.js.map +1 -0
  45. package/dist/templates/intelligence-types.d.ts +1 -0
  46. package/dist/templates/intelligence-types.js +24 -0
  47. package/dist/templates/intelligence-types.js.map +1 -0
  48. package/dist/templates/llm-client.d.ts +7 -0
  49. package/dist/templates/llm-client.js +300 -0
  50. package/dist/templates/llm-client.js.map +1 -0
  51. package/dist/templates/llm-key-pool.d.ts +7 -0
  52. package/dist/templates/llm-key-pool.js +211 -0
  53. package/dist/templates/llm-key-pool.js.map +1 -0
  54. package/dist/templates/llm-types.d.ts +5 -0
  55. package/dist/templates/llm-types.js +161 -0
  56. package/dist/templates/llm-types.js.map +1 -0
  57. package/dist/templates/llm-utils.d.ts +5 -0
  58. package/dist/templates/llm-utils.js +260 -0
  59. package/dist/templates/llm-utils.js.map +1 -0
  60. package/dist/templates/package-json.d.ts +2 -0
  61. package/dist/templates/package-json.js +37 -0
  62. package/dist/templates/package-json.js.map +1 -0
  63. package/dist/templates/persona.d.ts +2 -0
  64. package/dist/templates/persona.js +42 -0
  65. package/dist/templates/persona.js.map +1 -0
  66. package/dist/templates/planner.d.ts +5 -0
  67. package/dist/templates/planner.js +150 -0
  68. package/dist/templates/planner.js.map +1 -0
  69. package/dist/templates/readme.d.ts +5 -0
  70. package/dist/templates/readme.js +316 -0
  71. package/dist/templates/readme.js.map +1 -0
  72. package/dist/templates/setup-script.d.ts +6 -0
  73. package/dist/templates/setup-script.js +112 -0
  74. package/dist/templates/setup-script.js.map +1 -0
  75. package/dist/templates/test-brain.d.ts +6 -0
  76. package/dist/templates/test-brain.js +474 -0
  77. package/dist/templates/test-brain.js.map +1 -0
  78. package/dist/templates/test-facades.d.ts +6 -0
  79. package/dist/templates/test-facades.js +649 -0
  80. package/dist/templates/test-facades.js.map +1 -0
  81. package/dist/templates/test-llm.d.ts +7 -0
  82. package/dist/templates/test-llm.js +574 -0
  83. package/dist/templates/test-llm.js.map +1 -0
  84. package/dist/templates/test-loader.d.ts +5 -0
  85. package/dist/templates/test-loader.js +146 -0
  86. package/dist/templates/test-loader.js.map +1 -0
  87. package/dist/templates/test-planner.d.ts +5 -0
  88. package/dist/templates/test-planner.js +271 -0
  89. package/dist/templates/test-planner.js.map +1 -0
  90. package/dist/templates/test-vault.d.ts +5 -0
  91. package/dist/templates/test-vault.js +380 -0
  92. package/dist/templates/test-vault.js.map +1 -0
  93. package/dist/templates/tsconfig.d.ts +1 -0
  94. package/dist/templates/tsconfig.js +25 -0
  95. package/dist/templates/tsconfig.js.map +1 -0
  96. package/dist/templates/vault.d.ts +5 -0
  97. package/dist/templates/vault.js +263 -0
  98. package/dist/templates/vault.js.map +1 -0
  99. package/dist/templates/vitest-config.d.ts +1 -0
  100. package/dist/templates/vitest-config.js +27 -0
  101. package/dist/templates/vitest-config.js.map +1 -0
  102. package/dist/types.d.ts +89 -0
  103. package/dist/types.js +21 -0
  104. package/dist/types.js.map +1 -0
  105. package/package.json +42 -4
  106. package/src/__tests__/knowledge-installer.test.ts +805 -0
  107. package/src/__tests__/scaffolder.test.ts +323 -0
  108. package/src/facades/forge.facade.ts +150 -0
  109. package/src/index.ts +101 -0
  110. package/src/knowledge-installer.ts +532 -0
  111. package/src/scaffolder.ts +386 -0
  112. package/src/templates/activate.ts +145 -0
  113. package/src/templates/claude-md-template.ts +137 -0
  114. package/src/templates/core-facade.ts +457 -0
  115. package/src/templates/domain-facade.ts +121 -0
  116. package/src/templates/entry-point.ts +120 -0
  117. package/src/templates/inject-claude-md.ts +94 -0
  118. package/src/templates/llm-client.ts +301 -0
  119. package/src/templates/package-json.ts +39 -0
  120. package/src/templates/persona.ts +45 -0
  121. package/src/templates/readme.ts +319 -0
  122. package/src/templates/setup-script.ts +113 -0
  123. package/src/templates/test-facades.ts +656 -0
  124. package/src/templates/tsconfig.ts +25 -0
  125. package/src/templates/vitest-config.ts +26 -0
  126. package/src/types.ts +68 -0
  127. package/tsconfig.json +21 -0
  128. package/vitest.config.ts +15 -0
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Generate the main index.ts entry point for the agent.
3
+ */
4
+ export function generateEntryPoint(config) {
5
+ const facadeImports = config.domains
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');
15
+ return `#!/usr/bin/env node
16
+
17
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
18
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
19
+ import { dirname, join } from 'node:path';
20
+ import { fileURLToPath } from 'node:url';
21
+ import { homedir } from 'node:os';
22
+
23
+ import { Vault, Brain, Planner, KeyPool, loadKeyPoolConfig, loadIntelligenceData, registerAllFacades } from '@soleri/core';
24
+ ${facadeImports}
25
+ import { createCoreFacade } from './facades/core.facade.js';
26
+ import { LLMClient } from './llm/llm-client.js';
27
+ import { PERSONA, getPersonaPrompt } from './identity/persona.js';
28
+
29
+ const __dirname = dirname(fileURLToPath(import.meta.url));
30
+
31
+ async function main(): Promise<void> {
32
+ // Initialize persistent vault at ~/.${config.id}/vault.db
33
+ const vaultPath = join(homedir(), '.${config.id}', 'vault.db');
34
+ const vault = new Vault(vaultPath);
35
+ console.error(\`[\${PERSONA.name.toLowerCase()}] Vault: \${vaultPath}\`);
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 brain — intelligence layer for ranked search, auto-tagging, dedup
53
+ const brain = new Brain(vault);
54
+ console.error(\`[\${PERSONA.name.toLowerCase()}] Brain: vocabulary \${brain.getVocabularySize()} terms\`);
55
+
56
+ // Initialize LLM client (optional — works without API keys)
57
+ const keyPoolFiles = loadKeyPoolConfig('${config.id}');
58
+ const openaiKeyPool = new KeyPool(keyPoolFiles.openai);
59
+ const anthropicKeyPool = new KeyPool(keyPoolFiles.anthropic);
60
+ const llmClient = new LLMClient(openaiKeyPool, anthropicKeyPool);
61
+ const llmAvail = llmClient.isAvailable();
62
+ console.error(\`[\${PERSONA.name.toLowerCase()}] LLM: OpenAI \${llmAvail.openai ? 'available' : 'not configured'}, Anthropic \${llmAvail.anthropic ? 'available' : 'not configured'}\`);
63
+
64
+ // Create MCP server
65
+ const server = new McpServer({
66
+ name: '${config.id}-mcp',
67
+ version: '1.0.0',
68
+ });
69
+
70
+ // Register persona prompt
71
+ server.prompt('persona', 'Get agent persona and principles', async () => ({
72
+ messages: [{ role: 'assistant' as const, content: { type: 'text' as const, text: getPersonaPrompt() } }],
73
+ }));
74
+
75
+ // Create and register facades
76
+ const facades = [
77
+ ${facadeCreations}
78
+ createCoreFacade(vault, planner, brain, llmClient, openaiKeyPool, anthropicKeyPool),
79
+ ];
80
+
81
+ registerAllFacades(server, facades);
82
+
83
+ const stats = vault.stats();
84
+ console.error(\`[\${PERSONA.name.toLowerCase()}] \${PERSONA.name} — \${PERSONA.role}\`);
85
+ console.error(\`[\${PERSONA.name.toLowerCase()}] Vault: \${stats.totalEntries} entries across \${Object.keys(stats.byDomain).length} domains\`);
86
+ console.error(\`[\${PERSONA.name.toLowerCase()}] Registered \${facades.length} facades with \${facades.reduce((sum, f) => sum + f.ops.length, 0)} operations\`);
87
+
88
+ // Stdio transport
89
+ const transport = new StdioServerTransport();
90
+ await server.connect(transport);
91
+ console.error(\`[\${PERSONA.name.toLowerCase()}] Connected via stdio transport\`);
92
+ console.error(\`[\${PERSONA.name.toLowerCase()}] Say "Hello, \${PERSONA.name}!" to activate\`);
93
+
94
+ // Graceful shutdown
95
+ const shutdown = async (): Promise<void> => {
96
+ console.error(\`[\${PERSONA.name.toLowerCase()}] Shutting down...\`);
97
+ vault.close();
98
+ process.exit(0);
99
+ };
100
+ process.on('SIGTERM', shutdown);
101
+ process.on('SIGINT', shutdown);
102
+ }
103
+
104
+ main().catch((err) => {
105
+ console.error('[${config.id}] Fatal error:', err);
106
+ process.exit(1);
107
+ });
108
+ `;
109
+ }
110
+ function pascalCase(s) {
111
+ return s
112
+ .split(/[-_\s]+/)
113
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
114
+ .join('');
115
+ }
116
+ //# sourceMappingURL=entry-point.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry-point.js","sourceRoot":"","sources":["../../src/templates/entry-point.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAmB;IACpD,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO;SACjC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,EAAE,GAAG,SAAS,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC;QAC9B,OAAO,YAAY,EAAE,sBAAsB,IAAI,IAAI,CAAC;IACtD,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO;SACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,UAAU,CAAC,CAAC,CAAC,uBAAuB,CAAC;SAC7D,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;;;;;;;;;EASP,aAAa;;;;;;;;yCAQ0B,MAAM,CAAC,EAAE;wCACV,MAAM,CAAC,EAAE;;;;;;;;;;;;;;gCAcjB,MAAM,CAAC,EAAE;wCACD,MAAM,CAAC,EAAE;;;;;;;;;4CASL,MAAM,CAAC,EAAE;;;;;;;;;aASxC,MAAM,CAAC,EAAE;;;;;;;;;;;EAWpB,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBA4BG,MAAM,CAAC,EAAE;;;CAG5B,CAAC;AACF,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC;SACL,KAAK,CAAC,SAAS,CAAC;SAChB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAClD,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,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,11 @@
1
+ import type { AgentConfig } from '../types.js';
2
+ /**
3
+ * Generates src/activation/inject-claude-md.ts for a new agent.
4
+ * Provides idempotent CLAUDE.md injection — creates, appends, or updates
5
+ * the agent's section between HTML comment markers.
6
+ * Supports both project-level and global (~/.claude/CLAUDE.md) injection.
7
+ *
8
+ * Uses array-joined pattern because the generated code contains
9
+ * template literals (backticks) for path operations and string interpolation.
10
+ */
11
+ export declare function generateInjectClaudeMd(config: AgentConfig): string;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Generates src/activation/inject-claude-md.ts for a new agent.
3
+ * Provides idempotent CLAUDE.md injection — creates, appends, or updates
4
+ * the agent's section between HTML comment markers.
5
+ * Supports both project-level and global (~/.claude/CLAUDE.md) injection.
6
+ *
7
+ * Uses array-joined pattern because the generated code contains
8
+ * template literals (backticks) for path operations and string interpolation.
9
+ */
10
+ export function generateInjectClaudeMd(config) {
11
+ const marker = `${config.id}:mode`;
12
+ return [
13
+ "import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';",
14
+ "import { homedir } from 'node:os';",
15
+ "import { join } from 'node:path';",
16
+ "import { getClaudeMdContent, getClaudeMdMarker } from './claude-md-content.js';",
17
+ '',
18
+ 'export interface InjectResult {',
19
+ ' injected: boolean;',
20
+ ' path: string;',
21
+ " action: 'created' | 'updated' | 'appended';",
22
+ '}',
23
+ '',
24
+ '/**',
25
+ ' * Inject into a specific CLAUDE.md file path.',
26
+ ' * Shared logic for both project-level and global injection.',
27
+ ' */',
28
+ 'function injectIntoFile(filePath: string): InjectResult {',
29
+ ' const content = getClaudeMdContent();',
30
+ ' const marker = getClaudeMdMarker();',
31
+ ` const startMarker = '<!-- ' + marker + ' -->';`,
32
+ ` const endMarker = '<!-- /' + marker + ' -->';`,
33
+ '',
34
+ ' if (!existsSync(filePath)) {',
35
+ " writeFileSync(filePath, content + '\\n', 'utf-8');",
36
+ " return { injected: true, path: filePath, action: 'created' };",
37
+ ' }',
38
+ '',
39
+ " const existing = readFileSync(filePath, 'utf-8');",
40
+ '',
41
+ ' if (existing.includes(startMarker)) {',
42
+ ' const startIdx = existing.indexOf(startMarker);',
43
+ ' const endIdx = existing.indexOf(endMarker);',
44
+ ' if (endIdx === -1) {',
45
+ " const updated = existing.slice(0, startIdx) + content + '\\n' + existing.slice(startIdx + startMarker.length);",
46
+ " writeFileSync(filePath, updated, 'utf-8');",
47
+ " return { injected: true, path: filePath, action: 'updated' };",
48
+ ' }',
49
+ ' const updated = existing.slice(0, startIdx) + content + existing.slice(endIdx + endMarker.length);',
50
+ " writeFileSync(filePath, updated, 'utf-8');",
51
+ " return { injected: true, path: filePath, action: 'updated' };",
52
+ ' }',
53
+ '',
54
+ " const separator = existing.endsWith('\\n') ? '\\n' : '\\n\\n';",
55
+ " writeFileSync(filePath, existing + separator + content + '\\n', 'utf-8');",
56
+ " return { injected: true, path: filePath, action: 'appended' };",
57
+ '}',
58
+ '',
59
+ '/**',
60
+ ' * Inject agent CLAUDE.md section into a project.',
61
+ ' * - If no CLAUDE.md exists: creates one',
62
+ ' * - If markers found: replaces content between them (update)',
63
+ ' * - If no markers: appends section to end',
64
+ ' */',
65
+ 'export function injectClaudeMd(projectPath: string): InjectResult {',
66
+ " return injectIntoFile(join(projectPath, 'CLAUDE.md'));",
67
+ '}',
68
+ '',
69
+ '/**',
70
+ ' * Inject agent CLAUDE.md section into the global ~/.claude/CLAUDE.md.',
71
+ " * Creates ~/.claude/ directory if it doesn't exist.",
72
+ ' * This makes the activation phrase work in any project.',
73
+ ' */',
74
+ 'export function injectClaudeMdGlobal(): InjectResult {',
75
+ " const claudeDir = join(homedir(), '.claude');",
76
+ ' if (!existsSync(claudeDir)) {',
77
+ ' mkdirSync(claudeDir, { recursive: true });',
78
+ ' }',
79
+ " return injectIntoFile(join(claudeDir, 'CLAUDE.md'));",
80
+ '}',
81
+ '',
82
+ '/**',
83
+ ' * Check if the agent marker exists in a CLAUDE.md file.',
84
+ ' */',
85
+ 'export function hasAgentMarker(filePath: string): boolean {',
86
+ ' if (!existsSync(filePath)) return false;',
87
+ " const content = readFileSync(filePath, 'utf-8');",
88
+ ` return content.includes('<!-- ${marker} -->');`,
89
+ '}',
90
+ ].join('\n');
91
+ }
92
+ //# sourceMappingURL=inject-claude-md.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inject-claude-md.js","sourceRoot":"","sources":["../../src/templates/inject-claude-md.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAmB;IACxD,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,OAAO,CAAC;IAEnC,OAAO;QACL,+EAA+E;QAC/E,oCAAoC;QACpC,mCAAmC;QACnC,iFAAiF;QACjF,EAAE;QACF,iCAAiC;QACjC,sBAAsB;QACtB,iBAAiB;QACjB,+CAA+C;QAC/C,GAAG;QACH,EAAE;QACF,KAAK;QACL,gDAAgD;QAChD,8DAA8D;QAC9D,KAAK;QACL,2DAA2D;QAC3D,yCAAyC;QACzC,uCAAuC;QACvC,kDAAkD;QAClD,iDAAiD;QACjD,EAAE;QACF,gCAAgC;QAChC,wDAAwD;QACxD,mEAAmE;QACnE,KAAK;QACL,EAAE;QACF,qDAAqD;QACrD,EAAE;QACF,yCAAyC;QACzC,qDAAqD;QACrD,iDAAiD;QACjD,0BAA0B;QAC1B,sHAAsH;QACtH,kDAAkD;QAClD,qEAAqE;QACrE,OAAO;QACP,wGAAwG;QACxG,gDAAgD;QAChD,mEAAmE;QACnE,KAAK;QACL,EAAE;QACF,kEAAkE;QAClE,6EAA6E;QAC7E,kEAAkE;QAClE,GAAG;QACH,EAAE;QACF,KAAK;QACL,mDAAmD;QACnD,0CAA0C;QAC1C,+DAA+D;QAC/D,4CAA4C;QAC5C,KAAK;QACL,qEAAqE;QACrE,0DAA0D;QAC1D,GAAG;QACH,EAAE;QACF,KAAK;QACL,wEAAwE;QACxE,sDAAsD;QACtD,0DAA0D;QAC1D,KAAK;QACL,wDAAwD;QACxD,iDAAiD;QACjD,iCAAiC;QACjC,gDAAgD;QAChD,KAAK;QACL,wDAAwD;QACxD,GAAG;QACH,EAAE;QACF,KAAK;QACL,0DAA0D;QAC1D,KAAK;QACL,6DAA6D;QAC7D,4CAA4C;QAC5C,oDAAoD;QACpD,mCAAmC,MAAM,SAAS;QAClD,GAAG;KACJ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function generateIntelligenceLoader(): string;
@@ -0,0 +1,43 @@
1
+ export function generateIntelligenceLoader() {
2
+ return `import { readFileSync, readdirSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import type { IntelligenceBundle, IntelligenceEntry } from './types.js';
5
+
6
+ export function loadIntelligenceData(dataDir: string): IntelligenceEntry[] {
7
+ const entries: IntelligenceEntry[] = [];
8
+ let files: string[];
9
+ try {
10
+ files = readdirSync(dataDir).filter((f) => f.endsWith('.json'));
11
+ } catch {
12
+ console.warn('Intelligence data directory not found: ' + dataDir);
13
+ return entries;
14
+ }
15
+
16
+ for (const file of files) {
17
+ try {
18
+ const raw = readFileSync(join(dataDir, file), 'utf-8');
19
+ const bundle = JSON.parse(raw) as IntelligenceBundle;
20
+ if (!bundle.entries || !Array.isArray(bundle.entries)) continue;
21
+ for (const entry of bundle.entries) {
22
+ if (validateEntry(entry)) entries.push(entry);
23
+ }
24
+ } catch (err) {
25
+ console.warn('Failed to load ' + file + ': ' + (err instanceof Error ? err.message : err));
26
+ }
27
+ }
28
+ return entries;
29
+ }
30
+
31
+ function validateEntry(entry: IntelligenceEntry): boolean {
32
+ return (
33
+ typeof entry.id === 'string' && entry.id.length > 0 &&
34
+ ['pattern', 'anti-pattern', 'rule'].includes(entry.type) &&
35
+ typeof entry.title === 'string' && entry.title.length > 0 &&
36
+ typeof entry.description === 'string' && entry.description.length > 0 &&
37
+ ['critical', 'warning', 'suggestion'].includes(entry.severity) &&
38
+ Array.isArray(entry.tags)
39
+ );
40
+ }
41
+ `;
42
+ }
43
+ //# sourceMappingURL=intelligence-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intelligence-loader.js","sourceRoot":"","sources":["../../src/templates/intelligence-loader.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,0BAA0B;IACxC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCR,CAAC;AACF,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function generateIntelligenceTypes(): string;
@@ -0,0 +1,24 @@
1
+ export function generateIntelligenceTypes() {
2
+ return `export interface IntelligenceEntry {
3
+ id: string;
4
+ type: 'pattern' | 'anti-pattern' | 'rule';
5
+ domain: string;
6
+ title: string;
7
+ severity: 'critical' | 'warning' | 'suggestion';
8
+ description: string;
9
+ context?: string;
10
+ example?: string;
11
+ counterExample?: string;
12
+ why?: string;
13
+ tags: string[];
14
+ appliesTo?: string[];
15
+ }
16
+
17
+ export interface IntelligenceBundle {
18
+ domain: string;
19
+ version: string;
20
+ entries: IntelligenceEntry[];
21
+ }
22
+ `;
23
+ }
24
+ //# sourceMappingURL=intelligence-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intelligence-types.js","sourceRoot":"","sources":["../../src/templates/intelligence-types.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,yBAAyB;IACvC,OAAO;;;;;;;;;;;;;;;;;;;;CAoBR,CAAC;AACF,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { AgentConfig } from '../types.js';
2
+ /**
3
+ * Generate the LLM client file for a new agent.
4
+ * Contains LLMClient (OpenAI fetch + Anthropic SDK) and ModelRouter (inlined).
5
+ * Uses config.id to resolve ~/.{agentId}/model-routing.json.
6
+ */
7
+ export declare function generateLLMClient(config: AgentConfig): string;