byterover-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/README.md +781 -0
  2. package/bin/dev.cmd +4 -0
  3. package/bin/dev.js +7 -0
  4. package/bin/run.cmd +4 -0
  5. package/bin/run.js +7 -0
  6. package/dist/commands/add.d.ts +60 -0
  7. package/dist/commands/add.js +230 -0
  8. package/dist/commands/clear.d.ts +13 -0
  9. package/dist/commands/clear.js +57 -0
  10. package/dist/commands/complete.d.ts +108 -0
  11. package/dist/commands/complete.js +340 -0
  12. package/dist/commands/gen-rules.d.ts +26 -0
  13. package/dist/commands/gen-rules.js +89 -0
  14. package/dist/commands/init.d.ts +24 -0
  15. package/dist/commands/init.js +135 -0
  16. package/dist/commands/login.d.ts +22 -0
  17. package/dist/commands/login.js +103 -0
  18. package/dist/commands/push.d.ts +33 -0
  19. package/dist/commands/push.js +150 -0
  20. package/dist/commands/retrieve.d.ts +26 -0
  21. package/dist/commands/retrieve.js +101 -0
  22. package/dist/commands/space/list.d.ts +22 -0
  23. package/dist/commands/space/list.js +105 -0
  24. package/dist/commands/space/switch.d.ts +20 -0
  25. package/dist/commands/space/switch.js +110 -0
  26. package/dist/commands/status.d.ts +22 -0
  27. package/dist/commands/status.js +116 -0
  28. package/dist/config/auth.config.d.ts +32 -0
  29. package/dist/config/auth.config.js +35 -0
  30. package/dist/config/environment.d.ts +35 -0
  31. package/dist/config/environment.js +39 -0
  32. package/dist/constants.d.ts +11 -0
  33. package/dist/constants.js +12 -0
  34. package/dist/core/domain/entities/agent.d.ts +5 -0
  35. package/dist/core/domain/entities/agent.js +23 -0
  36. package/dist/core/domain/entities/auth-token.d.ts +43 -0
  37. package/dist/core/domain/entities/auth-token.js +70 -0
  38. package/dist/core/domain/entities/br-config.d.ts +25 -0
  39. package/dist/core/domain/entities/br-config.js +58 -0
  40. package/dist/core/domain/entities/bullet.d.ts +51 -0
  41. package/dist/core/domain/entities/bullet.js +94 -0
  42. package/dist/core/domain/entities/curator-output.d.ts +14 -0
  43. package/dist/core/domain/entities/curator-output.js +23 -0
  44. package/dist/core/domain/entities/delta-batch.d.ts +30 -0
  45. package/dist/core/domain/entities/delta-batch.js +52 -0
  46. package/dist/core/domain/entities/delta-operation.d.ts +31 -0
  47. package/dist/core/domain/entities/delta-operation.js +50 -0
  48. package/dist/core/domain/entities/event.d.ts +8 -0
  49. package/dist/core/domain/entities/event.js +15 -0
  50. package/dist/core/domain/entities/executor-output.d.ts +27 -0
  51. package/dist/core/domain/entities/executor-output.js +33 -0
  52. package/dist/core/domain/entities/memory.d.ts +55 -0
  53. package/dist/core/domain/entities/memory.js +90 -0
  54. package/dist/core/domain/entities/oauth-token-data.d.ts +13 -0
  55. package/dist/core/domain/entities/oauth-token-data.js +20 -0
  56. package/dist/core/domain/entities/playbook.d.ts +97 -0
  57. package/dist/core/domain/entities/playbook.js +275 -0
  58. package/dist/core/domain/entities/presigned-url.d.ts +9 -0
  59. package/dist/core/domain/entities/presigned-url.js +18 -0
  60. package/dist/core/domain/entities/presigned-urls-response.d.ts +10 -0
  61. package/dist/core/domain/entities/presigned-urls-response.js +18 -0
  62. package/dist/core/domain/entities/reflector-output.d.ts +38 -0
  63. package/dist/core/domain/entities/reflector-output.js +44 -0
  64. package/dist/core/domain/entities/retrieve-result.d.ts +35 -0
  65. package/dist/core/domain/entities/retrieve-result.js +35 -0
  66. package/dist/core/domain/entities/space.d.ts +24 -0
  67. package/dist/core/domain/entities/space.js +52 -0
  68. package/dist/core/domain/entities/team.d.ts +42 -0
  69. package/dist/core/domain/entities/team.js +89 -0
  70. package/dist/core/domain/entities/user.d.ts +20 -0
  71. package/dist/core/domain/entities/user.js +32 -0
  72. package/dist/core/domain/errors/ace-error.d.ts +34 -0
  73. package/dist/core/domain/errors/ace-error.js +53 -0
  74. package/dist/core/domain/errors/auth-error.d.ts +10 -0
  75. package/dist/core/domain/errors/auth-error.js +20 -0
  76. package/dist/core/domain/errors/discovery-error.d.ts +21 -0
  77. package/dist/core/domain/errors/discovery-error.js +33 -0
  78. package/dist/core/domain/errors/rule-error.d.ts +6 -0
  79. package/dist/core/domain/errors/rule-error.js +12 -0
  80. package/dist/core/interfaces/i-ace-prompt-builder.d.ts +48 -0
  81. package/dist/core/interfaces/i-ace-prompt-builder.js +1 -0
  82. package/dist/core/interfaces/i-auth-service.d.ts +35 -0
  83. package/dist/core/interfaces/i-auth-service.js +1 -0
  84. package/dist/core/interfaces/i-browser-launcher.d.ts +11 -0
  85. package/dist/core/interfaces/i-browser-launcher.js +1 -0
  86. package/dist/core/interfaces/i-bullet-content-store.d.ts +36 -0
  87. package/dist/core/interfaces/i-bullet-content-store.js +1 -0
  88. package/dist/core/interfaces/i-callback-handler.d.ts +35 -0
  89. package/dist/core/interfaces/i-callback-handler.js +1 -0
  90. package/dist/core/interfaces/i-delta-store.d.ts +15 -0
  91. package/dist/core/interfaces/i-delta-store.js +1 -0
  92. package/dist/core/interfaces/i-executor-output-store.d.ts +14 -0
  93. package/dist/core/interfaces/i-executor-output-store.js +1 -0
  94. package/dist/core/interfaces/i-file-service.d.ts +34 -0
  95. package/dist/core/interfaces/i-file-service.js +1 -0
  96. package/dist/core/interfaces/i-http-client.d.ts +33 -0
  97. package/dist/core/interfaces/i-http-client.js +1 -0
  98. package/dist/core/interfaces/i-memory-retrieval-service.d.ts +40 -0
  99. package/dist/core/interfaces/i-memory-retrieval-service.js +1 -0
  100. package/dist/core/interfaces/i-memory-storage-service.d.ts +55 -0
  101. package/dist/core/interfaces/i-memory-storage-service.js +1 -0
  102. package/dist/core/interfaces/i-oidc-discovery-service.d.ts +20 -0
  103. package/dist/core/interfaces/i-oidc-discovery-service.js +1 -0
  104. package/dist/core/interfaces/i-playbook-service.d.ts +69 -0
  105. package/dist/core/interfaces/i-playbook-service.js +1 -0
  106. package/dist/core/interfaces/i-playbook-store.d.ts +38 -0
  107. package/dist/core/interfaces/i-playbook-store.js +1 -0
  108. package/dist/core/interfaces/i-project-config-store.d.ts +26 -0
  109. package/dist/core/interfaces/i-project-config-store.js +1 -0
  110. package/dist/core/interfaces/i-reflection-store.d.ts +21 -0
  111. package/dist/core/interfaces/i-reflection-store.js +1 -0
  112. package/dist/core/interfaces/i-rule-template-service.d.ts +17 -0
  113. package/dist/core/interfaces/i-rule-template-service.js +4 -0
  114. package/dist/core/interfaces/i-rule-writer-service.d.ts +13 -0
  115. package/dist/core/interfaces/i-rule-writer-service.js +1 -0
  116. package/dist/core/interfaces/i-space-service.d.ts +28 -0
  117. package/dist/core/interfaces/i-space-service.js +1 -0
  118. package/dist/core/interfaces/i-team-service.d.ts +29 -0
  119. package/dist/core/interfaces/i-team-service.js +1 -0
  120. package/dist/core/interfaces/i-template-loader.d.ts +29 -0
  121. package/dist/core/interfaces/i-template-loader.js +1 -0
  122. package/dist/core/interfaces/i-token-store.d.ts +22 -0
  123. package/dist/core/interfaces/i-token-store.js +1 -0
  124. package/dist/core/interfaces/i-tracking-service.d.ts +21 -0
  125. package/dist/core/interfaces/i-tracking-service.js +1 -0
  126. package/dist/core/interfaces/i-user-service.d.ts +14 -0
  127. package/dist/core/interfaces/i-user-service.js +1 -0
  128. package/dist/index.d.ts +1 -0
  129. package/dist/index.js +1 -0
  130. package/dist/infra/ace/ace-file-utils.d.ts +46 -0
  131. package/dist/infra/ace/ace-file-utils.js +83 -0
  132. package/dist/infra/ace/ace-prompt-templates.d.ts +13 -0
  133. package/dist/infra/ace/ace-prompt-templates.js +177 -0
  134. package/dist/infra/ace/file-bullet-content-store.d.ts +27 -0
  135. package/dist/infra/ace/file-bullet-content-store.js +89 -0
  136. package/dist/infra/ace/file-delta-store.d.ts +9 -0
  137. package/dist/infra/ace/file-delta-store.js +26 -0
  138. package/dist/infra/ace/file-executor-output-store.d.ts +9 -0
  139. package/dist/infra/ace/file-executor-output-store.js +26 -0
  140. package/dist/infra/ace/file-playbook-store.d.ts +29 -0
  141. package/dist/infra/ace/file-playbook-store.js +107 -0
  142. package/dist/infra/ace/file-reflection-store.d.ts +10 -0
  143. package/dist/infra/ace/file-reflection-store.js +55 -0
  144. package/dist/infra/auth/oauth-service.d.ts +49 -0
  145. package/dist/infra/auth/oauth-service.js +126 -0
  146. package/dist/infra/auth/oidc-discovery-service.d.ts +51 -0
  147. package/dist/infra/auth/oidc-discovery-service.js +145 -0
  148. package/dist/infra/browser/system-browser-launcher.d.ts +10 -0
  149. package/dist/infra/browser/system-browser-launcher.js +18 -0
  150. package/dist/infra/config/file-config-store.d.ts +21 -0
  151. package/dist/infra/config/file-config-store.js +57 -0
  152. package/dist/infra/file/fs-file-service.d.ts +28 -0
  153. package/dist/infra/file/fs-file-service.js +57 -0
  154. package/dist/infra/http/authenticated-http-client.d.ts +46 -0
  155. package/dist/infra/http/authenticated-http-client.js +99 -0
  156. package/dist/infra/http/callback-handler.d.ts +13 -0
  157. package/dist/infra/http/callback-handler.js +24 -0
  158. package/dist/infra/http/callback-server.d.ts +18 -0
  159. package/dist/infra/http/callback-server.js +93 -0
  160. package/dist/infra/memory/http-memory-retrieval-service.d.ts +18 -0
  161. package/dist/infra/memory/http-memory-retrieval-service.js +63 -0
  162. package/dist/infra/memory/http-memory-storage-service.d.ts +18 -0
  163. package/dist/infra/memory/http-memory-storage-service.js +67 -0
  164. package/dist/infra/memory/memory-to-playbook-mapper.d.ts +33 -0
  165. package/dist/infra/memory/memory-to-playbook-mapper.js +51 -0
  166. package/dist/infra/playbook/file-playbook-service.d.ts +43 -0
  167. package/dist/infra/playbook/file-playbook-service.js +133 -0
  168. package/dist/infra/rule/agent-rule-config.d.ts +19 -0
  169. package/dist/infra/rule/agent-rule-config.js +77 -0
  170. package/dist/infra/rule/rule-template-service.d.ts +18 -0
  171. package/dist/infra/rule/rule-template-service.js +80 -0
  172. package/dist/infra/rule/rule-writer-service.d.ts +19 -0
  173. package/dist/infra/rule/rule-writer-service.js +43 -0
  174. package/dist/infra/space/http-space-service.d.ts +20 -0
  175. package/dist/infra/space/http-space-service.js +67 -0
  176. package/dist/infra/storage/keychain-token-store.d.ts +10 -0
  177. package/dist/infra/storage/keychain-token-store.js +40 -0
  178. package/dist/infra/team/http-team-service.d.ts +21 -0
  179. package/dist/infra/team/http-team-service.js +71 -0
  180. package/dist/infra/template/fs-template-loader.d.ts +33 -0
  181. package/dist/infra/template/fs-template-loader.js +62 -0
  182. package/dist/infra/tracking/mixpanel-tracking-service.d.ts +14 -0
  183. package/dist/infra/tracking/mixpanel-tracking-service.js +44 -0
  184. package/dist/infra/user/http-user-service.d.ts +12 -0
  185. package/dist/infra/user/http-user-service.js +26 -0
  186. package/dist/templates/README.md +103 -0
  187. package/dist/templates/base.md +3 -0
  188. package/dist/templates/sections/command-reference.md +141 -0
  189. package/dist/templates/sections/workflow.md +46 -0
  190. package/dist/utils/file-helpers.d.ts +15 -0
  191. package/dist/utils/file-helpers.js +45 -0
  192. package/oclif.manifest.json +476 -0
  193. package/package.json +82 -0
