@agile-vibe-coding/avc 0.1.1 → 0.3.1

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 (239) hide show
  1. package/cli/agent-loader.js +21 -0
  2. package/cli/agents/agent-selector.md +152 -0
  3. package/cli/agents/architecture-recommender.md +418 -0
  4. package/cli/agents/code-implementer.md +117 -0
  5. package/cli/agents/code-validator.md +80 -0
  6. package/cli/agents/context-reviewer-epic.md +101 -0
  7. package/cli/agents/context-reviewer-story.md +92 -0
  8. package/cli/agents/context-writer-epic.md +145 -0
  9. package/cli/agents/context-writer-story.md +111 -0
  10. package/cli/agents/database-deep-dive.md +470 -0
  11. package/cli/agents/database-recommender.md +634 -0
  12. package/cli/agents/doc-distributor.md +176 -0
  13. package/cli/agents/doc-writer-epic.md +42 -0
  14. package/cli/agents/doc-writer-story.md +43 -0
  15. package/cli/agents/documentation-updater.md +203 -0
  16. package/cli/agents/duplicate-detector.md +110 -0
  17. package/cli/agents/epic-story-decomposer.md +559 -0
  18. package/cli/agents/feature-context-generator.md +91 -0
  19. package/cli/agents/gap-checker-epic.md +52 -0
  20. package/cli/agents/impact-checker-story.md +51 -0
  21. package/cli/agents/migration-guide-generator.md +305 -0
  22. package/cli/agents/mission-scope-generator.md +143 -0
  23. package/cli/agents/mission-scope-validator.md +146 -0
  24. package/cli/agents/project-context-extractor.md +122 -0
  25. package/cli/agents/project-documentation-creator.json +226 -0
  26. package/cli/agents/project-documentation-creator.md +595 -0
  27. package/cli/agents/question-prefiller.md +269 -0
  28. package/cli/agents/refiner-epic.md +39 -0
  29. package/cli/agents/refiner-story.md +42 -0
  30. package/cli/agents/scaffolding-generator.md +99 -0
  31. package/cli/agents/seed-validator.md +71 -0
  32. package/cli/agents/story-doc-enricher.md +133 -0
  33. package/cli/agents/story-scope-reviewer.md +147 -0
  34. package/cli/agents/story-splitter.md +83 -0
  35. package/cli/agents/suggestion-business-analyst.md +88 -0
  36. package/cli/agents/suggestion-deployment-architect.md +263 -0
  37. package/cli/agents/suggestion-product-manager.md +129 -0
  38. package/cli/agents/suggestion-security-specialist.md +156 -0
  39. package/cli/agents/suggestion-technical-architect.md +269 -0
  40. package/cli/agents/suggestion-ux-researcher.md +93 -0
  41. package/cli/agents/task-subtask-decomposer.md +188 -0
  42. package/cli/agents/validator-documentation.json +183 -0
  43. package/cli/agents/validator-documentation.md +455 -0
  44. package/cli/agents/validator-selector.md +211 -0
  45. package/cli/ansi-colors.js +21 -0
  46. package/cli/api-reference-tool.js +368 -0
  47. package/cli/build-docs.js +29 -8
  48. package/cli/ceremony-history.js +369 -0
  49. package/cli/checks/catalog.json +76 -0
  50. package/cli/checks/code/quality.json +26 -0
  51. package/cli/checks/code/testing.json +14 -0
  52. package/cli/checks/code/traceability.json +26 -0
  53. package/cli/checks/cross-refs/epic.json +171 -0
  54. package/cli/checks/cross-refs/story.json +149 -0
  55. package/cli/checks/epic/api.json +114 -0
  56. package/cli/checks/epic/backend.json +126 -0
  57. package/cli/checks/epic/cloud.json +126 -0
  58. package/cli/checks/epic/data.json +102 -0
  59. package/cli/checks/epic/database.json +114 -0
  60. package/cli/checks/epic/developer.json +182 -0
  61. package/cli/checks/epic/devops.json +174 -0
  62. package/cli/checks/epic/frontend.json +162 -0
  63. package/cli/checks/epic/mobile.json +102 -0
  64. package/cli/checks/epic/qa.json +90 -0
  65. package/cli/checks/epic/security.json +184 -0
  66. package/cli/checks/epic/solution-architect.json +192 -0
  67. package/cli/checks/epic/test-architect.json +90 -0
  68. package/cli/checks/epic/ui.json +102 -0
  69. package/cli/checks/epic/ux.json +90 -0
  70. package/cli/checks/fixes/epic-fix-template.md +10 -0
  71. package/cli/checks/fixes/story-fix-template.md +10 -0
  72. package/cli/checks/story/api.json +186 -0
  73. package/cli/checks/story/backend.json +102 -0
  74. package/cli/checks/story/cloud.json +102 -0
  75. package/cli/checks/story/data.json +210 -0
  76. package/cli/checks/story/database.json +102 -0
  77. package/cli/checks/story/developer.json +168 -0
  78. package/cli/checks/story/devops.json +102 -0
  79. package/cli/checks/story/frontend.json +174 -0
  80. package/cli/checks/story/mobile.json +102 -0
  81. package/cli/checks/story/qa.json +210 -0
  82. package/cli/checks/story/security.json +198 -0
  83. package/cli/checks/story/solution-architect.json +230 -0
  84. package/cli/checks/story/test-architect.json +210 -0
  85. package/cli/checks/story/ui.json +102 -0
  86. package/cli/checks/story/ux.json +102 -0
  87. package/cli/coding-order.js +401 -0
  88. package/cli/command-logger.js +49 -12
  89. package/cli/components/static-output.js +63 -0
  90. package/cli/console-output-manager.js +94 -0
  91. package/cli/dependency-checker.js +72 -0
  92. package/cli/docs-sync.js +306 -0
  93. package/cli/epic-story-validator.js +659 -0
  94. package/cli/evaluation-prompts.js +1008 -0
  95. package/cli/execution-context.js +195 -0
  96. package/cli/generate-summary-table.js +340 -0
  97. package/cli/init-model-config.js +704 -0
  98. package/cli/init.js +1737 -278
  99. package/cli/kanban-server-manager.js +227 -0
  100. package/cli/llm-claude.js +150 -1
  101. package/cli/llm-gemini.js +109 -0
  102. package/cli/llm-local.js +493 -0
  103. package/cli/llm-mock.js +233 -0
  104. package/cli/llm-openai.js +454 -0
  105. package/cli/llm-provider.js +379 -3
  106. package/cli/llm-token-limits.js +211 -0
  107. package/cli/llm-verifier.js +662 -0
  108. package/cli/llm-xiaomi.js +143 -0
  109. package/cli/message-constants.js +49 -0
  110. package/cli/message-manager.js +334 -0
  111. package/cli/message-types.js +96 -0
  112. package/cli/messaging-api.js +291 -0
  113. package/cli/micro-check-fixer.js +335 -0
  114. package/cli/micro-check-runner.js +449 -0
  115. package/cli/micro-check-scorer.js +148 -0
  116. package/cli/micro-check-validator.js +538 -0
  117. package/cli/model-pricing.js +192 -0
  118. package/cli/model-query-engine.js +468 -0
  119. package/cli/model-recommendation-analyzer.js +495 -0
  120. package/cli/model-selector.js +270 -0
  121. package/cli/output-buffer.js +107 -0
  122. package/cli/process-manager.js +73 -2
  123. package/cli/prompt-logger.js +57 -0
  124. package/cli/repl-ink.js +4625 -1094
  125. package/cli/repl-old.js +3 -4
  126. package/cli/seed-processor.js +962 -0
  127. package/cli/sprint-planning-processor.js +4162 -0
  128. package/cli/template-processor.js +2149 -105
  129. package/cli/templates/project.md +25 -8
  130. package/cli/templates/vitepress-config.mts.template +5 -4
  131. package/cli/token-tracker.js +547 -0
  132. package/cli/tools/generate-story-validators.js +317 -0
  133. package/cli/tools/generate-validators.js +669 -0
  134. package/cli/update-checker.js +19 -17
  135. package/cli/update-notifier.js +4 -4
  136. package/cli/validation-router.js +667 -0
  137. package/cli/verification-tracker.js +563 -0
  138. package/cli/worktree-runner.js +654 -0
  139. package/kanban/README.md +386 -0
  140. package/kanban/client/README.md +205 -0
  141. package/kanban/client/components.json +20 -0
  142. package/kanban/client/dist/assets/index-D_KC5EQT.css +1 -0
  143. package/kanban/client/dist/assets/index-DjY5zqW7.js +351 -0
  144. package/kanban/client/dist/index.html +16 -0
  145. package/kanban/client/dist/vite.svg +1 -0
  146. package/kanban/client/index.html +15 -0
  147. package/kanban/client/package-lock.json +9442 -0
  148. package/kanban/client/package.json +44 -0
  149. package/kanban/client/postcss.config.js +6 -0
  150. package/kanban/client/public/vite.svg +1 -0
  151. package/kanban/client/src/App.jsx +651 -0
  152. package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
  153. package/kanban/client/src/components/ceremony/AskArchPopup.jsx +420 -0
  154. package/kanban/client/src/components/ceremony/AskModelPopup.jsx +629 -0
  155. package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +1133 -0
  156. package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
  157. package/kanban/client/src/components/ceremony/ProviderSwitcherButton.jsx +290 -0
  158. package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +686 -0
  159. package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +838 -0
  160. package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
  161. package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +136 -0
  162. package/kanban/client/src/components/ceremony/steps/DatabaseStep.jsx +202 -0
  163. package/kanban/client/src/components/ceremony/steps/DeploymentStep.jsx +123 -0
  164. package/kanban/client/src/components/ceremony/steps/MissionStep.jsx +106 -0
  165. package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +329 -0
  166. package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +249 -0
  167. package/kanban/client/src/components/kanban/CardDetailModal.jsx +646 -0
  168. package/kanban/client/src/components/kanban/EpicSection.jsx +146 -0
  169. package/kanban/client/src/components/kanban/FilterToolbar.jsx +222 -0
  170. package/kanban/client/src/components/kanban/GroupingSelector.jsx +63 -0
  171. package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
  172. package/kanban/client/src/components/kanban/KanbanCard.jsx +147 -0
  173. package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
  174. package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +784 -0
  175. package/kanban/client/src/components/kanban/RunButton.jsx +162 -0
  176. package/kanban/client/src/components/kanban/SeedButton.jsx +176 -0
  177. package/kanban/client/src/components/layout/LoadingScreen.jsx +82 -0
  178. package/kanban/client/src/components/process/ProcessMonitorBar.jsx +80 -0
  179. package/kanban/client/src/components/settings/AgentEditorPopup.jsx +171 -0
  180. package/kanban/client/src/components/settings/AgentsTab.jsx +381 -0
  181. package/kanban/client/src/components/settings/ApiKeysTab.jsx +142 -0
  182. package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +105 -0
  183. package/kanban/client/src/components/settings/CheckEditorPopup.jsx +507 -0
  184. package/kanban/client/src/components/settings/CostThresholdsTab.jsx +95 -0
  185. package/kanban/client/src/components/settings/ModelPricingTab.jsx +269 -0
  186. package/kanban/client/src/components/settings/OpenAIAuthSection.jsx +412 -0
  187. package/kanban/client/src/components/settings/ServersTab.jsx +121 -0
  188. package/kanban/client/src/components/settings/SettingsModal.jsx +84 -0
  189. package/kanban/client/src/components/stats/CostModal.jsx +384 -0
  190. package/kanban/client/src/components/ui/badge.jsx +27 -0
  191. package/kanban/client/src/components/ui/dialog.jsx +121 -0
  192. package/kanban/client/src/components/ui/tabs.jsx +85 -0
  193. package/kanban/client/src/hooks/__tests__/useGrouping.test.js +232 -0
  194. package/kanban/client/src/hooks/useGrouping.js +177 -0
  195. package/kanban/client/src/hooks/useWebSocket.js +120 -0
  196. package/kanban/client/src/lib/__tests__/api.test.js +196 -0
  197. package/kanban/client/src/lib/__tests__/status-grouping.test.js +94 -0
  198. package/kanban/client/src/lib/api.js +515 -0
  199. package/kanban/client/src/lib/status-grouping.js +154 -0
  200. package/kanban/client/src/lib/utils.js +11 -0
  201. package/kanban/client/src/main.jsx +10 -0
  202. package/kanban/client/src/store/__tests__/kanbanStore.test.js +164 -0
  203. package/kanban/client/src/store/ceremonyStore.js +172 -0
  204. package/kanban/client/src/store/filterStore.js +201 -0
  205. package/kanban/client/src/store/kanbanStore.js +123 -0
  206. package/kanban/client/src/store/processStore.js +65 -0
  207. package/kanban/client/src/store/sprintPlanningStore.js +33 -0
  208. package/kanban/client/src/styles/globals.css +59 -0
  209. package/kanban/client/tailwind.config.js +77 -0
  210. package/kanban/client/vite.config.js +28 -0
  211. package/kanban/client/vitest.config.js +28 -0
  212. package/kanban/dev-start.sh +47 -0
  213. package/kanban/package.json +12 -0
  214. package/kanban/server/index.js +537 -0
  215. package/kanban/server/routes/ceremony.js +454 -0
  216. package/kanban/server/routes/costs.js +163 -0
  217. package/kanban/server/routes/openai-oauth.js +366 -0
  218. package/kanban/server/routes/processes.js +50 -0
  219. package/kanban/server/routes/settings.js +736 -0
  220. package/kanban/server/routes/websocket.js +281 -0
  221. package/kanban/server/routes/work-items.js +487 -0
  222. package/kanban/server/services/CeremonyService.js +1441 -0
  223. package/kanban/server/services/FileSystemScanner.js +95 -0
  224. package/kanban/server/services/FileWatcher.js +144 -0
  225. package/kanban/server/services/HierarchyBuilder.js +196 -0
  226. package/kanban/server/services/ProcessRegistry.js +122 -0
  227. package/kanban/server/services/TaskRunnerService.js +261 -0
  228. package/kanban/server/services/WorkItemReader.js +123 -0
  229. package/kanban/server/services/WorkItemRefineService.js +510 -0
  230. package/kanban/server/start.js +49 -0
  231. package/kanban/server/utils/kanban-logger.js +132 -0
  232. package/kanban/server/utils/markdown.js +91 -0
  233. package/kanban/server/utils/status-grouping.js +107 -0
  234. package/kanban/server/workers/run-task-worker.js +121 -0
  235. package/kanban/server/workers/seed-worker.js +94 -0
  236. package/kanban/server/workers/sponsor-call-worker.js +92 -0
  237. package/kanban/server/workers/sprint-planning-worker.js +212 -0
  238. package/package.json +19 -7
  239. package/cli/agents/documentation.md +0 -302
