@agile-vibe-coding/avc 0.2.3 → 0.3.2

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 (262) hide show
  1. package/README.md +475 -3
  2. package/cli/agents/agent-selector.md +23 -0
  3. package/cli/agents/code-implementer.md +117 -0
  4. package/cli/agents/code-validator.md +80 -0
  5. package/cli/agents/context-reviewer-epic.md +101 -0
  6. package/cli/agents/context-reviewer-story.md +92 -0
  7. package/cli/agents/context-writer-epic.md +145 -0
  8. package/cli/agents/context-writer-story.md +111 -0
  9. package/cli/agents/doc-writer-epic.md +42 -0
  10. package/cli/agents/doc-writer-story.md +43 -0
  11. package/cli/agents/duplicate-detector.md +110 -0
  12. package/cli/agents/epic-story-decomposer.md +318 -39
  13. package/cli/agents/mission-scope-generator.md +68 -4
  14. package/cli/agents/mission-scope-validator.md +40 -6
  15. package/cli/agents/project-context-extractor.md +21 -6
  16. package/cli/agents/scaffolding-generator.md +99 -0
  17. package/cli/agents/seed-validator.md +71 -0
  18. package/cli/agents/story-scope-reviewer.md +147 -0
  19. package/cli/agents/story-splitter.md +83 -0
  20. package/cli/agents/validator-documentation.json +31 -0
  21. package/cli/agents/validator-documentation.md +3 -1
  22. package/cli/api-reference-tool.js +368 -0
  23. package/cli/checks/catalog.json +76 -0
  24. package/cli/checks/code/quality.json +26 -0
  25. package/cli/checks/code/testing.json +14 -0
  26. package/cli/checks/code/traceability.json +26 -0
  27. package/cli/checks/cross-refs/epic.json +171 -0
  28. package/cli/checks/cross-refs/story.json +149 -0
  29. package/cli/checks/epic/api.json +114 -0
  30. package/cli/checks/epic/backend.json +126 -0
  31. package/cli/checks/epic/cloud.json +126 -0
  32. package/cli/checks/epic/data.json +102 -0
  33. package/cli/checks/epic/database.json +114 -0
  34. package/cli/checks/epic/developer.json +182 -0
  35. package/cli/checks/epic/devops.json +174 -0
  36. package/cli/checks/epic/frontend.json +162 -0
  37. package/cli/checks/epic/mobile.json +102 -0
  38. package/cli/checks/epic/qa.json +90 -0
  39. package/cli/checks/epic/security.json +184 -0
  40. package/cli/checks/epic/solution-architect.json +192 -0
  41. package/cli/checks/epic/test-architect.json +90 -0
  42. package/cli/checks/epic/ui.json +102 -0
  43. package/cli/checks/epic/ux.json +90 -0
  44. package/cli/checks/fixes/epic-fix-template.md +10 -0
  45. package/cli/checks/fixes/story-fix-template.md +10 -0
  46. package/cli/checks/story/api.json +186 -0
  47. package/cli/checks/story/backend.json +102 -0
  48. package/cli/checks/story/cloud.json +102 -0
  49. package/cli/checks/story/data.json +210 -0
  50. package/cli/checks/story/database.json +102 -0
  51. package/cli/checks/story/developer.json +168 -0
  52. package/cli/checks/story/devops.json +102 -0
  53. package/cli/checks/story/frontend.json +174 -0
  54. package/cli/checks/story/mobile.json +102 -0
  55. package/cli/checks/story/qa.json +210 -0
  56. package/cli/checks/story/security.json +198 -0
  57. package/cli/checks/story/solution-architect.json +230 -0
  58. package/cli/checks/story/test-architect.json +210 -0
  59. package/cli/checks/story/ui.json +102 -0
  60. package/cli/checks/story/ux.json +102 -0
  61. package/cli/coding-order.js +401 -0
  62. package/cli/dependency-checker.js +72 -0
  63. package/cli/epic-story-validator.js +284 -799
  64. package/cli/index.js +0 -0
  65. package/cli/init-model-config.js +17 -10
  66. package/cli/init.js +514 -92
  67. package/cli/kanban-server-manager.js +1 -2
  68. package/cli/llm-claude.js +98 -31
  69. package/cli/llm-gemini.js +29 -5
  70. package/cli/llm-local.js +493 -0
  71. package/cli/llm-openai.js +262 -41
  72. package/cli/llm-provider.js +147 -8
  73. package/cli/llm-token-limits.js +113 -4
  74. package/cli/llm-verifier.js +209 -1
  75. package/cli/llm-xiaomi.js +143 -0
  76. package/cli/message-constants.js +3 -12
  77. package/cli/messaging-api.js +6 -12
  78. package/cli/micro-check-fixer.js +335 -0
  79. package/cli/micro-check-runner.js +449 -0
  80. package/cli/micro-check-scorer.js +148 -0
  81. package/cli/micro-check-validator.js +538 -0
  82. package/cli/model-pricing.js +23 -0
  83. package/cli/model-selector.js +3 -2
  84. package/cli/prompt-logger.js +57 -0
  85. package/cli/repl-ink.js +106 -346
  86. package/cli/repl-old.js +1 -2
  87. package/cli/seed-processor.js +194 -24
  88. package/cli/sprint-planning-processor.js +2638 -289
  89. package/cli/template-processor.js +50 -3
  90. package/cli/token-tracker.js +50 -23
  91. package/cli/tools/generate-story-validators.js +1 -1
  92. package/cli/validation-router.js +70 -8
  93. package/cli/worktree-runner.js +654 -0
  94. package/kanban/client/dist/assets/index-D_KC5EQT.css +1 -0
  95. package/kanban/client/dist/assets/index-DjY5zqW7.js +351 -0
  96. package/kanban/client/dist/index.html +2 -2
  97. package/kanban/client/src/App.jsx +43 -14
  98. package/kanban/client/src/components/ceremony/AskArchPopup.jsx +7 -3
  99. package/kanban/client/src/components/ceremony/AskModelPopup.jsx +23 -10
  100. package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +320 -133
  101. package/kanban/client/src/components/ceremony/ProviderSwitcherButton.jsx +290 -0
  102. package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +80 -13
  103. package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +156 -22
  104. package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +11 -11
  105. package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +3 -21
  106. package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +214 -10
  107. package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +23 -2
  108. package/kanban/client/src/components/kanban/CardDetailModal.jsx +97 -10
  109. package/kanban/client/src/components/kanban/GroupingSelector.jsx +7 -1
  110. package/kanban/client/src/components/kanban/KanbanCard.jsx +23 -14
  111. package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +9 -14
  112. package/kanban/client/src/components/kanban/RunButton.jsx +162 -0
  113. package/kanban/client/src/components/kanban/SeedButton.jsx +176 -0
  114. package/kanban/client/src/components/settings/AgentsTab.jsx +103 -75
  115. package/kanban/client/src/components/settings/ApiKeysTab.jsx +31 -2
  116. package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +9 -2
  117. package/kanban/client/src/components/settings/CheckEditorPopup.jsx +507 -0
  118. package/kanban/client/src/components/settings/CostThresholdsTab.jsx +3 -2
  119. package/kanban/client/src/components/settings/ModelPricingTab.jsx +72 -7
  120. package/kanban/client/src/components/settings/OpenAIAuthSection.jsx +412 -0
  121. package/kanban/client/src/components/settings/SettingsModal.jsx +4 -4
  122. package/kanban/client/src/components/stats/CostModal.jsx +34 -3
  123. package/kanban/client/src/hooks/useGrouping.js +59 -0
  124. package/kanban/client/src/lib/api.js +118 -4
  125. package/kanban/client/src/lib/status-grouping.js +10 -0
  126. package/kanban/client/src/store/kanbanStore.js +8 -0
  127. package/kanban/server/index.js +23 -2
  128. package/kanban/server/routes/ceremony.js +153 -4
  129. package/kanban/server/routes/costs.js +9 -3
  130. package/kanban/server/routes/openai-oauth.js +366 -0
  131. package/kanban/server/routes/settings.js +447 -14
  132. package/kanban/server/routes/websocket.js +7 -2
  133. package/kanban/server/routes/work-items.js +141 -1
  134. package/kanban/server/services/CeremonyService.js +275 -24
  135. package/kanban/server/services/TaskRunnerService.js +261 -0
  136. package/kanban/server/workers/run-task-worker.js +121 -0
  137. package/kanban/server/workers/seed-worker.js +94 -0
  138. package/kanban/server/workers/sponsor-call-worker.js +14 -6
  139. package/kanban/server/workers/sprint-planning-worker.js +94 -12
  140. package/package.json +2 -3
  141. package/cli/agents/solver-epic-api.json +0 -15
  142. package/cli/agents/solver-epic-api.md +0 -39
  143. package/cli/agents/solver-epic-backend.json +0 -15
  144. package/cli/agents/solver-epic-backend.md +0 -39
  145. package/cli/agents/solver-epic-cloud.json +0 -15
  146. package/cli/agents/solver-epic-cloud.md +0 -39
  147. package/cli/agents/solver-epic-data.json +0 -15
  148. package/cli/agents/solver-epic-data.md +0 -39
  149. package/cli/agents/solver-epic-database.json +0 -15
  150. package/cli/agents/solver-epic-database.md +0 -39
  151. package/cli/agents/solver-epic-developer.json +0 -15
  152. package/cli/agents/solver-epic-developer.md +0 -39
  153. package/cli/agents/solver-epic-devops.json +0 -15
  154. package/cli/agents/solver-epic-devops.md +0 -39
  155. package/cli/agents/solver-epic-frontend.json +0 -15
  156. package/cli/agents/solver-epic-frontend.md +0 -39
  157. package/cli/agents/solver-epic-mobile.json +0 -15
  158. package/cli/agents/solver-epic-mobile.md +0 -39
  159. package/cli/agents/solver-epic-qa.json +0 -15
  160. package/cli/agents/solver-epic-qa.md +0 -39
  161. package/cli/agents/solver-epic-security.json +0 -15
  162. package/cli/agents/solver-epic-security.md +0 -39
  163. package/cli/agents/solver-epic-solution-architect.json +0 -15
  164. package/cli/agents/solver-epic-solution-architect.md +0 -39
  165. package/cli/agents/solver-epic-test-architect.json +0 -15
  166. package/cli/agents/solver-epic-test-architect.md +0 -39
  167. package/cli/agents/solver-epic-ui.json +0 -15
  168. package/cli/agents/solver-epic-ui.md +0 -39
  169. package/cli/agents/solver-epic-ux.json +0 -15
  170. package/cli/agents/solver-epic-ux.md +0 -39
  171. package/cli/agents/solver-story-api.json +0 -15
  172. package/cli/agents/solver-story-api.md +0 -39
  173. package/cli/agents/solver-story-backend.json +0 -15
  174. package/cli/agents/solver-story-backend.md +0 -39
  175. package/cli/agents/solver-story-cloud.json +0 -15
  176. package/cli/agents/solver-story-cloud.md +0 -39
  177. package/cli/agents/solver-story-data.json +0 -15
  178. package/cli/agents/solver-story-data.md +0 -39
  179. package/cli/agents/solver-story-database.json +0 -15
  180. package/cli/agents/solver-story-database.md +0 -39
  181. package/cli/agents/solver-story-developer.json +0 -15
  182. package/cli/agents/solver-story-developer.md +0 -39
  183. package/cli/agents/solver-story-devops.json +0 -15
  184. package/cli/agents/solver-story-devops.md +0 -39
  185. package/cli/agents/solver-story-frontend.json +0 -15
  186. package/cli/agents/solver-story-frontend.md +0 -39
  187. package/cli/agents/solver-story-mobile.json +0 -15
  188. package/cli/agents/solver-story-mobile.md +0 -39
  189. package/cli/agents/solver-story-qa.json +0 -15
  190. package/cli/agents/solver-story-qa.md +0 -39
  191. package/cli/agents/solver-story-security.json +0 -15
  192. package/cli/agents/solver-story-security.md +0 -39
  193. package/cli/agents/solver-story-solution-architect.json +0 -15
  194. package/cli/agents/solver-story-solution-architect.md +0 -39
  195. package/cli/agents/solver-story-test-architect.json +0 -15
  196. package/cli/agents/solver-story-test-architect.md +0 -39
  197. package/cli/agents/solver-story-ui.json +0 -15
  198. package/cli/agents/solver-story-ui.md +0 -39
  199. package/cli/agents/solver-story-ux.json +0 -15
  200. package/cli/agents/solver-story-ux.md +0 -39
  201. package/cli/agents/validator-epic-api.json +0 -93
  202. package/cli/agents/validator-epic-api.md +0 -137
  203. package/cli/agents/validator-epic-backend.json +0 -93
  204. package/cli/agents/validator-epic-backend.md +0 -130
  205. package/cli/agents/validator-epic-cloud.json +0 -93
  206. package/cli/agents/validator-epic-cloud.md +0 -137
  207. package/cli/agents/validator-epic-data.json +0 -93
  208. package/cli/agents/validator-epic-data.md +0 -130
  209. package/cli/agents/validator-epic-database.json +0 -93
  210. package/cli/agents/validator-epic-database.md +0 -137
  211. package/cli/agents/validator-epic-developer.json +0 -74
  212. package/cli/agents/validator-epic-developer.md +0 -153
  213. package/cli/agents/validator-epic-devops.json +0 -74
  214. package/cli/agents/validator-epic-devops.md +0 -153
  215. package/cli/agents/validator-epic-frontend.json +0 -74
  216. package/cli/agents/validator-epic-frontend.md +0 -153
  217. package/cli/agents/validator-epic-mobile.json +0 -93
  218. package/cli/agents/validator-epic-mobile.md +0 -130
  219. package/cli/agents/validator-epic-qa.json +0 -93
  220. package/cli/agents/validator-epic-qa.md +0 -130
  221. package/cli/agents/validator-epic-security.json +0 -74
  222. package/cli/agents/validator-epic-security.md +0 -154
  223. package/cli/agents/validator-epic-solution-architect.json +0 -74
  224. package/cli/agents/validator-epic-solution-architect.md +0 -156
  225. package/cli/agents/validator-epic-test-architect.json +0 -93
  226. package/cli/agents/validator-epic-test-architect.md +0 -130
  227. package/cli/agents/validator-epic-ui.json +0 -93
  228. package/cli/agents/validator-epic-ui.md +0 -130
  229. package/cli/agents/validator-epic-ux.json +0 -93
  230. package/cli/agents/validator-epic-ux.md +0 -130
  231. package/cli/agents/validator-story-api.json +0 -104
  232. package/cli/agents/validator-story-api.md +0 -152
  233. package/cli/agents/validator-story-backend.json +0 -104
  234. package/cli/agents/validator-story-backend.md +0 -152
  235. package/cli/agents/validator-story-cloud.json +0 -104
  236. package/cli/agents/validator-story-cloud.md +0 -152
  237. package/cli/agents/validator-story-data.json +0 -104
  238. package/cli/agents/validator-story-data.md +0 -152
  239. package/cli/agents/validator-story-database.json +0 -104
  240. package/cli/agents/validator-story-database.md +0 -152
  241. package/cli/agents/validator-story-developer.json +0 -104
  242. package/cli/agents/validator-story-developer.md +0 -152
  243. package/cli/agents/validator-story-devops.json +0 -104
  244. package/cli/agents/validator-story-devops.md +0 -152
  245. package/cli/agents/validator-story-frontend.json +0 -104
  246. package/cli/agents/validator-story-frontend.md +0 -152
  247. package/cli/agents/validator-story-mobile.json +0 -104
  248. package/cli/agents/validator-story-mobile.md +0 -152
  249. package/cli/agents/validator-story-qa.json +0 -104
  250. package/cli/agents/validator-story-qa.md +0 -152
  251. package/cli/agents/validator-story-security.json +0 -104
  252. package/cli/agents/validator-story-security.md +0 -152
  253. package/cli/agents/validator-story-solution-architect.json +0 -104
  254. package/cli/agents/validator-story-solution-architect.md +0 -152
  255. package/cli/agents/validator-story-test-architect.json +0 -104
  256. package/cli/agents/validator-story-test-architect.md +0 -152
  257. package/cli/agents/validator-story-ui.json +0 -104
  258. package/cli/agents/validator-story-ui.md +0 -152
  259. package/cli/agents/validator-story-ux.json +0 -104
  260. package/cli/agents/validator-story-ux.md +0 -152
  261. package/kanban/client/dist/assets/index-CiD8PS2e.js +0 -306
  262. package/kanban/client/dist/assets/index-nLh0m82Q.css +0 -1
