@evo-dev/core 0.0.1-alpha → 0.0.1-alpha.10

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 (85) hide show
  1. package/assets/agents/review/code-reviewer/examples.md +1 -1
  2. package/assets/agents/review/code-reviewer/prompt.md +1 -1
  3. package/assets/agents/review/code-reviewer/verification.md +1 -1
  4. package/assets/skills/coding/knowledge-distillation/SKILL.md +251 -0
  5. package/assets/skills/coding/knowledge-distillation/manifest.json +10 -0
  6. package/assets/skills/coding/knowledge-distillation/references/knowledge-distillation-methods.md +126 -0
  7. package/assets/team/agents/code-reviewer.md +48 -0
  8. package/assets/team/agents/docs-maintainer.md +51 -0
  9. package/assets/team/agents/implementation-engineer.md +51 -0
  10. package/assets/team/agents/product-scope-analyst.md +58 -0
  11. package/assets/team/agents/release-engineer.md +55 -0
  12. package/assets/team/agents/security-boundary-reviewer.md +50 -0
  13. package/assets/team/agents/solution-architect.md +51 -0
  14. package/assets/team/agents/verification-engineer.md +51 -0
  15. package/assets/team/team.md +102 -0
  16. package/assets/workflows/rd-bug-fix/WORKFLOW.json +1 -1
  17. package/assets/workflows/rd-code-review/WORKFLOW.json +1 -1
  18. package/assets/workflows/rd-docs-update/WORKFLOW.json +1 -1
  19. package/assets/workflows/rd-feature-implementation/WORKFLOW.json +1 -1
  20. package/assets/workflows/rd-refactor/WORKFLOW.json +1 -1
  21. package/assets/workflows/rd-release-readiness/WORKFLOW.json +1 -1
  22. package/assets/workflows/rd-security-boundary-review/WORKFLOW.json +2 -2
  23. package/assets/workflows/rd-test-generation/WORKFLOW.json +1 -1
  24. package/dist/config/index.js +1115 -81
  25. package/dist/index.js +13796 -2196
  26. package/dist/plugins/index.js +32 -32
  27. package/package.json +5 -1
  28. package/src/agents/index.ts +63 -292
  29. package/src/code-agent-traces/index.ts +520 -0
  30. package/src/config/index.ts +7 -0
  31. package/src/config/paths.ts +30 -0
  32. package/src/config/settings.ts +201 -0
  33. package/src/config/store.ts +152 -0
  34. package/src/daemon/index.ts +462 -40
  35. package/src/evolution/candidates/index.ts +564 -0
  36. package/src/evolution/control/index.ts +20 -0
  37. package/src/evolution/evidence/analysis.ts +533 -0
  38. package/src/evolution/evidence/index.ts +3 -0
  39. package/src/evolution/evidence/session-memory/analysis.ts +281 -0
  40. package/src/evolution/evidence/session-memory/constants.ts +9 -0
  41. package/src/evolution/evidence/session-memory/index.ts +7 -0
  42. package/src/evolution/evidence/session-memory/paths.ts +29 -0
  43. package/src/evolution/evidence/session-memory/policy.ts +39 -0
  44. package/src/evolution/evidence/session-memory/segment.ts +202 -0
  45. package/src/evolution/evidence/session-memory/sensitivity.ts +335 -0
  46. package/src/evolution/evidence/session-memory/state-machine.ts +249 -0
  47. package/src/evolution/evidence/session-memory/storage.ts +379 -0
  48. package/src/evolution/evidence/session-memory/types.ts +221 -0
  49. package/src/evolution/evidence/session-memory/updater.ts +191 -0
  50. package/src/evolution/formatters.ts +169 -0
  51. package/src/evolution/index.ts +16 -0
  52. package/src/evolution/knowledge/index.ts +5427 -0
  53. package/src/evolution/paths.ts +44 -0
  54. package/src/evolution/processor/distillation.ts +518 -0
  55. package/src/evolution/processor/index.ts +3 -0
  56. package/src/evolution/processor/process.ts +528 -0
  57. package/src/{learning → evolution/review}/index.ts +10 -14
  58. package/src/evolution/schema.ts +568 -0
  59. package/src/evolution/shared.ts +758 -0
  60. package/src/evolution/triggers/classification.ts +102 -0
  61. package/src/evolution/triggers/index.ts +295 -0
  62. package/src/hooks/index.ts +652 -376
  63. package/src/index.ts +16 -3
  64. package/src/pack/index.ts +13 -13
  65. package/src/plugins/capabilities.ts +40 -42
  66. package/src/plugins/index.ts +0 -1
  67. package/src/plugins/types.ts +4 -0
  68. package/src/projects/index.ts +453 -0
  69. package/src/protected-zones/index.ts +29 -11
  70. package/src/runtime-logs/index.ts +790 -0
  71. package/src/sync/orchestrator.ts +6 -0
  72. package/src/team/index.ts +3642 -0
  73. package/src/team/mcp.ts +405 -0
  74. package/src/team/prompts.ts +141 -0
  75. package/src/utils/errors.ts +13 -0
  76. package/src/utils/fs.ts +40 -0
  77. package/src/utils/hash.ts +9 -0
  78. package/src/utils/ids.ts +12 -0
  79. package/src/utils/index.ts +7 -0
  80. package/src/utils/parsing.ts +11 -0
  81. package/src/utils/text.ts +18 -0
  82. package/src/utils/time.ts +5 -0
  83. package/src/workflow/index.ts +6 -24
  84. package/src/project/index.ts +0 -507
  85. package/src/task/index.ts +0 -840
@@ -21,12 +21,30 @@ function resolveEvoDevPaths(homeDir = getHomeDir()) {
21
21
  const normalizedHome = stripTrailingSlash(homeDir);
22
22
  const rootDir = `${normalizedHome}/.evodev`;
23
23
  const stateDir = `${rootDir}/state`;
24
+ const logsDir = `${rootDir}/logs`;
25
+ const knowledgeDir = `${rootDir}/knowledge`;
26
+ const evosDir = `${rootDir}/evos`;
27
+ const roleAgentsDir = `${rootDir}/agents/roles`;
28
+ const teamsDir = `${rootDir}/teams`;
29
+ const runsDir = `${teamsDir}/runs`;
24
30
  return {
25
31
  homeDir: normalizedHome,
26
32
  rootDir,
27
33
  settingsPath: `${rootDir}/settings.json`,
28
34
  registryPath: `${rootDir}/registry.json`,
29
35
  stateDir,
36
+ logsDir,
37
+ knowledgeDir,
38
+ knowledgeIndexPath: `${knowledgeDir}/index.json`,
39
+ evosDir,
40
+ evosCasesDir: `${evosDir}/cases`,
41
+ evosIndexPath: `${evosDir}/index.json`,
42
+ roleAgentsDir,
43
+ roleAgentsIndexPath: `${roleAgentsDir}/index.json`,
44
+ teamsDir,
45
+ teamsIndexPath: `${teamsDir}/index.json`,
46
+ runsDir,
47
+ latestRunPath: `${runsDir}/latest.json`,
30
48
  installStatePath: `${stateDir}/install.json`,
31
49
  syncStatePath: `${stateDir}/sync.json`
32
50
  };
@@ -98,42 +116,801 @@ function expectString(value, path) {
98
116
  }
99
117
  return value;
100
118
  }