@@ -0,0 +1,368 @@
1
+ /**
2
+ * api-reference-tool.js
3
+ *
4
+ * Provides an OpenAI-compatible tool definition and handler that fetches
5
+ * real API reference documentation from the internet. Designed to be used
6
+ * with local LLMs that support tool/function calling so that context writers
7
+ * can look up accurate payload formats, field names, and auth mechanisms
8
+ * instead of hallucinating them.
9
+ */
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Known API documentation sources
13
+ // ---------------------------------------------------------------------------
14
+
15
+ /**
16
+ * Maps service names to documentation URLs.
17
+ * Each entry can have:
18
+ * - `openapi` — OpenAPI/Swagger spec URL (JSON or YAML)
19
+ * - `docs` — Human-readable docs index URL
20
+ * - `topics` — Map of topic keywords → specific doc URLs
21
+ */
22
+ // No hardcoded URLs — all API references fetched dynamically via Context7.
23
+ // This avoids stale/broken URLs and ensures consistent, up-to-date documentation.
24
+ const SERVICE_REGISTRY = {};
25
+
26
+ // Aliases: normalize common service name variants
27
+ const SERVICE_ALIASES = {
28
+ // Twilio variants
29
+ 'twilio-whatsapp': 'twilio',
30
+ 'twilio-sms': 'twilio',
31
+ // Meta/WhatsApp variants (model uses many forms)
32
+ 'meta': 'whatsapp',
33
+ 'meta-cloud-api': 'whatsapp',
34
+ 'meta cloud api': 'whatsapp',
35
+ 'whatsapp-cloud': 'whatsapp',
36
+ 'whatsapp-business': 'whatsapp',
37
+ 'whatsapp cloud api': 'whatsapp',
38
+ 'whatsapp business api': 'whatsapp',
39
+ // Express variants
40
+ 'express.js': 'express',
41
+ 'expressjs': 'express',
42
+ // AWS variants
43
+ 's3': 'aws-s3',
44
+ 'amazon-s3': 'aws-s3',
45
+ // React ecosystem
46
+ 'react-query': 'tanstack-query',
47
+ 'tanstack query': 'tanstack-query',
48
+ '@tanstack/react-query': 'tanstack-query',
49
+ };
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Dynamic discovery via Context7 (Upstash)
53
+ // ---------------------------------------------------------------------------
54
+
55
+ const CONTEXT7_BASE = 'https://context7.com/api/v2';
56
+
57
+ /**
58
+ * Fetch topic-specific documentation from Context7.
59
+ * Two-step: resolve library ID → fetch docs.
60
+ * Works without API key; optional CONTEXT7_API_KEY env var for higher rate limits.
61
+ * @returns {{ libraryId: string, text: string } | null}
62
+ */
63
+ async function fetchFromContext7(serviceName, topic) {
64
+ const headers = {};
65
+ const apiKey = process.env.CONTEXT7_API_KEY;
66
+ if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;
67
+
68
+ // Step 1: Resolve library ID
69
+ const searchUrl = `${CONTEXT7_BASE}/libs/search?query=${encodeURIComponent(serviceName)}`;
70
+ const searchResult = await fetchWithLimit(searchUrl, 10_000, headers);
71
+ if (!searchResult.ok) return null;
72
+
73
+ let libraryId;
74
+ try {
75
+ const data = JSON.parse(searchResult.body);
76
+ const libs = data.libraries || data;
77
+ if (!Array.isArray(libs) || libs.length === 0) return null;
78
+ // Pick highest trust match — require trust >= 5 to avoid fuzzy noise
79
+ // (Context7 never returns empty results; garbage queries get fuzzy matches)
80
+ const best = libs.sort((a, b) => (b.trust || 0) - (a.trust || 0))[0];
81
+ if ((best.trust || 0) < 5) return null;
82
+ libraryId = best.id; // format: "owner/repo" e.g. "stripe/stripe-node"
83
+ } catch { return null; }
84
+
85
+ // Step 2: Fetch topic-specific documentation
86
+ const contextUrl = `${CONTEXT7_BASE}/context?libraryId=${encodeURIComponent(libraryId)}&query=${encodeURIComponent(topic)}&type=txt&tokenLimit=5000`;
87
+ const contextResult = await fetchWithLimit(contextUrl, 12_000, headers);
88
+ if (!contextResult.ok) return null;
89
+
90
+ const text = contextResult.body?.trim();
91
+ if (!text || text.length < 50) return null;
92
+
93
+ return { libraryId, text };
94
+ }
95
+
96
+ // ---------------------------------------------------------------------------
97
+ // Tool definition (OpenAI function-calling format)
98
+ // ---------------------------------------------------------------------------
99
+
100
+ export const API_REFERENCE_TOOL = {
101
+ type: 'function',
102
+ function: {
103
+ name: 'fetch_api_reference',
104
+ description:
105
+ 'Fetch API reference documentation for any external service or API. ' +
106
+ 'Supports thousands of libraries and APIs — documentation is discovered automatically. ' +
107
+ 'Use this when you need accurate payload formats, field names, authentication mechanisms, ' +
108
+ 'endpoint signatures, or webhook specifications.',
109
+ parameters: {
110
+ type: 'object',
111
+ properties: {
112
+ service: {
113
+ type: 'string',
114
+ description:
115
+ 'Service or API name (e.g. "twilio", "stripe", "prisma", "shopify", "express", "supabase")',
116
+ },
117
+ topic: {
118
+ type: 'string',
119
+ description:
120
+ 'Specific topic to look up (e.g. "webhook-payload", "authentication", "send-message", "signature-verification", "rate-limit")',
121
+ },
122
+ },
123
+ required: ['service', 'topic'],
124
+ },
125
+ },
126
+ };
127
+
128
+ // ---------------------------------------------------------------------------
129
+ // Fetch helper with timeout and size limit
130
+ // ---------------------------------------------------------------------------
131
+
132
+ const FETCH_TIMEOUT_MS = 15_000;
133
+ const MAX_BODY_BYTES = 512_000; // 500 KB — enough for most doc pages
134
+
135
+ async function fetchWithLimit(url, timeoutMs = FETCH_TIMEOUT_MS, extraHeaders = {}) {
136
+ const controller = new AbortController();
137
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
138
+ try {
139
+ const resp = await fetch(url, {
140
+ signal: controller.signal,
141
+ headers: { 'Accept': 'text/html, application/json, text/plain, */*', ...extraHeaders },
142
+ });
143
+ if (!resp.ok) {
144
+ return { ok: false, error: `HTTP ${resp.status} ${resp.statusText}` };
145
+ }
146
+ const contentType = resp.headers.get('content-type') || '';
147
+ const buffer = await resp.arrayBuffer();
148
+ const body = new TextDecoder().decode(buffer.slice(0, MAX_BODY_BYTES));
149
+ return { ok: true, body, contentType, truncated: buffer.byteLength > MAX_BODY_BYTES };
150
+ } catch (err) {
151
+ return { ok: false, error: err.name === 'AbortError' ? 'Request timed out' : err.message };
152
+ } finally {
153
+ clearTimeout(timer);
154
+ }
155
+ }
156
+
157
+ // ---------------------------------------------------------------------------
158
+ // Content extractors
159
+ // ---------------------------------------------------------------------------
160
+
161
+ /**
162
+ * Extract relevant text from an HTML page — strips tags, scripts, styles,
163
+ * and collapses whitespace. Returns a readable text summary.
164
+ */
165
+ function extractTextFromHTML(html, maxChars = 8000) {
166
+ let text = html;
167
+ // Remove script, style, nav, footer, header blocks
168
+ text = text.replace(/<(script|style|nav|footer|header|aside|svg)\b[^>]*>[\s\S]*?<\/\1>/gi, ' ');
169
+ // Remove HTML comments
170
+ text = text.replace(/<!--[\s\S]*?-->/g, ' ');
171
+ // Convert common block elements to newlines
172
+ text = text.replace(/<\/(p|div|h[1-6]|li|tr|br|hr)\s*>/gi, '\n');
173
+ text = text.replace(/<(br|hr)\s*\/?>/gi, '\n');
174
+ // Strip remaining tags
175
+ text = text.replace(/<[^>]+>/g, ' ');
176
+ // Decode basic HTML entities
177
+ text = text.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>')
178
+ .replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&nbsp;/g, ' ');
179
+ // Collapse whitespace
180
+ text = text.replace(/[ \t]+/g, ' ').replace(/\n[ \t]+/g, '\n').replace(/\n{3,}/g, '\n\n');
181
+ return text.trim().slice(0, maxChars);
182
+ }
183
+
184
+ /**
185
+ * Extract a relevant section from an OpenAPI spec JSON for a given topic.
186
+ */
187
+ function extractFromOpenAPI(specJson, topic) {
188
+ const topicLower = topic.toLowerCase();
189
+ const lines = [];
190
+ const paths = specJson.paths || {};
191
+
192
+ // Find paths matching the topic keywords
193
+ const keywords = topicLower.split(/[-_\s]+/);
194
+ const matchingPaths = Object.entries(paths).filter(([pathStr]) => {
195
+ const pathLower = pathStr.toLowerCase();
196
+ return keywords.some(kw => kw.length >= 3 && pathLower.includes(kw));
197
+ });
198
+
199
+ // Limit to first 5 matching paths
200
+ for (const [pathStr, methods] of matchingPaths.slice(0, 5)) {
201
+ lines.push(`\n### ${pathStr}`);
202
+ for (const [method, spec] of Object.entries(methods)) {
203
+ if (typeof spec !== 'object' || !spec) continue;
204
+ lines.push(`**${method.toUpperCase()}** — ${spec.summary || spec.description || ''}`);
205
+ // Request body schema
206
+ const reqBody = spec.requestBody?.content;
207
+ if (reqBody) {
208
+ for (const [contentType, media] of Object.entries(reqBody)) {
209
+ lines.push(`- Content-Type: ${contentType}`);
210
+ if (media.schema?.properties) {
211
+ const props = Object.entries(media.schema.properties).slice(0, 15);
212
+ for (const [prop, propSpec] of props) {
213
+ const req = (media.schema.required || []).includes(prop) ? ' (required)' : '';
214
+ lines.push(` - ${prop}: ${propSpec.type || 'any'}${req} — ${propSpec.description || ''}`);
215
+ }
216
+ }
217
+ }
218
+ }
219
+ // Response schema summary
220
+ const responses = spec.responses || {};
221
+ const successResp = responses['200'] || responses['201'] || responses['204'];
222
+ if (successResp) {
223
+ lines.push(`- Success: ${successResp.description || 'OK'}`);
224
+ }
225
+ }
226
+ }
227
+
228
+ if (lines.length === 0) {
229
+ // Fallback: show info section
230
+ const info = specJson.info || {};
231
+ return `API: ${info.title || 'Unknown'} (v${info.version || '?'})\n` +
232
+ `${info.description || ''}\n\n` +
233
+ `No paths matching topic "${topic}" found. Available paths (first 20):\n` +
234
+ Object.keys(paths).slice(0, 20).map(p => `- ${p}`).join('\n');
235
+ }
236
+
237
+ const info = specJson.info || {};
238
+ return `API: ${info.title || 'Unknown'} (v${info.version || '?'})\n` +
239
+ `Topic: ${topic}\n` +
240
+ lines.join('\n');
241
+ }
242
+
243
+ // ---------------------------------------------------------------------------
244
+ // Tool handler
245
+ // ---------------------------------------------------------------------------
246
+
247
+ /**
248
+ * Handle a fetch_api_reference tool call.
249
+ * @param {{ service: string, topic: string }} args - Tool call arguments
250
+ * @returns {Promise<string>} The fetched API reference text
251
+ */
252
+ export async function handleFetchApiReference({ service, topic }) {
253
+ const serviceLower = (service || '').toLowerCase().trim();
254
+ const topicLower = (topic || '').toLowerCase().trim();
255
+
256
+ // Resolve aliases
257
+ const resolvedService = SERVICE_ALIASES[serviceLower] || serviceLower;
258
+ const registry = SERVICE_REGISTRY[resolvedService];
259
+
260
+ // Tier 0: Curated registry (fastest, best quality for known services)
261
+ if (registry) {
262
+ // 1. Try topic-specific URL first
263
+ const topicUrl = findTopicUrl(registry.topics, topicLower);
264
+ if (topicUrl) {
265
+ console.log(`[DEBUG] API reference tool: fetching ${resolvedService}/${topicLower} from ${topicUrl}`);
266
+ const result = await fetchWithLimit(topicUrl);
267
+ if (result.ok) {
268
+ const text = extractTextFromHTML(result.body);
269
+ if (text.length > 100) {
270
+ return `## ${resolvedService} — ${topic}\nSource: ${topicUrl}\n\n${text}`;
271
+ }
272
+ } else {
273
+ console.log(`[DEBUG] API reference fetch failed: ${result.error}`);
274
+ }
275
+ }
276
+
277
+ // 2. Try OpenAPI spec for structured data
278
+ if (registry.openapi) {
279
+ console.log(`[DEBUG] API reference tool: fetching OpenAPI spec for ${resolvedService}`);
280
+ const specResult = await fetchWithLimit(registry.openapi, 20_000);
281
+ if (specResult.ok) {
282
+ try {
283
+ const spec = JSON.parse(specResult.body);
284
+ const extracted = extractFromOpenAPI(spec, topicLower);
285
+ if (extracted) {
286
+ return `## ${resolvedService} — ${topic} (from OpenAPI spec)\n\n${extracted}`;
287
+ }
288
+ } catch {
289
+ // Not valid JSON or truncated — fall through
290
+ }
291
+ }
292
+ }
293
+
294
+ // 3. Fall back to general docs page
295
+ if (registry.docs) {
296
+ console.log(`[DEBUG] API reference tool: falling back to general docs for ${resolvedService}`);
297
+ const docsResult = await fetchWithLimit(registry.docs);
298
+ if (docsResult.ok) {
299
+ const text = extractTextFromHTML(docsResult.body, 4000);
300
+ return `## ${resolvedService} — general documentation\nSource: ${registry.docs}\n\n${text}\n\n` +
301
+ `Note: Could not find specific docs for topic "${topic}". Above is the general API docs page.`;
302
+ }
303
+ }
304
+ }
305
+
306
+ // Tier 1: Dynamic discovery via Context7
307
+ console.log(`[DEBUG] API reference: "${service}" not in registry, trying Context7`);
308
+ const ctx7 = await fetchFromContext7(serviceLower, topicLower);
309
+ if (ctx7) {
310
+ return `## ${service} — ${topic} (via Context7: ${ctx7.libraryId})\n\n${ctx7.text}`;
311
+ }
312
+
313
+ return `Could not find API documentation for "${service}/${topic}". ` +
314
+ `The service may not have indexed documentation available. ` +
315
+ `Use your training knowledge as a fallback.`;
316
+ }
317
+
318
+ /**
319
+ * Find the best matching topic URL from the topics map.
320
+ */
321
+ function findTopicUrl(topics, topicLower) {
322
+ if (!topics) return null;
323
+
324
+ // Exact match
325
+ if (topics[topicLower]) return topics[topicLower];
326
+
327
+ // Partial match — find topic key that shares keywords with the query
328
+ const queryWords = topicLower.split(/[-_\s]+/);
329
+ let bestMatch = null;
330
+ let bestOverlap = 0;
331
+
332
+ for (const [key, url] of Object.entries(topics)) {
333
+ const keyWords = key.split(/[-_\s]+/);
334
+ const overlap = queryWords.filter(w => keyWords.includes(w)).length;
335
+ if (overlap > bestOverlap) {
336
+ bestOverlap = overlap;
337
+ bestMatch = url;
338
+ }
339
+ }
340
+
341
+ return bestOverlap > 0 ? bestMatch : null;
342
+ }
343
+
344
+ // ---------------------------------------------------------------------------
345
+ // Exports for provider integration
346
+ // ---------------------------------------------------------------------------
347
+
348
+ /**
349
+ * All tools available for context generation.
350
+ * Providers can pass this array in the `tools` parameter of chat completions.
351
+ */
352
+ export const CONTEXT_GENERATION_TOOLS = [API_REFERENCE_TOOL];
353
+
354
+ /**
355
+ * Dispatch a tool call by name.
356
+ * @param {string} name - Tool function name
357
+ * @param {Object} args - Tool call arguments (parsed JSON)
358
+ * @returns {Promise<string>} Tool result text
359
+ */
360
+ export async function dispatchToolCall(name, args) {
361
+ if (name === 'fetch_api_reference') {
362
+ return handleFetchApiReference(args);
363
+ }
364
+ return `Unknown tool: ${name}`;
365
+ }
366
+
367
+ // Exported for unit testing
368
+ export { findTopicUrl, extractFromOpenAPI, extractTextFromHTML, fetchFromContext7 };
package/cli/build-docs.js CHANGED
@@ -25,6 +25,13 @@ export class DocumentationBuilder {
25
25
  return fs.existsSync(this.docsDir);
26
26
  }