@@ -1,4 +1,4 @@
1
- import { exec } from 'child_process';
1
+ import { exec, execSync } from 'child_process';
2
2
  import { promisify } from 'util';
3
3
  import path from 'path';
4
4
  import fs from 'fs';
@@ -150,7 +150,6 @@ export class KanbanServerManager {
150
150
  if (pid && !isNaN(pid)) {
151
151
  // Try to get process name
152
152
  try {
153
- const { execSync } = require('child_process');
154
153
  const psOutput = execSync(`ps -p ${pid} -o comm=`, { encoding: 'utf8' });
155
154
  return { pid, command: psOutput.trim() };
156
155
  } catch {
package/cli/llm-claude.js CHANGED
@@ -30,43 +30,86 @@ export class ClaudeProvider extends LLMProvider {
30
30
  return response.content[0].text;
31
31
  }
32
32
 
33
- async generateJSON(prompt, agentInstructions = null) {
33
+ async generateJSON(prompt, agentInstructions = null, cachedContext = null) {
34
34
  if (!this._client) {
35
35
  this._client = this._createClient();
36
36
  }
37
37
 
38
- const fullPrompt = agentInstructions ? `${agentInstructions}\n\n${prompt}` : prompt;
39
-
40
38
  // Use model-specific maximum tokens
41
39
  const maxTokens = getMaxTokensForModel(this.model);
42
40
 
41
+ const JSON_SYSTEM = 'You are a helpful assistant that always returns valid JSON. Your response must be a valid JSON object or array, nothing else.';
42
+
43
+ let systemParam;
44
+ let userContent;
45
+
46
+ if (cachedContext) {
47
+ // Structured content blocks: cache_control on agentInstructions (system) and
48
+ // cachedContext (first user block) — both stay stable across multiple calls
49
+ // in the same ceremony, hitting the 5-min cache on subsequent validators.
50
+ systemParam = agentInstructions
51
+ ? [
52
+ { type: 'text', text: JSON_SYSTEM },
53
+ { type: 'text', text: agentInstructions, cache_control: { type: 'ephemeral' } },
54
+ ]
55
+ : [{ type: 'text', text: JSON_SYSTEM }];
56
+
57
+ userContent = [
58
+ { type: 'text', text: cachedContext, cache_control: { type: 'ephemeral' } },
59
+ { type: 'text', text: prompt },
60
+ ];
61
+ } else {
62
+ systemParam = JSON_SYSTEM;
63
+ userContent = agentInstructions ? `${agentInstructions}\n\n${prompt}` : prompt;
64
+ }
65
+
66
+ const requestParams = {
67
+ model: this.model,
68
+ max_tokens: maxTokens,
69
+ system: systemParam,
70
+ messages: [{ role: 'user', content: userContent }],
71
+ };
72
+
73
+ const fullPrompt = agentInstructions ? `${agentInstructions}\n\n${prompt}` : prompt;
74
+
75
+ const _t0Json = Date.now();
43
76
  const response = await this._withRetry(
44
- () => this._client.messages.create({
45
- model: this.model,
46
- max_tokens: maxTokens,
47
- messages: [{
48
- role: 'user',
49
- content: fullPrompt
50
- }],
51
- system: 'You are a helpful assistant that always returns valid JSON. Your response must be a valid JSON object or array, nothing else.'
52
- }),
77
+ () => this._client.messages.create(requestParams),
53
78
  'JSON generation (Claude)'
54
79
  );
55
80
 
56
- this._trackTokens(response.usage);
57
81
  const content = response.content[0].text;
58
-
59
- // Extract JSON from response (handle markdown code blocks)
60
- // Strip markdown code fences if present (more robust)
82
+ this._trackTokens(response.usage, {
83
+ prompt: fullPrompt,
84
+ agentInstructions: agentInstructions ?? null,
85
+ response: content,
86
+ elapsed: Date.now() - _t0Json,
87
+ });
88
+
89
+ // Extract JSON from response (handle markdown code blocks and preamble text)
61
90
  let jsonStr = content.trim();
91
+
92
+ // Strip markdown code fences if the response starts with one
62
93
  if (jsonStr.startsWith('```')) {
63
- // Remove opening fence (```json or ```)
64
94
  jsonStr = jsonStr.replace(/^```(?:json)?\s*\n?/, '');
65
- // Remove closing fence
66
95
  jsonStr = jsonStr.replace(/\n?\s*```\s*$/, '');
67
96
  jsonStr = jsonStr.trim();
68
97
  }
69
98
 
99
+ // If model added reasoning preamble before JSON, find the first { or [ and extract from there.
100
+ // This handles Claude responses like "I'll analyze...\n\n```json\n{...}\n```" or "Here is the JSON:\n{...}"
101
+ if (!jsonStr.startsWith('{') && !jsonStr.startsWith('[')) {
102
+ const firstBrace = jsonStr.indexOf('{');
103
+ const firstBracket = jsonStr.indexOf('[');
104
+ const jsonStart = firstBrace === -1 ? firstBracket
105
+ : firstBracket === -1 ? firstBrace
106
+ : Math.min(firstBrace, firstBracket);
107
+ if (jsonStart > 0) {
108
+ // Also strip trailing markdown fences that may follow the JSON block
109
+ jsonStr = jsonStr.slice(jsonStart).replace(/\n?\s*```\s*$/, '').trim();
110
+ }
111
+ }
112
+
70
113
  try {
71
114
  return JSON.parse(jsonStr);
72
115
  } catch (firstError) {
@@ -81,29 +124,53 @@ export class ClaudeProvider extends LLMProvider {
81
124
  }
82
125
  }
83
126
 
84
- async generateText(prompt, agentInstructions = null) {
127
+ async generateText(prompt, agentInstructions = null, cachedContext = null) {
85
128
  if (!this._client) {
86
129
  this._client = this._createClient();
87
130
  }
88
131
 
89
- const fullPrompt = agentInstructions ? `${agentInstructions}\n\n${prompt}` : prompt;
90
-
91
132
  // Use model-specific maximum tokens
92
133
  const maxTokens = getMaxTokensForModel(this.model);
93
134
 
135
+ let systemParam;
136
+ let userContent;
137
+
138
+ if (cachedContext) {
139
+ systemParam = agentInstructions
140
+ ? [
141
+ { type: 'text', text: agentInstructions, cache_control: { type: 'ephemeral' } },
142
+ ]
143
+ : undefined;
144
+ userContent = [
145
+ { type: 'text', text: cachedContext, cache_control: { type: 'ephemeral' } },
146
+ { type: 'text', text: prompt },
147
+ ];
148
+ } else {
149
+ userContent = agentInstructions ? `${agentInstructions}\n\n${prompt}` : prompt;
150
+ }
151
+
152
+ const requestParams = {
153
+ model: this.model,
154
+ max_tokens: maxTokens,
155
+ messages: [{ role: 'user', content: userContent }],
156
+ };
157
+ if (systemParam) requestParams.system = systemParam;
158
+
159
+ const fullPrompt = agentInstructions ? `${agentInstructions}\n\n${prompt}` : prompt;
160
+
161
+ const _t0Text = Date.now();
94
162
  const response = await this._withRetry(
95
- () => this._client.messages.create({
96
- model: this.model,
97
- max_tokens: maxTokens,
98
- messages: [{
99
- role: 'user',
100
- content: fullPrompt
101
- }]
102
- }),
163
+ () => this._client.messages.create(requestParams),
103
164
  'Text generation (Claude)'
104
165
  );
105
166
 
106
- this._trackTokens(response.usage);
107
- return response.content[0].text;
167
+ const text = response.content[0].text;
168
+ this._trackTokens(response.usage, {
169
+ prompt: fullPrompt,
170
+ agentInstructions: agentInstructions ?? null,
171
+ response: text,
172
+ elapsed: Date.now() - _t0Text,
173
+ });
174
+ return text;
108
175
  }
109
176
  }
package/cli/llm-gemini.js CHANGED
@@ -31,7 +31,7 @@ export class GeminiProvider extends LLMProvider {
31
31
  return response.text;
32
32
  }
33
33
 
34
- async generateJSON(prompt, agentInstructions = null) {
34
+ async generateJSON(prompt, agentInstructions = null, cachedContext = null) {
35
35
  if (!this._client) {
36
36
  this._client = this._createClient();
37
37
  }
@@ -50,6 +50,14 @@ export class GeminiProvider extends LLMProvider {
50
50
  }
51
51
  };
52
52
 
53
+ // When cachedContext is provided (e.g. project rootContextMd), set it as the
54
+ // systemInstruction — Gemini's implicit caching targets system instructions and
55
+ // stable prefix content, giving a best-effort discount with no extra setup.
56
+ if (cachedContext) {
57
+ params.systemInstruction = cachedContext;
58
+ }
59
+
60
+ const _t0Json = Date.now();
53
61
  const response = await this._withRetry(
54
62
  () => this._client.models.generateContent(params),
55
63
  'JSON generation (Gemini)'
@@ -58,8 +66,13 @@ export class GeminiProvider extends LLMProvider {
58
66
  throw new Error('Gemini returned no text (possible safety filter block).');
59
67
  }
60
68
 
61
- this._trackTokens(response.usageMetadata);
62
69
  const content = response.text;
70
+ this._trackTokens(response.usageMetadata, {
71
+ prompt: fullPrompt,
72
+ agentInstructions: agentInstructions ?? null,
73
+ response: content,
74
+ elapsed: Date.now() - _t0Json,
75
+ });
63
76
 
64
77
  // Strip markdown code fences if present (more robust)
65
78
  let jsonStr = content.trim();
@@ -83,7 +96,7 @@ export class GeminiProvider extends LLMProvider {
83
96
  }
84
97
  }
85
98
 
86
- async generateText(prompt, agentInstructions = null) {
99
+ async generateText(prompt, agentInstructions = null, cachedContext = null) {
87
100
  if (!this._client) {
88
101
  this._client = this._createClient();
89
102
  }
@@ -101,6 +114,11 @@ export class GeminiProvider extends LLMProvider {
101
114
  }
102
115
  };
103
116
 
117
+ if (cachedContext) {
118
+ params.systemInstruction = cachedContext;
119
+ }
120
+
121
+ const _t0Text = Date.now();
104
122
  const response = await this._withRetry(
105
123
  () => this._client.models.generateContent(params),
106
124
  'Text generation (Gemini)'
@@ -109,7 +127,13 @@ export class GeminiProvider extends LLMProvider {
109
127
  throw new Error('Gemini returned no text (possible safety filter block).');
110
128
  }
111
129
 
112
- this._trackTokens(response.usageMetadata);
113
- return response.text;
130
+ const text = response.text;
131
+ this._trackTokens(response.usageMetadata, {
132
+ prompt: fullPrompt,
133
+ agentInstructions: agentInstructions ?? null,
134
+ response: text,
135
+ elapsed: Date.now() - _t0Text,
136
+ });
137
+ return text;
114
138
  }
115
139
  }