@@ -0,0 +1,340 @@
1
+ import { Args, Command, Flags } from '@oclif/core';
2
+ import { basename } from 'node:path';
3
+ import { CuratorOutput } from '../core/domain/entities/curator-output.js';
4
+ import { DeltaBatch } from '../core/domain/entities/delta-batch.js';
5
+ import { ExecutorOutput } from '../core/domain/entities/executor-output.js';
6
+ import { ReflectorOutput } from '../core/domain/entities/reflector-output.js';
7
+ import { AcePromptTemplates } from '../infra/ace/ace-prompt-templates.js';
8
+ import { FileDeltaStore } from '../infra/ace/file-delta-store.js';
9
+ import { FileExecutorOutputStore } from '../infra/ace/file-executor-output-store.js';
10
+ import { FilePlaybookStore } from '../infra/ace/file-playbook-store.js';
11
+ import { FileReflectionStore } from '../infra/ace/file-reflection-store.js';
12
+ import { FilePlaybookService } from '../infra/playbook/file-playbook-service.js';
13
+ export default class Complete extends Command {
14
+ /* eslint-disable perfectionist/sort-objects */
15
+ static args = {
16
+ hint: Args.string({
17
+ description: 'Short hint for naming output files (e.g., "user-auth", "bug-fix")',
18
+ required: true,
19
+ }),
20
+ reasoning: Args.string({
21
+ description: 'Detailed reasoning and approach for completing the task',
22
+ required: true,
23
+ }),
24
+ finalAnswer: Args.string({
25
+ description: 'The final answer/solution to the task',
26
+ required: true,
27
+ }),
28
+ };
29
+ /* eslint-enable perfectionist/sort-objects */
30
+ static description = 'Complete ACE workflow: save executor output, generate reflection, and update playbook in one command';
31
+ static examples = [
32
+ String.raw `<%= config.bin %> <%= command.id %> "user-auth" "Implemented OAuth2 flow" "Auth works" --tool-usage "Read:src/auth.ts,Edit:src/auth.ts,Bash:npm test" --feedback "All tests passed"`,
33
+ String.raw `<%= config.bin %> <%= command.id %> "validation-fix" "Analyzed validator" "Fixed bug" --tool-usage "Grep:pattern:\"validate\",Read:src/validator.ts" --bullet-ids "bullet-123" --feedback "Tests passed"`,
34
+ String.raw `<%= config.bin %> <%= command.id %> "auth-update" "Improved error handling" "Better errors" --tool-usage "Edit:src/auth.ts" --feedback "Tests passed" --update-bullet "bullet-5"`,
35
+ ];
36
+ static flags = {
37
+ 'bullet-ids': Flags.string({
38
+ char: 'b',
39
+ default: '',
40
+ description: 'Comma-separated list of playbook bullet IDs referenced',
41
+ }),
42
+ feedback: Flags.string({
43
+ char: 'f',
44
+ description: 'Environment feedback about task execution (e.g., "Tests passed", "Build failed")',
45
+ required: true,
46
+ }),
47
+ 'tool-usage': Flags.string({
48
+ char: 't',
49
+ description: 'Comma-separated list of tool calls with arguments (format: "ToolName:argument", e.g., "Read:src/file.ts,Bash:npm test")',
50
+ required: true,
51
+ }),
52
+ 'update-bullet': Flags.string({
53
+ char: 'u',
54
+ description: 'Bullet ID to update with new knowledge (if not provided, adds new bullet)',
55
+ }),
56
+ };
57
+ createServices() {
58
+ return {
59
+ deltaStore: new FileDeltaStore(),
60
+ executorOutputStore: new FileExecutorOutputStore(),
61
+ playbookService: new FilePlaybookService(),
62
+ promptBuilder: new AcePromptTemplates(),
63
+ reflectionStore: new FileReflectionStore(),
64
+ };
65
+ }
66
+ async run() {
67
+ const { args, flags } = await this.parse(Complete);
68
+ try {
69
+ const { deltaStore, executorOutputStore, playbookService, promptBuilder, reflectionStore } = this.createServices();
70
+ // Parse comma-separated lists
71
+ const bulletIds = this.parseBulletIds(flags['bullet-ids']);
72
+ const toolUsage = this.parseToolUsage(flags['tool-usage']);
73
+ // Phase 1: Executor
74
+ const saveResult = await this.saveExecutorOutput(executorOutputStore, args, bulletIds, toolUsage);
75
+ // Phase 2: Reflector
76
+ const { reflection, reflectionFilePath, tagsApplied } = await this.generateReflectionAndApplyTags({ playbookService, promptBuilder, reflectionStore }, saveResult.executorOutput, flags.feedback);
77
+ // Phase 3: Curator
78
+ const { curatorOutput, deltaFilePath } = await this.generateCurationAndApplyDelta({ deltaStore, playbookService, promptBuilder }, reflection, saveResult.executorOutput, flags);
79
+ // Display final summary
80
+ this.displayFinalSummary({
81
+ curatorOutput,
82
+ deltaFilePath,
83
+ executorPath: saveResult.filePath,
84
+ hint: args.hint,
85
+ reflectionFilePath,
86
+ tagsApplied,
87
+ });
88
+ }
89
+ catch (error) {
90
+ this.error(error instanceof Error ? error.message : 'Failed to complete ACE workflow');
91
+ }
92
+ }
93
+ /**
94
+ * Displays a formatted summary of the completed ACE workflow.
95
+ * Shows file paths, delta operations breakdown, and success confirmation.
96
+ *
97
+ * @param summary - Summary data containing all workflow outputs
98
+ * @param summary.curatorOutput - The curator output containing delta operations
99
+ * @param summary.deltaFilePath - Path to the saved delta file
100
+ * @param summary.executorPath - Path to the saved executor output file
101
+ * @param summary.hint - The hint used for naming output files
102
+ * @param summary.reflectionFilePath - Path to the saved reflection file
103
+ * @param summary.tagsApplied - Number of tags applied to the playbook
104
+ */
105
+ displayFinalSummary(summary) {
106
+ const { delta } = summary.curatorOutput;
107
+ const operationsByType = delta.getOperationsByType();
108
+ this.log('='.repeat(80));
109
+ this.log('✅ ACE WORKFLOW COMPLETED SUCCESSFULLY!');
110
+ this.log('='.repeat(80));
111
+ this.log('');
112
+ this.log('Summary:');
113
+ this.log(` Hint: ${summary.hint}`);
114
+ this.log(` Executor output: ${summary.executorPath}`);
115
+ this.log(` Reflection: ${summary.reflectionFilePath}`);
116
+ this.log(` Delta: ${summary.deltaFilePath}`);
117
+ this.log(` Tags applied: ${summary.tagsApplied}`);
118
+ this.log('');
119
+ this.log('Delta operations:');
120
+ if (delta.isEmpty()) {
121
+ this.log(' - No operations (empty delta batch)');
122
+ }
123
+ else {
124
+ if (operationsByType.ADD) {
125
+ this.log(` - ADD: ${operationsByType.ADD.length}`);
126
+ }
127
+ if (operationsByType.UPDATE) {
128
+ this.log(` - UPDATE: ${operationsByType.UPDATE.length}`);
129
+ }
130
+ if (operationsByType.REMOVE) {
131
+ this.log(` - REMOVE: ${operationsByType.REMOVE.length}`);
132
+ }
133
+ this.log(` Total operations: ${delta.getOperationCount()}`);
134
+ }
135
+ this.log('');
136
+ this.log('🎉 Playbook has been updated with new knowledge!');
137
+ }
138
+ /**
139
+ * Extracts file paths from tool usage strings and formats them with project name prefix.
140
+ * Converts tool usage entries like "Read:src/file.ts" to "projectName/src/file.ts".
141
+ * Filters out non-file-like arguments (e.g., "Bash:npm test" is excluded).
142
+ *
143
+ * @param toolUsage - Array of tool usage strings (e.g., ["Read:src/file.ts", "Edit:src/other.ts"])
144
+ * @returns Array of formatted file paths with project name prefix
145
+ */
146
+ extractFilePaths(toolUsage) {
147
+ const cwd = process.cwd();
148
+ const projectName = basename(cwd);
149
+ return toolUsage
150
+ .map((usage) => {
151
+ const parts = usage.split(':');
152
+ if (parts.length <= 1)
153
+ return null;
154
+ const filePath = parts[1].trim();
155
+ // Filter out non-file-like paths (must contain / or . to be considered a file path)
156
+ if (!filePath.includes('/') && !filePath.includes('.')) {
157
+ return null;
158
+ }
159
+ // Remove leading ./ if present
160
+ const cleanPath = filePath.replace(/^\.\//, '');
161
+ // Combine project name with file path
162
+ const fullPath = `${projectName}/${cleanPath}`;
163
+ return fullPath;
164
+ })
165
+ .filter(Boolean);
166
+ }
167
+ /**
168
+ * Phase 3: Generates curation and applies delta operations to the playbook.
169
+ * Creates delta operations (ADD or UPDATE) based on reflection insights and applies them to the playbook.
170
+ *
171
+ * @param services - The service instances
172
+ * @param services.deltaStore - Delta store for persisting deltas
173
+ * @param services.playbookService - Playbook service for applying deltas
174
+ * @param services.promptBuilder - Prompt builder for curation prompts
175
+ * @param reflection - The reflection output from Phase 2
176
+ * @param executorOutput - The executor output from Phase 1
177
+ * @param flags - Command flags containing optional update-bullet ID
178
+ * @returns Object containing curator output and delta file path
179
+ */
180
+ async generateCurationAndApplyDelta(services, reflection, executorOutput, flags) {
181
+ this.log('🎨 Phase 3: Generating curation prompt...');
182
+ // Note: We load playbook temporarily just for validation
183
+ // The actual delta application will be done by playbookService
184
+ const tempStore = new FilePlaybookStore();
185
+ const updatedPlaybook = await tempStore.load();
186
+ if (!updatedPlaybook) {
187
+ this.error('Failed to reload playbook');
188
+ }
189
+ // Generate curation prompt (directly call promptBuilder instead of use case)
190
+ // Note: The prompt is generated but not currently used in this auto-generation flow
191
+ // In a full implementation, this would be sent to an LLM for processing
192
+ // services.promptBuilder.buildCuratorPrompt(reflection, updatedPlaybook, questionContext)
193
+ // Determine operation type based on --update-bullet flag
194
+ const updateBulletId = flags['update-bullet'];
195
+ let operationType = 'ADD';
196
+ // Validate bullet exists if UPDATE mode
197
+ if (updateBulletId) {
198
+ const bullet = updatedPlaybook.getBullet(updateBulletId);
199
+ if (!bullet) {
200
+ this.error(`Bullet with ID "${updateBulletId}" not found in playbook. Cannot update non-existent bullet.`);
201
+ }
202
+ operationType = 'UPDATE';
203
+ this.log(` ℹ️ Updating existing bullet: ${updateBulletId}`);
204
+ }
205
+ else {
206
+ this.log(' ℹ️ Adding new bullet to playbook...');
207
+ }
208
+ // Auto-generate curator delta
209
+ const relatedFiles = this.extractFilePaths(executorOutput.toolUsage);
210
+ const curatorJson = {
211
+ operations: [
212
+ {
213
+ bulletId: updateBulletId,
214
+ content: reflection.keyInsight,
215
+ metadata: {
216
+ relatedFiles,
217
+ tags: ['auto-generated'],
218
+ timestamp: new Date().toISOString(),
219
+ },
220
+ section: 'Lessons Learned',
221
+ type: operationType,
222
+ },
223
+ ],
224
+ reasoning: operationType === 'UPDATE'
225
+ ? `Updating bullet ${updateBulletId} with new insight: ${reflection.keyInsight}`
226
+ : `Adding key insight from task: ${reflection.keyInsight}`,
227
+ };
228
+ // Parse and save delta batch using service
229
+ const deltaBatch = DeltaBatch.fromJson(curatorJson);
230
+ const curatorOutput = new CuratorOutput(deltaBatch);
231
+ const deltaFilePath = await services.deltaStore.save(deltaBatch, reflection.hint);
232
+ // Apply delta operations using playbook service
233
+ const { operationsApplied } = await services.playbookService.applyDelta({ delta: curatorOutput.delta });
234
+ this.log(` ✓ Delta saved: ${deltaFilePath}`);
235
+ this.log(` ✓ Delta operations applied to playbook (${operationsApplied} operations)`);
236
+ this.log('');
237
+ return { curatorOutput, deltaFilePath };
238
+ }
239
+ /**
240
+ * Phase 2: Generates reflection based on executor output and applies tags to the playbook.
241
+ * Auto-generates reflection analysis from feedback and applies bullet tags to relevant playbook sections.
242
+ *
243
+ * @param services - The service instances
244
+ * @param services.playbookService - Playbook service for applying reflection tags
245
+ * @param services.promptBuilder - Prompt builder for reflection prompts
246
+ * @param services.reflectionStore - Reflection store for persisting reflections
247
+ * @param executorOutput - The executor output from Phase 1
248
+ * @param feedback - Environment feedback about task execution (e.g., "Tests passed", "Build failed")
249
+ * @returns Object containing reflection output, file path, and number of tags applied
250
+ */
251
+ async generateReflectionAndApplyTags(services, executorOutput, feedback) {
252
+ this.log('🤔 Phase 2: Generating reflection...');
253
+ // Note: We don't need to load playbook here anymore as applyReflectionTags handles it internally
254
+ // Generate reflection prompt (directly call promptBuilder instead of use case)
255
+ // Note: The prompt is generated but not currently used in this auto-generation flow
256
+ // In a full implementation, this would be sent to an LLM for processing
257
+ // const task = executorOutput.reasoning.split('\n')[0] || 'Task from executor'
258
+ // services.promptBuilder.buildReflectorPrompt(executorOutput, task, feedback, playbook, groundTruth)
259
+ // Auto-generate reflection based on feedback
260
+ this.log(' ℹ️ Auto-generating reflection based on feedback...');
261
+ const reflectionJson = {
262
+ bulletTags: [],
263
+ correctApproach: executorOutput.reasoning,
264
+ errorIdentification: feedback.toLowerCase().includes('fail') || feedback.toLowerCase().includes('error')
265
+ ? `Issues identified: ${feedback}`
266
+ : 'No critical errors identified',
267
+ hint: executorOutput.hint,
268
+ keyInsight: executorOutput.finalAnswer,
269
+ reasoning: `Analysis: ${feedback}. Approach: ${executorOutput.reasoning}`,
270
+ rootCauseAnalysis: feedback.toLowerCase().includes('fail') || feedback.toLowerCase().includes('error')
271
+ ? `Root cause requires investigation: ${feedback}`
272
+ : 'Successful execution without errors',
273
+ };
274
+ // Parse and save reflection using service
275
+ const reflection = ReflectorOutput.fromJson(reflectionJson);
276
+ const reflectionFilePath = await services.reflectionStore.save(reflection);
277
+ // Apply tags to playbook using playbook service
278
+ const { tagsApplied } = await services.playbookService.applyReflectionTags({ reflection });
279
+ this.log(` ✓ Reflection saved: ${reflectionFilePath}`);
280
+ this.log(` ✓ Tags applied to playbook: ${tagsApplied}`);
281
+ this.log('');
282
+ return { reflection, reflectionFilePath, tagsApplied };
283
+ }
284
+ /**
285
+ * Parses comma-separated bullet IDs string into an array of trimmed IDs.
286
+ * Empty strings and whitespace-only entries are filtered out.
287
+ *
288
+ * @param bulletIdsStr - Comma-separated string of bullet IDs (e.g., "bullet-1, bullet-2")
289
+ * @returns Array of trimmed bullet ID strings (empty array if input is empty string)
290
+ */
291
+ parseBulletIds(bulletIdsStr) {
292
+ return bulletIdsStr
293
+ .split(',')
294
+ .map((id) => id.trim())
295
+ .filter((id) => id.length > 0);
296
+ }
297
+ /**
298
+ * Parses comma-separated tool usage string into an array of trimmed entries.
299
+ * Empty strings and whitespace-only entries are filtered out.
300
+ *
301
+ * @param toolUsageStr - Comma-separated string of tool usage (e.g., "Read:file.ts, Edit:other.ts")
302
+ * @returns Array of trimmed tool usage strings
303
+ */
304
+ parseToolUsage(toolUsageStr) {
305
+ return toolUsageStr
306
+ .split(',')
307
+ .map((tool) => tool.trim())
308
+ .filter((tool) => tool.length > 0);
309
+ }
310
+ /**
311
+ * Phase 1: Saves executor output to a file.
312
+ * Creates an ExecutorOutput entity from command arguments and persists it using the executor output store service.
313
+ *
314
+ * @param executorOutputStore - The executor output store service
315
+ * @param args - Command arguments
316
+ * @param args.hint - Short hint for naming output files
317
+ * @param args.reasoning - Detailed reasoning and approach for completing the task
318
+ * @param args.finalAnswer - The final answer/solution to the task
319
+ * @param bulletIds - Array of playbook bullet IDs referenced during task execution
320
+ * @param toolUsage - Array of tool usage strings (e.g., ["Read:src/file.ts", "Bash:npm test"])
321
+ * @returns Object containing the executor output entity and file path where it was saved
322
+ */
323
+ async saveExecutorOutput(executorOutputStore, args, bulletIds, toolUsage) {
324
+ this.log('🚀 Starting ACE workflow...');
325
+ this.log('');
326
+ this.log('📝 Phase 1: Saving executor output...');
327
+ const executorOutput = new ExecutorOutput({
328
+ bulletIds,
329
+ finalAnswer: args.finalAnswer,
330
+ hint: args.hint,
331
+ reasoning: args.reasoning,
332
+ toolUsage,
333
+ });
334
+ // Save executor output using service
335
+ const filePath = await executorOutputStore.save(executorOutput);
336
+ this.log(` ✓ Executor output saved: ${filePath}`);
337
+ this.log('');
338
+ return { executorOutput, filePath };
339
+ }
340
+ }
@@ -0,0 +1,26 @@
1
+ import { Command } from '@oclif/core';
2
+ import { type Agent } from '../core/domain/entities/agent.js';
3
+ import { type IRuleWriterService } from '../core/interfaces/i-rule-writer-service.js';
4
+ import { ITrackingService } from '../core/interfaces/i-tracking-service.js';
5
+ export default class GenRules extends Command {
6
+ static description: string;
7
+ static examples: string[];
8
+ protected createServices(): {
9
+ ruleWriterService: IRuleWriterService;
10
+ trackingService: ITrackingService;
11
+ };
12
+ /**
13
+ * Prompts the user to select an agent.
14
+ * This method is protected to allow test overrides.
15
+ * @returns The selected agent
16
+ */
17
+ protected promptForAgentSelection(): Promise<Agent>;
18
+ /**
19
+ * Prompts the user to confirm overwriting an existing rule file.
20
+ * This method is protected to allow test overrides.
21
+ * @param agent The agent for which the rule file exists
22
+ * @returns True if the user confirms overwrite, false otherwise
23
+ */
24
+ protected promptForOverwriteConfirmation(agent: Agent): Promise<boolean>;
25
+ run(): Promise<void>;
26
+ }
@@ -0,0 +1,89 @@
1
+ import { confirm, search } from '@inquirer/prompts';
2
+ import { Command } from '@oclif/core';
3
+ import { AGENT_VALUES } from '../core/domain/entities/agent.js';
4
+ import { RuleExistsError } from '../core/domain/errors/rule-error.js';
5
+ import { FsFileService } from '../infra/file/fs-file-service.js';
6
+ import { RuleTemplateService } from '../infra/rule/rule-template-service.js';
7
+ import { RuleWriterService } from '../infra/rule/rule-writer-service.js';
8
+ import { KeychainTokenStore } from '../infra/storage/keychain-token-store.js';
9
+ import { FsTemplateLoader } from '../infra/template/fs-template-loader.js';
10
+ import { MixpanelTrackingService } from '../infra/tracking/mixpanel-tracking-service.js';
11
+ /**
12
+ * Array of all agents with name and value properties.
13
+ * Useful for UI components like select dropdowns.
14
+ */
15
+ const AGENTS = AGENT_VALUES.map((agent) => ({
16
+ name: agent,
17
+ value: agent,
18
+ }));
19
+ export default class GenRules extends Command {
20
+ static description = 'Generate rule instructions for coding agents to work with ByteRover correctly';
21
+ static examples = ['<%= config.bin %> <%= command.id %>'];
22
+ createServices() {
23
+ const fileService = new FsFileService();
24
+ const templateLoader = new FsTemplateLoader(fileService);
25
+ const templateService = new RuleTemplateService(templateLoader);
26
+ return {
27
+ ruleWriterService: new RuleWriterService(fileService, templateService),
28
+ trackingService: new MixpanelTrackingService(new KeychainTokenStore()),
29
+ };
30
+ }
31
+ /**
32
+ * Prompts the user to select an agent.
33
+ * This method is protected to allow test overrides.
34
+ * @returns The selected agent
35
+ */
36
+ async promptForAgentSelection() {
37
+ const answer = await search({
38
+ message: 'Which agent you are using (type to search):',
39
+ async source(input) {
40
+ if (!input)
41
+ return AGENTS;
42
+ return AGENTS.filter((agent) => agent.name.toLowerCase().includes(input.toLowerCase()) ||
43
+ agent.value.toLowerCase().includes(input.toLowerCase()));
44
+ },
45
+ });
46
+ return answer;
47
+ }
48
+ /**
49
+ * Prompts the user to confirm overwriting an existing rule file.
50
+ * This method is protected to allow test overrides.
51
+ * @param agent The agent for which the rule file exists
52
+ * @returns True if the user confirms overwrite, false otherwise
53
+ */
54
+ async promptForOverwriteConfirmation(agent) {
55
+ return confirm({
56
+ default: true,
57
+ message: `Rule file already exists for ${agent}. Overwrite?`,
58
+ });
59
+ }
60
+ async run() {
61
+ const { ruleWriterService, trackingService } = this.createServices();
62
+ // Track rule generation
63
+ await trackingService.track('rule:generate');
64
+ // Interactive selection with search
65
+ const answer = await this.promptForAgentSelection();
66
+ this.log(`Generating rules for: ${answer}`);
67
+ try {
68
+ await ruleWriterService.writeRule(answer, false);
69
+ this.log(`✅ Successfully generated rule file for ${answer}`);
70
+ }
71
+ catch (error) {
72
+ if (error instanceof RuleExistsError) {
73
+ const overwrite = await this.promptForOverwriteConfirmation(answer);
74
+ if (overwrite) {
75
+ // Retry with forced=true
76
+ await ruleWriterService.writeRule(answer, true);
77
+ this.log(`✅ Successfully generated rule file for ${answer}`);
78
+ }
79
+ else {
80
+ this.log(`Skipping rule file generation for ${answer}`);
81
+ }
82
+ }
83
+ else {
84
+ // Non-recoverable error
85
+ this.error(`Failed to generate rule file: ${error instanceof Error ? error.message : String(error)}`);
86
+ }
87
+ }
88
+ }
89
+ }
@@ -0,0 +1,24 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { Space } from '../core/domain/entities/space.js';
3
+ import type { Team } from '../core/domain/entities/team.js';
4
+ import type { IPlaybookService } from '../core/interfaces/i-playbook-service.js';
5
+ import type { IProjectConfigStore } from '../core/interfaces/i-project-config-store.js';
6
+ import type { ISpaceService } from '../core/interfaces/i-space-service.js';
7
+ import type { ITeamService } from '../core/interfaces/i-team-service.js';
8
+ import type { ITokenStore } from '../core/interfaces/i-token-store.js';
9
+ import { ITrackingService } from '../core/interfaces/i-tracking-service.js';
10
+ export default class Init extends Command {
11
+ static description: string;
12
+ static examples: string[];
13
+ protected createServices(): {
14
+ playbookService: IPlaybookService;
15
+ projectConfigStore: IProjectConfigStore;
16
+ spaceService: ISpaceService;
17
+ teamService: ITeamService;
18
+ tokenStore: ITokenStore;
19
+ trackingService: ITrackingService;
20
+ };
21
+ protected promptForSpaceSelection(spaces: Space[]): Promise<Space>;
22
+ protected promptForTeamSelection(teams: Team[]): Promise<Team>;
23
+ run(): Promise<void>;
24
+ }
@@ -0,0 +1,135 @@
1
+ import { select } from '@inquirer/prompts';
2
+ import { Command, ux } from '@oclif/core';
3
+ import { getCurrentConfig } from '../config/environment.js';
4
+ import { BrConfig } from '../core/domain/entities/br-config.js';
5
+ import { ProjectConfigStore } from '../infra/config/file-config-store.js';
6
+ import { FilePlaybookService } from '../infra/playbook/file-playbook-service.js';
7
+ import { HttpSpaceService } from '../infra/space/http-space-service.js';
8
+ import { KeychainTokenStore } from '../infra/storage/keychain-token-store.js';
9
+ import { HttpTeamService } from '../infra/team/http-team-service.js';
10
+ import { MixpanelTrackingService } from '../infra/tracking/mixpanel-tracking-service.js';
11
+ export default class Init extends Command {
12
+ static description = 'Initialize a project with ByteRover (creates .br/config.json with team/space selection and initializes ACE playbook)';
13
+ static examples = [
14
+ '<%= config.bin %> <%= command.id %>',
15
+ '# Re-initialize if config exists (will show current config and exit):\n<%= config.bin %> <%= command.id %>',
16
+ '# Full workflow: login then initialize:\n<%= config.bin %> login\n<%= config.bin %> <%= command.id %>',
17
+ ];
18
+ createServices() {
19
+ const envConfig = getCurrentConfig();
20
+ const tokenStore = new KeychainTokenStore();
21
+ const trackingService = new MixpanelTrackingService(tokenStore);
22
+ return {
23
+ playbookService: new FilePlaybookService(),
24
+ projectConfigStore: new ProjectConfigStore(),
25
+ spaceService: new HttpSpaceService({
26
+ apiBaseUrl: envConfig.apiBaseUrl,
27
+ }),
28
+ teamService: new HttpTeamService({
29
+ apiBaseUrl: envConfig.apiBaseUrl,
30
+ }),
31
+ tokenStore,
32
+ trackingService,
33
+ };
34
+ }
35
+ async promptForSpaceSelection(spaces) {
36
+ const selectedSpaceId = await select({
37
+ choices: spaces.map((space) => ({
38
+ name: space.getDisplayName(),
39
+ value: space.id,
40
+ })),
41
+ message: 'Select a space',
42
+ });
43
+ const selectedSpace = spaces.find((space) => space.id === selectedSpaceId);
44
+ if (!selectedSpace) {
45
+ this.error('Space selection failed');
46
+ }
47
+ return selectedSpace;
48
+ }
49
+ async promptForTeamSelection(teams) {
50
+ const selectedTeamId = await select({
51
+ choices: teams.map((team) => ({
52
+ name: team.name,
53
+ value: team.id,
54
+ })),
55
+ message: 'Select a team',
56
+ });
57
+ const selectedTeam = teams.find((team) => team.id === selectedTeamId);
58
+ if (!selectedTeam) {
59
+ this.error('Team selection failed');
60
+ }
61
+ return selectedTeam;
62
+ }
63
+ async run() {
64
+ try {
65
+ const { playbookService, projectConfigStore, spaceService, teamService, tokenStore, trackingService } = this.createServices();
66
+ // 1. Check if already initialized
67
+ const isInitialized = await projectConfigStore.exists();
68
+ if (isInitialized) {
69
+ this.log('Project is already initialized with ByteRover.');
70
+ const existingProjectConfig = await projectConfigStore.read();
71
+ this.log(`Your space for this project is: ${existingProjectConfig?.teamName}/${existingProjectConfig?.spaceName}`);
72
+ return;
73
+ }
74
+ this.log('Initializing ByteRover project...\n');
75
+ // 2. Load and validate authentication token
76
+ const token = await tokenStore.load();
77
+ if (token === undefined) {
78
+ this.error('Not authenticated. Please run "br login" first.');
79
+ }
80
+ if (!token.isValid()) {
81
+ this.error('Authentication token expired. Please run "br login" again.');
82
+ }
83
+ // 3. Fetch all teams with spinner
84
+ ux.action.start('Fetching all teams');
85
+ const teamResult = await teamService.getTeams(token.accessToken, token.sessionKey, { fetchAll: true });
86
+ ux.action.stop();
87
+ const { teams } = teamResult;
88
+ if (teams.length === 0) {
89
+ this.error('No teams found. Please create a team in the ByteRover dashboard first.');
90
+ }
91
+ // 4. Prompt for team selection
92
+ this.log();
93
+ const selectedTeam = await this.promptForTeamSelection(teams);
94
+ // 5. Fetch all spaces for the selected team with spinner
95
+ ux.action.start('Fetching all spaces');
96
+ const spaceResult = await spaceService.getSpaces(token.accessToken, token.sessionKey, selectedTeam.id, {
97
+ fetchAll: true,
98
+ });
99
+ ux.action.stop();
100
+ const { spaces } = spaceResult;
101
+ if (spaces.length === 0) {
102
+ this.error(`No spaces found in team "${selectedTeam.getDisplayName()}". Please create a space in the ByteRover dashboard first.`);
103
+ }
104
+ // 6. Prompt for space selection
105
+ this.log();
106
+ const selectedSpace = await this.promptForSpaceSelection(spaces);
107
+ // 7. Create and save configuration
108
+ const config = BrConfig.fromSpace(selectedSpace);
109
+ await projectConfigStore.write(config);
110
+ // 8. Initialize ACE playbook
111
+ this.log('\nInitializing ACE context...');
112
+ try {
113
+ const playbookPath = await playbookService.initialize();
114
+ this.log(`✓ ACE playbook initialized in ${playbookPath}`);
115
+ }
116
+ catch (error) {
117
+ // Warn but don't fail if ACE init fails
118
+ this.warn(`ACE initialization skipped: ${error instanceof Error ? error.message : 'Unknown error'}`);
119
+ }
120
+ // 9. Generate rules
121
+ this.log(`\nGenerate rule instructions for coding agents to work with ByteRover correctly`);
122
+ this.log();
123
+ await this.config.runCommand('gen-rules');
124
+ // Track space initialization
125
+ await trackingService.track('space:init');
126
+ // 10. Display success
127
+ this.log(`\n✓ Project initialized successfully!`);
128
+ this.log(`✓ Connected to space: ${selectedSpace.getDisplayName()}`);
129
+ this.log(`✓ Configuration saved to: .br/config.json`);
130
+ }
131
+ catch (error) {
132
+ this.error(error instanceof Error ? error.message : 'Initialization failed');
133
+ }
134
+ }
135
+ }
@@ -0,0 +1,22 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { IAuthService } from '../core/interfaces/i-auth-service.js';
3
+ import type { IBrowserLauncher } from '../core/interfaces/i-browser-launcher.js';
4
+ import type { ICallbackHandler } from '../core/interfaces/i-callback-handler.js';
5
+ import type { IOidcDiscoveryService } from '../core/interfaces/i-oidc-discovery-service.js';
6
+ import type { ITokenStore } from '../core/interfaces/i-token-store.js';
7
+ import type { IUserService } from '../core/interfaces/i-user-service.js';
8
+ import { ITrackingService } from '../core/interfaces/i-tracking-service.js';
9
+ export default class Login extends Command {
10
+ static description: string;
11
+ static examples: string[];
12
+ protected createAuthService(discoveryService: IOidcDiscoveryService): Promise<IAuthService>;
13
+ protected createServices(): {
14
+ browserLauncher: IBrowserLauncher;
15
+ callbackHandler: ICallbackHandler;
16
+ discoveryService: IOidcDiscoveryService;
17
+ tokenStore: ITokenStore;
18
+ trackingService: ITrackingService;
19
+ userService: IUserService;
20
+ };
21
+ run(): Promise<void>;
22
+ }