27
27
 
28
+ /**
29
+ * Check if synced project docs directory exists inside documentation
30
+ */
31
+ hasProjectDocs() {
32
+ return fs.existsSync(path.join(this.docsDir, 'project'));
33
+ }
34
+
28
35
  /**
29
36
  * Get documentation server port from avc.json config
30
37
  * Returns default port 4173 if not configured
@@ -40,7 +47,7 @@ export class DocumentationBuilder {
40
47
  const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
41
48
  return config.settings?.documentation?.port || 4173;
42
49
  } catch (error) {
43
- console.warn(`⚠️ Could not read port from avc.json: ${error.message}`);
50
+ console.warn(`Could not read port from avc.json: ${error.message}`);
44
51
  return 4173;
45
52
  }
46
53
  }
@@ -197,13 +204,9 @@ export class DocumentationBuilder {
197
204
  const port = this.getPort();
198
205
 
199
206
  try {
200
- // Build the documentation asynchronously to avoid blocking
201
- await execAsync('npx vitepress build', {
202
- cwd: this.docsDir
203
- });
204
-
205
- // Start the preview server
206
- const serverProcess = spawn('npx', ['vitepress', 'preview', '--port', String(port)], {
207
+ // Start the dev server no initial build needed, vitepress dev builds on-demand
208
+ // and hot-reloads the browser whenever source .md files change
209
+ const serverProcess = spawn('npx', ['vitepress', 'dev', '--port', String(port)], {
207
210
  cwd: this.docsDir,
208
211
  stdio: 'pipe'
209
212
  });
@@ -256,6 +259,22 @@ export class DocumentationBuilder {
256
259
  }
257
260
  }
258
261
 
262
+ /**
263
+ * Ensure ignoreDeadLinks: true is present in the VitePress config.
264
+ * Patches existing project configs that were created before this option was added.
265
+ */
266
+ _ensureIgnoreDeadLinks() {
267
+ const configPath = path.join(this.docsDir, '.vitepress', 'config.mts');
268
+ if (!fs.existsSync(configPath)) return;
269
+ const content = fs.readFileSync(configPath, 'utf8');
270
+ if (content.includes('ignoreDeadLinks')) return;
271
+ const patched = content.replace(
272
+ /defineConfig\(\{/,
273
+ 'defineConfig({\n ignoreDeadLinks: true,'
274
+ );
275
+ fs.writeFileSync(configPath, patched, 'utf8');
276
+ }
277
+
259
278
  /**
260
279
  * Build documentation without starting server
261
280
  */
@@ -264,6 +283,8 @@ export class DocumentationBuilder {
264
283
  throw new Error('Documentation not found. Run /init first to create documentation structure.');
265
284
  }
266
285
 
286
+ this._ensureIgnoreDeadLinks();
287
+
267
288
  try {
268
289
  // Build asynchronously to avoid blocking the event loop
269
290
  await execAsync('npx vitepress build', {