headlesscode 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/ATTRIBUTION.md +53 -0
  2. package/CODE_OF_CONDUCT.md +130 -0
  3. package/CONTRIBUTING.md +107 -0
  4. package/LICENSE +202 -0
  5. package/README.md +486 -0
  6. package/SECURITY.md +211 -0
  7. package/bin/headlesscode.mjs +83 -0
  8. package/package.json +63 -0
  9. package/shared/prompts/review-mode-prompt-short.md +93 -0
  10. package/shared/prompts/review-mode-prompt.md +281 -0
  11. package/shared/rules-code/rules.md +22 -0
  12. package/shared/stacks/cpp/rules.md +30 -0
  13. package/shared/stacks/fastapi/rules.md +30 -0
  14. package/shared/stacks/javascript/rules.md +37 -0
  15. package/shared/stacks/postgresql/rules.md +31 -0
  16. package/shared/stacks/python/rules.md +35 -0
  17. package/shared/stacks/react/rules.md +11 -0
  18. package/shared/stacks/typescript/rules.md +10 -0
  19. package/src/budget/budget.ts +221 -0
  20. package/src/budget/concurrency.ts +126 -0
  21. package/src/budget/cost.ts +309 -0
  22. package/src/budget/index.ts +8 -0
  23. package/src/checkpoints/cli.ts +256 -0
  24. package/src/checkpoints/service.ts +227 -0
  25. package/src/cli.ts +1535 -0
  26. package/src/cloud/docker-provider.ts +334 -0
  27. package/src/cloud/provider.ts +300 -0
  28. package/src/codeintel/call-graph.ts +78 -0
  29. package/src/codeintel/find-references.ts +123 -0
  30. package/src/codeintel/go-to-definition.ts +193 -0
  31. package/src/codeintel/handlers.ts +190 -0
  32. package/src/codeintel/import-graph.ts +173 -0
  33. package/src/codeintel/outline.ts +180 -0
  34. package/src/codeintel/position.ts +77 -0
  35. package/src/codeintel/program.ts +350 -0
  36. package/src/codeintel/rename-symbol.ts +213 -0
  37. package/src/codeintel/tools.ts +280 -0
  38. package/src/codemap/build.ts +135 -0
  39. package/src/codemap/cli.ts +190 -0
  40. package/src/codemap/extract.ts +339 -0
  41. package/src/codemap/files.ts +236 -0
  42. package/src/codemap/fingerprint.ts +65 -0
  43. package/src/codemap/flows.ts +62 -0
  44. package/src/codemap/html.ts +451 -0
  45. package/src/codemap/lock.ts +80 -0
  46. package/src/codemap/types.ts +101 -0
  47. package/src/codesearch/airunner-embedder.ts +185 -0
  48. package/src/codesearch/chunk.ts +339 -0
  49. package/src/codesearch/cli.ts +223 -0
  50. package/src/codesearch/embedder.ts +332 -0
  51. package/src/codesearch/files.ts +280 -0
  52. package/src/codesearch/index.ts +469 -0
  53. package/src/codesearch/ollama-embedder.ts +205 -0
  54. package/src/codesearch/search.ts +141 -0
  55. package/src/codesearch/types.ts +100 -0
  56. package/src/config/mode-models.ts +218 -0
  57. package/src/dashboard/aggregate.ts +364 -0
  58. package/src/dashboard/chat-thread.ts +141 -0
  59. package/src/dashboard/checkpoints.ts +124 -0
  60. package/src/dashboard/cli.ts +193 -0
  61. package/src/dashboard/codemap.ts +44 -0
  62. package/src/dashboard/files.ts +121 -0
  63. package/src/dashboard/page.ts +2803 -0
  64. package/src/dashboard/self-improvement-metrics.ts +282 -0
  65. package/src/dashboard/server.ts +1103 -0
  66. package/src/dashboard/session-launch.ts +310 -0
  67. package/src/dashboard/timeline.ts +273 -0
  68. package/src/dashboard/tool-exec.ts +107 -0
  69. package/src/dashboard/trend-cli.ts +141 -0
  70. package/src/dashboard/trend.ts +413 -0
  71. package/src/decision-proxy/cli.ts +261 -0
  72. package/src/decision-proxy/proxy.ts +569 -0
  73. package/src/deploy/gate-cli.ts +147 -0
  74. package/src/deploy/gate.ts +254 -0
  75. package/src/engine/condense.ts +512 -0
  76. package/src/engine/events.ts +428 -0
  77. package/src/engine/handoff.ts +71 -0
  78. package/src/engine/lazy-tools.ts +160 -0
  79. package/src/engine/local-explore.ts +653 -0
  80. package/src/engine/logger.ts +96 -0
  81. package/src/engine/loop.ts +5517 -0
  82. package/src/engine/parser.ts +347 -0
  83. package/src/engine/prompt.ts +860 -0
  84. package/src/engine/reports.ts +47 -0
  85. package/src/engine/stacks.ts +448 -0
  86. package/src/engine/types.ts +291 -0
  87. package/src/engine/usage.ts +186 -0
  88. package/src/github/app-auth.ts +161 -0
  89. package/src/github/cli.ts +448 -0
  90. package/src/github/installations.ts +133 -0
  91. package/src/github/pr.ts +321 -0
  92. package/src/github/provision.ts +118 -0
  93. package/src/github/push.ts +122 -0
  94. package/src/index-util.ts +50 -0
  95. package/src/index.ts +81 -0
  96. package/src/init/cli.ts +248 -0
  97. package/src/init/gitignore.ts +74 -0
  98. package/src/llm/ollama.ts +308 -0
  99. package/src/llm/openrouter.ts +868 -0
  100. package/src/llm/preflight.ts +367 -0
  101. package/src/llm/transcript-capture.ts +84 -0
  102. package/src/memory/embed.ts +110 -0
  103. package/src/memory/index.ts +22 -0
  104. package/src/memory/local.ts +259 -0
  105. package/src/memory/summarizer.ts +283 -0
  106. package/src/memory/types.ts +153 -0
  107. package/src/memory/uwuchat.ts +157 -0
  108. package/src/migrate/cli.ts +115 -0
  109. package/src/orchestrator/analyze-cli.ts +104 -0
  110. package/src/orchestrator/auto-split.ts +206 -0
  111. package/src/orchestrator/cleanup.ts +1003 -0
  112. package/src/orchestrator/cli.ts +3571 -0
  113. package/src/orchestrator/cost-estimate.ts +564 -0
  114. package/src/orchestrator/cost-history-cli.ts +242 -0
  115. package/src/orchestrator/cost-history.ts +397 -0
  116. package/src/orchestrator/git-sync.ts +250 -0
  117. package/src/orchestrator/index.ts +153 -0
  118. package/src/orchestrator/log-analysis.ts +0 -0
  119. package/src/orchestrator/merge-check.ts +108 -0
  120. package/src/orchestrator/pipeline.ts +411 -0
  121. package/src/orchestrator/resume.ts +1940 -0
  122. package/src/orchestrator/reviewer.ts +503 -0
  123. package/src/orchestrator/split.ts +296 -0
  124. package/src/orchestrator/state.ts +542 -0
  125. package/src/orchestrator/status.ts +697 -0
  126. package/src/orchestrator/verification-gate.ts +134 -0
  127. package/src/orchestrator/watch.ts +898 -0
  128. package/src/permissions/commands.ts +1083 -0
  129. package/src/permissions/config.ts +241 -0
  130. package/src/permissions/index.ts +12 -0
  131. package/src/permissions/protected-files.ts +96 -0
  132. package/src/permissions/store-protection.ts +272 -0
  133. package/src/project-store.ts +648 -0
  134. package/src/projects/cli.ts +382 -0
  135. package/src/qa/qa.ts +487 -0
  136. package/src/tools/browser/handler.ts +346 -0
  137. package/src/tools/browser/service.ts +406 -0
  138. package/src/tools/browser/smoke.ts +78 -0
  139. package/src/tools/browser/tool.ts +99 -0
  140. package/src/tools/executor.ts +2575 -0
  141. package/src/tools/language-detect.ts +183 -0
  142. package/src/tools/output-summarizer.ts +369 -0
  143. package/src/tools/run-tests.ts +302 -0
  144. package/src/tools/set-indentation-tool.ts +49 -0
  145. package/src/tools/test-selection.ts +160 -0
  146. package/src/vendor/tests/smoke.ts +103 -0
  147. package/src/vendor/zoo-code/VENDOR-NOTES.md +213 -0
  148. package/src/vendor/zoo-code/shim/anthropic.ts +71 -0
  149. package/src/vendor/zoo-code/shim/openai.d.ts +60 -0
  150. package/src/vendor/zoo-code/shim/os-name.ts +18 -0
  151. package/src/vendor/zoo-code/shim/strip-bom.ts +14 -0
  152. package/src/vendor/zoo-code/shim/vscode.ts +76 -0
  153. package/src/vendor/zoo-code/src/core/config/CustomModesManager.ts +1015 -0
  154. package/src/vendor/zoo-code/src/core/diff/strategies/multi-search-replace.ts +670 -0
  155. package/src/vendor/zoo-code/src/core/prompts/sections/capabilities.ts +46 -0
  156. package/src/vendor/zoo-code/src/core/prompts/sections/custom-instructions.ts +559 -0
  157. package/src/vendor/zoo-code/src/core/prompts/sections/index.ts +10 -0
  158. package/src/vendor/zoo-code/src/core/prompts/sections/markdown-formatting.ts +7 -0
  159. package/src/vendor/zoo-code/src/core/prompts/sections/modes.ts +35 -0
  160. package/src/vendor/zoo-code/src/core/prompts/sections/objective.ts +13 -0
  161. package/src/vendor/zoo-code/src/core/prompts/sections/rules.ts +95 -0
  162. package/src/vendor/zoo-code/src/core/prompts/sections/skills.ts +105 -0
  163. package/src/vendor/zoo-code/src/core/prompts/sections/system-info.ts +30 -0
  164. package/src/vendor/zoo-code/src/core/prompts/sections/tool-use-guidelines.ts +9 -0
  165. package/src/vendor/zoo-code/src/core/prompts/sections/tool-use.ts +7 -0
  166. package/src/vendor/zoo-code/src/core/prompts/system.ts +176 -0
  167. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/access_mcp_resource.ts +41 -0
  168. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/apply_diff.ts +40 -0
  169. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/apply_patch.ts +61 -0
  170. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/ask_followup_question.ts +62 -0
  171. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/attempt_completion.ts +33 -0
  172. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/codebase_search.ts +43 -0
  173. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/converters.ts +109 -0
  174. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/edit.ts +48 -0
  175. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/edit_file.ts +72 -0
  176. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/execute_command.ts +54 -0
  177. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/generate_image.ts +51 -0
  178. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/index.ts +75 -0
  179. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/list_files.ts +41 -0
  180. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/mcp_server.ts +75 -0
  181. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/new_task.ts +39 -0
  182. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/read_command_output.ts +81 -0
  183. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/read_file.ts +169 -0
  184. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/run_slash_command.ts +31 -0
  185. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/search_files.ts +50 -0
  186. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/search_replace.ts +51 -0
  187. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/skill.ts +33 -0
  188. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/switch_mode.ts +31 -0
  189. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/update_todo_list.ts +54 -0
  190. package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/write_to_file.ts +40 -0
  191. package/src/vendor/zoo-code/src/core/prompts/types.ts +12 -0
  192. package/src/vendor/zoo-code/src/i18n/index.ts +19 -0
  193. package/src/vendor/zoo-code/src/integrations/misc/extract-text.ts +81 -0
  194. package/src/vendor/zoo-code/src/services/checkpoints/RepoPerTaskCheckpointService.ts +15 -0
  195. package/src/vendor/zoo-code/src/services/checkpoints/ShadowCheckpointService.ts +553 -0
  196. package/src/vendor/zoo-code/src/services/checkpoints/excludes.ts +212 -0
  197. package/src/vendor/zoo-code/src/services/checkpoints/index.ts +3 -0
  198. package/src/vendor/zoo-code/src/services/checkpoints/types.ts +35 -0
  199. package/src/vendor/zoo-code/src/services/code-index/manager.ts +19 -0
  200. package/src/vendor/zoo-code/src/services/mcp/McpHub.ts +36 -0
  201. package/src/vendor/zoo-code/src/services/roo-config/index.ts +441 -0
  202. package/src/vendor/zoo-code/src/services/search/file-search.ts +143 -0
  203. package/src/vendor/zoo-code/src/services/skills/SkillsManager.ts +20 -0
  204. package/src/vendor/zoo-code/src/shared/globalFileNames.ts +9 -0
  205. package/src/vendor/zoo-code/src/shared/language.ts +43 -0
  206. package/src/vendor/zoo-code/src/shared/modes.ts +257 -0
  207. package/src/vendor/zoo-code/src/shared/tools.ts +385 -0
  208. package/src/vendor/zoo-code/src/utils/fs.ts +39 -0
  209. package/src/vendor/zoo-code/src/utils/globalContext.ts +22 -0
  210. package/src/vendor/zoo-code/src/utils/json-schema.ts +16 -0
  211. package/src/vendor/zoo-code/src/utils/logging.ts +21 -0
  212. package/src/vendor/zoo-code/src/utils/mcp-name.ts +190 -0
  213. package/src/vendor/zoo-code/src/utils/object.ts +18 -0
  214. package/src/vendor/zoo-code/src/utils/path.ts +94 -0
  215. package/src/vendor/zoo-code/src/utils/shell.ts +376 -0
  216. package/src/vendor/zoo-code/src/utils/text-normalization.ts +99 -0
  217. package/src/vendor/zoo-code/types/global-settings.ts +19 -0
  218. package/src/vendor/zoo-code/types/index.ts +22 -0
  219. package/src/vendor/zoo-code/types/message.ts +375 -0
  220. package/src/vendor/zoo-code/types/mode.ts +241 -0
  221. package/src/vendor/zoo-code/types/todo.ts +19 -0
  222. package/src/vendor/zoo-code/types/tool-params.ts +116 -0
  223. package/src/vendor/zoo-code/types/tool.ts +67 -0
  224. package/src/vendor/zoo-code/types/vscode.ts +84 -0
  225. package/src/vision/describe.ts +242 -0
  226. package/src/vision/tool.ts +91 -0
  227. package/src/watcher/cli.ts +369 -0
  228. package/src/watcher/github.ts +304 -0
  229. package/src/watcher/index.ts +59 -0
  230. package/src/watcher/state.ts +254 -0
  231. package/src/watcher/watch.ts +562 -0
  232. package/tsconfig.json +18 -0
