@agile-vibe-coding/avc 0.1.0 → 0.2.3

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 (290) hide show
  1. package/README.md +2 -0
  2. package/cli/agent-loader.js +21 -0
  3. package/cli/agents/agent-selector.md +129 -0
  4. package/cli/agents/architecture-recommender.md +418 -0
  5. package/cli/agents/database-deep-dive.md +470 -0
  6. package/cli/agents/database-recommender.md +634 -0
  7. package/cli/agents/doc-distributor.md +176 -0
  8. package/cli/agents/documentation-updater.md +203 -0
  9. package/cli/agents/epic-story-decomposer.md +280 -0
  10. package/cli/agents/feature-context-generator.md +91 -0
  11. package/cli/agents/gap-checker-epic.md +52 -0
  12. package/cli/agents/impact-checker-story.md +51 -0
  13. package/cli/agents/migration-guide-generator.md +305 -0
  14. package/cli/agents/mission-scope-generator.md +79 -0
  15. package/cli/agents/mission-scope-validator.md +112 -0
  16. package/cli/agents/project-context-extractor.md +107 -0
  17. package/cli/agents/project-documentation-creator.json +226 -0
  18. package/cli/agents/project-documentation-creator.md +595 -0
  19. package/cli/agents/question-prefiller.md +269 -0
  20. package/cli/agents/refiner-epic.md +39 -0
  21. package/cli/agents/refiner-story.md +42 -0
  22. package/cli/agents/solver-epic-api.json +15 -0
  23. package/cli/agents/solver-epic-api.md +39 -0
  24. package/cli/agents/solver-epic-backend.json +15 -0
  25. package/cli/agents/solver-epic-backend.md +39 -0
  26. package/cli/agents/solver-epic-cloud.json +15 -0
  27. package/cli/agents/solver-epic-cloud.md +39 -0
  28. package/cli/agents/solver-epic-data.json +15 -0
  29. package/cli/agents/solver-epic-data.md +39 -0
  30. package/cli/agents/solver-epic-database.json +15 -0
  31. package/cli/agents/solver-epic-database.md +39 -0
  32. package/cli/agents/solver-epic-developer.json +15 -0
  33. package/cli/agents/solver-epic-developer.md +39 -0
  34. package/cli/agents/solver-epic-devops.json +15 -0
  35. package/cli/agents/solver-epic-devops.md +39 -0
  36. package/cli/agents/solver-epic-frontend.json +15 -0
  37. package/cli/agents/solver-epic-frontend.md +39 -0
  38. package/cli/agents/solver-epic-mobile.json +15 -0
  39. package/cli/agents/solver-epic-mobile.md +39 -0
  40. package/cli/agents/solver-epic-qa.json +15 -0
  41. package/cli/agents/solver-epic-qa.md +39 -0
  42. package/cli/agents/solver-epic-security.json +15 -0
  43. package/cli/agents/solver-epic-security.md +39 -0
  44. package/cli/agents/solver-epic-solution-architect.json +15 -0
  45. package/cli/agents/solver-epic-solution-architect.md +39 -0
  46. package/cli/agents/solver-epic-test-architect.json +15 -0
  47. package/cli/agents/solver-epic-test-architect.md +39 -0
  48. package/cli/agents/solver-epic-ui.json +15 -0
  49. package/cli/agents/solver-epic-ui.md +39 -0
  50. package/cli/agents/solver-epic-ux.json +15 -0
  51. package/cli/agents/solver-epic-ux.md +39 -0
  52. package/cli/agents/solver-story-api.json +15 -0
  53. package/cli/agents/solver-story-api.md +39 -0
  54. package/cli/agents/solver-story-backend.json +15 -0
  55. package/cli/agents/solver-story-backend.md +39 -0
  56. package/cli/agents/solver-story-cloud.json +15 -0
  57. package/cli/agents/solver-story-cloud.md +39 -0
  58. package/cli/agents/solver-story-data.json +15 -0
  59. package/cli/agents/solver-story-data.md +39 -0
  60. package/cli/agents/solver-story-database.json +15 -0
  61. package/cli/agents/solver-story-database.md +39 -0
  62. package/cli/agents/solver-story-developer.json +15 -0
  63. package/cli/agents/solver-story-developer.md +39 -0
  64. package/cli/agents/solver-story-devops.json +15 -0
  65. package/cli/agents/solver-story-devops.md +39 -0
  66. package/cli/agents/solver-story-frontend.json +15 -0
  67. package/cli/agents/solver-story-frontend.md +39 -0
  68. package/cli/agents/solver-story-mobile.json +15 -0
  69. package/cli/agents/solver-story-mobile.md +39 -0
  70. package/cli/agents/solver-story-qa.json +15 -0
  71. package/cli/agents/solver-story-qa.md +39 -0
  72. package/cli/agents/solver-story-security.json +15 -0
  73. package/cli/agents/solver-story-security.md +39 -0
  74. package/cli/agents/solver-story-solution-architect.json +15 -0
  75. package/cli/agents/solver-story-solution-architect.md +39 -0
  76. package/cli/agents/solver-story-test-architect.json +15 -0
  77. package/cli/agents/solver-story-test-architect.md +39 -0
  78. package/cli/agents/solver-story-ui.json +15 -0
  79. package/cli/agents/solver-story-ui.md +39 -0
  80. package/cli/agents/solver-story-ux.json +15 -0
  81. package/cli/agents/solver-story-ux.md +39 -0
  82. package/cli/agents/story-doc-enricher.md +133 -0
  83. package/cli/agents/suggestion-business-analyst.md +88 -0
  84. package/cli/agents/suggestion-deployment-architect.md +263 -0
  85. package/cli/agents/suggestion-product-manager.md +129 -0
  86. package/cli/agents/suggestion-security-specialist.md +156 -0
  87. package/cli/agents/suggestion-technical-architect.md +269 -0
  88. package/cli/agents/suggestion-ux-researcher.md +93 -0
  89. package/cli/agents/task-subtask-decomposer.md +188 -0
  90. package/cli/agents/validator-documentation.json +152 -0
  91. package/cli/agents/validator-documentation.md +453 -0
  92. package/cli/agents/validator-epic-api.json +93 -0
  93. package/cli/agents/validator-epic-api.md +137 -0
  94. package/cli/agents/validator-epic-backend.json +93 -0
  95. package/cli/agents/validator-epic-backend.md +130 -0
  96. package/cli/agents/validator-epic-cloud.json +93 -0
  97. package/cli/agents/validator-epic-cloud.md +137 -0
  98. package/cli/agents/validator-epic-data.json +93 -0
  99. package/cli/agents/validator-epic-data.md +130 -0
  100. package/cli/agents/validator-epic-database.json +93 -0
  101. package/cli/agents/validator-epic-database.md +137 -0
  102. package/cli/agents/validator-epic-developer.json +74 -0
  103. package/cli/agents/validator-epic-developer.md +153 -0
  104. package/cli/agents/validator-epic-devops.json +74 -0
  105. package/cli/agents/validator-epic-devops.md +153 -0
  106. package/cli/agents/validator-epic-frontend.json +74 -0
  107. package/cli/agents/validator-epic-frontend.md +153 -0
  108. package/cli/agents/validator-epic-mobile.json +93 -0
  109. package/cli/agents/validator-epic-mobile.md +130 -0
  110. package/cli/agents/validator-epic-qa.json +93 -0
  111. package/cli/agents/validator-epic-qa.md +130 -0
  112. package/cli/agents/validator-epic-security.json +74 -0
  113. package/cli/agents/validator-epic-security.md +154 -0
  114. package/cli/agents/validator-epic-solution-architect.json +74 -0
  115. package/cli/agents/validator-epic-solution-architect.md +156 -0
  116. package/cli/agents/validator-epic-test-architect.json +93 -0
  117. package/cli/agents/validator-epic-test-architect.md +130 -0
  118. package/cli/agents/validator-epic-ui.json +93 -0
  119. package/cli/agents/validator-epic-ui.md +130 -0
  120. package/cli/agents/validator-epic-ux.json +93 -0
  121. package/cli/agents/validator-epic-ux.md +130 -0
  122. package/cli/agents/validator-selector.md +211 -0
  123. package/cli/agents/validator-story-api.json +104 -0
  124. package/cli/agents/validator-story-api.md +152 -0
  125. package/cli/agents/validator-story-backend.json +104 -0
  126. package/cli/agents/validator-story-backend.md +152 -0
  127. package/cli/agents/validator-story-cloud.json +104 -0
  128. package/cli/agents/validator-story-cloud.md +152 -0
  129. package/cli/agents/validator-story-data.json +104 -0
  130. package/cli/agents/validator-story-data.md +152 -0
  131. package/cli/agents/validator-story-database.json +104 -0
  132. package/cli/agents/validator-story-database.md +152 -0
  133. package/cli/agents/validator-story-developer.json +104 -0
  134. package/cli/agents/validator-story-developer.md +152 -0
  135. package/cli/agents/validator-story-devops.json +104 -0
  136. package/cli/agents/validator-story-devops.md +152 -0
  137. package/cli/agents/validator-story-frontend.json +104 -0
  138. package/cli/agents/validator-story-frontend.md +152 -0
  139. package/cli/agents/validator-story-mobile.json +104 -0
  140. package/cli/agents/validator-story-mobile.md +152 -0
  141. package/cli/agents/validator-story-qa.json +104 -0
  142. package/cli/agents/validator-story-qa.md +152 -0
  143. package/cli/agents/validator-story-security.json +104 -0
  144. package/cli/agents/validator-story-security.md +152 -0
  145. package/cli/agents/validator-story-solution-architect.json +104 -0
  146. package/cli/agents/validator-story-solution-architect.md +152 -0
  147. package/cli/agents/validator-story-test-architect.json +104 -0
  148. package/cli/agents/validator-story-test-architect.md +152 -0
  149. package/cli/agents/validator-story-ui.json +104 -0
  150. package/cli/agents/validator-story-ui.md +152 -0
  151. package/cli/agents/validator-story-ux.json +104 -0
  152. package/cli/agents/validator-story-ux.md +152 -0
  153. package/cli/ansi-colors.js +21 -0
  154. package/cli/build-docs.js +298 -0
  155. package/cli/ceremony-history.js +369 -0
  156. package/cli/command-logger.js +245 -0
  157. package/cli/components/static-output.js +63 -0
  158. package/cli/console-output-manager.js +94 -0
  159. package/cli/docs-sync.js +306 -0
  160. package/cli/epic-story-validator.js +1174 -0
  161. package/cli/evaluation-prompts.js +1008 -0
  162. package/cli/execution-context.js +195 -0
  163. package/cli/generate-summary-table.js +340 -0
  164. package/cli/index.js +3 -25
  165. package/cli/init-model-config.js +697 -0
  166. package/cli/init.js +1765 -100
  167. package/cli/kanban-server-manager.js +228 -0
  168. package/cli/llm-claude.js +109 -0
  169. package/cli/llm-gemini.js +115 -0
  170. package/cli/llm-mock.js +233 -0
  171. package/cli/llm-openai.js +233 -0
  172. package/cli/llm-provider.js +300 -0
  173. package/cli/llm-token-limits.js +102 -0
  174. package/cli/llm-verifier.js +454 -0
  175. package/cli/logger.js +32 -5
  176. package/cli/message-constants.js +58 -0
  177. package/cli/message-manager.js +334 -0
  178. package/cli/message-types.js +96 -0
  179. package/cli/messaging-api.js +297 -0
  180. package/cli/model-pricing.js +169 -0
  181. package/cli/model-query-engine.js +468 -0
  182. package/cli/model-recommendation-analyzer.js +495 -0
  183. package/cli/model-selector.js +269 -0
  184. package/cli/output-buffer.js +107 -0
  185. package/cli/process-manager.js +332 -0
  186. package/cli/repl-ink.js +5840 -504
  187. package/cli/repl-old.js +4 -4
  188. package/cli/seed-processor.js +792 -0
  189. package/cli/sprint-planning-processor.js +1813 -0
  190. package/cli/template-processor.js +2306 -108
  191. package/cli/templates/project.md +25 -8
  192. package/cli/templates/vitepress-config.mts.template +34 -0
  193. package/cli/token-tracker.js +520 -0
  194. package/cli/tools/generate-story-validators.js +317 -0
  195. package/cli/tools/generate-validators.js +669 -0
  196. package/cli/update-checker.js +19 -17
  197. package/cli/update-notifier.js +4 -4
  198. package/cli/validation-router.js +605 -0
  199. package/cli/verification-tracker.js +563 -0
  200. package/kanban/README.md +386 -0
  201. package/kanban/client/README.md +205 -0
  202. package/kanban/client/components.json +20 -0
  203. package/kanban/client/dist/assets/index-CiD8PS2e.js +306 -0
  204. package/kanban/client/dist/assets/index-nLh0m82Q.css +1 -0
  205. package/kanban/client/dist/index.html +16 -0
  206. package/kanban/client/dist/vite.svg +1 -0
  207. package/kanban/client/index.html +15 -0
  208. package/kanban/client/package-lock.json +9442 -0
  209. package/kanban/client/package.json +44 -0
  210. package/kanban/client/postcss.config.js +6 -0
  211. package/kanban/client/public/vite.svg +1 -0
  212. package/kanban/client/src/App.jsx +622 -0
  213. package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
  214. package/kanban/client/src/components/ceremony/AskArchPopup.jsx +416 -0
  215. package/kanban/client/src/components/ceremony/AskModelPopup.jsx +616 -0
  216. package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +946 -0
  217. package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
  218. package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +619 -0
  219. package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +704 -0
  220. package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
  221. package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +154 -0
  222. package/kanban/client/src/components/ceremony/steps/DatabaseStep.jsx +202 -0
  223. package/kanban/client/src/components/ceremony/steps/DeploymentStep.jsx +123 -0
  224. package/kanban/client/src/components/ceremony/steps/MissionStep.jsx +106 -0
  225. package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +125 -0
  226. package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +228 -0
  227. package/kanban/client/src/components/kanban/CardDetailModal.jsx +559 -0
  228. package/kanban/client/src/components/kanban/EpicSection.jsx +146 -0
  229. package/kanban/client/src/components/kanban/FilterToolbar.jsx +222 -0
  230. package/kanban/client/src/components/kanban/GroupingSelector.jsx +57 -0
  231. package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
  232. package/kanban/client/src/components/kanban/KanbanCard.jsx +138 -0
  233. package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
  234. package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +789 -0
  235. package/kanban/client/src/components/layout/LoadingScreen.jsx +82 -0
  236. package/kanban/client/src/components/process/ProcessMonitorBar.jsx +80 -0
  237. package/kanban/client/src/components/settings/AgentEditorPopup.jsx +171 -0
  238. package/kanban/client/src/components/settings/AgentsTab.jsx +353 -0
  239. package/kanban/client/src/components/settings/ApiKeysTab.jsx +113 -0
  240. package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +98 -0
  241. package/kanban/client/src/components/settings/CostThresholdsTab.jsx +94 -0
  242. package/kanban/client/src/components/settings/ModelPricingTab.jsx +204 -0
  243. package/kanban/client/src/components/settings/ServersTab.jsx +121 -0
  244. package/kanban/client/src/components/settings/SettingsModal.jsx +84 -0
  245. package/kanban/client/src/components/stats/CostModal.jsx +353 -0
  246. package/kanban/client/src/components/ui/badge.jsx +27 -0
  247. package/kanban/client/src/components/ui/dialog.jsx +121 -0
  248. package/kanban/client/src/components/ui/tabs.jsx +85 -0
  249. package/kanban/client/src/hooks/__tests__/useGrouping.test.js +232 -0
  250. package/kanban/client/src/hooks/useGrouping.js +118 -0
  251. package/kanban/client/src/hooks/useWebSocket.js +120 -0
  252. package/kanban/client/src/lib/__tests__/api.test.js +196 -0
  253. package/kanban/client/src/lib/__tests__/status-grouping.test.js +94 -0
  254. package/kanban/client/src/lib/api.js +401 -0
  255. package/kanban/client/src/lib/status-grouping.js +144 -0
  256. package/kanban/client/src/lib/utils.js +11 -0
  257. package/kanban/client/src/main.jsx +10 -0
  258. package/kanban/client/src/store/__tests__/kanbanStore.test.js +164 -0
  259. package/kanban/client/src/store/ceremonyStore.js +172 -0
  260. package/kanban/client/src/store/filterStore.js +201 -0
  261. package/kanban/client/src/store/kanbanStore.js +115 -0
  262. package/kanban/client/src/store/processStore.js +65 -0
  263. package/kanban/client/src/store/sprintPlanningStore.js +33 -0
  264. package/kanban/client/src/styles/globals.css +59 -0
  265. package/kanban/client/tailwind.config.js +77 -0
  266. package/kanban/client/vite.config.js +28 -0
  267. package/kanban/client/vitest.config.js +28 -0
  268. package/kanban/dev-start.sh +47 -0
  269. package/kanban/package.json +12 -0
  270. package/kanban/server/index.js +516 -0
  271. package/kanban/server/routes/ceremony.js +305 -0
  272. package/kanban/server/routes/costs.js +157 -0
  273. package/kanban/server/routes/processes.js +50 -0
  274. package/kanban/server/routes/settings.js +303 -0
  275. package/kanban/server/routes/websocket.js +276 -0
  276. package/kanban/server/routes/work-items.js +347 -0
  277. package/kanban/server/services/CeremonyService.js +1190 -0
  278. package/kanban/server/services/FileSystemScanner.js +95 -0
  279. package/kanban/server/services/FileWatcher.js +144 -0
  280. package/kanban/server/services/HierarchyBuilder.js +196 -0
  281. package/kanban/server/services/ProcessRegistry.js +122 -0
  282. package/kanban/server/services/WorkItemReader.js +123 -0
  283. package/kanban/server/services/WorkItemRefineService.js +510 -0
  284. package/kanban/server/start.js +49 -0
  285. package/kanban/server/utils/kanban-logger.js +132 -0
  286. package/kanban/server/utils/markdown.js +91 -0
  287. package/kanban/server/utils/status-grouping.js +107 -0
  288. package/kanban/server/workers/sponsor-call-worker.js +84 -0
  289. package/kanban/server/workers/sprint-planning-worker.js +130 -0
  290. package/package.json +34 -7