101
- // packages/core/src/task/index.ts
102
- var FORBIDDEN_TASK_WRITE_SEGMENTS = new Set([".claude", ".codex"]);
103
- var FORBIDDEN_PROJECT_ASSET_SEGMENTS = new Set(["packages", "src"]);
104
- var FORBIDDEN_TASK_WRITE_FILES = new Set(["agents.md", "claude.md", "package.json", "readme.md"]);
119
+ // packages/core/src/config/settings.ts
120
+ import { readFile as readFile2 } from "node:fs/promises";
121
+ // packages/core/src/utils/parsing.ts
122
+ function optionalBoolean(value, fallback) {
123
+ return typeof value === "boolean" ? value : fallback;
124
+ }
125
+ function positiveInteger(value, fallback) {
126
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : fallback;
127
+ }
128
+ // packages/core/src/evolution/evidence/session-memory/sensitivity.ts
129
+ var SESSION_MEMORY_CREDENTIAL_REDACTION = "[credential-redacted]";
130
+ var CREDENTIAL_FIELD_KEYS = new Set([
131
+ "accesstoken",
132
+ "apikey",
133
+ "auth",
134
+ "authtoken",
135
+ "authorization",
136
+ "clientsecret",
137
+ "cookie",
138
+ "credential",
139
+ "credentials",
140
+ "idtoken",
141
+ "password",
142
+ "passwd",
143
+ "privatekey",
144
+ "proxyauthorization",
145
+ "refreshtoken",
146
+ "secret",
147
+ "secretvalue",
148
+ "sessiontoken",
149
+ "setcookie",
150
+ "signingkey",
151
+ "token"
152
+ ]);
153
+ var CREDENTIAL_KEY_PATTERN = String.raw`(?:access[_-]?token|api[_-]?key|auth(?:orization)?|client[_-]?secret|cookie|credential(?:s)?|id[_-]?token|password|passwd|private[_-]?key|proxy[_-]?authorization|refresh[_-]?token|secret(?:[_-]?value)?|session[_-]?token|set[_-]?cookie|signing[_-]?key|token)`;
154
+ var CREDENTIAL_SHAPE_PATTERNS = [
155
+ /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----/i,
156
+ /\bAKIA[0-9A-Z]{16}\b/,
157
+ /\bgh[pousr]_[A-Za-z0-9]{20,}\b/,
158
+ /\bxox[baprs]-[A-Za-z0-9-]{16,}\b/,
159
+ /\bBearer\s+[A-Za-z0-9._~+/=-]+/i,
160
+ /\bBasic\s+[A-Za-z0-9+/]+={0,2}/i,
161
+ new RegExp(String.raw`(?:^|[^A-Za-z0-9_])["']?${CREDENTIAL_KEY_PATTERN}["']?\s*[:=]\s*["']?[A-Za-z0-9][A-Za-z0-9._~+/=-]*`, "i"),
162
+ new RegExp(String.raw`(?:^|[^A-Za-z0-9_])["']?${CREDENTIAL_KEY_PATTERN}["']?\s*[:=]\s*["'](?!\[credential-redacted\]["'])[^"'\r\n]+["']`, "i")
163
+ ];
164
+ var PRIVATE_KEY_BLOCK_PATTERN = /-----BEGIN ([A-Z0-9 ]*PRIVATE KEY)-----[\s\S]*?-----END \1-----/giu;
165
+ var AUTHORIZATION_HEADER_PATTERN = /(^|[\r\n])(\s*(?:authorization|proxy-authorization)\s*:\s*)(?:Bearer|Basic)\s+[^\r\n]+/gimu;
166
+ var COOKIE_HEADER_PATTERN = /(^|[\r\n])(\s*(?:cookie|set-cookie)\s*:\s*)[^\r\n]+/gimu;
167
+ var BEARER_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]+/giu;
168
+ var BASIC_PATTERN = /\bBasic\s+[A-Za-z0-9+/]+={0,2}/giu;
169
+ var AWS_ACCESS_KEY_PATTERN = /\bAKIA[0-9A-Z]{16}\b/gu;
170
+ var GITHUB_TOKEN_PATTERN = /\bgh[pousr]_[A-Za-z0-9]{20,}\b/gu;
171
+ var SLACK_TOKEN_PATTERN = /\bxox[baprs]-[A-Za-z0-9-]{16,}\b/gu;
172
+ var QUOTED_SERIALIZED_CREDENTIAL_PATTERN = new RegExp(String.raw`(^|[^A-Za-z0-9_])(["']?${CREDENTIAL_KEY_PATTERN}["']?\s*[:=]\s*)(["'])([^"'\r\n]+)(\3)`, "gimu");
173
+ var SERIALIZED_CREDENTIAL_PATTERN = new RegExp(String.raw`(^|[^A-Za-z0-9_])(["']?${CREDENTIAL_KEY_PATTERN}["']?\s*[:=]\s*)(["']?)([A-Za-z0-9][A-Za-z0-9._~+/=-]*)(\3)`, "gimu");
174
+ var URL_USERINFO_PATTERN = /(https?:\/\/)[^@\s/"']+@/giu;
175
+ var URL_CREDENTIAL_PARAMETER_PATTERN = new RegExp(String.raw`([?&#]["']?${CREDENTIAL_KEY_PATTERN}["']?=)[^&#\s"'<>]+`, "giu");
176
+ function redactSessionMemoryCredentialText(value) {
177
+ let redacted = value.replace(PRIVATE_KEY_BLOCK_PATTERN, SESSION_MEMORY_CREDENTIAL_REDACTION).replace(AUTHORIZATION_HEADER_PATTERN, (_, boundary, prefix) => {
178
+ return `${boundary}${prefix}${SESSION_MEMORY_CREDENTIAL_REDACTION}`;
179
+ }).replace(COOKIE_HEADER_PATTERN, (_, boundary, prefix) => {
180
+ return `${boundary}${prefix}${SESSION_MEMORY_CREDENTIAL_REDACTION}`;
181
+ }).replace(BEARER_PATTERN, `Bearer ${SESSION_MEMORY_CREDENTIAL_REDACTION}`).replace(BASIC_PATTERN, `Basic ${SESSION_MEMORY_CREDENTIAL_REDACTION}`).replace(AWS_ACCESS_KEY_PATTERN, SESSION_MEMORY_CREDENTIAL_REDACTION).replace(GITHUB_TOKEN_PATTERN, SESSION_MEMORY_CREDENTIAL_REDACTION).replace(SLACK_TOKEN_PATTERN, SESSION_MEMORY_CREDENTIAL_REDACTION).replace(QUOTED_SERIALIZED_CREDENTIAL_PATTERN, (_, boundary, prefix, quote) => `${boundary}${prefix}${quote}${SESSION_MEMORY_CREDENTIAL_REDACTION}${quote}`).replace(SERIALIZED_CREDENTIAL_PATTERN, (_, boundary, prefix, quote, credentialValue) => {
182
+ const trailingPunctuation = quote === "" ? credentialValue.match(/[.,;!?]+$/u)?.[0] ?? "" : "";
183
+ return `${boundary}${prefix}${quote}${SESSION_MEMORY_CREDENTIAL_REDACTION}${quote}${trailingPunctuation}`;
184
+ }).replace(URL_CREDENTIAL_PARAMETER_PATTERN, (_, prefix) => `${prefix}${SESSION_MEMORY_CREDENTIAL_REDACTION}`).replace(URL_USERINFO_PATTERN, (_, protocol) => protocol);
185
+ if (redacted === "")
186
+ redacted = value;
187
+ return { value: redacted, redacted: redacted !== value };
188
+ }
189
+
190
+ // packages/core/src/evolution/shared.ts
191
+ var MAX_PROPOSED_CHANGE_LENGTH = 16 * 1024;
192
+ var PROCESS_LOCK_STALE_MS = 5 * 60 * 1000;
105
193
  var FORBIDDEN_RAW_KEYS = new Set([
194
+ "commandhistory",
195
+ "commandoutput",
196
+ "credential",
197
+ "credentials",
198
+ "env",
199
+ "fullsource",
200
+ "memorybody",
201
+ "password",
202
+ "privatekey",
203
+ "prompt",
204
+ "promptbody",
205
+ "prompttext",
206
+ "rawcommand",
207
+ "rawcommandoutput",
208
+ "rawlog",
209
+ "rawlogs",
106
210
  "rawoutput",
107
- "raw_output",
108
- "stdout",
109
- "stderr",
211
+ "rawpayload",
212
+ "rawprompt",
213
+ "secret",
214
+ "secretvalue",
110
215
  "source",
216
+ "sourcebody",
217
+ "sourcecode",
111
218
  "sourcecontent",
112
- "source_content",
113
219
  "sourcetext",
114
- "source_text",
115
- "prompt",
116
- "prompttext",
117
- "prompt_text",
220
+ "stderr",
221
+ "stdout",
222
+ "token",
118
223
  "transcript",
119
- "transcripttext",
120
- "transcript_text",
121
- "secret",
122
- "secretvalue",
123
- "secret_value"
224
+ "transcriptbody",
225
+ "transcripttext"
124
226
  ]);
125
- var ALLOWED_VERIFICATION_KEYS = new Set([
126
- "acceptanceResults",
127
- "antiCriteriaResults",
128
- "commands",
129
- "evidence",
227
+ // packages/core/src/evolution/evidence/session-memory/policy.ts
228
+ function createDefaultSessionMemoryPolicy() {
229
+ return {
230
+ enabled: true,
231
+ storeRawSegments: true,
232
+ maxRawSegmentBytes: 200000,
233
+ retentionDays: 30,
234
+ minimumMessageTokensToInit: 1e4,
235
+ minimumTokensBetweenUpdate: 5000,
236
+ toolCallsBetweenUpdates: 9
237
+ };
238
+ }
239
+ function parseSessionMemoryPolicy(value) {
240
+ const defaults = createDefaultSessionMemoryPolicy();
241
+ if (value === undefined)
242
+ return defaults;
243
+ return {
244
+ enabled: optionalBoolean(value.enabled, defaults.enabled),
245
+ storeRawSegments: optionalBoolean(value.storeRawSegments, defaults.storeRawSegments),
246
+ maxRawSegmentBytes: positiveInteger(value.maxRawSegmentBytes, defaults.maxRawSegmentBytes),
247
+ retentionDays: positiveInteger(value.retentionDays, defaults.retentionDays),
248
+ minimumMessageTokensToInit: positiveInteger(value.minimumMessageTokensToInit, defaults.minimumMessageTokensToInit),
249
+ minimumTokensBetweenUpdate: positiveInteger(value.minimumTokensBetweenUpdate, defaults.minimumTokensBetweenUpdate),
250
+ toolCallsBetweenUpdates: positiveInteger(value.toolCallsBetweenUpdates, defaults.toolCallsBetweenUpdates)
251
+ };
252
+ }
253
+ // packages/core/src/runtime-logs/index.ts
254
+ var SAFE_EXECUTION_METADATA_KEYS = new Set([
255
+ "phase",
256
+ "runtimeSurface",
257
+ "stdinBytes",
258
+ "durationMs",
259
+ "enabled",
260
+ "stateWriteCount",
261
+ "warningCount",
262
+ "commandClass",
130
263
  "exitCode",
131
- "id",
132
264
  "status",
133
- "summary",
134
- "type"
265
+ "redactionCount",
266
+ "redactionLabels",
267
+ "normalizedEventType"
268
+ ]);
269
+ var SAFE_REDACTION_LABELS = new Set([
270
+ "raw-command",
271
+ "raw-command-output",
272
+ "raw-prompt",
273
+ "sensitive-command",
274
+ "sensitive-text"
135
275
  ]);
136
276
 
277
+ // packages/core/src/evolution/evidence/session-memory/constants.ts
278
+ var DEFAULT_MAX_RAW_EVENT_BYTES = 64 * 1024;
279
+ // packages/core/src/evolution/knowledge/index.ts
280
+ import { mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
281
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
282
+ var RESERVED_OKF_FILENAMES = new Set(["index.md", "log.md"]);
283
+ var ACTIVE_OKF_REVIEW_STATES = ["accepted", "auto-accepted"];
284
+ var OKF_REVIEW_STATES = [
285
+ "auto-accepted",
286
+ "accepted",
287
+ "needs-human",
288
+ "rejected",
289
+ "deferred",
290
+ "stale",
291
+ "deprecated",
292
+ "revoked",
293
+ "superseded",
294
+ "auto-stored/unreviewed"
295
+ ];
296
+ var OKF_LIFECYCLE_STATUSES = [
297
+ "active",
298
+ "stale",
299
+ "deprecated",
300
+ "revoked",
301
+ "superseded"
302
+ ];
303
+ var BEHAVIOR_CHANGE_KINDS = new Set([
304
+ "skill-improvement",
305
+ "role-agent-suggestion",
306
+ "team-suggestion",
307
+ "workflow-improvement",
308
+ "task-split-improvement",
309
+ "tool-use-improvement",
310
+ "repo-asset-suggestion"
311
+ ]);
312
+ function resolveOkfKnowledgePaths(homeDir) {
313
+ const paths2 = resolveEvoDevPaths(homeDir);
314
+ return {
315
+ knowledgeDir: paths2.knowledgeDir,
316
+ okfDir: join(paths2.knowledgeDir, "okf"),
317
+ indexesDir: join(paths2.knowledgeDir, "indexes"),
318
+ tmpDir: join(paths2.knowledgeDir, "tmp")
319
+ };
320
+ }
321
+ async function ensureOkfKnowledgeBase(homeDir) {
322
+ const paths2 = resolveOkfKnowledgePaths(homeDir);
323
+ await ensureLocalKnowledgeGitRepository(paths2.knowledgeDir);
324
+ await mkdir(paths2.indexesDir, { recursive: true });
325
+ await mkdir(paths2.tmpDir, { recursive: true });
326
+ const directories = [
327
+ {
328
+ path: "",
329
+ title: "EvoDev Knowledge",
330
+ description: "User-local active engineering knowledge."
331
+ },
332
+ { path: "concepts", title: "Core Concepts", description: "Canonical core knowledge." },
333
+ { path: "concepts/rules", title: "Rules", description: "Normative engineering rules." },
334
+ {
335
+ path: "concepts/decisions",
336
+ title: "Decisions",
337
+ description: "Durable engineering decisions."
338
+ },
339
+ { path: "concepts/patterns", title: "Patterns", description: "Reusable engineering patterns." },
340
+ { path: "concepts/warnings", title: "Warnings", description: "Risks and anti-patterns." },
341
+ { path: "concepts/checklists", title: "Checklists", description: "Verification checklists." },
342
+ {
343
+ path: "concepts/verification",
344
+ title: "Verification",
345
+ description: "Repeatable verification patterns."
346
+ },
347
+ { path: "concepts/evos", title: "Evolution Cases", description: "Reviewed evolution cases." },
348
+ { path: "concepts/glossary", title: "Glossary", description: "Terms and taxonomy notes." },
349
+ { path: "repos", title: "Repositories", description: "Repository attention overlays." },
350
+ { path: "roles", title: "Roles", description: "Role attention overlays." },
351
+ { path: "workflows", title: "Workflows", description: "Workflow attention overlays." },
352
+ { path: "references", title: "References", description: "Cited references." }
353
+ ];
354
+ for (const directory of directories) {
355
+ await ensureOkfDirectory(paths2.okfDir, directory.path, directory.title, directory.description);
356
+ }
357
+ await rebuildOkfKnowledgeIndexes({ homeDir });
358
+ }
359
+ async function rebuildOkfKnowledgeIndexes(input) {
360
+ const paths2 = resolveOkfKnowledgePaths(input.homeDir);
361
+ await mkdir(paths2.indexesDir, { recursive: true });
362
+ const concepts = (await listOkfKnowledgeConcepts({ homeDir: input.homeDir })).filter(isActiveOkfConcept);
363
+ const conceptSummaries = concepts.map((concept) => ({
364
+ id: concept.id,
365
+ type: concept.type,
366
+ title: concept.title,
367
+ description: concept.description,
368
+ sourceLink: concept.sourceLink,
369
+ stableKey: concept.stableKey,
370
+ reviewState: concept.reviewState,
371
+ tags: concept.tags,
372
+ repoTags: concept.repoTags,
373
+ roleTags: concept.roleTags,
374
+ workflowTags: concept.workflowTags,
375
+ pathScopes: concept.pathScopes
376
+ }));
377
+ const pathsWritten = [
378
+ join(paths2.indexesDir, "index.json"),
379
+ join(paths2.indexesDir, "concepts.json"),
380
+ join(paths2.indexesDir, "repos.json"),
381
+ join(paths2.indexesDir, "roles.json"),
382
+ join(paths2.indexesDir, "workflows.json")
383
+ ];
384
+ await writeJson2(pathsWritten[0], {
385
+ schemaVersion: 1,
386
+ kind: "okf-knowledge-index",
387
+ conceptCount: concepts.length,
388
+ updatedAt: new Date().toISOString()
389
+ }, { overwrite: true });
390
+ await writeJson2(pathsWritten[1], {
391
+ schemaVersion: 1,
392
+ kind: "okf-concepts-index",
393
+ concepts: conceptSummaries
394
+ }, { overwrite: true });
395
+ await writeJson2(pathsWritten[2], {
396
+ schemaVersion: 1,
397
+ kind: "okf-repos-index",
398
+ repos: groupConceptsByTag(concepts, "repoTags")
399
+ }, { overwrite: true });
400
+ await writeJson2(pathsWritten[3], {
401
+ schemaVersion: 1,
402
+ kind: "okf-roles-index",
403
+ roles: groupConceptsByTag(concepts, "roleTags")
404
+ }, { overwrite: true });
405
+ await writeJson2(pathsWritten[4], {
406
+ schemaVersion: 1,
407
+ kind: "okf-workflows-index",
408
+ workflows: groupConceptsByTag(concepts, "workflowTags")
409
+ }, { overwrite: true });
410
+ return pathsWritten;
411
+ }
412
+ async function listOkfKnowledgeConcepts(input) {
413
+ const okfDir = resolveOkfKnowledgePaths(input.homeDir).okfDir;
414
+ if (!await pathExists2(okfDir))
415
+ return [];
416
+ const files = await listMarkdownFiles(okfDir);
417
+ const concepts = [];
418
+ for (const file of files) {
419
+ if (RESERVED_OKF_FILENAMES.has(file.name))
420
+ continue;
421
+ const content = await readFile(file.path, "utf8");
422
+ const parsed = parseOkfConceptFile(okfDir, file.path, content);
423
+ if (parsed !== null)
424
+ concepts.push(parsed);
425
+ }
426
+ return concepts.filter((concept) => matchesConceptFilters(concept, input)).sort((left, right) => left.id.localeCompare(right.id));
427
+ }
428
+ async function ensureLocalKnowledgeGitRepository(knowledgeDir) {
429
+ await mkdir(knowledgeDir, { recursive: true });
430
+ const gitDir = join(knowledgeDir, ".git");
431
+ if (await pathExists2(gitDir))
432
+ return;
433
+ await mkdir(join(gitDir, "objects", "info"), { recursive: true });
434
+ await mkdir(join(gitDir, "objects", "pack"), { recursive: true });
435
+ await mkdir(join(gitDir, "refs", "heads"), { recursive: true });
436
+ await mkdir(join(gitDir, "refs", "tags"), { recursive: true });
437
+ await mkdir(join(gitDir, "info"), { recursive: true });
438
+ await writeTextIfMissing(join(gitDir, "HEAD"), `ref: refs/heads/main
439
+ `);
440
+ await writeTextIfMissing(join(gitDir, "config"), [
441
+ "[core]",
442
+ "\trepositoryformatversion = 0",
443
+ "\tfilemode = true",
444
+ "\tbare = false",
445
+ "\tlogallrefupdates = true",
446
+ ""
447
+ ].join(`
448
+ `));
449
+ await writeTextIfMissing(join(gitDir, "info", "exclude"), [
450
+ "# EvoDev user-local knowledge git repository.",
451
+ "# No remote is configured by default.",
452
+ ""
453
+ ].join(`
454
+ `));
455
+ }
456
+ async function writeTextIfMissing(path, value) {
457
+ if (await pathExists2(path))
458
+ return;
459
+ await mkdir(dirname(path), { recursive: true });
460
+ await writeFile(path, value, { encoding: "utf8", flag: "wx" });
461
+ }
462
+ function sanitizeOkfText(value) {
463
+ return redactSessionMemoryCredentialText(value).value.replace(/\s+/gu, " ").trim().slice(0, 800);
464
+ }
465
+ async function ensureOkfDirectory(okfDir, relativeDir, title, description) {
466
+ const dir = relativeDir === "" || relativeDir === "." ? okfDir : resolveOkfTargetPath(okfDir, relativeDir);
467
+ await mkdir(dir, { recursive: true });
468
+ const isRoot = dir === okfDir;
469
+ const indexPath = join(dir, "index.md");
470
+ if (!await pathExists2(indexPath)) {
471
+ await writeFile(indexPath, isRoot ? [
472
+ "---",
473
+ `okf_version: ${yamlString("0.1")}`,
474
+ `title: ${yamlString(title)}`,
475
+ `description: ${yamlString(description)}`,
476
+ "---",
477
+ "",
478
+ `# ${title}`,
479
+ "",
480
+ description,
481
+ ""
482
+ ].join(`
483
+ `) : [`# ${title}`, "", description, ""].join(`
484
+ `), "utf8");
485
+ }
486
+ const logPath = join(dir, "log.md");
487
+ if (!await pathExists2(logPath)) {
488
+ await writeFile(logPath, [
489
+ "# Directory Update Log",
490
+ "",
491
+ `## ${todayIsoDate()}`,
492
+ "* **Initialization**: Created OKF directory.",
493
+ ""
494
+ ].join(`
495
+ `), "utf8");
496
+ }
497
+ }
498
+ function parseOkfConceptFile(okfDir, filePath, content) {
499
+ const parsed = extractFrontmatter(content);
500
+ if (parsed === null)
501
+ return null;
502
+ const rel = toOkfRelativePath(okfDir, filePath);
503
+ const id = rel.replace(/\.md$/, "");
504
+ const frontmatter = parsed.frontmatter;
505
+ const stableKey = readIndentedYamlScalar(frontmatter, "stableKey") ?? `legacy:${rel.replace(/\.md$/, "")}`;
506
+ const reviewState = parseOkfReviewState(readIndentedYamlScalar(frontmatter, "reviewState") ?? "auto-stored/unreviewed");
507
+ const type = readYamlScalar(frontmatter, "type") ?? "Unknown";
508
+ const title = readYamlScalar(frontmatter, "title") ?? titleFromSlug(id.split("/").at(-1) ?? id);
509
+ const description = readYamlScalar(frontmatter, "description") ?? "";
510
+ const tags = readYamlList(frontmatter, "tags");
511
+ const repoTags = readYamlList(frontmatter, "repoTags");
512
+ const roleTags = readYamlList(frontmatter, "roleTags");
513
+ const workflowTags = readYamlList(frontmatter, "workflowTags");
514
+ const pathScopes = readYamlList(frontmatter, "pathScopes");
515
+ const lifecycleParsed = parseOkfLifecycle(frontmatter, {
516
+ type,
517
+ path: rel,
518
+ tags,
519
+ title,
520
+ reviewState,
521
+ createdAt: readYamlScalar(frontmatter, "timestamp") ?? undefined
522
+ });
523
+ return {
524
+ id,
525
+ path: filePath,
526
+ sourceLink: `/${rel}`,
527
+ type,
528
+ stableKey,
529
+ reviewState,
530
+ lifecycle: lifecycleParsed.lifecycle,
531
+ lifecyclePersisted: lifecycleParsed.persisted,
532
+ title,
533
+ description,
534
+ tags,
535
+ repoTags,
536
+ roleTags,
537
+ workflowTags,
538
+ pathScopes,
539
+ body: parsed.body
540
+ };
541
+ }
542
+ function matchesConceptFilters(concept, input) {
543
+ if (input.projectKey !== undefined && concept.repoTags.length > 0 && !concept.repoTags.includes(sanitizeSlug(input.projectKey)) && !concept.tags.includes(`repo:${sanitizeSlug(input.projectKey)}`)) {
544
+ return false;
545
+ }
546
+ if (input.roleId !== undefined && concept.roleTags.length > 0 && !concept.roleTags.includes(sanitizeSlug(input.roleId)) && !concept.tags.includes(`role:${sanitizeSlug(input.roleId)}`)) {
547
+ return false;
548
+ }
549
+ if (input.workflowId !== undefined && concept.workflowTags.length > 0 && !concept.workflowTags.includes(sanitizeSlug(input.workflowId)) && !concept.tags.includes(`workflow:${sanitizeSlug(input.workflowId)}`)) {
550
+ return false;
551
+ }
552
+ if (input.paths !== undefined && input.paths.length > 0 && concept.pathScopes.length > 0) {
553
+ return input.paths.some((path) => concept.pathScopes.some((scope) => path.startsWith(scope) || scope.startsWith(path)));
554
+ }
555
+ return true;
556
+ }
557
+ function isActiveOkfConcept(concept) {
558
+ return resolveOkfConceptQueryEligibility(concept, {
559
+ includeStale: false,
560
+ now: new Date
561
+ }).include;
562
+ }
563
+ function isActiveOkfReviewState(reviewState) {
564
+ return ACTIVE_OKF_REVIEW_STATES.includes(reviewState);
565
+ }
566
+ function resolveOkfConceptQueryEligibility(concept, input) {
567
+ if (concept.lifecycle.status === "deprecated" || concept.lifecycle.status === "revoked" || concept.lifecycle.status === "superseded" || concept.reviewState === "deprecated" || concept.reviewState === "revoked" || concept.reviewState === "superseded") {
568
+ return { include: false, stale: false, scoreAdjustment: 0, reason: "inactive" };
569
+ }
570
+ const staleByStatus = concept.lifecycle.status === "stale" || concept.reviewState === "stale";
571
+ const staleByDate = isLifecycleDateDue(concept.lifecycle.staleAfter, input.now);
572
+ const stale = staleByStatus || staleByDate;
573
+ if (stale) {
574
+ return {
575
+ include: input.includeStale,
576
+ stale: input.includeStale,
577
+ scoreAdjustment: input.includeStale ? -250 : 0,
578
+ reason: staleByStatus ? "status=stale" : `staleAfter=${concept.lifecycle.staleAfter}`
579
+ };
580
+ }
581
+ return {
582
+ include: isActiveOkfReviewState(concept.reviewState) && concept.lifecycle.status === "active",
583
+ stale: false,
584
+ scoreAdjustment: 0,
585
+ reason: "active"
586
+ };
587
+ }
588
+ function isLifecycleDateDue(value, now) {
589
+ const normalized = normalizeIsoDateString(value);
590
+ return normalized !== null && Date.parse(normalized) <= now.getTime();
591
+ }
592
+ function createDefaultOkfLifecycle(input) {
593
+ const createdAt = normalizeIsoDateString(input.createdAt) ?? "1970-01-01T00:00:00.000Z";
594
+ const policy2 = resolveOkfLifecyclePolicy(input);
595
+ return {
596
+ status: lifecycleStatusFromReviewState(input.reviewState),
597
+ createdAt,
598
+ lastVerifiedAt: createdAt,
599
+ reviewAfter: addDaysIso(createdAt, policy2.reviewAfterDays),
600
+ staleAfter: addDaysIso(createdAt, policy2.staleAfterDays),
601
+ supersedes: [],
602
+ supersededBy: null,
603
+ revokedAt: null,
604
+ revokedReason: null
605
+ };
606
+ }
607
+ function parseOkfLifecycle(frontmatter, input) {
608
+ const defaults = createDefaultOkfLifecycle(input);
609
+ const block = extractNestedYamlBlock(frontmatter, "evodev", "lifecycle");
610
+ if (block === null)
611
+ return { lifecycle: defaults, persisted: false };
612
+ const status = readIndentedYamlScalar(block, "status");
613
+ const createdAt = readIndentedYamlScalar(block, "createdAt");
614
+ const lastVerifiedAt = readIndentedYamlScalar(block, "lastVerifiedAt");
615
+ const reviewAfter = readIndentedYamlScalar(block, "reviewAfter");
616
+ const staleAfter = readIndentedYamlScalar(block, "staleAfter");
617
+ const revokedAt = readIndentedYamlScalar(block, "revokedAt");
618
+ return {
619
+ persisted: true,
620
+ lifecycle: {
621
+ status: isOkfLifecycleStatus(status) ? status : defaults.status,
622
+ createdAt: normalizeIsoDateString(createdAt) ?? defaults.createdAt,
623
+ lastVerifiedAt: normalizeIsoDateString(lastVerifiedAt) ?? defaults.lastVerifiedAt,
624
+ reviewAfter: normalizeIsoDateString(reviewAfter) ?? defaults.reviewAfter,
625
+ staleAfter: normalizeIsoDateString(staleAfter) ?? defaults.staleAfter,
626
+ supersedes: readYamlList(block, "supersedes"),
627
+ supersededBy: readNullableLifecycleString(readIndentedYamlScalar(block, "supersededBy")),
628
+ revokedAt: readNullableLifecycleString(revokedAt) === null ? null : normalizeIsoDateString(revokedAt),
629
+ revokedReason: readNullableLifecycleString(readIndentedYamlScalar(block, "revokedReason"))
630
+ }
631
+ };
632
+ }
633
+ function lifecycleStatusFromReviewState(reviewState) {
634
+ if (reviewState === "stale")
635
+ return "stale";
636
+ if (reviewState === "deprecated")
637
+ return "deprecated";
638
+ if (reviewState === "revoked")
639
+ return "revoked";
640
+ if (reviewState === "superseded")
641
+ return "superseded";
642
+ return "active";
643
+ }
644
+ function resolveOkfLifecyclePolicy(input) {
645
+ const comparable = `${input.type} ${input.path} ${input.tags.join(" ")} ${input.title}`.toLowerCase();
646
+ if (/\bverification\b/u.test(comparable))
647
+ return { reviewAfterDays: 120, staleAfterDays: 240 };
648
+ if (/\bworkflow\b/u.test(comparable))
649
+ return { reviewAfterDays: 60, staleAfterDays: 120 };
650
+ return { reviewAfterDays: 90, staleAfterDays: 180 };
651
+ }
652
+ function isOkfLifecycleStatus(value) {
653
+ return OKF_LIFECYCLE_STATUSES.includes(value);
654
+ }
655
+ function normalizeIsoDateString(value) {
656
+ if (value === undefined || value === null || value === "" || value === "null")
657
+ return null;
658
+ const time2 = Date.parse(value);
659
+ if (!Number.isFinite(time2))
660
+ return null;
661
+ return new Date(time2).toISOString();
662
+ }
663
+ function addDaysIso(value, days) {
664
+ const date = new Date(value);
665
+ date.setUTCDate(date.getUTCDate() + days);
666
+ return date.toISOString();
667
+ }
668
+ function readNullableLifecycleString(value) {
669
+ if (value === null)
670
+ return null;
671
+ const normalized = sanitizeOkfText(value);
672
+ return normalized === "" || normalized === "null" ? null : normalized;
673
+ }
674
+ function yamlString(value) {
675
+ return JSON.stringify(value);
676
+ }
677
+ function extractFrontmatter(content) {
678
+ if (!content.startsWith(`---
679
+ `))
680
+ return null;
681
+ const end = content.indexOf(`
682
+ ---`, 4);
683
+ if (end < 0)
684
+ return null;
685
+ const frontmatter = content.slice(4, end).trim();
686
+ const body = content.slice(end + 4).replace(/^\n/u, "");
687
+ return { frontmatter, body };
688
+ }
689
+ function extractNestedYamlBlock(frontmatter, rootKey, childKey) {
690
+ const lines = frontmatter.split(`
691
+ `);
692
+ const rootIndex = lines.findIndex((line) => line.trim() === `${rootKey}:`);
693
+ if (rootIndex < 0)
694
+ return null;
695
+ const rootEnd = findYamlBlockEnd(lines, rootIndex, 0);
696
+ const childIndex = lines.findIndex((line, index) => index > rootIndex && index < rootEnd && line.startsWith(" ") && line.trim() === `${childKey}:`);
697
+ if (childIndex < 0)
698
+ return null;
699
+ const childEnd = findYamlBlockEnd(lines, childIndex, 2);
700
+ return lines.slice(childIndex + 1, childEnd).join(`
701
+ `);
702
+ }
703
+ function findYamlBlockEnd(lines, startIndex, parentIndent) {
704
+ for (let index = startIndex + 1;index < lines.length; index += 1) {
705
+ const line = lines[index] ?? "";
706
+ if (line.trim() === "")
707
+ continue;
708
+ const indent = line.length - line.trimStart().length;
709
+ if (indent <= parentIndent)
710
+ return index;
711
+ }
712
+ return lines.length;
713
+ }
714
+ function readYamlScalar(frontmatter, key) {
715
+ const match = frontmatter.match(new RegExp(`^${escapeRegExp(key)}:\\s*(.+?)\\s*$`, "m"));
716
+ if (match?.[1] === undefined)
717
+ return null;
718
+ return parseYamlValue(match[1]);
719
+ }
720
+ function readIndentedYamlScalar(frontmatter, key) {
721
+ const match = frontmatter.match(new RegExp(`^\\s*${escapeRegExp(key)}:\\s*(.+?)\\s*$`, "m"));
722
+ if (match?.[1] === undefined)
723
+ return null;
724
+ return parseYamlValue(match[1]);
725
+ }
726
+ function parseOkfReviewState(value) {
727
+ return OKF_REVIEW_STATES.includes(value) ? value : "auto-stored/unreviewed";
728
+ }
729
+ function readYamlList(frontmatter, key) {
730
+ const inline = frontmatter.match(new RegExp(`^\\s*${escapeRegExp(key)}:\\s*\\[(.*?)\\]\\s*$`, "m"));
731
+ if (inline?.[1] !== undefined) {
732
+ return inline[1].split(",").map((item) => parseYamlValue(item.trim())).filter((item) => item !== "");
733
+ }
734
+ const lines = frontmatter.split(`
735
+ `);
736
+ const values = [];
737
+ for (let index = 0;index < lines.length; index += 1) {
738
+ const line = lines[index] ?? "";
739
+ if (!line.trim().startsWith(`${key}:`))
740
+ continue;
741
+ for (let next = index + 1;next < lines.length; next += 1) {
742
+ const candidate = lines[next] ?? "";
743
+ if (!/^\s+-\s+/u.test(candidate))
744
+ break;
745
+ values.push(parseYamlValue(candidate.replace(/^\s+-\s+/u, "")));
746
+ }
747
+ }
748
+ return [...new Set(values.filter((value) => value !== ""))];
749
+ }
750
+ function parseYamlValue(value) {
751
+ const trimmed = value.trim();
752
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
753
+ try {
754
+ return JSON.parse(trimmed);
755
+ } catch {
756
+ return trimmed.slice(1, -1);
757
+ }
758
+ }
759
+ return trimmed.replace(/^['"]|['"]$/gu, "");
760
+ }
761
+ async function listMarkdownFiles(root) {
762
+ if (!await pathExists2(root))
763
+ return [];
764
+ const entries = await readdir(root, { withFileTypes: true });
765
+ const files = [];
766
+ for (const entry of entries) {
767
+ const path = join(root, entry.name);
768
+ if (entry.isDirectory()) {
769
+ files.push(...await listMarkdownFiles(path));
770
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
771
+ files.push({ path, name: entry.name });
772
+ }
773
+ }
774
+ return files;
775
+ }
776
+ function groupConceptsByTag(concepts, field) {
777
+ const groups = new Map;
778
+ for (const concept of concepts) {
779
+ for (const id of concept[field]) {
780
+ groups.set(id, [
781
+ ...groups.get(id) ?? [],
782
+ { id: concept.id, title: concept.title, sourceLink: concept.sourceLink }
783
+ ]);
784
+ }
785
+ }
786
+ return [...groups.entries()].map(([id, groupedConcepts]) => ({ id, concepts: groupedConcepts }));
787
+ }
788
+ function resolveOkfTargetPath(okfDir, targetPath) {
789
+ const clean = targetPath.replace(/^\/+/u, "");
790
+ if (clean.split("/").some((segment) => segment === ".." || segment === "." || segment === "")) {
791
+ throw new Error(`Unsafe OKF target path: ${targetPath}`);
792
+ }
793
+ const resolved = join(okfDir, clean);
794
+ const rel = relative(okfDir, resolved);
795
+ if (rel.startsWith("..") || rel === "")
796
+ throw new Error(`Unsafe OKF target path: ${targetPath}`);
797
+ return resolved;
798
+ }
799
+ function toOkfRelativePath(okfDir, path) {
800
+ return relative(okfDir, path).replace(/\\/gu, "/");
801
+ }
802
+ function sanitizeSlug(value) {
803
+ const slug = value.trim().toLowerCase().replace(/[^a-z0-9._/-]+/gu, "-").replace(/\/+/gu, "/").replace(/^-+|-+$/gu, "");
804
+ return slug === "" ? "unknown" : slug;
805
+ }
806
+ function titleFromSlug(value) {
807
+ return value.replace(/\.md$/u, "").split(/[/-]/u).filter(Boolean).map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`).join(" ");
808
+ }
809
+ function escapeRegExp(value) {
810
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
811
+ }
812
+ function todayIsoDate() {
813
+ return new Date().toISOString().slice(0, 10);
814
+ }
815
+ async function writeJson2(path, value, options = {}) {
816
+ await mkdir(dirname(path), { recursive: true });
817
+ const flag = options.overwrite === true ? "w" : "wx";
818
+ await writeFile(path, `${JSON.stringify(value, null, 2)}
819
+ `, { encoding: "utf8", flag });
820
+ }
821
+ async function pathExists2(path) {
822
+ try {
823
+ await stat(path);
824
+ return true;
825
+ } catch (error) {
826
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
827
+ return false;
828
+ throw error;
829
+ }
830
+ }
831
+
832
+ // packages/core/src/team/prompts.ts
833
+ var TEAM_ROLE_STARTUP_PROMPT_TEMPLATE = [
834
+ "You are an EvoDev managed role agent.",
835
+ "Team run: {{runId}}",
836
+ "Repository: {{repoRoot}}",
837
+ "Role id: {{roleId}}",
838
+ "Role name: {{roleName}}",
839
+ "Runtime: {{runtime}}",
840
+ "Native Code Agent binding: {{nativeAgentBinding}}",
841
+ "Model: {{model}}",
842
+ "Thinking level: {{thinkingLevel}}",
843
+ "Write mode: {{writeMode}}",
844
+ "Transcript recording: {{transcriptRecording}}",
845
+ "",
846
+ "Current known agents:",
847
+ "{{roster}}",
848
+ "",
849
+ "EvoDev team runtime control contract:",
850
+ "This run is already inside the EvoDev-managed team runtime.",
851
+ "For EvoDev role-agent lifecycle, use the current EvoDev run control plane.",
852
+ "Unprefixed user requests such as 'start the team', 'execute team', 'create agents', or 'spawn roles' mean: use the current EvoDev run control plane.",
853
+ "Do not answer those requests with only a role plan when a role agent should be created.",
854
+ "",
855
+ "Use Teams MCP as the primary control plane:",
856
+ "- list_agents: discover current EvoDev role agents.",
857
+ "- spawn_role: create or reuse exactly one EvoDev role agent.",
858
+ "- send_message: communicate through the EvoDev broker.",
859
+ "- stop_role: stop a role agent when allowed.",
860
+ "If Teams MCP is unavailable, fall back to the EvoDev CLI from this repository:",
861
+ "- evodev team spawn --role <roleId>",
862
+ "- evodev team send --to <roleId> --message <text>",
863
+ "- evodev team status",
864
+ "Teams MCP defaults are inherited from the environment:",
865
+ "EVODEV_TEAM_RUN_ID={{runId}}",
866
+ "EVODEV_TEAM_ROLE_ID={{roleId}}",
867
+ "When calling Teams MCP tools, let the MCP server-bound environment identify this run and role.",
868
+ "Do not operate tmux directly for role lifecycle; let EvoDev create and track panes.",
869
+ "Any server-bound role may request role lifecycle changes; EvoDev records and tracks panes but does not use role policy to stop execution.",
870
+ "{{roleGuidance}}",
871
+ "{{nativeAgentInstruction}}",
872
+ "Team messages are durably queued and delivered at hook safe points, not pasted into a live prompt. cc values are audit context; they are not an instruction for main to immediately forward or act.",
873
+ "{{scopedContext}}",
874
+ "",
875
+ "{{rolePrompt}}"
876
+ ].join(`
877
+ `);
878
+ var MAIN_ROLE_GUIDANCE = [
879
+ "As main, you are the planner and delegator for complex work, not a standby worker.",
880
+ "On each user prompt or hook-delivered inbox message, decide whether to answer directly, ask for clarification, or use team execution.",
881
+ "Use team execution only when role separation improves correctness, coverage, safety, or latency.",
882
+ "When team execution is needed, create an upfront task plan with required role ids, role-specific assignments, dependencies, and runnable batches.",
883
+ "Spawn or reuse all roles needed for the first runnable batch and send each role a self-contained task message.",
884
+ "After all currently runnable tasks are delegated, finish your current turn immediately; do not call sleep, wait idly, poll list_agents/status, or keep the turn alive to watch progress.",
885
+ "Use list_agents only for one-time roster discovery when the current roster is genuinely unknown, never as a progress check.",
886
+ "Resume coordination only when the user sends new input or a role message is delivered by hooks; then decide whether to adjust tasks, send supplemental instructions, spawn dependent roles, synthesize completed results, or ask the user.",
887
+ "While delegated role work is pending, do not perform concrete implementation, testing, package research, or review work yourself."
888
+ ].join(" ");
889
+
890
+ // packages/core/src/team/index.ts
891
+ var TEAM_INTERNAL_WAKE_SIGNAL = [
892
+ "[EvoDev internal wake signal]",
893
+ "No user request is included in this message. Continue only from EvoDev team inbox messages injected by hooks."
894
+ ].join(`
895
+ `);
896
+ var BUILT_IN_TEAM_DEFINITION = {
897
+ version: 1,
898
+ name: "builtin-minimal-team",
899
+ description: "Built-in minimal EvoDev team fallback.",
900
+ agents: {
901
+ executor: "builtin:executor",
902
+ reviewer: "builtin:reviewer",
903
+ tester: "builtin:tester"
904
+ },
905
+ body: [
906
+ "# Built-in Minimal Team",
907
+ "",
908
+ "Use role agents only when delegation improves correctness, coverage, safety, or latency.",
909
+ "Spawn roles on demand and send self-contained assignments through Teams MCP."
910
+ ].join(`
911
+ `)
912
+ };
913
+
137
914
  // packages/core/src/hooks/index.ts
138
915
  var CANONICAL_HOOK_EVENT_TYPES = [
139
916
  "SessionStart",
@@ -162,41 +939,61 @@ var CANONICAL_HOOK_EVENT_TYPES = [
162
939
  "WorktreeRemove"
163
940
  ];
164
941
  var DEFAULT_EVENT_SETTINGS = {
165
- SessionStart: false,
166
- UserPromptSubmit: false,
167
- UserPromptExpansion: false,
168
- PreToolUse: false,
169
- PermissionRequest: false,
170
- PostToolUse: false,
171
- PostToolUseFailure: false,
172
- PostToolBatch: false,
173
- PermissionDenied: false,
174
- SubagentStart: false,
175
- Stop: false,
176
- StopFailure: false,
177
- TeammateIdle: false,
178
- SubagentStop: false,
179
- TaskCreated: false,
180
- TaskCompleted: false,
181
- PreCompact: false,
182
- PostCompact: false,
183
- SessionEnd: false,
184
- ConfigChange: false,
185
- CwdChanged: false,
186
- FileChanged: false,
187
- WorktreeCreate: false,
188
- WorktreeRemove: false
942
+ SessionStart: true,
943
+ UserPromptSubmit: true,
944
+ UserPromptExpansion: true,
945
+ PreToolUse: true,
946
+ PermissionRequest: true,
947
+ PostToolUse: true,
948
+ PostToolUseFailure: true,
949
+ PostToolBatch: true,
950
+ PermissionDenied: true,
951
+ SubagentStart: true,
952
+ Stop: true,
953
+ StopFailure: true,
954
+ TeammateIdle: true,
955
+ SubagentStop: true,
956
+ TaskCreated: true,
957
+ TaskCompleted: true,
958
+ PreCompact: true,
959
+ PostCompact: true,
960
+ SessionEnd: true,
961
+ ConfigChange: true,
962
+ CwdChanged: true,
963
+ FileChanged: true,
964
+ WorktreeCreate: true,
965
+ WorktreeRemove: true
189
966
  };
967
+ var TEAM_MESSAGE_DELIVERY_EVENTS = new Set([
968
+ "SessionStart",
969
+ "UserPromptSubmit",
970
+ "PostToolUse",
971
+ "PostToolUseFailure",
972
+ "Stop",
973
+ "TeammateIdle",
974
+ "SubagentStop",
975
+ "TaskCompleted"
976
+ ]);
977
+ var CODEX_STOP_EVENTS_WITHOUT_ADDITIONAL_CONTEXT = new Set([
978
+ "Stop",
979
+ "SubagentStop"
980
+ ]);
981
+ var COMPLETION_EVENTS_WITHOUT_DEVELOPMENT_DIAGNOSTICS = new Set([
982
+ "Stop",
983
+ "SubagentStop",
984
+ "TaskCompleted",
985
+ "TeammateIdle"
986
+ ]);
190
987
  function createDefaultHookSettings() {
191
988
  return {
192
- enabled: false,
989
+ enabled: true,
193
990
  targets: {
194
991
  claude: {
195
- enabled: false,
992
+ enabled: true,
196
993
  events: { ...DEFAULT_EVENT_SETTINGS }
197
994
  },
198
995
  codex: {
199
- enabled: false,
996
+ enabled: true,
200
997
  events: { ...DEFAULT_EVENT_SETTINGS }
201
998
  }
202
999
  },
@@ -217,8 +1014,11 @@ function parseHookSettings(value) {
217
1014
  return defaults;
218
1015
  if (!isRecord(value))
219
1016
  throw new Error("Invalid hooks settings; expected object.");
1017
+ const observability = isRecord(value.observability) ? value.observability : undefined;
1018
+ optionalBoolean2(observability?.metadataOnly, defaults.observability.metadataOnly, "hooks.observability.metadataOnly");
1019
+ optionalBoolean2(observability?.rawPayloadStorage, defaults.observability.rawPayloadStorage, "hooks.observability.rawPayloadStorage");
220
1020
  return {
221
- enabled: optionalBoolean(value.enabled, defaults.enabled, "hooks.enabled"),
1021
+ enabled: optionalBoolean2(value.enabled, defaults.enabled, "hooks.enabled"),
222
1022
  targets: {
223
1023
  claude: parseHookTargetSettings(value.targets, defaults.targets.claude, "claude"),
224
1024
  codex: parseHookTargetSettings(value.targets, defaults.targets.codex, "codex")
@@ -226,7 +1026,7 @@ function parseHookSettings(value) {
226
1026
  observability: {
227
1027
  metadataOnly: true,
228
1028
  rawPayloadStorage: false,
229
- appendEvents: false
1029
+ appendEvents: optionalBoolean2(observability?.appendEvents, defaults.observability.appendEvents, "hooks.observability.appendEvents")
230
1030
  },
231
1031
  learning: {
232
1032
  emitCandidates: false,
@@ -240,14 +1040,14 @@ function parseHookTargetSettings(value, defaults, target) {
240
1040
  const events = isRecord(targetSettings.events) ? targetSettings.events : {};
241
1041
  const parsedEvents = { ...defaults.events };
242
1042
  for (const eventType of CANONICAL_HOOK_EVENT_TYPES) {
243
- parsedEvents[eventType] = optionalBoolean(events[eventType], defaults.events[eventType], `hooks.targets.${target}.events.${eventType}`);
1043
+ parsedEvents[eventType] = optionalBoolean2(events[eventType], defaults.events[eventType], `hooks.targets.${target}.events.${eventType}`);
244
1044
  }
245
1045
  return {
246
- enabled: optionalBoolean(targetSettings.enabled, defaults.enabled, `hooks.targets.${target}.enabled`),
1046
+ enabled: optionalBoolean2(targetSettings.enabled, defaults.enabled, `hooks.targets.${target}.enabled`),
247
1047
  events: parsedEvents
248
1048
  };
249
1049
  }
250
- function optionalBoolean(value, fallback, path) {
1050
+ function optionalBoolean2(value, fallback, path) {
251
1051
  if (value === undefined)
252
1052
  return fallback;
253
1053
  if (typeof value !== "boolean")
@@ -286,7 +1086,37 @@ function createDefaultSettings(os = process.platform) {
286
1086
  doctor: {
287
1087
  lastRunAt: null
288
1088
  },
289
- hooks: createDefaultHookSettings()
1089
+ hooks: createDefaultHookSettings(),
1090
+ teamRuntime: createDefaultTeamRuntimeSettings(),
1091
+ memory: createDefaultMemorySettings(),
1092
+ evolution: createDefaultEvolutionSettings()
1093
+ };
1094
+ }
1095
+ function createDefaultTeamRuntimeSettings() {
1096
+ return {
1097
+ defaultRuntime: "codex",
1098
+ defaultModel: null,
1099
+ defaultThinkingLevel: null,
1100
+ recordTranscript: false,
1101
+ displayMode: "normal"
1102
+ };
1103
+ }
1104
+ function createDefaultMemorySettings() {
1105
+ return {
1106
+ autoAccept: true,
1107
+ runtimeInjection: true,
1108
+ staleReview: true,
1109
+ lexicalIndex: true,
1110
+ sessionMemory: createDefaultSessionMemoryPolicy()
1111
+ };
1112
+ }
1113
+ function createDefaultEvolutionSettings() {
1114
+ return {
1115
+ automation: {
1116
+ knowledge: true,
1117
+ semanticKnowledge: false,
1118
+ recommendations: false
1119
+ }
290
1120
  };
291
1121
  }
292
1122
  function mergeSettings(existing, defaults = createDefaultSettings()) {
@@ -321,10 +1151,36 @@ function mergeSettings(existing, defaults = createDefaultSettings()) {
321
1151
  ...defaults.doctor,
322
1152
  ...existing.doctor
323
1153
  },
324
- hooks: existing.hooks ?? defaults.hooks
1154
+ hooks: existing.hooks ?? defaults.hooks,
1155
+ teamRuntime: {
1156
+ ...defaults.teamRuntime,
1157
+ ...existing.teamRuntime
1158
+ },
1159
+ memory: {
1160
+ ...defaults.memory,
1161
+ ...existing.memory
1162
+ },
1163
+ evolution: {
1164
+ ...defaults.evolution,
1165
+ ...existing.evolution,
1166
+ automation: {
1167
+ ...defaults.evolution.automation,
1168
+ ...existing.evolution?.automation
1169
+ }
1170
+ }
325
1171
  };
326
1172
  return parseSettings(merged);
327
1173
  }
1174
+ async function readRuntimeInjectionSettings(homeDir) {
1175
+ const paths2 = resolveEvoDevPaths(homeDir);
1176
+ try {
1177
+ return parseSettings(JSON.parse(await readFile2(paths2.settingsPath, "utf8"))).memory;
1178
+ } catch (error) {
1179
+ if (isNotFoundError2(error))
1180
+ return createDefaultMemorySettings();
1181
+ throw error;
1182
+ }
1183
+ }
328
1184
  function parseSettings(value) {
329
1185
  const root = expectRecord2(value, "settings");
330
1186
  const version = root.version;
@@ -355,10 +1211,58 @@ function parseSettings(value) {
355
1211
  doctor: {
356
1212
  lastRunAt: expectNullableString(doctor.lastRunAt, "settings.doctor.lastRunAt")
357
1213
  },
358
- hooks: parseHookSettings(root.hooks)
1214
+ hooks: parseHookSettings(root.hooks),
1215
+ teamRuntime: parseTeamRuntimeSettings(root.teamRuntime ?? createDefaultTeamRuntimeSettings(), "settings.teamRuntime"),
1216
+ memory: parseMemorySettings(root.memory ?? createDefaultMemorySettings(), "settings.memory"),
1217
+ evolution: parseEvolutionSettings(root.evolution ?? createDefaultEvolutionSettings(), "settings.evolution")
359
1218
  };
360
1219
  return parsed;
361
1220
  }
1221
+ function parseEvolutionSettings(value, path) {
1222
+ const input = expectRecord2(value, path);
1223
+ const defaults = createDefaultEvolutionSettings();
1224
+ const automation = expectRecord2(input.automation ?? defaults.automation, `${path}.automation`);
1225
+ return {
1226
+ automation: {
1227
+ knowledge: automation.knowledge === undefined ? defaults.automation.knowledge : expectBoolean(automation.knowledge, `${path}.automation.knowledge`),
1228
+ semanticKnowledge: automation.semanticKnowledge === undefined ? defaults.automation.semanticKnowledge : expectBoolean(automation.semanticKnowledge, `${path}.automation.semanticKnowledge`),
1229
+ recommendations: automation.recommendations === undefined ? defaults.automation.recommendations : expectBoolean(automation.recommendations, `${path}.automation.recommendations`)
1230
+ }
1231
+ };
1232
+ }
1233
+ function parseMemorySettings(value, path) {
1234
+ const input = expectRecord2(value, path);
1235
+ const defaults = createDefaultMemorySettings();
1236
+ return {
1237
+ autoAccept: input.autoAccept === undefined ? defaults.autoAccept : expectBoolean(input.autoAccept, `${path}.autoAccept`),
1238
+ runtimeInjection: input.runtimeInjection === undefined ? defaults.runtimeInjection : expectBoolean(input.runtimeInjection, `${path}.runtimeInjection`),
1239
+ staleReview: input.staleReview === undefined ? defaults.staleReview : expectBoolean(input.staleReview, `${path}.staleReview`),
1240
+ lexicalIndex: input.lexicalIndex === undefined ? defaults.lexicalIndex : expectBoolean(input.lexicalIndex, `${path}.lexicalIndex`),
1241
+ sessionMemory: parseSessionMemoryPolicy(isPlainRecord(input.sessionMemory) ? input.sessionMemory : defaults.sessionMemory)
1242
+ };
1243
+ }
1244
+ function parseTeamRuntimeSettings(value, path) {
1245
+ const input = expectRecord2(value, path);
1246
+ const defaults = createDefaultTeamRuntimeSettings();
1247
+ const defaultRuntime = input.defaultRuntime ?? defaults.defaultRuntime;
1248
+ if (defaultRuntime !== "codex" && defaultRuntime !== "claude") {
1249
+ throw new EvoDevConfigError(`Invalid ${path}.defaultRuntime; expected codex or claude`);
1250
+ }
1251
+ return {
1252
+ defaultRuntime,
1253
+ defaultModel: input.defaultModel === undefined ? defaults.defaultModel : expectNullableString(input.defaultModel, `${path}.defaultModel`),
1254
+ defaultThinkingLevel: input.defaultThinkingLevel === undefined ? defaults.defaultThinkingLevel : expectNullableString(input.defaultThinkingLevel, `${path}.defaultThinkingLevel`),
1255
+ recordTranscript: input.recordTranscript === undefined ? defaults.recordTranscript : expectBoolean(input.recordTranscript, `${path}.recordTranscript`),
1256
+ displayMode: parseTeamRuntimeDisplayMode(input.displayMode, defaults.displayMode, path)
1257
+ };
1258
+ }
1259
+ function parseTeamRuntimeDisplayMode(value, fallback, path) {
1260
+ if (value === undefined)
1261
+ return fallback;
1262
+ if (value === "normal" || value === "development")
1263
+ return value;
1264
+ throw new EvoDevConfigError(`Invalid ${path}.displayMode; expected normal or development`);
1265
+ }
362
1266
  function parsePluginSettings(value, path) {
363
1267
  const input = expectRecord2(value, path);
364
1268
  const parsed = {
@@ -378,6 +1282,12 @@ function expectRecord2(value, path) {
378
1282
  }
379
1283
  return value;
380
1284
  }
1285
+ function isPlainRecord(value) {
1286
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1287
+ }
1288
+ function isNotFoundError2(error) {
1289
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
1290
+ }
381
1291
  function expectString2(value, path) {
382
1292
  if (typeof value !== "string" || value.length === 0) {
383
1293
  throw new EvoDevConfigError(`Invalid ${path}; expected non-empty string`);
@@ -477,60 +1387,141 @@ function expectNonNegativeInteger(value, path) {
477
1387
  return value;
478
1388
  }
479
1389
  // packages/core/src/config/store.ts
480
- import { mkdir, readFile, writeFile } from "node:fs/promises";
481
- import { dirname } from "node:path";
1390
+ import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
1391
+ import { dirname as dirname2 } from "node:path";
482
1392
  function createCoreConfigStore(homeDir) {
483
- const paths = resolveEvoDevPaths(homeDir);
1393
+ const paths2 = resolveEvoDevPaths(homeDir);
484
1394
  return {
485
- paths,
1395
+ paths: paths2,
486
1396
  async ensureBaseDirs() {
487
- await mkdir(paths.stateDir, { recursive: true });
1397
+ await mkdir2(paths2.stateDir, { recursive: true });
1398
+ await mkdir2(paths2.logsDir, { recursive: true });
1399
+ await mkdir2(paths2.knowledgeDir, { recursive: true });
1400
+ await mkdir2(paths2.evosCasesDir, { recursive: true });
1401
+ await mkdir2(paths2.roleAgentsDir, { recursive: true });
1402
+ await mkdir2(paths2.teamsDir, { recursive: true });
1403
+ await mkdir2(paths2.runsDir, { recursive: true });
1404
+ },
1405
+ async ensureKnowledgeBase() {
1406
+ await ensureKnowledgeBaseFiles(paths2);
488
1407
  },
489
1408
  async readSettings() {
490
- return readJsonFile(paths.settingsPath, parseSettings);
1409
+ return readJsonFile(paths2.settingsPath, parseSettings);
491
1410
  },
492
1411
  async writeSettings(settings) {
493
- await writeJsonFile(paths.settingsPath, parseSettings(settings));
1412
+ await writeJsonFile2(paths2.settingsPath, parseSettings(settings));
494
1413
  },
495
1414
  async mergeAndWriteSettings(input) {
496
- const current = await readJsonFileOrDefault(paths.settingsPath, parseSettings, createDefaultSettings());
1415
+ const current = await readJsonFileOrDefault(paths2.settingsPath, parseSettings, createDefaultSettings());
497
1416
  const merged = mergeSettings(input, current);
498
- await writeJsonFile(paths.settingsPath, merged);
1417
+ await writeJsonFile2(paths2.settingsPath, merged);
499
1418
  return merged;
500
1419
  },
501
1420
  async readRegistry() {
502
- return readJsonFile(paths.registryPath, parseRegistry);
1421
+ return readJsonFile(paths2.registryPath, parseRegistry);
503
1422
  },
504
1423
  async writeRegistry(registry) {
505
- await writeJsonFile(paths.registryPath, parseRegistry(registry));
1424
+ await writeJsonFile2(paths2.registryPath, parseRegistry(registry));
506
1425
  },
507
1426
  async readInstallState() {
508
- return readJsonFile(paths.installStatePath, parseInstallState);
1427
+ return readJsonFile(paths2.installStatePath, parseInstallState);
509
1428
  },
510
1429
  async writeInstallState(state) {
511
- await writeJsonFile(paths.installStatePath, parseInstallState(state));
1430
+ await writeJsonFile2(paths2.installStatePath, parseInstallState(state));
512
1431
  },
513
1432
  async readSyncState() {
514
- return readJsonFile(paths.syncStatePath, parseSyncState);
1433
+ return readJsonFile(paths2.syncStatePath, parseSyncState);
515
1434
  },
516
1435
  async writeSyncState(state) {
517
- await writeJsonFile(paths.syncStatePath, parseSyncState(state));
1436
+ await writeJsonFile2(paths2.syncStatePath, parseSyncState(state));
518
1437
  }
519
1438
  };
520
1439
  }
521
1440
  async function initializeCoreConfig(homeDir) {
522
1441
  const store = createCoreConfigStore(homeDir);
523
1442
  await store.ensureBaseDirs();
1443
+ await store.ensureKnowledgeBase();
524
1444
  await writeIfMissing(store.paths.settingsPath, createDefaultSettings());
525
1445
  await writeIfMissing(store.paths.registryPath, createDefaultRegistry());
526
1446
  await writeIfMissing(store.paths.installStatePath, createDefaultInstallState());
527
1447
  await writeIfMissing(store.paths.syncStatePath, createDefaultSyncState());
528
1448
  return store;
529
1449
  }
1450
+ async function ensureKnowledgeBaseFiles(paths2) {
1451
+ await mkdir2(paths2.knowledgeDir, { recursive: true });
1452
+ await mkdir2(paths2.evosCasesDir, { recursive: true });
1453
+ await ensureOkfKnowledgeBase(paths2.homeDir);
1454
+ await writeTextIfMissing2(`${paths2.knowledgeDir}/README.md`, [
1455
+ "# EvoDev Knowledge",
1456
+ "",
1457
+ "Local-private knowledge base for user-accepted facts, decisions, architecture notes, and reusable domain context.",
1458
+ "",
1459
+ "EvoDev must not populate this directory from source code, prompts, command output, logs, or transcripts without an explicit consent flow.",
1460
+ ""
1461
+ ].join(`
1462
+ `));
1463
+ await writeIndexIfMissingOrMigrate(paths2.knowledgeIndexPath, "knowledge-index", {
1464
+ version: 1,
1465
+ kind: "knowledge-index",
1466
+ roleTags: [],
1467
+ entries: []
1468
+ });
1469
+ await writeTextIfMissing2(`${paths2.evosDir}/README.md`, [
1470
+ "# EvoDev Evos",
1471
+ "",
1472
+ "Local-private evolution case library for reviewed improvement cases and reusable process changes.",
1473
+ "",
1474
+ "Cases start empty. Future automation may propose candidates, but accepted evos require explicit review before they can influence workflows or routing.",
1475
+ ""
1476
+ ].join(`
1477
+ `));
1478
+ await writeTextIfMissing2(`${paths2.evosCasesDir}/README.md`, [
1479
+ "# Evolution Cases",
1480
+ "",
1481
+ "Store one reviewed evolution case per file. Do not store raw prompts, source dumps, secrets, transcripts, or raw command output here.",
1482
+ ""
1483
+ ].join(`
1484
+ `));
1485
+ await writeIndexIfMissingOrMigrate(paths2.evosIndexPath, "evos-index", {
1486
+ version: 1,
1487
+ kind: "evos-index",
1488
+ roleTags: [],
1489
+ cases: []
1490
+ });
1491
+ await writeTextIfMissing2(`${paths2.roleAgentsDir}/README.md`, [
1492
+ "# Role Agents",
1493
+ "",
1494
+ "Local-private role agent registry for EvoDev-managed agent roles and user-reviewed role extensions.",
1495
+ "",
1496
+ "Repository-specific role agents should be proposed first and written into a user repository only after explicit project opt-in.",
1497
+ ""
1498
+ ].join(`
1499
+ `));
1500
+ await writeIndexIfMissingOrMigrate(paths2.roleAgentsIndexPath, "role-agent-index", {
1501
+ version: 1,
1502
+ kind: "role-agent-index",
1503
+ roles: [],
1504
+ projectExtensions: []
1505
+ });
1506
+ await writeTextIfMissing2(`${paths2.teamsDir}/README.md`, [
1507
+ "# Agent Teams",
1508
+ "",
1509
+ "Local-private EvoHub team registry for reviewed role-agent team definitions.",
1510
+ "",
1511
+ "Teams may reference role agents and role-tagged knowledge, but they must not contain raw source, prompts, transcripts, secrets, or raw command output.",
1512
+ ""
1513
+ ].join(`
1514
+ `));
1515
+ await writeIndexIfMissingOrMigrate(paths2.teamsIndexPath, "agent-team-index", {
1516
+ version: 1,
1517
+ kind: "agent-team-index",
1518
+ teams: []
1519
+ });
1520
+ }
530
1521
  async function readJsonFile(filePath, parse) {
531
1522
  let raw;
532
1523
  try {
533
- raw = await readFile(filePath, "utf8");
1524
+ raw = await readFile3(filePath, "utf8");
534
1525
  } catch (error) {
535
1526
  throw new EvoDevConfigError(`Cannot read config file (${describeFileError(error)})`, filePath);
536
1527
  }
@@ -561,18 +1552,54 @@ async function readJsonFileOrDefault(filePath, parse, fallback) {
561
1552
  }
562
1553
  async function writeIfMissing(filePath, value) {
563
1554
  try {
564
- await readFile(filePath, "utf8");
1555
+ await readFile3(filePath, "utf8");
565
1556
  } catch (error) {
566
1557
  if (isNodeError(error) && error.code === "ENOENT") {
567
- await writeJsonFile(filePath, value);
1558
+ await writeJsonFile2(filePath, value);
568
1559
  return;
569
1560
  }
570
1561
  throw new EvoDevConfigError(`Cannot inspect config file (${describeFileError(error)})`, filePath);
571
1562
  }
572
1563
  }
573
- async function writeJsonFile(filePath, value) {
574
- await mkdir(dirname(filePath), { recursive: true });
575
- await writeFile(filePath, `${JSON.stringify(value, null, 2)}
1564
+ async function writeIndexIfMissingOrMigrate(filePath, kind, defaults) {
1565
+ let raw;
1566
+ try {
1567
+ raw = await readFile3(filePath, "utf8");
1568
+ } catch (error) {
1569
+ if (isNodeError(error) && error.code === "ENOENT") {
1570
+ await writeJsonFile2(filePath, defaults);
1571
+ return;
1572
+ }
1573
+ throw new EvoDevConfigError(`Cannot inspect config file (${describeFileError(error)})`, filePath);
1574
+ }
1575
+ let existing;
1576
+ try {
1577
+ existing = JSON.parse(raw);
1578
+ } catch (error) {
1579
+ throw new EvoDevConfigError(`Invalid bootstrap index JSON (${describeFileError(error)})`, filePath);
1580
+ }
1581
+ if (!isRecord2(existing) || existing.kind !== kind)
1582
+ return;
1583
+ const migrated = { ...defaults, ...existing };
1584
+ if (Object.keys(defaults).every((key) => (key in existing)))
1585
+ return;
1586
+ await writeJsonFile2(filePath, migrated);
1587
+ }
1588
+ async function writeTextIfMissing2(filePath, value) {
1589
+ try {
1590
+ await readFile3(filePath, "utf8");
1591
+ } catch (error) {
1592
+ if (isNodeError(error) && error.code === "ENOENT") {
1593
+ await mkdir2(dirname2(filePath), { recursive: true });
1594
+ await writeFile2(filePath, value, "utf8");
1595
+ return;
1596
+ }
1597
+ throw new EvoDevConfigError(`Cannot inspect config file (${describeFileError(error)})`, filePath);
1598
+ }
1599
+ }
1600
+ async function writeJsonFile2(filePath, value) {
1601
+ await mkdir2(dirname2(filePath), { recursive: true });
1602
+ await writeFile2(filePath, `${JSON.stringify(value, null, 2)}
576
1603
  `, "utf8");
577
1604
  }
578
1605
  function describeFileError(error) {
@@ -584,18 +1611,25 @@ function describeFileError(error) {
584
1611
  function isNodeError(error) {
585
1612
  return error instanceof Error && "code" in error;
586
1613
  }
1614
+ function isRecord2(value) {
1615
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1616
+ }
587
1617
  export {
588
1618
  resolveEvoDevPaths,
1619
+ readRuntimeInjectionSettings,
589
1620
  parseSyncState,
590
1621
  parseSettings,
591
1622
  parseRegistry,
592
1623
  parseInstallState,
593
1624
  mergeSettings,
594
1625
  initializeCoreConfig,
1626
+ createDefaultTeamRuntimeSettings,
595
1627
  createDefaultSyncState,
596
1628
  createDefaultSettings,
597
1629
  createDefaultRegistry,
1630
+ createDefaultMemorySettings,
598
1631
  createDefaultInstallState,
1632
+ createDefaultEvolutionSettings,
599
1633
  createCoreConfigStore,
600
1634
  EvoDevConfigError
601
1635
  };