clementine-agent 1.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 (190) hide show
  1. package/.env.example +44 -0
  2. package/LICENSE +21 -0
  3. package/README.md +795 -0
  4. package/dist/agent/agent-manager.d.ts +69 -0
  5. package/dist/agent/agent-manager.js +441 -0
  6. package/dist/agent/assistant.d.ts +225 -0
  7. package/dist/agent/assistant.js +3888 -0
  8. package/dist/agent/auto-update.d.ts +32 -0
  9. package/dist/agent/auto-update.js +186 -0
  10. package/dist/agent/daily-planner.d.ts +24 -0
  11. package/dist/agent/daily-planner.js +379 -0
  12. package/dist/agent/execution-advisor.d.ts +10 -0
  13. package/dist/agent/execution-advisor.js +272 -0
  14. package/dist/agent/hooks.d.ts +45 -0
  15. package/dist/agent/hooks.js +564 -0
  16. package/dist/agent/insight-engine.d.ts +66 -0
  17. package/dist/agent/insight-engine.js +225 -0
  18. package/dist/agent/intent-classifier.d.ts +48 -0
  19. package/dist/agent/intent-classifier.js +214 -0
  20. package/dist/agent/link-extractor.d.ts +19 -0
  21. package/dist/agent/link-extractor.js +90 -0
  22. package/dist/agent/mcp-bridge.d.ts +62 -0
  23. package/dist/agent/mcp-bridge.js +435 -0
  24. package/dist/agent/metacognition.d.ts +66 -0
  25. package/dist/agent/metacognition.js +221 -0
  26. package/dist/agent/orchestrator.d.ts +81 -0
  27. package/dist/agent/orchestrator.js +790 -0
  28. package/dist/agent/profiles.d.ts +22 -0
  29. package/dist/agent/profiles.js +91 -0
  30. package/dist/agent/prompt-cache.d.ts +24 -0
  31. package/dist/agent/prompt-cache.js +68 -0
  32. package/dist/agent/prompt-evolver.d.ts +28 -0
  33. package/dist/agent/prompt-evolver.js +279 -0
  34. package/dist/agent/role-scaffolds.d.ts +28 -0
  35. package/dist/agent/role-scaffolds.js +433 -0
  36. package/dist/agent/safe-restart.d.ts +41 -0
  37. package/dist/agent/safe-restart.js +150 -0
  38. package/dist/agent/self-improve.d.ts +66 -0
  39. package/dist/agent/self-improve.js +1706 -0
  40. package/dist/agent/session-event-log.d.ts +114 -0
  41. package/dist/agent/session-event-log.js +233 -0
  42. package/dist/agent/skill-extractor.d.ts +72 -0
  43. package/dist/agent/skill-extractor.js +435 -0
  44. package/dist/agent/source-mods.d.ts +61 -0
  45. package/dist/agent/source-mods.js +230 -0
  46. package/dist/agent/source-preflight.d.ts +25 -0
  47. package/dist/agent/source-preflight.js +100 -0
  48. package/dist/agent/stall-guard.d.ts +62 -0
  49. package/dist/agent/stall-guard.js +109 -0
  50. package/dist/agent/strategic-planner.d.ts +60 -0
  51. package/dist/agent/strategic-planner.js +352 -0
  52. package/dist/agent/team-bus.d.ts +89 -0
  53. package/dist/agent/team-bus.js +556 -0
  54. package/dist/agent/team-router.d.ts +26 -0
  55. package/dist/agent/team-router.js +37 -0
  56. package/dist/agent/tool-loop-detector.d.ts +59 -0
  57. package/dist/agent/tool-loop-detector.js +242 -0
  58. package/dist/agent/workflow-runner.d.ts +36 -0
  59. package/dist/agent/workflow-runner.js +317 -0
  60. package/dist/agent/workflow-variables.d.ts +16 -0
  61. package/dist/agent/workflow-variables.js +62 -0
  62. package/dist/channels/discord-agent-bot.d.ts +101 -0
  63. package/dist/channels/discord-agent-bot.js +881 -0
  64. package/dist/channels/discord-bot-manager.d.ts +80 -0
  65. package/dist/channels/discord-bot-manager.js +262 -0
  66. package/dist/channels/discord-utils.d.ts +51 -0
  67. package/dist/channels/discord-utils.js +293 -0
  68. package/dist/channels/discord.d.ts +12 -0
  69. package/dist/channels/discord.js +1832 -0
  70. package/dist/channels/slack-agent-bot.d.ts +73 -0
  71. package/dist/channels/slack-agent-bot.js +320 -0
  72. package/dist/channels/slack-bot-manager.d.ts +66 -0
  73. package/dist/channels/slack-bot-manager.js +236 -0
  74. package/dist/channels/slack-utils.d.ts +39 -0
  75. package/dist/channels/slack-utils.js +189 -0
  76. package/dist/channels/slack.d.ts +11 -0
  77. package/dist/channels/slack.js +196 -0
  78. package/dist/channels/telegram.d.ts +10 -0
  79. package/dist/channels/telegram.js +235 -0
  80. package/dist/channels/webhook.d.ts +9 -0
  81. package/dist/channels/webhook.js +78 -0
  82. package/dist/channels/whatsapp.d.ts +11 -0
  83. package/dist/channels/whatsapp.js +181 -0
  84. package/dist/cli/chat.d.ts +14 -0
  85. package/dist/cli/chat.js +220 -0
  86. package/dist/cli/cron.d.ts +17 -0
  87. package/dist/cli/cron.js +552 -0
  88. package/dist/cli/dashboard.d.ts +15 -0
  89. package/dist/cli/dashboard.js +17677 -0
  90. package/dist/cli/index.d.ts +3 -0
  91. package/dist/cli/index.js +2474 -0
  92. package/dist/cli/routes/delegations.d.ts +19 -0
  93. package/dist/cli/routes/delegations.js +154 -0
  94. package/dist/cli/routes/digest.d.ts +17 -0
  95. package/dist/cli/routes/digest.js +375 -0
  96. package/dist/cli/routes/goals.d.ts +14 -0
  97. package/dist/cli/routes/goals.js +258 -0
  98. package/dist/cli/routes/workflows.d.ts +18 -0
  99. package/dist/cli/routes/workflows.js +97 -0
  100. package/dist/cli/setup.d.ts +8 -0
  101. package/dist/cli/setup.js +619 -0
  102. package/dist/cli/tunnel.d.ts +35 -0
  103. package/dist/cli/tunnel.js +141 -0
  104. package/dist/config.d.ts +145 -0
  105. package/dist/config.js +278 -0
  106. package/dist/events/bus.d.ts +43 -0
  107. package/dist/events/bus.js +136 -0
  108. package/dist/gateway/cron-scheduler.d.ts +166 -0
  109. package/dist/gateway/cron-scheduler.js +1767 -0
  110. package/dist/gateway/delivery-queue.d.ts +30 -0
  111. package/dist/gateway/delivery-queue.js +110 -0
  112. package/dist/gateway/heartbeat-scheduler.d.ts +99 -0
  113. package/dist/gateway/heartbeat-scheduler.js +1298 -0
  114. package/dist/gateway/heartbeat.d.ts +3 -0
  115. package/dist/gateway/heartbeat.js +3 -0
  116. package/dist/gateway/lanes.d.ts +24 -0
  117. package/dist/gateway/lanes.js +76 -0
  118. package/dist/gateway/notifications.d.ts +29 -0
  119. package/dist/gateway/notifications.js +75 -0
  120. package/dist/gateway/router.d.ts +210 -0
  121. package/dist/gateway/router.js +1330 -0
  122. package/dist/index.d.ts +12 -0
  123. package/dist/index.js +1015 -0
  124. package/dist/memory/chunker.d.ts +28 -0
  125. package/dist/memory/chunker.js +226 -0
  126. package/dist/memory/consolidation.d.ts +44 -0
  127. package/dist/memory/consolidation.js +171 -0
  128. package/dist/memory/context-assembler.d.ts +50 -0
  129. package/dist/memory/context-assembler.js +149 -0
  130. package/dist/memory/embeddings.d.ts +38 -0
  131. package/dist/memory/embeddings.js +180 -0
  132. package/dist/memory/graph-store.d.ts +66 -0
  133. package/dist/memory/graph-store.js +613 -0
  134. package/dist/memory/mmr.d.ts +21 -0
  135. package/dist/memory/mmr.js +75 -0
  136. package/dist/memory/search.d.ts +26 -0
  137. package/dist/memory/search.js +67 -0
  138. package/dist/memory/store.d.ts +530 -0
  139. package/dist/memory/store.js +2022 -0
  140. package/dist/security/integrity.d.ts +24 -0
  141. package/dist/security/integrity.js +58 -0
  142. package/dist/security/patterns.d.ts +34 -0
  143. package/dist/security/patterns.js +110 -0
  144. package/dist/security/scanner.d.ts +32 -0
  145. package/dist/security/scanner.js +263 -0
  146. package/dist/tools/admin-tools.d.ts +12 -0
  147. package/dist/tools/admin-tools.js +1278 -0
  148. package/dist/tools/external-tools.d.ts +11 -0
  149. package/dist/tools/external-tools.js +1327 -0
  150. package/dist/tools/goal-tools.d.ts +9 -0
  151. package/dist/tools/goal-tools.js +159 -0
  152. package/dist/tools/mcp-server.d.ts +13 -0
  153. package/dist/tools/mcp-server.js +141 -0
  154. package/dist/tools/memory-tools.d.ts +10 -0
  155. package/dist/tools/memory-tools.js +568 -0
  156. package/dist/tools/session-tools.d.ts +6 -0
  157. package/dist/tools/session-tools.js +146 -0
  158. package/dist/tools/shared.d.ts +216 -0
  159. package/dist/tools/shared.js +340 -0
  160. package/dist/tools/team-tools.d.ts +6 -0
  161. package/dist/tools/team-tools.js +447 -0
  162. package/dist/tools/tool-meta.d.ts +34 -0
  163. package/dist/tools/tool-meta.js +133 -0
  164. package/dist/tools/vault-tools.d.ts +8 -0
  165. package/dist/tools/vault-tools.js +457 -0
  166. package/dist/types.d.ts +716 -0
  167. package/dist/types.js +16 -0
  168. package/dist/vault-migrations/0001-add-execution-framework.d.ts +10 -0
  169. package/dist/vault-migrations/0001-add-execution-framework.js +47 -0
  170. package/dist/vault-migrations/0002-add-agentic-communication.d.ts +12 -0
  171. package/dist/vault-migrations/0002-add-agentic-communication.js +79 -0
  172. package/dist/vault-migrations/0003-update-execution-pipeline-narration.d.ts +11 -0
  173. package/dist/vault-migrations/0003-update-execution-pipeline-narration.js +73 -0
  174. package/dist/vault-migrations/helpers.d.ts +14 -0
  175. package/dist/vault-migrations/helpers.js +44 -0
  176. package/dist/vault-migrations/runner.d.ts +14 -0
  177. package/dist/vault-migrations/runner.js +139 -0
  178. package/dist/vault-migrations/types.d.ts +42 -0
  179. package/dist/vault-migrations/types.js +9 -0
  180. package/install.sh +320 -0
  181. package/package.json +84 -0
  182. package/scripts/postinstall.js +125 -0
  183. package/vault/00-System/AGENTS.md +66 -0
  184. package/vault/00-System/CRON.md +71 -0
  185. package/vault/00-System/HEARTBEAT.md +58 -0
  186. package/vault/00-System/MEMORY.md +16 -0
  187. package/vault/00-System/SOUL.md +96 -0
  188. package/vault/05-Tasks/TASKS.md +19 -0
  189. package/vault/06-Templates/_Daily-Template.md +28 -0
  190. package/vault/06-Templates/_People-Template.md +22 -0
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Clementine TypeScript — Agent profile management.
3
+ *
4
+ * Profiles are Markdown files with YAML frontmatter stored in
5
+ * vault/00-System/profiles/. Each profile defines a persona with its own
6
+ * tone, tool restrictions, security tier, and system prompt body.
7
+ *
8
+ * ProfileManager scans the directory, caches AgentProfile objects, and
9
+ * hot-reloads when files change (60-second TTL).
10
+ */
11
+ import type { AgentProfile } from '../types.js';
12
+ export declare class ProfileManager {
13
+ private dir;
14
+ private cache;
15
+ private cacheTime;
16
+ constructor(profilesDir: string);
17
+ private refreshIfStale;
18
+ private loadProfile;
19
+ get(slug: string): AgentProfile | null;
20
+ listAll(): AgentProfile[];
21
+ }
22
+ //# sourceMappingURL=profiles.d.ts.map
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Clementine TypeScript — Agent profile management.
3
+ *
4
+ * Profiles are Markdown files with YAML frontmatter stored in
5
+ * vault/00-System/profiles/. Each profile defines a persona with its own
6
+ * tone, tool restrictions, security tier, and system prompt body.
7
+ *
8
+ * ProfileManager scans the directory, caches AgentProfile objects, and
9
+ * hot-reloads when files change (60-second TTL).
10
+ */
11
+ import fs from 'node:fs';
12
+ import path from 'node:path';
13
+ import matter from 'gray-matter';
14
+ const CACHE_TTL_MS = 60_000;
15
+ export class ProfileManager {
16
+ dir;
17
+ cache = new Map();
18
+ cacheTime = 0;
19
+ constructor(profilesDir) {
20
+ this.dir = profilesDir;
21
+ }
22
+ refreshIfStale() {
23
+ const now = Date.now();
24
+ if (now - this.cacheTime < CACHE_TTL_MS && this.cache.size > 0) {
25
+ return;
26
+ }
27
+ if (!fs.existsSync(this.dir)) {
28
+ this.cache.clear();
29
+ this.cacheTime = now;
30
+ return;
31
+ }
32
+ const profiles = new Map();
33
+ const files = fs.readdirSync(this.dir).filter((f) => f.endsWith('.md') && !f.startsWith('_')).sort();
34
+ for (const file of files) {
35
+ try {
36
+ const filePath = path.join(this.dir, file);
37
+ const profile = this.loadProfile(filePath, file);
38
+ const slug = file.replace(/\.md$/, '');
39
+ profiles.set(slug, profile);
40
+ }
41
+ catch {
42
+ // Skip malformed profile files
43
+ }
44
+ }
45
+ this.cache = profiles;
46
+ this.cacheTime = now;
47
+ }
48
+ loadProfile(filePath, fileName) {
49
+ const raw = fs.readFileSync(filePath, 'utf-8');
50
+ const { data: meta, content } = matter(raw);
51
+ const slug = fileName.replace(/\.md$/, '');
52
+ // Cap tier at 2 — profiles can never grant Tier 3
53
+ const tier = Math.min(Number(meta.tier ?? 1), 2);
54
+ // Parse team-specific frontmatter
55
+ let team;
56
+ const channelName = Array.isArray(meta.channelName)
57
+ ? meta.channelName.map(String).filter(Boolean)
58
+ : meta.channelName ? String(meta.channelName) : undefined;
59
+ const canMessage = Array.isArray(meta.canMessage)
60
+ ? meta.canMessage.map(String).filter(Boolean)
61
+ : [];
62
+ const allowedTools = Array.isArray(meta.allowedTools)
63
+ ? meta.allowedTools.map(String).filter(Boolean)
64
+ : undefined;
65
+ if (channelName && (typeof channelName === 'string' || channelName.length > 0)) {
66
+ // channels[] populated at runtime by the agent's own bot
67
+ const teamChat = meta.teamChat === true || meta.teamChat === 'true';
68
+ const respondToAll = meta.respondToAll === true || meta.respondToAll === 'true';
69
+ team = { channelName, channels: [], canMessage, allowedTools, teamChat, respondToAll: respondToAll || undefined };
70
+ }
71
+ return {
72
+ slug,
73
+ name: String(meta.name ?? slug.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase())),
74
+ tier,
75
+ description: String(meta.description ?? meta.role ?? ''),
76
+ systemPromptBody: content.trim(),
77
+ model: meta.model ? String(meta.model) : undefined,
78
+ avatar: meta.avatar ? String(meta.avatar) : undefined,
79
+ team,
80
+ };
81
+ }
82
+ get(slug) {
83
+ this.refreshIfStale();
84
+ return this.cache.get(slug) ?? null;
85
+ }
86
+ listAll() {
87
+ this.refreshIfStale();
88
+ return [...this.cache.values()];
89
+ }
90
+ }
91
+ //# sourceMappingURL=profiles.js.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Clementine TypeScript — File-watcher cache for system prompt files.
3
+ *
4
+ * Pre-reads vault files and invalidates on fs change, avoiding 50-200ms
5
+ * of readFileSync + gray-matter parsing on every message.
6
+ */
7
+ interface CacheEntry {
8
+ content: string;
9
+ data: Record<string, unknown>;
10
+ }
11
+ export declare class PromptCache {
12
+ private cache;
13
+ private watchers;
14
+ /** Get parsed file content. Loads and caches on first access. */
15
+ get(filePath: string): CacheEntry | null;
16
+ /** Start watching a file. Re-reads on change. */
17
+ watch(filePath: string): void;
18
+ private load;
19
+ /** Switch watched daily note path (call when date changes). */
20
+ swapWatch(oldPath: string, newPath: string): void;
21
+ dispose(): void;
22
+ }
23
+ export {};
24
+ //# sourceMappingURL=prompt-cache.d.ts.map
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Clementine TypeScript — File-watcher cache for system prompt files.
3
+ *
4
+ * Pre-reads vault files and invalidates on fs change, avoiding 50-200ms
5
+ * of readFileSync + gray-matter parsing on every message.
6
+ */
7
+ import { readFileSync, existsSync, watch } from 'node:fs';
8
+ import matter from 'gray-matter';
9
+ export class PromptCache {
10
+ cache = new Map();
11
+ watchers = new Map();
12
+ /** Get parsed file content. Loads and caches on first access. */
13
+ get(filePath) {
14
+ if (this.cache.has(filePath))
15
+ return this.cache.get(filePath);
16
+ return this.load(filePath);
17
+ }
18
+ /** Start watching a file. Re-reads on change. */
19
+ watch(filePath) {
20
+ if (this.watchers.has(filePath))
21
+ return;
22
+ this.load(filePath);
23
+ if (!existsSync(filePath))
24
+ return;
25
+ try {
26
+ const w = watch(filePath, () => { this.cache.delete(filePath); });
27
+ w.unref(); // Don't keep daemon alive
28
+ this.watchers.set(filePath, w);
29
+ }
30
+ catch {
31
+ // Watch failed (e.g. file deleted between check and watch) — cache still works
32
+ }
33
+ }
34
+ load(filePath) {
35
+ if (!existsSync(filePath)) {
36
+ this.cache.set(filePath, null);
37
+ return null;
38
+ }
39
+ try {
40
+ const raw = readFileSync(filePath, 'utf-8');
41
+ const parsed = matter(raw);
42
+ const entry = { content: parsed.content, data: parsed.data };
43
+ this.cache.set(filePath, entry);
44
+ return entry;
45
+ }
46
+ catch {
47
+ this.cache.set(filePath, null);
48
+ return null;
49
+ }
50
+ }
51
+ /** Switch watched daily note path (call when date changes). */
52
+ swapWatch(oldPath, newPath) {
53
+ const w = this.watchers.get(oldPath);
54
+ if (w) {
55
+ w.close();
56
+ this.watchers.delete(oldPath);
57
+ }
58
+ this.cache.delete(oldPath);
59
+ this.watch(newPath);
60
+ }
61
+ dispose() {
62
+ for (const w of this.watchers.values())
63
+ w.close();
64
+ this.watchers.clear();
65
+ this.cache.clear();
66
+ }
67
+ }
68
+ //# sourceMappingURL=prompt-cache.js.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Clementine TypeScript — Adaptive Prompt Evolution.
3
+ *
4
+ * Evolves static cron prompts by enriching them with lessons from
5
+ * reflections, progress state, and goal context. Returns the enrichment
6
+ * string to be appended to the original prompt.
7
+ */
8
+ interface PromptEvolutionContext {
9
+ jobName: string;
10
+ originalPrompt: string;
11
+ agentSlug?: string;
12
+ }
13
+ /**
14
+ * Evolve a static cron prompt by enriching it with lessons from reflections,
15
+ * progress state, and goal context. Returns the enrichment string (not the full prompt).
16
+ */
17
+ export declare function evolvePrompt(ctx: PromptEvolutionContext): string;
18
+ /**
19
+ * Log a strategy observation after a cron run completes.
20
+ * Called by the cron scheduler to track which approaches work/fail.
21
+ */
22
+ export declare function logStrategyObservation(jobName: string, observation: {
23
+ toolsUsed: string[];
24
+ quality: number;
25
+ status: 'ok' | 'error';
26
+ }): void;
27
+ export {};
28
+ //# sourceMappingURL=prompt-evolver.d.ts.map
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Clementine TypeScript — Adaptive Prompt Evolution.
3
+ *
4
+ * Evolves static cron prompts by enriching them with lessons from
5
+ * reflections, progress state, and goal context. Returns the enrichment
6
+ * string to be appended to the original prompt.
7
+ */
8
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync } from 'node:fs';
9
+ import path from 'node:path';
10
+ import pino from 'pino';
11
+ import { BASE_DIR, CRON_REFLECTIONS_DIR, CRON_PROGRESS_DIR, GOALS_DIR } from '../config.js';
12
+ const logger = pino({ name: 'clementine.prompt-evolver' });
13
+ /**
14
+ * Evolve a static cron prompt by enriching it with lessons from reflections,
15
+ * progress state, and goal context. Returns the enrichment string (not the full prompt).
16
+ */
17
+ export function evolvePrompt(ctx) {
18
+ const parts = [];
19
+ try {
20
+ const reflectionLessons = extractReflectionLessons(ctx.jobName);
21
+ if (reflectionLessons)
22
+ parts.push(reflectionLessons);
23
+ }
24
+ catch (err) {
25
+ logger.debug({ err, job: ctx.jobName }, 'Failed to extract reflection lessons');
26
+ }
27
+ try {
28
+ const strategyInsights = extractStrategyInsights(ctx.jobName);
29
+ if (strategyInsights)
30
+ parts.push(strategyInsights);
31
+ }
32
+ catch (err) {
33
+ logger.debug({ err, job: ctx.jobName }, 'Failed to extract strategy insights');
34
+ }
35
+ try {
36
+ const progressInsights = extractProgressInsights(ctx.jobName);
37
+ if (progressInsights)
38
+ parts.push(progressInsights);
39
+ }
40
+ catch (err) {
41
+ logger.debug({ err, job: ctx.jobName }, 'Failed to extract progress insights');
42
+ }
43
+ try {
44
+ const goalGuidance = extractGoalGuidance(ctx.jobName);
45
+ if (goalGuidance)
46
+ parts.push(goalGuidance);
47
+ }
48
+ catch (err) {
49
+ logger.debug({ err, job: ctx.jobName }, 'Failed to extract goal guidance');
50
+ }
51
+ return parts.join('\n\n');
52
+ }
53
+ /**
54
+ * Log a strategy observation after a cron run completes.
55
+ * Called by the cron scheduler to track which approaches work/fail.
56
+ */
57
+ export function logStrategyObservation(jobName, observation) {
58
+ try {
59
+ const strategyDir = path.join(BASE_DIR, 'cron', 'strategies');
60
+ mkdirSync(strategyDir, { recursive: true });
61
+ const safe = jobName.replace(/[^a-zA-Z0-9_-]/g, '_');
62
+ const logFile = path.join(strategyDir, `${safe}.strategy.jsonl`);
63
+ appendFileSync(logFile, JSON.stringify({
64
+ timestamp: new Date().toISOString(),
65
+ toolsUsed: observation.toolsUsed.slice(0, 20),
66
+ quality: observation.quality,
67
+ status: observation.status,
68
+ }) + '\n');
69
+ }
70
+ catch {
71
+ // non-fatal
72
+ }
73
+ }
74
+ /**
75
+ * Extract lessons from past reflections where quality was low or gaps were noted.
76
+ * Deduplicates gap strings and formats them as guidance.
77
+ */
78
+ function extractReflectionLessons(jobName) {
79
+ if (!existsSync(CRON_REFLECTIONS_DIR))
80
+ return null;
81
+ const safe = jobName.replace(/[^a-zA-Z0-9_-]/g, '_');
82
+ const reflectionFile = path.join(CRON_REFLECTIONS_DIR, `${safe}.jsonl`);
83
+ if (!existsSync(reflectionFile))
84
+ return null;
85
+ try {
86
+ const lines = readFileSync(reflectionFile, 'utf-8').trim().split('\n').filter(Boolean);
87
+ const recent = lines.slice(-10);
88
+ const gaps = new Set();
89
+ const commNotes = [];
90
+ for (const line of recent) {
91
+ try {
92
+ const entry = JSON.parse(line);
93
+ if (entry.quality < 3 && entry.gap) {
94
+ gaps.add(entry.gap.trim());
95
+ }
96
+ if (entry.commNote) {
97
+ commNotes.push(entry.commNote.trim());
98
+ }
99
+ }
100
+ catch {
101
+ continue;
102
+ }
103
+ }
104
+ if (gaps.size === 0 && commNotes.length === 0)
105
+ return null;
106
+ const parts = ['## Lessons from Previous Runs', 'Based on past performance reviews:'];
107
+ if (gaps.size > 0) {
108
+ for (const gap of gaps) {
109
+ parts.push(`- ${gap}`);
110
+ }
111
+ parts.push('Avoid these pitfalls in this run.');
112
+ }
113
+ if (commNotes.length > 0) {
114
+ parts.push('Communication notes:');
115
+ for (const note of commNotes.slice(-3)) {
116
+ parts.push(`- ${note}`);
117
+ }
118
+ }
119
+ return parts.join('\n');
120
+ }
121
+ catch {
122
+ return null;
123
+ }
124
+ }
125
+ /**
126
+ * Extract strategy insights by correlating tool usage patterns with quality scores.
127
+ * Identifies which approaches (tool combinations) tend to succeed vs. fail.
128
+ */
129
+ function extractStrategyInsights(jobName) {
130
+ const strategyDir = path.join(BASE_DIR, 'cron', 'strategies');
131
+ const safe = jobName.replace(/[^a-zA-Z0-9_-]/g, '_');
132
+ const logFile = path.join(strategyDir, `${safe}.strategy.jsonl`);
133
+ if (!existsSync(logFile))
134
+ return null;
135
+ try {
136
+ const lines = readFileSync(logFile, 'utf-8').trim().split('\n').filter(Boolean);
137
+ if (lines.length < 3)
138
+ return null; // need enough data
139
+ const recent = lines.slice(-15);
140
+ // Tally quality by top tool used
141
+ const toolStats = new Map();
142
+ for (const line of recent) {
143
+ try {
144
+ const entry = JSON.parse(line);
145
+ const topTools = (entry.toolsUsed ?? []).slice(0, 3);
146
+ const key = topTools.sort().join('+') || 'no-tools';
147
+ const stats = toolStats.get(key) ?? { total: 0, successCount: 0, qualitySum: 0 };
148
+ stats.total++;
149
+ if (entry.status === 'ok' && entry.quality >= 3)
150
+ stats.successCount++;
151
+ stats.qualitySum += entry.quality ?? 3;
152
+ toolStats.set(key, stats);
153
+ }
154
+ catch {
155
+ continue;
156
+ }
157
+ }
158
+ if (toolStats.size === 0)
159
+ return null;
160
+ // Find best and worst approaches
161
+ const ranked = [...toolStats.entries()]
162
+ .filter(([, s]) => s.total >= 2)
163
+ .map(([approach, s]) => ({
164
+ approach,
165
+ successRate: s.successCount / s.total,
166
+ avgQuality: s.qualitySum / s.total,
167
+ total: s.total,
168
+ }))
169
+ .sort((a, b) => b.avgQuality - a.avgQuality);
170
+ if (ranked.length === 0)
171
+ return null;
172
+ const parts = ['## Strategy Insights'];
173
+ const best = ranked[0];
174
+ if (best.successRate >= 0.7) {
175
+ parts.push(`Recommended approach: **${best.approach}** (${Math.round(best.successRate * 100)}% success rate over ${best.total} runs, avg quality: ${best.avgQuality.toFixed(1)})`);
176
+ }
177
+ const worst = ranked[ranked.length - 1];
178
+ if (ranked.length > 1 && worst.successRate < 0.5) {
179
+ parts.push(`Avoid: **${worst.approach}** (${Math.round(worst.successRate * 100)}% success rate — consider alternative tools)`);
180
+ }
181
+ return parts.length > 1 ? parts.join('\n') : null;
182
+ }
183
+ catch {
184
+ return null;
185
+ }
186
+ }
187
+ /**
188
+ * Extract insights from progress history — what's been done and what remains.
189
+ */
190
+ function extractProgressInsights(jobName) {
191
+ if (!existsSync(CRON_PROGRESS_DIR))
192
+ return null;
193
+ const safe = jobName.replace(/[^a-zA-Z0-9_-]/g, '_');
194
+ const progressFile = path.join(CRON_PROGRESS_DIR, `${safe}.json`);
195
+ if (!existsSync(progressFile))
196
+ return null;
197
+ try {
198
+ const progress = JSON.parse(readFileSync(progressFile, 'utf-8'));
199
+ const parts = ['## Progress Continuity'];
200
+ if (progress.completedItems?.length > 0) {
201
+ const recentCompleted = progress.completedItems.slice(-5);
202
+ parts.push(`Recently completed (${progress.completedItems.length} total):`);
203
+ for (const item of recentCompleted) {
204
+ parts.push(`- Done: ${item}`);
205
+ }
206
+ }
207
+ if (progress.pendingItems?.length > 0) {
208
+ parts.push(`Still pending (${progress.pendingItems.length}):`);
209
+ for (const item of progress.pendingItems.slice(0, 5)) {
210
+ parts.push(`- TODO: ${item}`);
211
+ }
212
+ }
213
+ if (progress.notes) {
214
+ parts.push(`Previous notes: ${progress.notes.slice(0, 300)}`);
215
+ }
216
+ if (parts.length <= 1)
217
+ return null;
218
+ return parts.join('\n');
219
+ }
220
+ catch {
221
+ return null;
222
+ }
223
+ }
224
+ /**
225
+ * Find goals that reference this cron job and inject alignment guidance.
226
+ */
227
+ function extractGoalGuidance(jobName) {
228
+ if (!existsSync(GOALS_DIR))
229
+ return null;
230
+ try {
231
+ const files = readdirSync(GOALS_DIR).filter(f => f.endsWith('.json'));
232
+ const linkedGoals = [];
233
+ for (const f of files) {
234
+ try {
235
+ const goal = JSON.parse(readFileSync(path.join(GOALS_DIR, f), 'utf-8'));
236
+ if (goal.status !== 'active')
237
+ continue;
238
+ if (goal.linkedCronJobs?.includes(jobName)) {
239
+ linkedGoals.push(goal);
240
+ }
241
+ }
242
+ catch {
243
+ continue;
244
+ }
245
+ }
246
+ if (linkedGoals.length === 0)
247
+ return null;
248
+ const parts = ['## Goal Alignment'];
249
+ for (const goal of linkedGoals) {
250
+ parts.push(`This job contributes to goal: "${goal.title}" (${goal.priority} priority)`);
251
+ if (goal.nextActions?.length) {
252
+ parts.push('Goal next actions to consider:');
253
+ for (const action of goal.nextActions.slice(0, 3)) {
254
+ parts.push(`- ${action}`);
255
+ }
256
+ }
257
+ if (goal.blockers?.length) {
258
+ parts.push('Known blockers:');
259
+ for (const blocker of goal.blockers) {
260
+ parts.push(`- Blocker: ${blocker}`);
261
+ }
262
+ }
263
+ if (goal.targetDate) {
264
+ const daysLeft = Math.floor((new Date(goal.targetDate).getTime() - Date.now()) / 86_400_000);
265
+ if (daysLeft <= 7 && daysLeft >= 0) {
266
+ parts.push(`Target date approaching: ${goal.targetDate} (${daysLeft} day(s) left)`);
267
+ }
268
+ else if (daysLeft < 0) {
269
+ parts.push(`Target date OVERDUE: ${goal.targetDate} (${Math.abs(daysLeft)} day(s) past)`);
270
+ }
271
+ }
272
+ }
273
+ return parts.join('\n');
274
+ }
275
+ catch {
276
+ return null;
277
+ }
278
+ }
279
+ //# sourceMappingURL=prompt-evolver.js.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Clementine TypeScript — Role scaffolding templates.
3
+ *
4
+ * When an agent is created with a role template, these generate the full
5
+ * working directory: CRON.md, playbook, sequence definitions, and email
6
+ * writing guidelines. This is what turns an empty agent into a working employee.
7
+ */
8
+ export interface RoleScaffold {
9
+ /** CRON.md content — the agent's autonomous job definitions. */
10
+ cronMd: string;
11
+ /** Playbook/ICP file — the agent's knowledge base for decision-making. */
12
+ playbook: string;
13
+ /** Sequence definitions — multi-step cadence templates. */
14
+ sequences?: string;
15
+ }
16
+ /**
17
+ * Generate scaffolding files for an SDR agent.
18
+ * @param agentName — Display name (e.g., "Alex the SDR")
19
+ * @param agentSlug — URL-safe slug (e.g., "alex-the-sdr")
20
+ */
21
+ export declare function scaffoldSdr(agentName: string, agentSlug: string): RoleScaffold;
22
+ /**
23
+ * Generate scaffolding files for a Researcher agent.
24
+ */
25
+ export declare function scaffoldResearcher(agentName: string, agentSlug: string): RoleScaffold;
26
+ /** Get scaffold generator for a role, or null if no scaffold exists. */
27
+ export declare function getScaffoldForRole(role: string): ((name: string, slug: string) => RoleScaffold) | null;
28
+ //# sourceMappingURL=role-scaffolds.d.ts.map