@@ -1,4 +1,4 @@
1
- You will be helping to create a comprehensive software application definition document. This document will serve as a foundational specification that can later be broken down into domain-specific details and related prompts for development agents.
1
+ You will be helping to create a comprehensive Project Brief. This document will serve as a foundational specification that can later be broken down into domain-specific details and related prompts for development agents.
2
2
 
3
3
  ## Project Information
4
4
 
@@ -14,10 +14,18 @@ You will be helping to create a comprehensive software application definition do
14
14
 
15
15
  {{INITIAL_SCOPE}}
16
16
 
17
+ ### Deployment Target
18
+
19
+ {{DEPLOYMENT_TARGET}}
20
+
17
21
  ### Technical Considerations
18
22
 
19
23
  {{TECHNICAL_CONSIDERATIONS}}
20
24
 
25
+ ### Technology Exclusions
26
+
27
+ {{TECHNICAL_EXCLUSIONS}}
28
+
21
29
  ### Security and Compliance
22
30
 
23
31
  {{SECURITY_AND_COMPLIANCE_REQUIREMENTS}}
@@ -39,24 +47,33 @@ In your scratchpad, consider:
39
47
  - What are the primary user journeys through the application?
40
48
  </scratchpad>
41
49
 
42
- Now, create a comprehensive application definition that includes the following sections:
50
+ Now, create a comprehensive Project Brief that includes the following sections:
43
51
 
