aspectcode 0.4.0 → 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 (242) hide show
  1. package/README.md +13 -0
  2. package/dist/agentsMdRenderer.d.ts +16 -0
  3. package/dist/agentsMdRenderer.d.ts.map +1 -0
  4. package/dist/agentsMdRenderer.js +137 -0
  5. package/dist/agentsMdRenderer.js.map +1 -0
  6. package/dist/auth.d.ts +31 -0
  7. package/dist/auth.d.ts.map +1 -0
  8. package/dist/auth.js +385 -0
  9. package/dist/auth.js.map +1 -0
  10. package/dist/autoResolve.d.ts +41 -0
  11. package/dist/autoResolve.d.ts.map +1 -0
  12. package/dist/autoResolve.js +196 -0
  13. package/dist/autoResolve.js.map +1 -0
  14. package/dist/changeEvaluator.d.ts +56 -0
  15. package/dist/changeEvaluator.d.ts.map +1 -0
  16. package/dist/changeEvaluator.js +674 -0
  17. package/dist/changeEvaluator.js.map +1 -0
  18. package/dist/cli.d.ts +12 -1
  19. package/dist/cli.d.ts.map +1 -1
  20. package/dist/cli.js +1 -1
  21. package/dist/cli.js.map +1 -1
  22. package/dist/config.d.ts +37 -17
  23. package/dist/config.d.ts.map +1 -1
  24. package/dist/config.js +50 -2
  25. package/dist/config.js.map +1 -1
  26. package/dist/dreamCycle.d.ts +57 -0
  27. package/dist/dreamCycle.d.ts.map +1 -0
  28. package/dist/dreamCycle.js +334 -0
  29. package/dist/dreamCycle.js.map +1 -0
  30. package/dist/kbBuilder.d.ts +1 -2
  31. package/dist/kbBuilder.d.ts.map +1 -1
  32. package/dist/kbBuilder.js +1 -2
  33. package/dist/kbBuilder.js.map +1 -1
  34. package/dist/main.d.ts +2 -1
  35. package/dist/main.d.ts.map +1 -1
  36. package/dist/main.js +149 -8
  37. package/dist/main.js.map +1 -1
  38. package/dist/optimize.d.ts +13 -6
  39. package/dist/optimize.d.ts.map +1 -1
  40. package/dist/optimize.js +433 -142
  41. package/dist/optimize.js.map +1 -1
  42. package/dist/pipeline.d.ts +21 -18
  43. package/dist/pipeline.d.ts.map +1 -1
  44. package/dist/pipeline.js +1139 -162
  45. package/dist/pipeline.js.map +1 -1
  46. package/dist/preferences.d.ts +80 -0
  47. package/dist/preferences.d.ts.map +1 -0
  48. package/dist/preferences.js +238 -0
  49. package/dist/preferences.js.map +1 -0
  50. package/dist/runtimeState.d.ts +30 -0
  51. package/dist/runtimeState.d.ts.map +1 -0
  52. package/dist/runtimeState.js +39 -0
  53. package/dist/runtimeState.js.map +1 -0
  54. package/dist/scopedRules.d.ts +84 -0
  55. package/dist/scopedRules.d.ts.map +1 -0
  56. package/dist/scopedRules.js +449 -0
  57. package/dist/scopedRules.js.map +1 -0
  58. package/dist/ui/Dashboard.d.ts +4 -16
  59. package/dist/ui/Dashboard.d.ts.map +1 -1
  60. package/dist/ui/Dashboard.js +339 -141
  61. package/dist/ui/Dashboard.js.map +1 -1
  62. package/dist/ui/MemoryMap.d.ts +16 -0
  63. package/dist/ui/MemoryMap.d.ts.map +1 -0
  64. package/dist/ui/MemoryMap.js +266 -0
  65. package/dist/ui/MemoryMap.js.map +1 -0
  66. package/dist/ui/SettingsPanel.d.ts +18 -0
  67. package/dist/ui/SettingsPanel.d.ts.map +1 -0
  68. package/dist/ui/SettingsPanel.js +241 -0
  69. package/dist/ui/SettingsPanel.js.map +1 -0
  70. package/dist/ui/prompts.d.ts +7 -0
  71. package/dist/ui/prompts.d.ts.map +1 -1
  72. package/dist/ui/prompts.js +63 -0
  73. package/dist/ui/prompts.js.map +1 -1
  74. package/dist/ui/store.d.ts +154 -18
  75. package/dist/ui/store.d.ts.map +1 -1
  76. package/dist/ui/store.js +154 -24
  77. package/dist/ui/store.js.map +1 -1
  78. package/dist/ui/theme.d.ts +1 -8
  79. package/dist/ui/theme.d.ts.map +1 -1
  80. package/dist/ui/theme.js +2 -20
  81. package/dist/ui/theme.js.map +1 -1
  82. package/dist/updateChecker.d.ts +13 -0
  83. package/dist/updateChecker.d.ts.map +1 -0
  84. package/dist/updateChecker.js +66 -0
  85. package/dist/updateChecker.js.map +1 -0
  86. package/dist/usageTracker.d.ts +12 -0
  87. package/dist/usageTracker.d.ts.map +1 -0
  88. package/dist/usageTracker.js +89 -0
  89. package/dist/usageTracker.js.map +1 -0
  90. package/dist/writer.d.ts +1 -7
  91. package/dist/writer.d.ts.map +1 -1
  92. package/dist/writer.js +1 -11
  93. package/dist/writer.js.map +1 -1
  94. package/node_modules/@aspectcode/core/dist/analysis/repo.d.ts.map +1 -1
  95. package/node_modules/@aspectcode/core/dist/analysis/repo.js +13 -2
  96. package/node_modules/@aspectcode/core/dist/analysis/repo.js.map +1 -1
  97. package/node_modules/@aspectcode/core/dist/index.d.ts +1 -3
  98. package/node_modules/@aspectcode/core/dist/index.d.ts.map +1 -1
  99. package/node_modules/@aspectcode/core/dist/index.js +1 -3
  100. package/node_modules/@aspectcode/core/dist/index.js.map +1 -1
  101. package/node_modules/@aspectcode/core/dist/parsers/genericExtractors.d.ts +14 -0
  102. package/node_modules/@aspectcode/core/dist/parsers/genericExtractors.d.ts.map +1 -0
  103. package/node_modules/@aspectcode/core/dist/parsers/genericExtractors.js +191 -0
  104. package/node_modules/@aspectcode/core/dist/parsers/genericExtractors.js.map +1 -0
  105. package/node_modules/@aspectcode/core/dist/parsers/index.d.ts +1 -0
  106. package/node_modules/@aspectcode/core/dist/parsers/index.d.ts.map +1 -1
  107. package/node_modules/@aspectcode/core/dist/parsers/index.js +6 -1
  108. package/node_modules/@aspectcode/core/dist/parsers/index.js.map +1 -1
  109. package/node_modules/@aspectcode/core/dist/parsers/languages.d.ts +20 -0
  110. package/node_modules/@aspectcode/core/dist/parsers/languages.d.ts.map +1 -1
  111. package/node_modules/@aspectcode/core/dist/parsers/languages.js +25 -0
  112. package/node_modules/@aspectcode/core/dist/parsers/languages.js.map +1 -1
  113. package/node_modules/@aspectcode/core/dist/parsers/tsJsExtractors.d.ts.map +1 -1
  114. package/node_modules/@aspectcode/core/dist/parsers/tsJsExtractors.js +4 -1
  115. package/node_modules/@aspectcode/core/dist/parsers/tsJsExtractors.js.map +1 -1
  116. package/node_modules/@aspectcode/core/package.json +2 -2
  117. package/node_modules/@aspectcode/core/parsers/cpp.wasm +0 -0
  118. package/node_modules/@aspectcode/core/parsers/go.wasm +0 -0
  119. package/node_modules/@aspectcode/core/parsers/php.wasm +0 -0
  120. package/node_modules/@aspectcode/core/parsers/ruby.wasm +0 -0
  121. package/node_modules/@aspectcode/core/parsers/rust.wasm +0 -0
  122. package/node_modules/@aspectcode/emitters/dist/index.d.ts +1 -17
  123. package/node_modules/@aspectcode/emitters/dist/index.d.ts.map +1 -1
  124. package/node_modules/@aspectcode/emitters/dist/index.js +2 -89
  125. package/node_modules/@aspectcode/emitters/dist/index.js.map +1 -1
  126. package/node_modules/@aspectcode/emitters/dist/instructions/index.d.ts +0 -2
  127. package/node_modules/@aspectcode/emitters/dist/instructions/index.d.ts.map +1 -1
  128. package/node_modules/@aspectcode/emitters/dist/instructions/index.js +1 -7
  129. package/node_modules/@aspectcode/emitters/dist/instructions/index.js.map +1 -1
  130. package/node_modules/@aspectcode/emitters/dist/kb/analyzers.d.ts +0 -18
  131. package/node_modules/@aspectcode/emitters/dist/kb/analyzers.d.ts.map +1 -1
  132. package/node_modules/@aspectcode/emitters/dist/kb/analyzers.js +0 -57
  133. package/node_modules/@aspectcode/emitters/dist/kb/analyzers.js.map +1 -1
  134. package/node_modules/@aspectcode/emitters/dist/kb/conventions.d.ts +0 -18
  135. package/node_modules/@aspectcode/emitters/dist/kb/conventions.d.ts.map +1 -1
  136. package/node_modules/@aspectcode/emitters/dist/kb/conventions.js +0 -130
  137. package/node_modules/@aspectcode/emitters/dist/kb/conventions.js.map +1 -1
  138. package/node_modules/@aspectcode/emitters/dist/kb/index.d.ts +2 -4
  139. package/node_modules/@aspectcode/emitters/dist/kb/index.d.ts.map +1 -1
  140. package/node_modules/@aspectcode/emitters/dist/kb/index.js +1 -11
  141. package/node_modules/@aspectcode/emitters/dist/kb/index.js.map +1 -1
  142. package/node_modules/@aspectcode/emitters/package.json +3 -3
  143. package/node_modules/@aspectcode/evaluator/dist/apply.d.ts +55 -0
  144. package/node_modules/@aspectcode/evaluator/dist/apply.d.ts.map +1 -0
  145. package/node_modules/@aspectcode/evaluator/dist/apply.js +368 -0
  146. package/node_modules/@aspectcode/evaluator/dist/apply.js.map +1 -0
  147. package/node_modules/@aspectcode/evaluator/dist/diagnosis.d.ts +16 -25
  148. package/node_modules/@aspectcode/evaluator/dist/diagnosis.d.ts.map +1 -1
  149. package/node_modules/@aspectcode/evaluator/dist/diagnosis.js +115 -138
  150. package/node_modules/@aspectcode/evaluator/dist/diagnosis.js.map +1 -1
  151. package/node_modules/@aspectcode/evaluator/dist/index.d.ts +8 -43
  152. package/node_modules/@aspectcode/evaluator/dist/index.d.ts.map +1 -1
  153. package/node_modules/@aspectcode/evaluator/dist/index.js +15 -61
  154. package/node_modules/@aspectcode/evaluator/dist/index.js.map +1 -1
  155. package/node_modules/@aspectcode/evaluator/dist/judge.d.ts +32 -0
  156. package/node_modules/@aspectcode/evaluator/dist/judge.d.ts.map +1 -0
  157. package/node_modules/@aspectcode/evaluator/dist/judge.js +165 -0
  158. package/node_modules/@aspectcode/evaluator/dist/judge.js.map +1 -0
  159. package/node_modules/@aspectcode/evaluator/dist/llmUtil.d.ts +15 -0
  160. package/node_modules/@aspectcode/evaluator/dist/llmUtil.d.ts.map +1 -0
  161. package/node_modules/@aspectcode/evaluator/dist/llmUtil.js +41 -0
  162. package/node_modules/@aspectcode/evaluator/dist/llmUtil.js.map +1 -0
  163. package/node_modules/@aspectcode/evaluator/dist/probes.d.ts +20 -29
  164. package/node_modules/@aspectcode/evaluator/dist/probes.d.ts.map +1 -1
  165. package/node_modules/@aspectcode/evaluator/dist/probes.js +188 -204
  166. package/node_modules/@aspectcode/evaluator/dist/probes.js.map +1 -1
  167. package/node_modules/@aspectcode/evaluator/dist/runner.d.ts +7 -32
  168. package/node_modules/@aspectcode/evaluator/dist/runner.d.ts.map +1 -1
  169. package/node_modules/@aspectcode/evaluator/dist/runner.js +21 -146
  170. package/node_modules/@aspectcode/evaluator/dist/runner.js.map +1 -1
  171. package/node_modules/@aspectcode/evaluator/dist/types.d.ts +141 -99
  172. package/node_modules/@aspectcode/evaluator/dist/types.d.ts.map +1 -1
  173. package/node_modules/@aspectcode/evaluator/dist/types.js +10 -2
  174. package/node_modules/@aspectcode/evaluator/dist/types.js.map +1 -1
  175. package/node_modules/@aspectcode/evaluator/package.json +4 -4
  176. package/node_modules/@aspectcode/optimizer/dist/index.d.ts +3 -10
  177. package/node_modules/@aspectcode/optimizer/dist/index.d.ts.map +1 -1
  178. package/node_modules/@aspectcode/optimizer/dist/index.js +1 -19
  179. package/node_modules/@aspectcode/optimizer/dist/index.js.map +1 -1
  180. package/node_modules/@aspectcode/optimizer/dist/providers/anthropic.d.ts.map +1 -1
  181. package/node_modules/@aspectcode/optimizer/dist/providers/anthropic.js +40 -0
  182. package/node_modules/@aspectcode/optimizer/dist/providers/anthropic.js.map +1 -1
  183. package/node_modules/@aspectcode/optimizer/dist/providers/aspectcode.d.ts +9 -0
  184. package/node_modules/@aspectcode/optimizer/dist/providers/aspectcode.d.ts.map +1 -0
  185. package/node_modules/@aspectcode/optimizer/dist/providers/aspectcode.js +83 -0
  186. package/node_modules/@aspectcode/optimizer/dist/providers/aspectcode.js.map +1 -0
  187. package/node_modules/@aspectcode/optimizer/dist/providers/index.d.ts +4 -3
  188. package/node_modules/@aspectcode/optimizer/dist/providers/index.d.ts.map +1 -1
  189. package/node_modules/@aspectcode/optimizer/dist/providers/index.js +24 -10
  190. package/node_modules/@aspectcode/optimizer/dist/providers/index.js.map +1 -1
  191. package/node_modules/@aspectcode/optimizer/dist/providers/openai.d.ts.map +1 -1
  192. package/node_modules/@aspectcode/optimizer/dist/providers/openai.js +22 -0
  193. package/node_modules/@aspectcode/optimizer/dist/providers/openai.js.map +1 -1
  194. package/node_modules/@aspectcode/optimizer/dist/providers/retry.d.ts +14 -0
  195. package/node_modules/@aspectcode/optimizer/dist/providers/retry.d.ts.map +1 -1
  196. package/node_modules/@aspectcode/optimizer/dist/providers/retry.js +1 -0
  197. package/node_modules/@aspectcode/optimizer/dist/providers/retry.js.map +1 -1
  198. package/node_modules/@aspectcode/optimizer/dist/types.d.ts +14 -0
  199. package/node_modules/@aspectcode/optimizer/dist/types.d.ts.map +1 -1
  200. package/node_modules/@aspectcode/optimizer/dist/types.js.map +1 -1
  201. package/node_modules/@aspectcode/optimizer/package.json +2 -2
  202. package/package.json +6 -7
  203. package/dist/complaintProcessor.d.ts +0 -16
  204. package/dist/complaintProcessor.d.ts.map +0 -1
  205. package/dist/complaintProcessor.js +0 -134
  206. package/dist/complaintProcessor.js.map +0 -1
  207. package/node_modules/@aspectcode/emitters/dist/emitter.d.ts +0 -72
  208. package/node_modules/@aspectcode/emitters/dist/emitter.d.ts.map +0 -1
  209. package/node_modules/@aspectcode/emitters/dist/emitter.js +0 -10
  210. package/node_modules/@aspectcode/emitters/dist/emitter.js.map +0 -1
  211. package/node_modules/@aspectcode/emitters/dist/instructions/content.d.ts +0 -15
  212. package/node_modules/@aspectcode/emitters/dist/instructions/content.d.ts.map +0 -1
  213. package/node_modules/@aspectcode/emitters/dist/instructions/content.js +0 -289
  214. package/node_modules/@aspectcode/emitters/dist/instructions/content.js.map +0 -1
  215. package/node_modules/@aspectcode/emitters/dist/instructions/detection.d.ts +0 -13
  216. package/node_modules/@aspectcode/emitters/dist/instructions/detection.d.ts.map +0 -1
  217. package/node_modules/@aspectcode/emitters/dist/instructions/detection.js +0 -55
  218. package/node_modules/@aspectcode/emitters/dist/instructions/detection.js.map +0 -1
  219. package/node_modules/@aspectcode/emitters/dist/instructions/instructionsEmitter.d.ts +0 -9
  220. package/node_modules/@aspectcode/emitters/dist/instructions/instructionsEmitter.d.ts.map +0 -1
  221. package/node_modules/@aspectcode/emitters/dist/instructions/instructionsEmitter.js +0 -30
  222. package/node_modules/@aspectcode/emitters/dist/instructions/instructionsEmitter.js.map +0 -1
  223. package/node_modules/@aspectcode/emitters/dist/kb/kbEmitter.d.ts +0 -21
  224. package/node_modules/@aspectcode/emitters/dist/kb/kbEmitter.d.ts.map +0 -1
  225. package/node_modules/@aspectcode/emitters/dist/kb/kbEmitter.js +0 -125
  226. package/node_modules/@aspectcode/emitters/dist/kb/kbEmitter.js.map +0 -1
  227. package/node_modules/@aspectcode/emitters/dist/manifest.d.ts +0 -37
  228. package/node_modules/@aspectcode/emitters/dist/manifest.d.ts.map +0 -1
  229. package/node_modules/@aspectcode/emitters/dist/manifest.js +0 -50
  230. package/node_modules/@aspectcode/emitters/dist/manifest.js.map +0 -1
  231. package/node_modules/@aspectcode/emitters/dist/report.d.ts +0 -22
  232. package/node_modules/@aspectcode/emitters/dist/report.d.ts.map +0 -1
  233. package/node_modules/@aspectcode/emitters/dist/report.js +0 -3
  234. package/node_modules/@aspectcode/emitters/dist/report.js.map +0 -1
  235. package/node_modules/@aspectcode/emitters/dist/stableJson.d.ts +0 -14
  236. package/node_modules/@aspectcode/emitters/dist/stableJson.d.ts.map +0 -1
  237. package/node_modules/@aspectcode/emitters/dist/stableJson.js +0 -40
  238. package/node_modules/@aspectcode/emitters/dist/stableJson.js.map +0 -1
  239. package/node_modules/@aspectcode/emitters/dist/transaction.d.ts +0 -29
  240. package/node_modules/@aspectcode/emitters/dist/transaction.d.ts.map +0 -1
  241. package/node_modules/@aspectcode/emitters/dist/transaction.js +0 -104
  242. package/node_modules/@aspectcode/emitters/dist/transaction.js.map +0 -1