@@ -0,0 +1,441 @@
1
+ import * as path from "path"
2
+ import * as os from "os"
3
+ import fs from "fs/promises"
4
+
5
+ /**
6
+ * Gets the GLOBAL shared-config directory used for the mode-agnostic rules
7
+ * (`<dir>/rules/`) and per-mode rules (`<dir>/rules-<mode>/`) that
8
+ * `addCustomInstructions()` scans (see src/core/prompts/sections/
9
+ * custom-instructions.ts).
10
+ *
11
+ * DELIBERATE DEVIATION FROM THE VENDORED UPSTREAM: upstream returns
12
+ * `~/.roo` (the Zoo-Code-branded location). headlesscode relocated the global
13
+ * shared instructions to its own native data location,
14
+ * `~/.local/share/headlesscode/shared/` (see src/project-store.ts), to
15
+ * complete the decoupling from Zoo Code as a tool — the file FORMAT and the
16
+ * rules-directory-scanning convention are unchanged, only the lookup path
17
+ * moved. Project-local `.roo/rules(-<mode>)/` is untouched (the project dir
18
+ * still wins on any overlap).
19
+ *
20
+ * @returns The absolute path to the global shared-instructions directory:
21
+ * `~/.local/share/headlesscode/shared` on macOS/Linux
22
+ */
23
+ export function getGlobalRooDirectory(): string {
24
+ const homeDir = os.homedir()
25
+ return path.join(homeDir, ".local", "share", "headlesscode", "shared")
26
+ }
27
+
28
+ /**
29
+ * Gets the global .agents directory path based on the current platform.
30
+ * This is a shared directory for agent skills across different AI coding tools.
31
+ *
32
+ * @returns The absolute path to the global .agents directory
33
+ *
34
+ * @example Platform-specific paths:
35
+ * ```
36
+ * // macOS/Linux: ~/.agents/
37
+ * // Example: /Users/john/.agents
38
+ *
39
+ * // Windows: %USERPROFILE%\.agents\
40
+ * // Example: C:\Users\john\.agents
41
+ * ```
42
+ *
43
+ * @example Usage:
44
+ * ```typescript
45
+ * const globalAgentsDir = getGlobalAgentsDirectory()
46
+ * // Returns: "/Users/john/.agents" (on macOS/Linux)
47
+ * // Returns: "C:\\Users\\john\\.agents" (on Windows)
48
+ * ```
49
+ */
50
+ export function getGlobalAgentsDirectory(): string {
51
+ const homeDir = os.homedir()
52
+ return path.join(homeDir, ".agents")
53
+ }
54
+
55
+ /**
56
+ * Gets the project-local .agents directory path for a given cwd.
57
+ * This is a shared directory for agent skills across different AI coding tools.
58
+ *
59
+ * @param cwd - Current working directory (project path)
60
+ * @returns The absolute path to the project-local .agents directory
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * const projectAgentsDir = getProjectAgentsDirectoryForCwd('/Users/john/my-project')
65
+ * // Returns: "/Users/john/my-project/.agents"
66
+ * ```
67
+ */
68
+ export function getProjectAgentsDirectoryForCwd(cwd: string): string {
69
+ return path.join(cwd, ".agents")
70
+ }
71
+
72
+ /**
73
+ * Gets the project-local .roo directory path for a given cwd
74
+ *
75
+ * @param cwd - Current working directory (project path)
76
+ * @returns The absolute path to the project-local .roo directory
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * const projectDir = getProjectRooDirectoryForCwd('/Users/john/my-project')
81
+ * // Returns: "/Users/john/my-project/.roo"
82
+ *
83
+ * const windowsProjectDir = getProjectRooDirectoryForCwd('C:\\Users\\john\\my-project')
84
+ * // Returns: "C:\\Users\\john\\my-project\\.roo"
85
+ * ```
86
+ *
87
+ * @example Directory structure:
88
+ * ```
89
+ * /Users/john/my-project/
90
+ * ├── .roo/ # Project-local configuration directory
91
+ * │ ├── rules/
92
+ * │ │ └── rules.md
93
+ * │ ├── custom-instructions.md
94
+ * │ └── config/
95
+ * │ └── settings.json
96
+ * ├── src/
97
+ * │ └── index.ts
98
+ * └── package.json
99
+ * ```
100
+ */
101
+ export function getProjectRooDirectoryForCwd(cwd: string): string {
102
+ return path.join(cwd, ".roo")
103
+ }
104
+
105
+ /**
106
+ * Checks if a directory exists
107
+ */
108
+ export async function directoryExists(dirPath: string): Promise<boolean> {
109
+ try {
110
+ const stat = await fs.stat(dirPath)
111
+ return stat.isDirectory()
112
+ } catch (error: any) {
113
+ // Only catch expected "not found" errors
114
+ if (error.code === "ENOENT" || error.code === "ENOTDIR") {
115
+ return false
116
+ }
117
+ // Re-throw unexpected errors (permission, I/O, etc.)
118
+ throw error
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Checks if a file exists
124
+ */
125
+ export async function fileExists(filePath: string): Promise<boolean> {
126
+ try {
127
+ const stat = await fs.stat(filePath)
128
+ return stat.isFile()
129
+ } catch (error: any) {
130
+ // Only catch expected "not found" errors
131
+ if (error.code === "ENOENT" || error.code === "ENOTDIR") {
132
+ return false
133
+ }
134
+ // Re-throw unexpected errors (permission, I/O, etc.)
135
+ throw error
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Reads a file safely, returning null if it doesn't exist
141
+ */
142
+ export async function readFileIfExists(filePath: string): Promise<string | null> {
143
+ try {
144
+ return await fs.readFile(filePath, "utf-8")
145
+ } catch (error: any) {
146
+ // Only catch expected "not found" errors
147
+ if (error.code === "ENOENT" || error.code === "ENOTDIR" || error.code === "EISDIR") {
148
+ return null
149
+ }
150
+ // Re-throw unexpected errors (permission, I/O, etc.)
151
+ throw error
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Discovers all .roo directories in subdirectories of the workspace
157
+ *
158
+ * @param cwd - Current working directory (workspace root)
159
+ * @returns Array of absolute paths to .roo directories found in subdirectories,
160
+ * sorted alphabetically. Does not include the root .roo directory.
161
+ *
162
+ * @example
163
+ * ```typescript
164
+ * const subfolderRoos = await discoverSubfolderRooDirectories('/Users/john/monorepo')
165
+ * // Returns:
166
+ * // [
167
+ * // '/Users/john/monorepo/package-a/.roo',
168
+ * // '/Users/john/monorepo/package-b/.roo',
169
+ * // '/Users/john/monorepo/packages/shared/.roo'
170
+ * // ]
171
+ * ```
172
+ *
173
+ * @example Directory structure:
174
+ * ```
175
+ * /Users/john/monorepo/
176
+ * ├── .roo/ # Root .roo (NOT included - use getProjectRooDirectoryForCwd)
177
+ * ├── package-a/
178
+ * │ └── .roo/ # Included
179
+ * │ └── rules/
180
+ * ├── package-b/
181
+ * │ └── .roo/ # Included
182
+ * │ └── rules-code/
183
+ * └── packages/
184
+ * └── shared/
185
+ * └── .roo/ # Included (nested)
186
+ * └── rules/
187
+ * ```
188
+ */
189
+ export async function discoverSubfolderRooDirectories(cwd: string): Promise<string[]> {
190
+ try {
191
+ // Dynamic import to avoid vscode dependency at module load time
192
+ // This is necessary because file-search.ts imports vscode, which is not
193
+ // available in the webview context
194
+ const { executeRipgrep } = await import("../search/file-search")
195
+
196
+ // Use ripgrep to find any file inside any .roo directory
197
+ // This efficiently discovers all .roo folders regardless of their content
198
+ const args = [
199
+ "--files",
200
+ "--hidden",
201
+ "--follow",
202
+ "-g",
203
+ "**/.roo/**",
204
+ "-g",
205
+ "!node_modules/**",
206
+ "-g",
207
+ "!.git/**",
208
+ cwd,
209
+ ]
210
+
211
+ const results = await executeRipgrep({ args, workspacePath: cwd })
212
+
213
+ // Extract unique .roo directory paths
214
+ const rooDirs = new Set<string>()
215
+ const rootRooDir = path.join(cwd, ".roo")
216
+
217
+ for (const result of results) {
218
+ // Match paths like "subfolder/.roo/anything" or "subfolder/nested/.roo/anything"
219
+ // Handle both forward slashes (Unix) and backslashes (Windows)
220
+ const match = result.path.match(/^(.+?)[/\\]\.roo[/\\]/)
221
+ if (match) {
222
+ const rooDir = path.join(cwd, match[1], ".roo")
223
+ // Exclude the root .roo directory (already handled by getProjectRooDirectoryForCwd)
224
+ if (rooDir !== rootRooDir) {
225
+ rooDirs.add(rooDir)
226
+ }
227
+ }
228
+ }
229
+
230
+ // Return sorted alphabetically
231
+ return Array.from(rooDirs).sort()
232
+ } catch (error) {
233
+ // If discovery fails (e.g., ripgrep not available), return empty array
234
+ return []
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Gets the ordered list of .roo directories to check (global first, then project-local)
240
+ *
241
+ * @param cwd - Current working directory (project path)
242
+ * @returns Array of directory paths to check in order [global, project-local]
243
+ *
244
+ * @example
245
+ * ```typescript
246
+ * // For a project at /Users/john/my-project
247
+ * const directories = getRooDirectoriesForCwd('/Users/john/my-project')
248
+ * // Returns:
249
+ * // [
250
+ * // '/Users/john/.roo', // Global directory
251
+ * // '/Users/john/my-project/.roo' // Project-local directory
252
+ * // ]
253
+ * ```
254
+ *
255
+ * @example Directory structure:
256
+ * ```
257
+ * /Users/john/
258
+ * ├── .roo/ # Global configuration
259
+ * │ ├── rules/
260
+ * │ │ └── rules.md
261
+ * │ └── custom-instructions.md
262
+ * └── my-project/
263
+ * ├── .roo/ # Project-specific configuration
264
+ * │ ├── rules/
265
+ * │ │ └── rules.md # Overrides global rules
266
+ * │ └── project-notes.md
267
+ * └── src/
268
+ * └── index.ts
269
+ * ```
270
+ */
271
+ export function getRooDirectoriesForCwd(cwd: string): string[] {
272
+ const directories: string[] = []
273
+
274
+ // Add global directory first
275
+ directories.push(getGlobalRooDirectory())
276
+
277
+ // Add project-local directory second
278
+ directories.push(getProjectRooDirectoryForCwd(cwd))
279
+
280
+ return directories
281
+ }
282
+
283
+ /**
284
+ * Gets the ordered list of all .roo directories including subdirectories
285
+ *
286
+ * @param cwd - Current working directory (project path)
287
+ * @returns Array of directory paths in order: [global, project-local, ...subfolders (alphabetically)]
288
+ *
289
+ * @example
290
+ * ```typescript
291
+ * // For a monorepo at /Users/john/monorepo with .roo in subfolders
292
+ * const directories = await getAllRooDirectoriesForCwd('/Users/john/monorepo')
293
+ * // Returns:
294
+ * // [
295
+ * // '/Users/john/.roo', // Global directory
296
+ * // '/Users/john/monorepo/.roo', // Project-local directory
297
+ * // '/Users/john/monorepo/package-a/.roo', // Subfolder (alphabetical)
298
+ * // '/Users/john/monorepo/package-b/.roo' // Subfolder (alphabetical)
299
+ * // ]
300
+ * ```
301
+ */
302
+ export async function getAllRooDirectoriesForCwd(cwd: string): Promise<string[]> {
303
+ const directories: string[] = []
304
+
305
+ // Add global directory first
306
+ directories.push(getGlobalRooDirectory())
307
+
308
+ // Add project-local directory second
309
+ directories.push(getProjectRooDirectoryForCwd(cwd))
310
+
311
+ // Discover and add subfolder .roo directories
312
+ const subfolderDirs = await discoverSubfolderRooDirectories(cwd)
313
+ directories.push(...subfolderDirs)
314
+
315
+ return directories
316
+ }
317
+
318
+ /**
319
+ * Gets parent directories containing .roo folders, in order from root to subfolders
320
+ *
321
+ * @param cwd - Current working directory (project path)
322
+ * @returns Array of parent directory paths (not .roo paths) containing AGENTS.md or .roo
323
+ *
324
+ * @example
325
+ * ```typescript
326
+ * const dirs = await getAgentsDirectoriesForCwd('/Users/john/monorepo')
327
+ * // Returns: ['/Users/john/monorepo', '/Users/john/monorepo/package-a', ...]
328
+ * ```
329
+ */
330
+ export async function getAgentsDirectoriesForCwd(cwd: string): Promise<string[]> {
331
+ const directories: string[] = []
332
+
333
+ // Always include the root directory
334
+ directories.push(cwd)
335
+
336
+ // Get all subfolder .roo directories
337
+ const subfolderRooDirs = await discoverSubfolderRooDirectories(cwd)
338
+
339
+ // Extract parent directories (remove .roo from path)
340
+ for (const rooDir of subfolderRooDirs) {
341
+ const parentDir = path.dirname(rooDir)
342
+ directories.push(parentDir)
343
+ }
344
+
345
+ return directories
346
+ }
347
+
348
+ /**
349
+ * Loads configuration from multiple .roo directories with project overriding global
350
+ *
351
+ * @param relativePath - The relative path within each .roo directory (e.g., 'rules/rules.md')
352
+ * @param cwd - Current working directory (project path)
353
+ * @returns Object with global and project content, plus merged content
354
+ *
355
+ * @example
356
+ * ```typescript
357
+ * // Load rules configuration for a project
358
+ * const config = await loadConfiguration('rules/rules.md', '/Users/john/my-project')
359
+ *
360
+ * // Returns:
361
+ * // {
362
+ * // global: "Global rules content...", // From ~/.roo/rules/rules.md
363
+ * // project: "Project rules content...", // From /Users/john/my-project/.roo/rules/rules.md
364
+ * // merged: "Global rules content...\n\n# Project-specific rules (override global):\n\nProject rules content..."
365
+ * // }
366
+ * ```
367
+ *
368
+ * @example File paths resolved:
369
+ * ```
370
+ * relativePath: 'rules/rules.md'
371
+ * cwd: '/Users/john/my-project'
372
+ *
373
+ * Reads from:
374
+ * - Global: /Users/john/.roo/rules/rules.md
375
+ * - Project: /Users/john/my-project/.roo/rules/rules.md
376
+ *
377
+ * Other common relativePath examples:
378
+ * - 'custom-instructions.md'
379
+ * - 'config/settings.json'
380
+ * - 'templates/component.tsx'
381
+ * ```
382
+ *
383
+ * @example Merging behavior:
384
+ * ```
385
+ * // If only global exists:
386
+ * { global: "content", project: null, merged: "content" }
387
+ *
388
+ * // If only project exists:
389
+ * { global: null, project: "content", merged: "content" }
390
+ *
391
+ * // If both exist:
392
+ * {
393
+ * global: "global content",
394
+ * project: "project content",
395
+ * merged: "global content\n\n# Project-specific rules (override global):\n\nproject content"
396
+ * }
397
+ * ```
398
+ */
399
+ export async function loadConfiguration(
400
+ relativePath: string,
401
+ cwd: string,
402
+ ): Promise<{
403
+ global: string | null
404
+ project: string | null
405
+ merged: string
406
+ }> {
407
+ const globalDir = getGlobalRooDirectory()
408
+ const projectDir = getProjectRooDirectoryForCwd(cwd)
409
+
410
+ const globalFilePath = path.join(globalDir, relativePath)
411
+ const projectFilePath = path.join(projectDir, relativePath)
412
+
413
+ // Read global configuration
414
+ const globalContent = await readFileIfExists(globalFilePath)
415
+
416
+ // Read project-local configuration
417
+ const projectContent = await readFileIfExists(projectFilePath)
418
+
419
+ // Merge configurations - project overrides global
420
+ let merged = ""
421
+
422
+ if (globalContent) {
423
+ merged += globalContent
424
+ }
425
+
426
+ if (projectContent) {
427
+ if (merged) {
428
+ merged += "\n\n# Project-specific rules (override global):\n\n"
429
+ }
430
+ merged += projectContent
431
+ }
432
+
433
+ return {
434
+ global: globalContent,
435
+ project: projectContent,
436
+ merged: merged || "",
437
+ }
438
+ }
439
+
440
+ // Export with backward compatibility alias
441
+ export const loadRooConfiguration: typeof loadConfiguration = loadConfiguration
@@ -0,0 +1,143 @@
1
+ /**
2
+ * SHIM — replaces zoo-code/src/services/search/file-search.ts.
3
+ *
4
+ * The original shells out to ripgrep (`executeRipgrep`) to discover files by
5
+ * glob pattern. Two vendored call sites use it:
6
+ * - `services/roo-config/index.ts#discoverSubfolderRooDirectories()`: finds
7
+ * any file under a nested ".roo" directory, excluding node_modules and
8
+ * ".git" (double-star glob, "-g" flags — see that file for the literal args).
9
+ * - `services/checkpoints/ShadowCheckpointService.ts#getNestedGitRepository()`:
10
+ * finds ".git" + "HEAD" pairs anywhere under the workspace (nested-repo
11
+ * detection — see that file for the literal args).
12
+ *
13
+ * This shim implements the same `{ args, workspacePath } -> results` shape
14
+ * with pure Node `fs`, supporting the small glob subset both callers use
15
+ * (double-star, single-star, and "!"-prefixed excludes via repeated "-g"
16
+ * flags). Each result has `{ path, type }` (relative to `workspacePath`,
17
+ * forward-slash separated), matching the real ripgrep-backed
18
+ * implementation's `FileResult` shape closely enough for both callers (the
19
+ * checkpoints caller destructures `type`).
20
+ *
21
+ * To keep this bounded on real repos, directories named ".git" are not
22
+ * recursed into wholesale (that would walk the entire object store) —
23
+ * instead only their "HEAD" file is checked directly, and "node_modules"
24
+ * directories are never recursed into. Both are reasonable defaults for the
25
+ * two callers above, which either exclude these directories explicitly or
26
+ * (in the checkpoints case) only ever care about a top-level "HEAD" file.
27
+ */
28
+
29
+ import * as fs from "fs/promises"
30
+ import * as path from "path"
31
+
32
+ export interface RipgrepResult {
33
+ path: string
34
+ type: "file" | "folder"
35
+ }
36
+
37
+ function globToRegex(glob: string): RegExp {
38
+ let re = ""
39
+ for (let i = 0; i < glob.length; i++) {
40
+ const c = glob[i]
41
+ if (c === "*") {
42
+ if (glob[i + 1] === "*") {
43
+ re += ".*"
44
+ i++
45
+ if (glob[i + 1] === "/") {
46
+ i++
47
+ }
48
+ } else {
49
+ re += "[^/]*"
50
+ }
51
+ } else if ("+.^${}()|[]\\".includes(c)) {
52
+ re += "\\" + c
53
+ } else {
54
+ re += c
55
+ }
56
+ }
57
+ return new RegExp(`^${re}$`)
58
+ }
59
+
60
+ function parsePatterns(args: string[]): { include: RegExp[]; exclude: RegExp[] } {
61
+ const include: RegExp[] = []
62
+ const exclude: RegExp[] = []
63
+
64
+ for (let i = 0; i < args.length; i++) {
65
+ if (args[i] === "-g" && args[i + 1] !== undefined) {
66
+ const pattern = args[i + 1]
67
+ if (pattern.startsWith("!")) {
68
+ exclude.push(globToRegex(pattern.slice(1)))
69
+ } else {
70
+ include.push(globToRegex(pattern))
71
+ }
72
+ i++
73
+ }
74
+ }
75
+
76
+ return { include, exclude }
77
+ }
78
+
79
+ export async function executeRipgrep({
80
+ args,
81
+ workspacePath,
82
+ }: {
83
+ args: string[]
84
+ workspacePath: string
85
+ }): Promise<RipgrepResult[]> {
86
+ const { include, exclude } = parsePatterns(args)
87
+ const results: RipgrepResult[] = []
88
+
89
+ const isExcluded = (relPath: string, isDir: boolean) => {
90
+ const candidates = isDir ? [relPath, `${relPath}/`] : [relPath]
91
+ return exclude.some((re) => candidates.some((c) => re.test(c)))
92
+ }
93
+
94
+ const matchesInclude = (relPath: string) => include.length === 0 || include.some((re) => re.test(relPath))
95
+
96
+ async function walk(dir: string, relative: string): Promise<void> {
97
+ let entries: import("fs").Dirent[]
98
+
99
+ try {
100
+ entries = await fs.readdir(dir, { withFileTypes: true })
101
+ } catch {
102
+ return
103
+ }
104
+
105
+ for (const entry of entries) {
106
+ const entryRelative = relative ? `${relative}/${entry.name}` : entry.name
107
+
108
+ if (entry.isDirectory()) {
109
+ if (entry.name === "node_modules" || isExcluded(entryRelative, true)) {
110
+ continue
111
+ }
112
+
113
+ if (entry.name === ".git") {
114
+ // Don't walk the object store; only surface HEAD, mirroring what
115
+ // the checkpoints caller's `**/.git/HEAD` pattern is looking for.
116
+ const headRelative = `${entryRelative}/HEAD`
117
+ const headAbsolute = path.join(dir, entry.name, "HEAD")
118
+
119
+ try {
120
+ const stat = await fs.stat(headAbsolute)
121
+
122
+ if (stat.isFile() && matchesInclude(headRelative) && !isExcluded(headRelative, false)) {
123
+ results.push({ path: headRelative, type: "file" })
124
+ }
125
+ } catch {
126
+ // No HEAD file — not a real git dir, nothing to report.
127
+ }
128
+
129
+ continue
130
+ }
131
+
132
+ await walk(path.join(dir, entry.name), entryRelative)
133
+ } else if (entry.isFile()) {
134
+ if (matchesInclude(entryRelative) && !isExcluded(entryRelative, false)) {
135
+ results.push({ path: entryRelative, type: "file" })
136
+ }
137
+ }
138
+ }
139
+ }
140
+
141
+ await walk(workspacePath, "")
142
+ return results
143
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * SHIM — replaces zoo-code/src/services/skills/SkillsManager.ts.
3
+ *
4
+ * The real SkillsManager scans global/project skills directories. The vendored
5
+ * `core/prompts/sections/skills.ts` only reads `getSkillsForMode(mode)`, which
6
+ * this stub satisfies with an empty list (no skills registered headlessly).
7
+ * Skill loading is out of scope for Phase 1.
8
+ */
9
+
10
+ export interface SkillLike {
11
+ name: string
12
+ description: string
13
+ path: string
14
+ }
15
+
16
+ export class SkillsManager {
17
+ getSkillsForMode(_mode: string): SkillLike[] {
18
+ return []
19
+ }
20
+ }
@@ -0,0 +1,9 @@
1
+ export const GlobalFileNames = {
2
+ apiConversationHistory: "api_conversation_history.json",
3
+ uiMessages: "ui_messages.json",
4
+ mcpSettings: "mcp_settings.json",
5
+ customModes: "custom_modes.yaml",
6
+ taskMetadata: "task_metadata.json",
7
+ historyItem: "history_item.json",
8
+ historyIndex: "_index.json",
9
+ }
@@ -0,0 +1,43 @@
1
+ import { type Language, isLanguage } from "../../types"
2
+
3
+ /**
4
+ * Language name mapping from ISO codes to full language names.
5
+ */
6
+
7
+ export const LANGUAGES: Record<Language, string> = {
8
+ ca: "Català",
9
+ de: "Deutsch",
10
+ en: "English",
11
+ es: "Español",
12
+ fr: "Français",
13
+ hi: "हिन्दी",
14
+ id: "Bahasa Indonesia",
15
+ it: "Italiano",
16
+ ja: "日本語",
17
+ ko: "한국어",
18
+ nl: "Nederlands",
19
+ pl: "Polski",
20
+ "pt-BR": "Português",
21
+ ru: "Русский",
22
+ tr: "Türkçe",
23
+ vi: "Tiếng Việt",
24
+ "zh-CN": "简体中文",
25
+ "zh-TW": "繁體中文",
26
+ }
27
+
28
+ /**
29
+ * Formats a VSCode locale string to ensure the region code is uppercase.
30
+ * For example, transforms "en-us" to "en-US" or "fr-ca" to "fr-CA".
31
+ *
32
+ * @param vscodeLocale - The VSCode locale string to format (e.g., "en-us", "fr-ca")
33
+ * @returns The formatted locale string with uppercase region code
34
+ */
35
+
36
+ export function formatLanguage(vscodeLocale: string): Language {
37
+ if (!vscodeLocale) {
38
+ return "en"
39
+ }
40
+
41
+ const formattedLocale = vscodeLocale.replace(/-(\w+)$/, (_, region) => `-${region.toUpperCase()}`)
42
+ return isLanguage(formattedLocale) ? formattedLocale : "en"
43
+ }