44
52
  1. **Application Overview**: A clear, concise summary of what the application does and its primary purpose
45
53
 
46
54
  2. **Target Users and Stakeholders**: Identify the different types of users who will interact with this application, their roles, and their key needs
47
55
 
48
- 3. **Key Features and Functionality**: Describe the essential features organized by functional area or domain (e.g., user management, data processing, reporting, etc.)
56
+ 3. **Initial Scope**: Describe the essential features organized by functional area or domain (e.g., user management, data processing, reporting, etc.)
49
57
 
50
58
  4. **User Workflows**: Outline the primary user journeys or workflows through the application with step-by-step descriptions
51
59
 
52
- 5. **Technical Architecture**: Note any important technical requirements, constraints, or preferences (e.g., platform, technology stack, scalability needs, performance requirements)
60
+ 5. **UI/UX Design**: Describe the frontend technology approach, user interface requirements, and user experience considerations
61
+ - Frontend framework/technology selection (React, Vue, Angular, VitePress, Next.js, etc.)
62
+ - UI component library or design system approach (Material-UI, Tailwind, custom design system)
63
+ - Responsive design strategy (mobile-first, desktop-first, adaptive)
64
+ - Accessibility requirements (WCAG compliance level, screen reader support, keyboard navigation)
65
+ - User experience patterns (navigation structure, form design, loading states, error handling)
66
+ - Visual design considerations (branding, color scheme, typography)
67
+ - Internationalization needs (multi-language support, RTL layouts)
68
+
69
+ 6. **Technical Architecture**: Note any important backend and infrastructure requirements, constraints, or preferences (e.g., backend technology stack, database, hosting platform, scalability needs, performance requirements)
53
70
 