package/dist/optimize.js CHANGED
@@ -2,31 +2,34 @@
2
2
  /**
3
3
  * Optimize wrapper — tries LLM optimization, falls back to static content.
4
4
  *
5
- * - If an API key is available → run the optimization agent
6
- * - If evaluator is enabled → single-pass optimize then probe & diagnose
5
+ * - If an API key is available → generate seed AGENTS.md from KB
6
+ * - If probeAndRefine=true run multi-iteration probe-and-refine loop
7
7
  * - If no API key → warn and write static AGENTS.md content
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.tryOptimize = tryOptimize;
11
11
  const optimizer_1 = require("@aspectcode/optimizer");
12
12
  const evaluator_1 = require("@aspectcode/evaluator");
13
- const emitters_1 = require("@aspectcode/emitters");
14
13
  const logger_1 = require("./logger");
15
14
  const store_1 = require("./ui/store");
15
+ const auth_1 = require("./auth");
16
+ const usageTracker_1 = require("./usageTracker");
17
+ const preferences_1 = require("./preferences");
16
18
  /**
17
19
  * Try to generate AGENTS.md content via LLM using static analysis as context.
18
20
  * Falls back to static instruction content when no API key is available.
21
+ *
22
+ * @param probeAndRefine When true, run the multi-iteration probe-and-refine loop
23
+ * after generating the seed. Only on first run or manual rerun.
19
24
  */
20
- async function tryOptimize(ctx, kbContent, toolInstructions, config, baseContent) {
25
+ async function tryOptimize(ctx, kbContent, _toolInstructions, config, _baseContent, probeAndRefine = false, preferences, userSettings, staticRules) {
21
26
  const { flags, log, root } = ctx;
22
- const optConfig = config?.optimize;
23
27
  const evalConfig = config?.evaluate;
24
- const evaluatorEnabled = evalConfig?.enabled !== false; // Default: true
25
- // ── Resolve settings ──────────────────────────────────────
26
- const temperature = flags.temperature ?? optConfig?.temperature;
27
- const model = flags.model ?? optConfig?.model;
28
- const providerName = flags.provider ?? optConfig?.provider;
29
- const maxTokens = optConfig?.maxTokens;
28
+ const evaluatorEnabled = probeAndRefine && evalConfig?.enabled !== false;
29
+ // ── Resolve settings (CLI flags > cloud user settings > defaults) ──
30
+ const temperature = flags.temperature ?? userSettings?.temperature;
31
+ const model = flags.model ?? userSettings?.model;
32
+ const maxTokens = userSettings?.maxTokens;
30
33
  // ── Load .env and try to resolve a provider ───────────────
31
34
  let env;
32
35
  try {
@@ -35,8 +38,19 @@ async function tryOptimize(ctx, kbContent, toolInstructions, config, baseContent
35
38
  catch {
36
39
  env = {};
37
40
  }
38
- if (providerName && !env['LLM_PROVIDER']) {
39
- env['LLM_PROVIDER'] = providerName;
41
+ // Inject BYOK key from aspectcode.json if set
42
+ if (config?.apiKey && !env['ASPECTCODE_LLM_KEY']) {
43
+ env['ASPECTCODE_LLM_KEY'] = config.apiKey;
44
+ }
45
+ // Pass CLI token so the aspectcode hosted provider can authenticate
46
+ const creds = (0, auth_1.loadCredentials)();
47
+ if (creds && !env['ASPECTCODE_CLI_TOKEN']) {
48
+ env['ASPECTCODE_CLI_TOKEN'] = creds.token;
49
+ }
50
+ // Only set LLM_PROVIDER from CLI flags, not user settings.
51
+ // When logged in, the hosted proxy handles model selection server-side.
52
+ if (flags.provider && !env['LLM_PROVIDER']) {
53
+ env['LLM_PROVIDER'] = flags.provider;
40
54
  }
41
55
  const providerOptions = {};
42
56
  if (model)
@@ -46,169 +60,446 @@ async function tryOptimize(ctx, kbContent, toolInstructions, config, baseContent
46
60
  if (maxTokens !== undefined)
47
61
  providerOptions.maxTokens = maxTokens;
48
62
  let provider;
63
+ let diagnosisProvider; // Sonnet for higher-quality diagnosis
49
64
  try {
50
- provider = (0, optimizer_1.resolveProvider)(env, providerOptions);
65
+ provider = (0, usageTracker_1.withUsageTracking)((0, optimizer_1.resolveProvider)(env, providerOptions));
66
+ // Use Sonnet for the diagnosis step (higher quality for the most impactful call)
67
+ try {
68
+ diagnosisProvider = (0, usageTracker_1.withUsageTracking)((0, optimizer_1.resolveProvider)(env, { ...providerOptions, model: 'claude-sonnet-4-20250514' }));
69
+ }
70
+ catch {
71
+ diagnosisProvider = provider; // Fall back to same provider
72
+ }
51
73
  }
52
- catch {
53
- // No API key available — fall back to static content
54
- store_1.store.addSetupNote('no API key static mode');
55
- log.warn('No LLM API key found. Set OPENAI_API_KEY or ANTHROPIC_API_KEY in .env for optimization.');
56
- return {
57
- content: (0, emitters_1.generateCanonicalContentForMode)('safe', kbContent.length > 0),
58
- reasoning: [],
59
- };
74
+ catch (providerErr) {
75
+ store_1.store.addSetupNote('no LLM available — using static content');
76
+ const errMsg = providerErr instanceof Error ? providerErr.message : String(providerErr);
77
+ log.warn(`LLM failed: ${errMsg}`);
78
+ return { content: _baseContent, reasoning: [], scopedRules: [], deleteSlugs: [] };
60
79
  }
61
80
  const providerLabel = model ? `${provider.name} (${model})` : provider.name;
62
- store_1.store.addSetupNote(`API key: ${provider.name}`);
63
- if (evaluatorEnabled) {
64
- store_1.store.addSetupNote('evaluator on');
65
- }
66
81
  log.info(`Generating with ${logger_1.fmt.cyan(provider.name)}${model ? ` (${logger_1.fmt.cyan(model)})` : ''}…`);
67
82
  store_1.store.setProvider(providerLabel);
68
- // ── Use static content only as fallback (LLM error / cancellation) ─
69
- const fallbackContent = baseContent;
70
- // ── Build tool instructions context string ────────────────
71
- let toolContext = '';
72
- if (toolInstructions.size > 0) {
73
- const parts = [];
74
- for (const [tool, content] of toolInstructions) {
75
- parts.push(`### ${tool}\n${content}`);
76
- }
77
- toolContext = parts.join('\n\n');
78
- }
79
- // ── Progress callbacks for live dashboard updates ─────────
80
- const onProgress = (step) => {
81
- switch (step.kind) {
82
- case 'generating':
83
- store_1.store.setPhase('optimizing', 'generating AGENTS.md…');
84
- break;
85
- case 'done':
86
- store_1.store.setPhase('optimizing', 'generation complete');
87
- break;
88
- }
89
- };
90
- // ── Wrap logger for dashboard ─────────────────────────────
91
83
  const optLog = flags.quiet ? undefined : {
92
84
  info(msg) { log.info(msg); },
93
85
  warn(msg) { log.warn(msg); },
94
86
  error(msg) { log.error(msg); },
95
87
  debug(msg) { log.debug(msg); },
96
88
  };
97
- // ── Harvest prompts for evaluator (if enabled) ────────────
98
- let harvestedPrompts = [];
99
- if (evaluatorEnabled && (evalConfig?.harvestPrompts !== false)) {
100
- try {
101
- store_1.store.setEvalStatus({ phase: 'harvesting' });
102
- store_1.store.setPhase('optimizing', 'harvesting prompts');
103
- harvestedPrompts = await (0, evaluator_1.harvestPrompts)({
104
- root,
105
- sources: evalConfig?.harvestSources,
106
- maxPerSource: 50,
107
- log: optLog,
108
- });
109
- store_1.store.setEvalStatus({ phase: 'harvesting', harvestCount: harvestedPrompts.length });
110
- if (harvestedPrompts.length > 0) {
111
- log.info(`Harvested ${harvestedPrompts.length} prompts from AI tool history`);
112
- }
113
- }
114
- catch (err) {
115
- const msg = err instanceof Error ? err.message : String(err);
116
- log.debug(`Prompt harvesting failed (non-fatal): ${msg}`);
117
- }
89
+ // ── Enrich KB with user preferences ─────────────────────
90
+ let enrichedKb = kbContent;
91
+ if (preferences) {
92
+ const prefBlock = (0, preferences_1.formatPreferencesForPrompt)(preferences);
93
+ if (prefBlock)
94
+ enrichedKb = kbContent + '\n\n' + prefBlock;
118
95
  }
119
- const result = await (0, optimizer_1.runGenerateAgent)({
120
- currentInstructions: fallbackContent,
121
- kb: kbContent,
122
- toolInstructions: toolContext || undefined,
123
- provider,
124
- log: optLog,
125
- onProgress,
126
- });
127
- for (const reason of result.reasoning) {
128
- log.debug(` ${logger_1.fmt.dim(reason)}`);
96
+ // ── Use base content as seed AGENTS.md ──────────────────
97
+ const projectName = root.split('/').pop() || root.split('\\').pop() || 'Project';
98
+ store_1.store.setPhase('optimizing', 'generating seed AGENTS.md…');
99
+ let finalContent;
100
+ if (evaluatorEnabled) {
101
+ // Use the base content (rendered directly from AnalysisModel) as the seed
102
+ finalContent = _baseContent;
103
+ log.info('Using base AGENTS.md for probe-and-refine tuning');
129
104
  }
130
- // Surface token usage from generation call
131
- if (result.usage) {
132
- store_1.store.setTokenUsage(result.usage);
105
+ else {
106
+ // Use base content directly (already rendered from AnalysisModel)
107
+ finalContent = _baseContent;
133
108
  }
134
- // ── Evaluate with probes (if enabled) ─────────────────────
135
- let finalContent = result.optimizedInstructions;
109
+ store_1.store.setPhase('optimizing', 'generation complete');
110
+ // ── Probe-and-refine loop ──────────────────────────────────
111
+ const scopedRuleCreates = [];
112
+ const scopedRuleDeletes = [];
113
+ const allAppliedEdits = [];
136
114
  if (evaluatorEnabled) {
115
+ const iterationSummaries = [];
116
+ let totalEditsApplied = 0;
117
+ // ── Abort controller for immediate cancellation ──────
118
+ const abortController = new AbortController();
119
+ const { signal } = abortController;
120
+ const onStoreChange = () => {
121
+ if (store_1.store.state.evalStatus.cancelled && !signal.aborted) {
122
+ abortController.abort();
123
+ }
124
+ };
125
+ store_1.store.on('change', onStoreChange);
137
126
  try {
127
+ const loopConfig = {
128
+ maxIterations: evalConfig?.maxIterations ?? evaluator_1.DEFAULT_PROBE_REFINE_CONFIG.maxIterations,
129
+ targetProbesPerIteration: evalConfig?.maxProbes ?? evaluator_1.DEFAULT_PROBE_REFINE_CONFIG.targetProbesPerIteration,
130
+ maxEditsPerIteration: evalConfig?.maxEditsPerIteration ?? evaluator_1.DEFAULT_PROBE_REFINE_CONFIG.maxEditsPerIteration,
131
+ charBudget: evalConfig?.charBudget ?? evaluator_1.DEFAULT_PROBE_REFINE_CONFIG.charBudget,
132
+ };
138
133
  store_1.store.setPhase('evaluating');
139
- store_1.store.setEvalStatus({ phase: 'probing' });
140
- log.info('Running probe-based evaluation…');
141
- const maxProbes = evalConfig?.maxProbes ?? 10;
142
- const probes = (0, evaluator_1.generateProbes)({
143
- kb: kbContent,
144
- harvestedPrompts: harvestedPrompts.length > 0 ? harvestedPrompts : undefined,
145
- maxProbes,
146
- });
147
- // Per-probe progress callback for live dashboard
148
- const onProbeProgress = (info) => {
149
- if (info.phase === 'starting') {
150
- store_1.store.setEvalStatus({
134
+ const priorTasks = [];
135
+ let noChangeStreak = 0;
136
+ let probeExhaustionStreak = 0;
137
+ let convergedReason;
138
+ /** Cooperative cancel check — returns true if the user pressed [x]. */
139
+ const isCancelled = () => signal.aborted;
140
+ /** Preserves iterationSummaries across setEvalStatus calls. */
141
+ const setEval = (status) => {
142
+ store_1.store.setEvalStatus({
143
+ ...status,
144
+ iterationSummaries: iterationSummaries.length > 0 ? [...iterationSummaries] : undefined,
145
+ cancelled: signal.aborted,
146
+ });
147
+ };
148
+ for (let iteration = 1; iteration <= loopConfig.maxIterations; iteration++) {
149
+ if (isCancelled())
150
+ break;
151
+ log.info(`\n── Iteration ${iteration}/${loopConfig.maxIterations} ──`);
152
+ // ── Step 1: Generate probes ──────────────────────
153
+ setEval({
154
+ phase: 'generating-probes',
155
+ iteration,
156
+ maxIterations: loopConfig.maxIterations,
157
+ });
158
+ store_1.store.setPhase('evaluating');
159
+ const probes = await (0, evaluator_1.generateProbes)({
160
+ kb: enrichedKb,
161
+ currentAgentsMd: finalContent,
162
+ priorProbeTasks: priorTasks,
163
+ maxProbes: loopConfig.targetProbesPerIteration,
164
+ provider,
165
+ projectName,
166
+ log: optLog,
167
+ signal,
168
+ });
169
+ if (isCancelled())
170
+ break;
171
+ // Track prior tasks for cross-iteration dedup
172
+ for (const p of probes)
173
+ priorTasks.push(p.task);
174
+ if (probes.length === 0) {
175
+ probeExhaustionStreak++;
176
+ log.info(`No new probes generated (streak: ${probeExhaustionStreak})`);
177
+ if (probeExhaustionStreak >= 2) {
178
+ convergedReason = 'probe diversity exhausted';
179
+ log.info(`Converged: ${convergedReason}`);
180
+ break;
181
+ }
182
+ continue;
183
+ }
184
+ else {
185
+ probeExhaustionStreak = 0;
186
+ }
187
+ // ── Step 2: Simulate probes ──────────────────────
188
+ const probeTasks = probes.map((p) => p.task);
189
+ setEval({
190
+ phase: 'probing',
191
+ iteration,
192
+ maxIterations: loopConfig.maxIterations,
193
+ probesTotal: probes.length,
194
+ probeTasks,
195
+ });
196
+ store_1.store.setPhase('evaluating');
197
+ const onProbeProgress = (info) => {
198
+ const currentTask = probes[info.probeIndex]?.task;
199
+ const brief = currentTask && currentTask.length > 60
200
+ ? currentTask.slice(0, 57) + '...'
201
+ : currentTask;
202
+ setEval({
151
203
  phase: 'probing',
152
- probesPassed: undefined,
204
+ iteration,
205
+ maxIterations: loopConfig.maxIterations,
206
+ probesPassed: info.probeIndex + (info.phase === 'done' ? 1 : 0),
153
207
  probesTotal: info.total,
208
+ probeTasks,
209
+ currentProbeTask: brief,
210
+ });
211
+ };
212
+ const simResults = await (0, evaluator_1.runProbes)(finalContent, probes, provider, optLog, signal, onProbeProgress);
213
+ if (isCancelled())
214
+ break;
215
+ // ── Step 3: Judge each probe ─────────────────────
216
+ setEval({
217
+ phase: 'judging',
218
+ iteration,
219
+ maxIterations: loopConfig.maxIterations,
220
+ probesTotal: simResults.length,
221
+ judgedCount: 0,
222
+ weakCount: 0,
223
+ strongCount: 0,
224
+ });
225
+ store_1.store.setPhase('evaluating');
226
+ const judgedResults = [];
227
+ let weakCount = 0;
228
+ let strongCount = 0;
229
+ const probeResults = [];
230
+ // Pre-populate pending entries for all probes
231
+ for (const sim of simResults) {
232
+ const brief = sim.task.length > 60 ? sim.task.slice(0, 57) + '...' : sim.task;
233
+ probeResults.push({ task: brief, status: 'pending' });
234
+ }
235
+ for (let i = 0; i < simResults.length; i++) {
236
+ if (isCancelled())
237
+ break;
238
+ const sim = simResults[i];
239
+ const probe = probes[i];
240
+ if (!sim.response)
241
+ continue;
242
+ const judged = await (0, evaluator_1.judgeProbe)({
243
+ task: sim.task,
244
+ response: sim.response,
245
+ expectedBehaviors: probe.expectedBehaviors,
246
+ probeId: sim.probeId,
247
+ provider,
248
+ log: optLog,
249
+ signal,
250
+ });
251
+ judgedResults.push(judged);
252
+ const hasWeak = judged.behaviorReviews.some((b) => b.assessment !== 'strong');
253
+ if (hasWeak)
254
+ weakCount++;
255
+ else
256
+ strongCount++;
257
+ probeResults[i] = { task: probeResults[i].task, status: hasWeak ? 'weak' : 'strong' };
258
+ setEval({
259
+ phase: 'judging',
260
+ iteration,
261
+ maxIterations: loopConfig.maxIterations,
262
+ probesTotal: simResults.length,
263
+ judgedCount: i + 1,
264
+ weakCount,
265
+ strongCount,
266
+ currentProbeTask: probeResults[i].task,
267
+ probeResults: [...probeResults],
154
268
  });
155
- store_1.store.setPhase('evaluating', `probe ${info.probeIndex + 1}/${info.total}: ${info.probeId}`);
269
+ log.info(` ${hasWeak ? '✖' : '✔'} ${sim.probeId} (${hasWeak ? 'weak' : 'strong'})`);
270
+ }
271
+ if (isCancelled())
272
+ break;
273
+ // ── Step 4: Aggregate diagnosis ──────────────────
274
+ setEval({
275
+ phase: 'diagnosing',
276
+ iteration,
277
+ maxIterations: loopConfig.maxIterations,
278
+ weakCount,
279
+ strongCount,
280
+ probesTotal: judgedResults.length,
281
+ });
282
+ store_1.store.setPhase('evaluating');
283
+ // Build scoped rules context for diagnosis
284
+ const rulesCtx = staticRules?.map((r) => `### ${r.slug} (${r.source})\nGlobs: ${r.globs.join(', ')}\n${r.content}`).join('\n---\n') || '';
285
+ const staticData = staticRules?.length
286
+ ? `${staticRules.length} candidate rules from static analysis (hubs, conventions, circular deps). The LLM decides which are worth keeping as scoped rules vs folding into AGENTS.md.`
287
+ : '';
288
+ const diagnosisEdits = await (0, evaluator_1.diagnose)({
289
+ judgedResults,
290
+ agentsContent: finalContent,
291
+ provider: diagnosisProvider ?? provider,
292
+ log: optLog,
293
+ signal,
294
+ scopedRulesContext: rulesCtx,
295
+ staticAnalysisData: staticData,
296
+ });
297
+ if (isCancelled())
298
+ break;
299
+ // Merge per-probe edits + diagnosis edits, deduplicate
300
+ const allEdits = [];
301
+ for (const jr of judgedResults) {
302
+ allEdits.push(...jr.proposedEdits);
303
+ }
304
+ allEdits.push(...diagnosisEdits);
305
+ // Deduplicate by content similarity
306
+ const seen = new Set();
307
+ const dedupedEdits = [];
308
+ for (const edit of allEdits) {
309
+ const key = `${edit.section}:${edit.action}:${edit.content.toLowerCase().trim()}`;
310
+ if (!seen.has(key)) {
311
+ seen.add(key);
312
+ dedupedEdits.push(edit);
313
+ }
314
+ }
315
+ // Separate AGENTS.md edits from scoped rule edits
316
+ const agentsMdEdits = dedupedEdits.filter((e) => !e.section.startsWith('scoped:'));
317
+ const scopedEdits = dedupedEdits.filter((e) => e.section.startsWith('scoped:'));
318
+ // Collect scoped rule operations for later
319
+ for (const edit of scopedEdits) {
320
+ if (edit.section.startsWith('scoped:DELETE:')) {
321
+ const slug = edit.section.replace('scoped:DELETE:', '');
322
+ scopedRuleDeletes.push(slug);
323
+ }
324
+ else if (edit.section.startsWith('scoped:CREATE:')) {
325
+ const slug = edit.section.replace('scoped:CREATE:', '');
326
+ if (edit.content && edit.globs?.length) {
327
+ scopedRuleCreates.push({
328
+ slug,
329
+ description: edit.description || `Rule for ${slug}`,
330
+ globs: edit.globs,
331
+ content: edit.content,
332
+ source: 'probe',
333
+ });
334
+ }
335
+ }
336
+ else {
337
+ // scoped:slug — update existing rule
338
+ const slug = edit.section.replace('scoped:', '');
339
+ if (edit.content) {
340
+ scopedRuleCreates.push({
341
+ slug,
342
+ description: edit.description || `Updated rule for ${slug}`,
343
+ globs: edit.globs || [`**`],
344
+ content: edit.content,
345
+ source: 'probe',
346
+ });
347
+ }
348
+ }
349
+ }
350
+ const cappedEdits = agentsMdEdits.slice(0, loopConfig.maxEditsPerIteration);
351
+ const totalEditCount = cappedEdits.length + scopedEdits.length;
352
+ log.info(`Applying ${cappedEdits.length} AGENTS.md edits, ${scopedEdits.length} scoped rule edits (${allEdits.length} total)`);
353
+ // ── Step 5: Apply AGENTS.md edits deterministically ────────
354
+ const pendingEditSummaries = [
355
+ ...cappedEdits.map((e) => `${e.action} ${e.section}: "${e.content.length > 60 ? e.content.slice(0, 57) + '...' : e.content}"`),
356
+ ...scopedEdits.map((e) => `${e.section}`),
357
+ ];
358
+ setEval({
359
+ phase: 'applying',
360
+ iteration,
361
+ maxIterations: loopConfig.maxIterations,
362
+ proposedEditCount: totalEditCount,
363
+ editSummaries: pendingEditSummaries,
364
+ });
365
+ store_1.store.setPhase('evaluating');
366
+ const guidanceBefore = finalContent;
367
+ const applyResult = await (0, evaluator_1.applyEditsWithLlm)(finalContent, cappedEdits, loopConfig.charBudget, provider, signal);
368
+ finalContent = applyResult.content;
369
+ totalEditsApplied += applyResult.applied + scopedEdits.length;
370
+ allAppliedEdits.push(...cappedEdits.slice(0, applyResult.applied));
371
+ allAppliedEdits.push(...scopedEdits);
372
+ if (applyResult.trimmed > 0) {
373
+ log.info(`Trimmed ${applyResult.trimmed} bullets to fit ${loopConfig.charBudget}-char budget`);
374
+ }
375
+ // ── Step 6: Convergence check ────────────────────
376
+ const guidanceChanged = finalContent !== guidanceBefore;
377
+ if (guidanceChanged) {
378
+ noChangeStreak = 0;
379
+ log.info(`Guidance updated (${guidanceBefore.length} → ${finalContent.length} chars)`);
156
380
  }
157
381
  else {
158
- // 'done' — accumulate pass count from results so far
159
- store_1.store.setPhase('evaluating', `probe ${info.probeIndex + 1}/${info.total} ${info.passed ? '✔' : '✖'}`);
382
+ noChangeStreak++;
383
+ log.info(`No guidance change (streak: ${noChangeStreak})`);
160
384
  }
161
- };
162
- const probeResults = await (0, evaluator_1.runProbes)(finalContent, probes, provider, undefined, // fileContents
163
- optLog, undefined, // signal
164
- onProbeProgress);
165
- const failures = probeResults.filter((r) => !r.passed);
166
- const passCount = probeResults.length - failures.length;
385
+ // Build iteration summary
386
+ const roundParts = [];
387
+ roundParts.push(`${probes.length} scenarios`);
388
+ if (weakCount > 0)
389
+ roundParts.push(`${weakCount} gap${weakCount === 1 ? '' : 's'}`);
390
+ else
391
+ roundParts.push('all passed');
392
+ if (applyResult.applied > 0)
393
+ roundParts.push(`${applyResult.applied} improvement${applyResult.applied === 1 ? '' : 's'}`);
394
+ iterationSummaries.push(`Round ${iteration}: ${roundParts.join(', ')}`);
395
+ if (noChangeStreak >= 2) {
396
+ convergedReason = 'guidance converged (no changes)';
397
+ log.info(`Converged: ${convergedReason}`);
398
+ break;
399
+ }
400
+ }
401
+ const editSummaries = allAppliedEdits.map((e) => {
402
+ const verb = { add: 'Added', remove: 'Removed', strengthen: 'Strengthened', modify: 'Updated' }[e.action];
403
+ const brief = e.content.length > 60 ? e.content.slice(0, 57) + '...' : e.content;
404
+ return `${verb}: ${brief} (${e.section})`;
405
+ });
406
+ const wasCancelled = isCancelled();
167
407
  store_1.store.setEvalStatus({
168
- phase: 'probing',
169
- probesPassed: passCount,
170
- probesTotal: probeResults.length,
408
+ phase: 'done',
409
+ iteration: loopConfig.maxIterations,
410
+ maxIterations: loopConfig.maxIterations,
411
+ diagnosisEdits: totalEditsApplied,
412
+ convergedReason: wasCancelled ? undefined : convergedReason,
413
+ editSummaries,
414
+ iterationSummaries: iterationSummaries.length > 0 ? iterationSummaries : undefined,
415
+ cancelled: wasCancelled,
171
416
  });
172
- log.info(`Probes: ${passCount}/${probeResults.length} passed` +
173
- (failures.length > 0 ? `, ${failures.length} failed` : ''));
174
- // Diagnose and apply edits if there are failures
175
- if (failures.length > 0) {
176
- store_1.store.setEvalStatus({ phase: 'diagnosing' });
177
- store_1.store.setPhase('evaluating', 'diagnosing failures');
178
- const diagnosis = await (0, evaluator_1.diagnose)(failures, finalContent, provider, optLog);
179
- if (diagnosis && diagnosis.edits.length > 0) {
180
- store_1.store.setPhase('evaluating', 'applying fixes');
181
- log.info(`Applying ${diagnosis.edits.length} diagnosis-driven edits…`);
182
- const fixed = await (0, evaluator_1.applyDiagnosisEdits)(finalContent, diagnosis, provider, optLog);
183
- finalContent = fixed.content;
184
- log.info(`Diagnosis edits applied (${fixed.appliedEdits.length} changes)`);
185
- }
186
- store_1.store.setEvalStatus({
187
- phase: 'done',
188
- probesPassed: passCount,
189
- probesTotal: probeResults.length,
190
- diagnosisEdits: diagnosis?.edits.length ?? 0,
191
- });
417
+ store_1.store.removeListener('change', onStoreChange);
418
+ if (wasCancelled) {
419
+ log.info(`Probe-and-refine cancelled by user (${totalEditsApplied} edits applied)`);
420
+ }
421
+ else if (convergedReason) {
422
+ log.info(`Probe-and-refine complete: ${convergedReason}`);
192
423
  }
193
424
  else {
425
+ log.info(`Probe-and-refine complete: max iterations reached`);
426
+ }
427
+ }
428
+ catch (err) {
429
+ // Clean up abort listener if we exit via exception
430
+ store_1.store.removeListener('change', onStoreChange);
431
+ // Tier exhaustion — re-throw so pipeline can show upgrade prompt
432
+ if (err?.tierExhausted)
433
+ throw err;
434
+ // AbortError from cancel — not a real error
435
+ if (err instanceof DOMException && err.name === 'AbortError') {
436
+ const editSummaries = allAppliedEdits.map((e) => {
437
+ const verb = { add: 'Added', remove: 'Removed', strengthen: 'Strengthened', modify: 'Updated' }[e.action];
438
+ const brief = e.content.length > 60 ? e.content.slice(0, 57) + '...' : e.content;
439
+ return `${verb}: ${brief} (${e.section})`;
440
+ });
194
441
  store_1.store.setEvalStatus({
195
442
  phase: 'done',
196
- probesPassed: passCount,
197
- probesTotal: probeResults.length,
198
- diagnosisEdits: 0,
443
+ diagnosisEdits: totalEditsApplied,
444
+ editSummaries,
445
+ iterationSummaries: iterationSummaries.length > 0 ? iterationSummaries : undefined,
446
+ cancelled: true,
199
447
  });
448
+ log.info(`Probe-and-refine cancelled by user (${totalEditsApplied} edits applied)`);
449
+ }
450
+ else {
451
+ const msg = err instanceof Error ? err.message : String(err);
452
+ log.warn(`Probe and refine failed (non-fatal): ${msg}`);
200
453
  }
201
454
  }
202
- catch (err) {
203
- const msg = err instanceof Error ? err.message : String(err);
204
- log.warn(`Evaluation failed (non-fatal): ${msg}`);
455
+ }
456
+ store_1.store.setReasoning([]);
457
+ // ── Build final scoped rules from evaluator decisions ──────
458
+ // The diagnosis step decided what scoped rules to create/delete.
459
+ // Start with static rules, apply evaluator modifications.
460
+ let finalRules = staticRules ?? [];
461
+ if (evaluatorEnabled) {
462
+ // Remove rules the evaluator marked for deletion
463
+ if (scopedRuleDeletes.length > 0) {
464
+ const deleteSet = new Set(scopedRuleDeletes);
465
+ finalRules = finalRules.filter((r) => !deleteSet.has(r.slug));
466
+ log.info(`Evaluator pruned ${scopedRuleDeletes.length} scoped rule${scopedRuleDeletes.length === 1 ? '' : 's'}`);
467
+ }
468
+ // Add/update rules the evaluator created
469
+ if (scopedRuleCreates.length > 0) {
470
+ const createBySlug = new Map(scopedRuleCreates.map((r) => [r.slug, r]));
471
+ // Update existing rules or add new ones
472
+ finalRules = finalRules.map((r) => createBySlug.get(r.slug) ?? r);
473
+ // Add truly new rules (not updates)
474
+ const existingSlugs = new Set(finalRules.map((r) => r.slug));
475
+ for (const r of scopedRuleCreates) {
476
+ if (!existingSlugs.has(r.slug))
477
+ finalRules.push(r);
478
+ }
479
+ log.info(`Evaluator created/updated ${scopedRuleCreates.length} scoped rule${scopedRuleCreates.length === 1 ? '' : 's'}`);
205
480
  }
206
481
  }
207
- store_1.store.setReasoning(result.reasoning);
208
- return {
209
- content: finalContent,
210
- reasoning: result.reasoning,
211
- tokenUsage: result.usage,
212
- };
482
+ // ── Save probe-refine edits as preferences ───────────────
483
+ if (evaluatorEnabled && allAppliedEdits.length > 0 && preferences) {
484
+ try {
485
+ let prefs = { ...preferences };
486
+ for (const edit of allAppliedEdits) {
487
+ const isSpecific = edit.section.startsWith('scoped:') ||
488
+ /(?:^|\s)(?:\.\/|src\/|lib\/|app\/|packages\/|test\/|tests\/)\S+\.\w{1,4}/m.test(edit.content);
489
+ prefs = (0, preferences_1.addPreference)(prefs, {
490
+ rule: `probe-refine:${edit.section}`,
491
+ pattern: edit.content.slice(0, 200),
492
+ disposition: 'deny',
493
+ directory: edit.globs?.[0],
494
+ details: `action: ${edit.action}, section: ${edit.section}`,
495
+ suggestion: edit.content,
496
+ source: isSpecific ? 'probe-refine-specific' : 'probe-refine',
497
+ });
498
+ }
499
+ (0, preferences_1.savePreferences)(root, prefs);
500
+ }
501
+ catch { /* best-effort — don't break the pipeline */ }
502
+ }
503
+ return { content: finalContent, reasoning: [], scopedRules: finalRules, deleteSlugs: scopedRuleDeletes };
213
504
  }
214
505
  //# sourceMappingURL=optimize.js.map