54
- 6. **Integration Requirements**: Identify any external systems, APIs, or data sources the application needs to connect with
71
+ 7. **Integration Requirements**: Identify any external systems, APIs, or data sources the application needs to connect with
55
72
 
56
- 7. **Security and Compliance**: Highlight any security, privacy, or regulatory compliance requirements
73
+ 8. **Security and Compliance**: Highlight any security, privacy, or regulatory compliance requirements
57
74
 
58
- 8. **Success Criteria**: Define what success looks like for this application
75
+ 9. **Success Criteria**: Define what success looks like for this application
59
76
 
60
- Your final output should be a complete, well-structured application definition document that provides enough detail to serve as a foundation for creating domain-specific specifications and work items for AI agents, while remaining at a high enough level to give a comprehensive view of the entire application.
77
+ Your final output should be a complete, well-structured Project Brief that provides enough detail to serve as a foundation for creating domain-specific specifications and work items for AI agents, while remaining at a high enough level to give a comprehensive view of the entire project.
61
78
 
62
79
  Use clear markdown formatting with headers, bullet points, and emphasis where appropriate. Do not include the scratchpad in your final output - only include the sections listed above.
@@ -0,0 +1,34 @@
1
+ import { defineConfig } from 'vitepress'
2
+
3
+ export default defineConfig({
4
+ title: '{{PROJECT_NAME}}',
5
+ description: 'Project documentation powered by Agile Vibe Coding',
6
+ base: '/',
7
+ ignoreDeadLinks: true,
8
+
9
+ // Custom head tags for AVC documentation identification
10
+ head: [
11
+ ['meta', { name: 'avc-documentation', content: 'true' }],
12
+ ['meta', { name: 'generator', content: 'Agile Vibe Coding' }]
13
+ ],
14
+
15
+ themeConfig: {
16
+ nav: [
17
+ { text: 'Home', link: '/' }
18
+ ],
19
+
20
+ sidebar: [
21
+ {
22
+ items: [
23
+ { text: 'Project Brief', link: '/' }
24
+ ]
25
+ }
26
+ // @@AVC-WORK-ITEMS-START@@
27
+ // @@AVC-WORK-ITEMS-END@@
28
+ ],
29
+
30
+ socialLinks: [
31
+ { icon: 'github', link: 'https://github.com/yourusername/yourproject' }
32
+ ]
33
+ }
34
+ })
@@ -0,0 +1,520 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ export class TokenTracker {
5
+ constructor(avcPath = path.join(process.cwd(), '.avc')) {
6
+ this.avcPath = avcPath;
7
+ this.tokenHistoryPath = path.join(avcPath, 'token-history.json');
8
+ this.data = null;
9
+ }
10
+
11
+ /**
12
+ * Initialize token history file if it doesn't exist
13
+ */
14
+ init() {
15
+ // Ensure .avc directory exists
16
+ if (!fs.existsSync(this.avcPath)) {
17
+ fs.mkdirSync(this.avcPath, { recursive: true });
18
+ }
19
+
20
+ if (!fs.existsSync(this.tokenHistoryPath)) {
21
+ const initialData = {
22
+ version: "1.0",
23
+ lastUpdated: new Date().toISOString(),
24
+ totals: {
25
+ daily: {},
26
+ weekly: {},
27
+ monthly: {},
28
+ allTime: {
29
+ input: 0,
30
+ output: 0,
31
+ total: 0,
32
+ executions: 0
33
+ }
34
+ }
35
+ };
36
+ this._writeData(initialData);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Load token history from disk
42
+ */
43
+ load() {
44
+ if (fs.existsSync(this.tokenHistoryPath)) {
45
+ this.data = JSON.parse(fs.readFileSync(this.tokenHistoryPath, 'utf8'));
46
+ } else {
47
+ this.init();
48
+ this.data = JSON.parse(fs.readFileSync(this.tokenHistoryPath, 'utf8'));
49
+ }
50
+ return this.data;
51
+ }
52
+
53
+ /**
54
+ * Write data to disk atomically
55
+ */
56
+ _writeData(data) {
57
+ // Ensure directory exists before writing (handles race conditions in tests)
58
+ if (!fs.existsSync(this.avcPath)) {
59
+ fs.mkdirSync(this.avcPath, { recursive: true });
60
+ }
61
+
62
+ try {
63
+ const tempPath = this.tokenHistoryPath + '.tmp';
64
+ fs.writeFileSync(tempPath, JSON.stringify(data, null, 2), 'utf8');
65
+ fs.renameSync(tempPath, this.tokenHistoryPath);
66
+ } catch (error) {
67
+ // Fallback to direct write if atomic write fails
68
+ try {
69
+ fs.writeFileSync(this.tokenHistoryPath, JSON.stringify(data, null, 2), 'utf8');
70
+ } catch (fallbackError) {
71
+ console.error(`Failed to write token history: ${fallbackError.message}`);
72
+ console.error(`Path: ${this.tokenHistoryPath}`);
73
+ throw fallbackError;
74
+ }
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Read avc.json configuration
80
+ */
81
+ readConfig() {
82
+ try {
83
+ const configPath = path.join(this.avcPath, 'avc.json');
84
+ if (fs.existsSync(configPath)) {
85
+ return JSON.parse(fs.readFileSync(configPath, 'utf8'));
86
+ }
87
+ } catch (error) {
88
+ console.warn(`Could not read avc.json: ${error.message}`);
89
+ }
90
+ return { settings: {} };
91
+ }
92
+
93
+ /**
94
+ * Calculate cost for token usage based on model pricing
95
+ * @param {number} inputTokens - Input tokens consumed
96
+ * @param {number} outputTokens - Output tokens consumed
97
+ * @param {string} modelId - Model identifier (e.g., 'claude-sonnet-4-5-20250929')
98
+ * @returns {Object} Cost breakdown { input: number, output: number, total: number }
99
+ */
100
+ calculateCost(inputTokens, outputTokens, modelId) {
101
+ const config = this.readConfig();
102
+ const modelConfig = config.settings?.models?.[modelId];
103
+
104
+ if (!modelConfig || !modelConfig.pricing) {
105
+ // Model not found or no pricing - return zero cost
106
+ return { input: 0, output: 0, total: 0 };
107
+ }
108
+
109
+ const pricing = modelConfig.pricing;
110
+ const divisor = pricing.unit === 'million' ? 1_000_000 : 1_000;
111
+
112
+ // Calculate costs (price per unit * tokens / divisor)
113
+ const inputCost = (pricing.input * inputTokens) / divisor;
114
+ const outputCost = (pricing.output * outputTokens) / divisor;
115
+
116
+ return {
117
+ input: inputCost,
118
+ output: outputCost,
119
+ total: inputCost + outputCost
120
+ };
121
+ }
122
+
123
+ /**
124
+ * Add tokens from a completed ceremony execution
125
+ * @param {string} ceremonyType - e.g., 'sponsor-call'
126
+ * @param {Object} tokens - { input, output }
127
+ * @param {string} modelId - Model identifier (optional)
128
+ */
129
+ addExecution(ceremonyType, tokens, modelId = null) {
130
+ try {
131
+ this.load();
132
+
133
+ const now = new Date();
134
+ const dateKey = now.toISOString().split('T')[0]; // YYYY-MM-DD
135
+ const weekKey = this._getWeekKey(now); // YYYY-Www
136
+ const monthKey = dateKey.substring(0, 7); // YYYY-MM
137
+ const timestamp = now.toISOString();
138
+
139
+ const tokenData = {
140
+ input: tokens.input || 0,
141
+ output: tokens.output || 0,
142
+ total: (tokens.input || 0) + (tokens.output || 0),
143
+ provider: tokens.provider || 'unknown',
144
+ model: tokens.model || modelId || 'unknown'
145
+ };
146
+
147
+ // Calculate cost: prefer per-model pricing from avc.json, fall back to provider estimate
148
+ let costData = null;
149
+ const effectiveModelId = tokens.model || modelId;
150
+ if (effectiveModelId) {
151
+ costData = this.calculateCost(tokenData.input, tokenData.output, effectiveModelId);
152
+ }
153
+ // If no per-model pricing configured but provider sent a pre-computed estimate, use it
154
+ if ((!costData || costData.total === 0) && tokens.estimatedCost) {
155
+ costData = { input: 0, output: 0, total: tokens.estimatedCost };
156
+ }
157
+
158
+ console.log(` → Tracking tokens for ${ceremonyType}: ${tokenData.input} input, ${tokenData.output} output (${tokenData.provider})`);
159
+ if (costData && costData.total > 0) {
160
+ console.log(` → Estimated cost: $${costData.total.toFixed(4)} (${effectiveModelId})`);
161
+ }
162
+
163
+ // Update totals (global)
164
+ this._updateAggregation(this.data.totals, dateKey, weekKey, monthKey, tokenData, timestamp, costData);
165
+
166
+ // Update ceremony-specific aggregations
167
+ if (!this.data[ceremonyType]) {
168
+ this.data[ceremonyType] = {
169
+ daily: {},
170
+ weekly: {},
171
+ monthly: {},
172
+ allTime: {
173
+ input: 0,
174
+ output: 0,
175
+ total: 0,
176
+ executions: 0,
177
+ cost: { input: 0, output: 0, total: 0 }
178
+ }
179
+ };
180
+ }
181
+ this._updateAggregation(this.data[ceremonyType], dateKey, weekKey, monthKey, tokenData, timestamp, costData);
182
+
183
+ // Update lastUpdated
184
+ this.data.lastUpdated = timestamp;
185
+
186
+ // Write to disk
187
+ this._writeData(this.data);
188
+ console.log(` → Token history saved to ${this.tokenHistoryPath}`);
189
+ } catch (error) {
190
+ console.error(`Failed to add token execution: ${error.message}`);
191
+ throw error;
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Record tokens from a single LLM API call without incrementing the executions counter.
197
+ * Called after every individual LLM call so tokens are persisted crash-safely.
198
+ * @param {string} ceremonyType - e.g., 'sprint-planning'
199
+ * @param {Object} delta - { input, output, provider, model, estimatedCost? }
200
+ */
201
+ addIncremental(ceremonyType, delta) {
202
+ if (!delta.input && !delta.output) return;
203
+ try {
204
+ this.load();
205
+ const now = new Date();
206
+ const dateKey = now.toISOString().split('T')[0];
207
+ const weekKey = this._getWeekKey(now);
208
+ const monthKey = dateKey.substring(0, 7);
209
+ const timestamp = now.toISOString();
210
+
211
+ const tokenData = {
212
+ input: delta.input || 0,
213
+ output: delta.output || 0,
214
+ total: (delta.input || 0) + (delta.output || 0),
215
+ provider: delta.provider || 'unknown',
216
+ model: delta.model || 'unknown'
217
+ };
218
+
219
+ let costData = null;
220
+ if (delta.model) {
221
+ costData = this.calculateCost(tokenData.input, tokenData.output, delta.model);
222
+ }
223
+ if ((!costData || costData.total === 0) && delta.estimatedCost) {
224
+ costData = { input: 0, output: 0, total: delta.estimatedCost };
225
+ }
226
+
227
+ if (!this.data[ceremonyType]) {
228
+ this.data[ceremonyType] = {
229
+ daily: {}, weekly: {}, monthly: {},
230
+ allTime: { input: 0, output: 0, total: 0, executions: 0, cost: { input: 0, output: 0, total: 0 } }
231
+ };
232
+ }
233
+
234
+ this._updateAggregation(this.data.totals, dateKey, weekKey, monthKey, tokenData, timestamp, costData, false);
235
+ this._updateAggregation(this.data[ceremonyType], dateKey, weekKey, monthKey, tokenData, timestamp, costData, false);
236
+ this.data.lastUpdated = timestamp;
237
+ this._writeData(this.data);
238
+ } catch (error) {
239
+ console.error(`addIncremental failed: ${error.message}`);
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Increment executions counter to mark a completed run.
245
+ * Call this once per ceremony run after all LLM calls are done.
246
+ * Tokens are already persisted by addIncremental() — this only bumps the count.
247
+ * @param {string} ceremonyType - e.g., 'sprint-planning'
248
+ */
249
+ finalizeRun(ceremonyType) {
250
+ try {
251
+ this.load();
252
+ const now = new Date();
253
+ const dateKey = now.toISOString().split('T')[0];
254
+ const weekKey = this._getWeekKey(now);
255
+ const monthKey = dateKey.substring(0, 7);
256
+ const timestamp = now.toISOString();
257
+
258
+ if (!this.data[ceremonyType]) {
259
+ this.data[ceremonyType] = {
260
+ daily: {}, weekly: {}, monthly: {},
261
+ allTime: { input: 0, output: 0, total: 0, executions: 0, cost: { input: 0, output: 0, total: 0 } }
262
+ };
263
+ }
264
+
265
+ // Zero-token update — only increments executions counters
266
+ const zero = { input: 0, output: 0, total: 0, provider: 'unknown', model: 'unknown' };
267
+ this._updateAggregation(this.data.totals, dateKey, weekKey, monthKey, zero, timestamp, null, true);
268
+ this._updateAggregation(this.data[ceremonyType], dateKey, weekKey, monthKey, zero, timestamp, null, true);
269
+ this.data.lastUpdated = timestamp;
270
+ this._writeData(this.data);
271
+ console.log(` → Ceremony run finalized for ${ceremonyType}`);
272
+ } catch (error) {
273
+ console.error(`finalizeRun failed: ${error.message}`);
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Update aggregations for a given scope (totals or ceremony-type)
279
+ * @param {boolean} incExec - Whether to increment executions counters (default true)
280
+ */
281
+ _updateAggregation(scope, dateKey, weekKey, monthKey, tokenData, timestamp, costData = null, incExec = true) {
282
+ // Update daily
283
+ if (!scope.daily[dateKey]) {
284
+ scope.daily[dateKey] = {
285
+ date: dateKey,
286
+ input: 0,
287
+ output: 0,
288
+ total: 0,
289
+ executions: 0,
290
+ cost: { input: 0, output: 0, total: 0 }
291
+ };
292
+ }
293
+ scope.daily[dateKey].input += tokenData.input;
294
+ scope.daily[dateKey].output += tokenData.output;
295
+ scope.daily[dateKey].total += tokenData.total;
296
+ if (incExec) scope.daily[dateKey].executions++;
297
+ if (costData) {
298
+ scope.daily[dateKey].cost.input += costData.input;
299
+ scope.daily[dateKey].cost.output += costData.output;
300
+ scope.daily[dateKey].cost.total += costData.total;
301
+ }
302
+
303
+ // Update weekly
304
+ if (!scope.weekly[weekKey]) {
305
+ scope.weekly[weekKey] = {
306
+ week: weekKey,
307
+ input: 0,
308
+ output: 0,
309
+ total: 0,
310
+ executions: 0,
311
+ cost: { input: 0, output: 0, total: 0 }
312
+ };
313
+ }
314
+ scope.weekly[weekKey].input += tokenData.input;
315
+ scope.weekly[weekKey].output += tokenData.output;
316
+ scope.weekly[weekKey].total += tokenData.total;
317
+ if (incExec) scope.weekly[weekKey].executions++;
318
+ if (costData) {
319
+ scope.weekly[weekKey].cost.input += costData.input;
320
+ scope.weekly[weekKey].cost.output += costData.output;
321
+ scope.weekly[weekKey].cost.total += costData.total;
322
+ }
323
+
324
+ // Update monthly
325
+ if (!scope.monthly[monthKey]) {
326
+ scope.monthly[monthKey] = {
327
+ month: monthKey,
328
+ input: 0,
329
+ output: 0,
330
+ total: 0,
331
+ executions: 0,
332
+ cost: { input: 0, output: 0, total: 0 }
333
+ };
334
+ }
335
+ scope.monthly[monthKey].input += tokenData.input;
336
+ scope.monthly[monthKey].output += tokenData.output;
337
+ scope.monthly[monthKey].total += tokenData.total;
338
+ if (incExec) scope.monthly[monthKey].executions++;
339
+ if (costData) {
340
+ scope.monthly[monthKey].cost.input += costData.input;
341
+ scope.monthly[monthKey].cost.output += costData.output;
342
+ scope.monthly[monthKey].cost.total += costData.total;
343
+ }
344
+
345
+ // Update all-time
346
+ scope.allTime.input += tokenData.input;
347
+ scope.allTime.output += tokenData.output;
348
+ scope.allTime.total += tokenData.total;
349
+ if (incExec) scope.allTime.executions++;
350
+
351
+ // Initialize cost tracking if not present
352
+ if (!scope.allTime.cost) {
353
+ scope.allTime.cost = { input: 0, output: 0, total: 0 };
354
+ }
355
+ if (costData) {
356
+ scope.allTime.cost.input += costData.input;
357
+ scope.allTime.cost.output += costData.output;
358
+ scope.allTime.cost.total += costData.total;
359
+ }
360
+
361
+ if (!scope.allTime.firstExecution) {
362
+ scope.allTime.firstExecution = timestamp;
363
+ }
364
+ scope.allTime.lastExecution = timestamp;
365
+
366
+ // Cleanup old rolling windows
367
+ this._cleanupRollingWindows();
368
+ }
369
+
370
+ /**
371
+ * Get ISO week key (YYYY-Www)
372
+ */
373
+ _getWeekKey(date) {
374
+ const tempDate = new Date(date.getTime());
375
+ tempDate.setHours(0, 0, 0, 0);
376
+ tempDate.setDate(tempDate.getDate() + 3 - (tempDate.getDay() + 6) % 7);
377
+ const week1 = new Date(tempDate.getFullYear(), 0, 4);
378
+ const weekNum = 1 + Math.round(((tempDate - week1) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7);
379
+ return `${tempDate.getFullYear()}-W${String(weekNum).padStart(2, '0')}`;
380
+ }
381
+
382
+ /**
383
+ * Cleanup old entries from rolling windows
384
+ */
385
+ _cleanupRollingWindows() {
386
+ const now = new Date();
387
+
388
+ // Keep last 31 days
389
+ const cutoffDaily = new Date(now);
390
+ cutoffDaily.setDate(cutoffDaily.getDate() - 31);
391
+
392
+ // Clean totals.daily
393
+ Object.keys(this.data.totals.daily).forEach(key => {
394
+ if (new Date(key) < cutoffDaily) {
395
+ delete this.data.totals.daily[key];
396
+ }
397
+ });
398
+
399
+ // Clean ceremony-type daily
400
+ Object.keys(this.data).forEach(key => {
401
+ if (key !== 'version' && key !== 'lastUpdated' && key !== 'totals' && this.data[key].daily) {
402
+ Object.keys(this.data[key].daily).forEach(dateKey => {
403
+ if (new Date(dateKey) < cutoffDaily) {
404
+ delete this.data[key].daily[dateKey];
405
+ }
406
+ });
407
+ }
408
+ });
409
+
410
+ // Keep last 12 weeks
411
+ const cutoffWeekly = new Date(now);
412
+ cutoffWeekly.setDate(cutoffWeekly.getDate() - 84);
413
+
414
+ Object.keys(this.data.totals.weekly).forEach(key => {
415
+ const [year, week] = key.split('-W');
416
+ const weekDate = this._getDateFromWeek(parseInt(year), parseInt(week));
417
+ if (weekDate < cutoffWeekly) {
418
+ delete this.data.totals.weekly[key];
419
+ }
420
+ });
421
+
422
+ Object.keys(this.data).forEach(key => {
423
+ if (key !== 'version' && key !== 'lastUpdated' && key !== 'totals' && this.data[key].weekly) {
424
+ Object.keys(this.data[key].weekly).forEach(weekKey => {
425
+ const [year, week] = weekKey.split('-W');
426
+ const weekDate = this._getDateFromWeek(parseInt(year), parseInt(week));
427
+ if (weekDate < cutoffWeekly) {
428
+ delete this.data[key].weekly[weekKey];
429
+ }
430
+ });
431
+ }
432
+ });
433
+
434
+ // Keep last 12 months
435
+ const cutoffMonthly = new Date(now);
436
+ cutoffMonthly.setMonth(cutoffMonthly.getMonth() - 12);
437
+
438
+ Object.keys(this.data.totals.monthly).forEach(key => {
439
+ if (new Date(key + '-01') < cutoffMonthly) {
440
+ delete this.data.totals.monthly[key];
441
+ }
442
+ });
443
+
444
+ Object.keys(this.data).forEach(key => {
445
+ if (key !== 'version' && key !== 'lastUpdated' && key !== 'totals' && this.data[key].monthly) {
446
+ Object.keys(this.data[key].monthly).forEach(monthKey => {
447
+ if (new Date(monthKey + '-01') < cutoffMonthly) {
448
+ delete this.data[key].monthly[monthKey];
449
+ }
450
+ });
451
+ }
452
+ });
453
+ }
454
+
455
+ /**
456
+ * Get date from ISO week
457
+ */
458
+ _getDateFromWeek(year, week) {
459
+ const simple = new Date(year, 0, 1 + (week - 1) * 7);
460
+ const dow = simple.getDay();
461
+ const ISOweekStart = simple;
462
+ if (dow <= 4) ISOweekStart.setDate(simple.getDate() - simple.getDay() + 1);
463
+ else ISOweekStart.setDate(simple.getDate() + 8 - simple.getDay());
464
+ return ISOweekStart;
465
+ }
466
+
467
+ // Query methods
468
+ getTotalsToday() {
469
+ this.load();
470
+ const today = new Date().toISOString().split('T')[0];
471
+ return this.data.totals.daily[today] || { date: today, input: 0, output: 0, total: 0, executions: 0 };
472
+ }
473
+
474
+ getTotalsThisWeek() {
475
+ this.load();
476
+ const week = this._getWeekKey(new Date());
477
+ return this.data.totals.weekly[week] || { week, input: 0, output: 0, total: 0, executions: 0 };
478
+ }
479
+
480
+ getTotalsThisMonth() {
481
+ this.load();
482
+ const month = new Date().toISOString().substring(0, 7);
483
+ return this.data.totals.monthly[month] || { month, input: 0, output: 0, total: 0, executions: 0 };
484
+ }
485
+
486
+ getTotalsAllTime() {
487
+ this.load();
488
+ return this.data.totals.allTime;
489
+ }
490
+
491
+ getCeremonyToday(ceremonyType) {
492
+ this.load();
493
+ const today = new Date().toISOString().split('T')[0];
494
+ return this.data[ceremonyType]?.daily[today] || { date: today, input: 0, output: 0, total: 0, executions: 0 };
495
+ }
496
+
497
+ getCeremonyThisWeek(ceremonyType) {
498
+ this.load();
499
+ const week = this._getWeekKey(new Date());
500
+ return this.data[ceremonyType]?.weekly[week] || { week, input: 0, output: 0, total: 0, executions: 0 };
501
+ }
502
+
503
+ getCeremonyThisMonth(ceremonyType) {
504
+ this.load();
505
+ const month = new Date().toISOString().substring(0, 7);
506
+ return this.data[ceremonyType]?.monthly[month] || { month, input: 0, output: 0, total: 0, executions: 0 };
507
+ }
508
+
509
+ getCeremonyAllTime(ceremonyType) {
510
+ this.load();
511
+ return this.data[ceremonyType]?.allTime || { input: 0, output: 0, total: 0, executions: 0 };
512
+ }
513
+
514
+ getAllCeremonyTypes() {
515
+ this.load();
516
+ return Object.keys(this.data).filter(key =>
517
+ key !== 'version' && key !== 'lastUpdated' && key !== 'totals'
518
+ );
519
+ }
520
+ }