@soederpop/luca 0.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 (358) hide show
  1. package/CLAUDE.md +71 -0
  2. package/README.md +78 -0
  3. package/bun.lock +2928 -0
  4. package/bunfig.toml +3 -0
  5. package/commands/audit-docs.ts +740 -0
  6. package/commands/build-scaffolds.ts +154 -0
  7. package/commands/generate-api-docs.ts +114 -0
  8. package/commands/update-introspection.ts +67 -0
  9. package/docs/CLI.md +335 -0
  10. package/docs/README.md +88 -0
  11. package/docs/TABLE-OF-CONTENTS.md +157 -0
  12. package/docs/apis/clients/elevenlabs.md +84 -0
  13. package/docs/apis/clients/graph.md +56 -0
  14. package/docs/apis/clients/openai.md +69 -0
  15. package/docs/apis/clients/rest.md +41 -0
  16. package/docs/apis/clients/websocket.md +107 -0
  17. package/docs/apis/features/agi/assistant.md +471 -0
  18. package/docs/apis/features/agi/assistants-manager.md +154 -0
  19. package/docs/apis/features/agi/claude-code.md +602 -0
  20. package/docs/apis/features/agi/conversation-history.md +352 -0
  21. package/docs/apis/features/agi/conversation.md +333 -0
  22. package/docs/apis/features/agi/docs-reader.md +121 -0
  23. package/docs/apis/features/agi/openai-codex.md +318 -0
  24. package/docs/apis/features/agi/openapi.md +138 -0
  25. package/docs/apis/features/agi/semantic-search.md +387 -0
  26. package/docs/apis/features/agi/skills-library.md +216 -0
  27. package/docs/apis/features/node/container-link.md +133 -0
  28. package/docs/apis/features/node/content-db.md +313 -0
  29. package/docs/apis/features/node/disk-cache.md +379 -0
  30. package/docs/apis/features/node/dns.md +651 -0
  31. package/docs/apis/features/node/docker.md +705 -0
  32. package/docs/apis/features/node/downloader.md +81 -0
  33. package/docs/apis/features/node/esbuild.md +59 -0
  34. package/docs/apis/features/node/file-manager.md +182 -0
  35. package/docs/apis/features/node/fs.md +581 -0
  36. package/docs/apis/features/node/git.md +330 -0
  37. package/docs/apis/features/node/google-auth.md +174 -0
  38. package/docs/apis/features/node/google-calendar.md +187 -0
  39. package/docs/apis/features/node/google-docs.md +151 -0
  40. package/docs/apis/features/node/google-drive.md +225 -0
  41. package/docs/apis/features/node/google-sheets.md +179 -0
  42. package/docs/apis/features/node/grep.md +290 -0
  43. package/docs/apis/features/node/helpers.md +135 -0
  44. package/docs/apis/features/node/ink.md +334 -0
  45. package/docs/apis/features/node/ipc-socket.md +260 -0
  46. package/docs/apis/features/node/json-tree.md +86 -0
  47. package/docs/apis/features/node/launcher-app-command-listener.md +145 -0
  48. package/docs/apis/features/node/networking.md +281 -0
  49. package/docs/apis/features/node/nlp.md +133 -0
  50. package/docs/apis/features/node/opener.md +97 -0
  51. package/docs/apis/features/node/os.md +118 -0
  52. package/docs/apis/features/node/package-finder.md +402 -0
  53. package/docs/apis/features/node/postgres.md +212 -0
  54. package/docs/apis/features/node/proc.md +430 -0
  55. package/docs/apis/features/node/process-manager.md +210 -0
  56. package/docs/apis/features/node/python.md +278 -0
  57. package/docs/apis/features/node/repl.md +88 -0
  58. package/docs/apis/features/node/runpod.md +673 -0
  59. package/docs/apis/features/node/secure-shell.md +169 -0
  60. package/docs/apis/features/node/semantic-search.md +401 -0
  61. package/docs/apis/features/node/sqlite.md +211 -0
  62. package/docs/apis/features/node/telegram.md +254 -0
  63. package/docs/apis/features/node/tts.md +118 -0
  64. package/docs/apis/features/node/ui.md +703 -0
  65. package/docs/apis/features/node/vault.md +64 -0
  66. package/docs/apis/features/node/vm.md +84 -0
  67. package/docs/apis/features/node/window-manager.md +337 -0
  68. package/docs/apis/features/node/yaml-tree.md +85 -0
  69. package/docs/apis/features/node/yaml.md +176 -0
  70. package/docs/apis/features/web/asset-loader.md +47 -0
  71. package/docs/apis/features/web/container-link.md +133 -0
  72. package/docs/apis/features/web/esbuild.md +59 -0
  73. package/docs/apis/features/web/helpers.md +135 -0
  74. package/docs/apis/features/web/network.md +30 -0
  75. package/docs/apis/features/web/speech.md +55 -0
  76. package/docs/apis/features/web/vault.md +64 -0
  77. package/docs/apis/features/web/vm.md +84 -0
  78. package/docs/apis/features/web/voice.md +67 -0
  79. package/docs/apis/servers/express.md +127 -0
  80. package/docs/apis/servers/mcp.md +213 -0
  81. package/docs/apis/servers/websocket.md +99 -0
  82. package/docs/documentation-audit.md +134 -0
  83. package/docs/examples/content-db.md +77 -0
  84. package/docs/examples/disk-cache.md +83 -0
  85. package/docs/examples/docker.md +101 -0
  86. package/docs/examples/downloader.md +70 -0
  87. package/docs/examples/esbuild.md +80 -0
  88. package/docs/examples/file-manager.md +82 -0
  89. package/docs/examples/fs.md +83 -0
  90. package/docs/examples/git.md +85 -0
  91. package/docs/examples/google-auth.md +88 -0
  92. package/docs/examples/google-calendar.md +94 -0
  93. package/docs/examples/google-docs.md +82 -0
  94. package/docs/examples/google-drive.md +96 -0
  95. package/docs/examples/google-sheets.md +95 -0
  96. package/docs/examples/grep.md +85 -0
  97. package/docs/examples/ink-blocks.md +75 -0
  98. package/docs/examples/ink-renderer.md +41 -0
  99. package/docs/examples/ink.md +103 -0
  100. package/docs/examples/ipc-socket.md +103 -0
  101. package/docs/examples/json-tree.md +91 -0
  102. package/docs/examples/launcher-app-command-listener.md +120 -0
  103. package/docs/examples/networking.md +58 -0
  104. package/docs/examples/nlp.md +91 -0
  105. package/docs/examples/opener.md +78 -0
  106. package/docs/examples/os.md +72 -0
  107. package/docs/examples/package-finder.md +89 -0
  108. package/docs/examples/port-exposer.md +89 -0
  109. package/docs/examples/postgres.md +91 -0
  110. package/docs/examples/proc.md +81 -0
  111. package/docs/examples/process-manager.md +79 -0
  112. package/docs/examples/python.md +91 -0
  113. package/docs/examples/repl.md +93 -0
  114. package/docs/examples/runpod.md +119 -0
  115. package/docs/examples/secure-shell.md +92 -0
  116. package/docs/examples/sqlite.md +86 -0
  117. package/docs/examples/telegram.md +77 -0
  118. package/docs/examples/tts.md +86 -0
  119. package/docs/examples/ui.md +80 -0
  120. package/docs/examples/vault.md +70 -0
  121. package/docs/examples/vm.md +86 -0
  122. package/docs/examples/window-manager.md +125 -0
  123. package/docs/examples/yaml-tree.md +93 -0
  124. package/docs/examples/yaml.md +104 -0
  125. package/docs/ideas/class-registration-refactor-possibilities.md +197 -0
  126. package/docs/ideas/container-use-api.md +9 -0
  127. package/docs/ideas/easy-auth-for-express-servers-and-luca-serve.md +0 -0
  128. package/docs/ideas/feature-stacks.md +22 -0
  129. package/docs/ideas/luca-cli-self-sufficiency-demo.md +23 -0
  130. package/docs/ideas/mcp-design.md +9 -0
  131. package/docs/ideas/web-container-debugging-feature.md +13 -0
  132. package/docs/introspection-audit.md +49 -0
  133. package/docs/introspection.md +154 -0
  134. package/docs/mcp/readme.md +162 -0
  135. package/docs/models.ts +38 -0
  136. package/docs/philosophy.md +85 -0
  137. package/docs/principles.md +7 -0
  138. package/docs/prompts/audit-codebase-for-failures-to-use-the-container.md +34 -0
  139. package/docs/prompts/mcp-test-easy-command.md +27 -0
  140. package/docs/reports/assistant-bugs.md +38 -0
  141. package/docs/reports/attach-pattern-usage.md +18 -0
  142. package/docs/reports/code-audit-results.md +391 -0
  143. package/docs/reports/introspection-audit-tasks.md +378 -0
  144. package/docs/reports/luca-mcp-improvements.md +128 -0
  145. package/docs/scaffolds/client.md +140 -0
  146. package/docs/scaffolds/command.md +106 -0
  147. package/docs/scaffolds/endpoint.md +176 -0
  148. package/docs/scaffolds/feature.md +148 -0
  149. package/docs/scaffolds/server.md +187 -0
  150. package/docs/tasks/web-container-helper-discovery.md +71 -0
  151. package/docs/todos.md +1 -0
  152. package/docs/tutorials/01-getting-started.md +106 -0
  153. package/docs/tutorials/02-container.md +210 -0
  154. package/docs/tutorials/03-scripts.md +194 -0
  155. package/docs/tutorials/04-features-overview.md +196 -0
  156. package/docs/tutorials/05-state-and-events.md +171 -0
  157. package/docs/tutorials/06-servers.md +157 -0
  158. package/docs/tutorials/07-endpoints.md +198 -0
  159. package/docs/tutorials/08-commands.md +171 -0
  160. package/docs/tutorials/09-clients.md +162 -0
  161. package/docs/tutorials/10-creating-features.md +198 -0
  162. package/docs/tutorials/11-contentbase.md +191 -0
  163. package/docs/tutorials/12-assistants.md +215 -0
  164. package/docs/tutorials/13-introspection.md +147 -0
  165. package/docs/tutorials/14-type-system.md +174 -0
  166. package/docs/tutorials/15-project-patterns.md +222 -0
  167. package/docs/tutorials/16-google-features.md +534 -0
  168. package/docs/tutorials/17-tui-blocks.md +530 -0
  169. package/docs/tutorials/18-semantic-search.md +334 -0
  170. package/index.ts +1 -0
  171. package/luca.console.ts +9 -0
  172. package/main.py +6 -0
  173. package/package.json +154 -0
  174. package/pyproject.toml +7 -0
  175. package/scripts/animations/chrome-glitch.ts +55 -0
  176. package/scripts/animations/index.ts +16 -0
  177. package/scripts/animations/neon-pulse.ts +64 -0
  178. package/scripts/animations/types.ts +6 -0
  179. package/scripts/build-web.ts +28 -0
  180. package/scripts/examples/ask-luca-expert.ts +42 -0
  181. package/scripts/examples/assistant-questions.ts +12 -0
  182. package/scripts/examples/excalidraw-expert.ts +75 -0
  183. package/scripts/examples/expert-chat.ts +0 -0
  184. package/scripts/examples/file-manager.ts +14 -0
  185. package/scripts/examples/ideas.ts +12 -0
  186. package/scripts/examples/interactive-chat.ts +20 -0
  187. package/scripts/examples/openai-tool-calls.ts +113 -0
  188. package/scripts/examples/opening-a-web-browser.ts +5 -0
  189. package/scripts/examples/telegram-bot.ts +79 -0
  190. package/scripts/examples/telegram-ink-ui.ts +302 -0
  191. package/scripts/examples/using-assistant-with-mcp.ts +560 -0
  192. package/scripts/examples/using-claude-code.ts +10 -0
  193. package/scripts/examples/using-contentdb.ts +35 -0
  194. package/scripts/examples/using-conversations.ts +35 -0
  195. package/scripts/examples/using-disk-cache.ts +10 -0
  196. package/scripts/examples/using-docker-shell.ts +75 -0
  197. package/scripts/examples/using-elevenlabs.ts +25 -0
  198. package/scripts/examples/using-google-calendar.ts +57 -0
  199. package/scripts/examples/using-google-docs.ts +74 -0
  200. package/scripts/examples/using-google-drive.ts +74 -0
  201. package/scripts/examples/using-google-sheets.ts +89 -0
  202. package/scripts/examples/using-nlp.ts +55 -0
  203. package/scripts/examples/using-ollama.ts +10 -0
  204. package/scripts/examples/using-openai-codex.ts +23 -0
  205. package/scripts/examples/using-postgres.ts +55 -0
  206. package/scripts/examples/using-runpod.ts +32 -0
  207. package/scripts/examples/using-tts.ts +40 -0
  208. package/scripts/examples/vm-loading-esm-modules.ts +16 -0
  209. package/scripts/scaffold.ts +391 -0
  210. package/scripts/scratch.ts +15 -0
  211. package/scripts/test-command-listener.ts +123 -0
  212. package/scripts/test-window-manager-lifecycle.ts +86 -0
  213. package/scripts/test-window-manager.ts +43 -0
  214. package/scripts/update-introspection-data.ts +58 -0
  215. package/src/agi/README.md +14 -0
  216. package/src/agi/container.server.ts +114 -0
  217. package/src/agi/endpoints/ask.ts +60 -0
  218. package/src/agi/endpoints/conversations/[id].ts +45 -0
  219. package/src/agi/endpoints/conversations.ts +31 -0
  220. package/src/agi/endpoints/experts.ts +37 -0
  221. package/src/agi/features/assistant.ts +767 -0
  222. package/src/agi/features/assistants-manager.ts +260 -0
  223. package/src/agi/features/claude-code.ts +1111 -0
  224. package/src/agi/features/conversation-history.ts +497 -0
  225. package/src/agi/features/conversation.ts +799 -0
  226. package/src/agi/features/openai-codex.ts +631 -0
  227. package/src/agi/features/openapi.ts +438 -0
  228. package/src/agi/features/skills-library.ts +425 -0
  229. package/src/agi/index.ts +6 -0
  230. package/src/agi/lib/token-counter.ts +122 -0
  231. package/src/browser.ts +25 -0
  232. package/src/bus.ts +100 -0
  233. package/src/cli/cli.ts +70 -0
  234. package/src/client.ts +461 -0
  235. package/src/clients/civitai/index.ts +541 -0
  236. package/src/clients/client-template.ts +41 -0
  237. package/src/clients/comfyui/index.ts +597 -0
  238. package/src/clients/elevenlabs/index.ts +291 -0
  239. package/src/clients/openai/index.ts +451 -0
  240. package/src/clients/supabase/index.ts +366 -0
  241. package/src/command.ts +164 -0
  242. package/src/commands/chat.ts +182 -0
  243. package/src/commands/console.ts +192 -0
  244. package/src/commands/describe.ts +433 -0
  245. package/src/commands/eval.ts +116 -0
  246. package/src/commands/help.ts +214 -0
  247. package/src/commands/index.ts +14 -0
  248. package/src/commands/mcp.ts +64 -0
  249. package/src/commands/prompt.ts +807 -0
  250. package/src/commands/run.ts +257 -0
  251. package/src/commands/sandbox-mcp.ts +439 -0
  252. package/src/commands/scaffold.ts +79 -0
  253. package/src/commands/serve.ts +172 -0
  254. package/src/container.ts +781 -0
  255. package/src/endpoint.ts +340 -0
  256. package/src/feature.ts +75 -0
  257. package/src/hash-object.ts +97 -0
  258. package/src/helper.ts +543 -0
  259. package/src/introspection/generated.agi.ts +23388 -0
  260. package/src/introspection/generated.node.ts +18899 -0
  261. package/src/introspection/generated.web.ts +2021 -0
  262. package/src/introspection/index.ts +256 -0
  263. package/src/introspection/scan.ts +912 -0
  264. package/src/node/container.ts +354 -0
  265. package/src/node/feature.ts +13 -0
  266. package/src/node/features/container-link.ts +558 -0
  267. package/src/node/features/content-db.ts +475 -0
  268. package/src/node/features/disk-cache.ts +382 -0
  269. package/src/node/features/dns.ts +655 -0
  270. package/src/node/features/docker.ts +912 -0
  271. package/src/node/features/downloader.ts +92 -0
  272. package/src/node/features/esbuild.ts +68 -0
  273. package/src/node/features/file-manager.ts +357 -0
  274. package/src/node/features/fs.ts +534 -0
  275. package/src/node/features/git.ts +492 -0
  276. package/src/node/features/google-auth.ts +502 -0
  277. package/src/node/features/google-calendar.ts +300 -0
  278. package/src/node/features/google-docs.ts +404 -0
  279. package/src/node/features/google-drive.ts +339 -0
  280. package/src/node/features/google-sheets.ts +279 -0
  281. package/src/node/features/grep.ts +406 -0
  282. package/src/node/features/helpers.ts +374 -0
  283. package/src/node/features/ink.ts +490 -0
  284. package/src/node/features/ipc-socket.ts +459 -0
  285. package/src/node/features/json-tree.ts +188 -0
  286. package/src/node/features/launcher-app-command-listener.ts +388 -0
  287. package/src/node/features/networking.ts +925 -0
  288. package/src/node/features/nlp.ts +211 -0
  289. package/src/node/features/opener.ts +166 -0
  290. package/src/node/features/os.ts +157 -0
  291. package/src/node/features/package-finder.ts +539 -0
  292. package/src/node/features/port-exposer.ts +342 -0
  293. package/src/node/features/postgres.ts +273 -0
  294. package/src/node/features/proc.ts +502 -0
  295. package/src/node/features/process-manager.ts +542 -0
  296. package/src/node/features/python.ts +444 -0
  297. package/src/node/features/repl.ts +194 -0
  298. package/src/node/features/runpod.ts +802 -0
  299. package/src/node/features/secure-shell.ts +248 -0
  300. package/src/node/features/semantic-search.ts +924 -0
  301. package/src/node/features/sqlite.ts +289 -0
  302. package/src/node/features/telegram.ts +342 -0
  303. package/src/node/features/tts.ts +184 -0
  304. package/src/node/features/ui.ts +857 -0
  305. package/src/node/features/vault.ts +164 -0
  306. package/src/node/features/vm.ts +312 -0
  307. package/src/node/features/window-manager.ts +804 -0
  308. package/src/node/features/yaml-tree.ts +149 -0
  309. package/src/node/features/yaml.ts +132 -0
  310. package/src/node.ts +70 -0
  311. package/src/react/index.ts +175 -0
  312. package/src/registry.ts +199 -0
  313. package/src/scaffolds/generated.ts +1613 -0
  314. package/src/scaffolds/template.ts +37 -0
  315. package/src/schemas/base.ts +255 -0
  316. package/src/server.ts +135 -0
  317. package/src/servers/express.ts +209 -0
  318. package/src/servers/mcp.ts +805 -0
  319. package/src/servers/socket.ts +120 -0
  320. package/src/state.ts +101 -0
  321. package/src/web/clients/socket.ts +82 -0
  322. package/src/web/container.ts +74 -0
  323. package/src/web/extension.ts +30 -0
  324. package/src/web/feature.ts +12 -0
  325. package/src/web/features/asset-loader.ts +64 -0
  326. package/src/web/features/container-link.ts +385 -0
  327. package/src/web/features/esbuild.ts +79 -0
  328. package/src/web/features/helpers.ts +267 -0
  329. package/src/web/features/network.ts +61 -0
  330. package/src/web/features/speech.ts +87 -0
  331. package/src/web/features/vault.ts +189 -0
  332. package/src/web/features/vm.ts +78 -0
  333. package/src/web/features/voice-recognition.ts +129 -0
  334. package/src/web/shims/isomorphic-vm.ts +149 -0
  335. package/test/bus.test.ts +134 -0
  336. package/test/clients-servers.test.ts +216 -0
  337. package/test/container-link.test.ts +274 -0
  338. package/test/features.test.ts +160 -0
  339. package/test/integration.test.ts +787 -0
  340. package/test/node-container.test.ts +121 -0
  341. package/test/rate-limit.test.ts +272 -0
  342. package/test/semantic-search.test.ts +550 -0
  343. package/test/state.test.ts +121 -0
  344. package/test-integration/assistant.test.ts +138 -0
  345. package/test-integration/assistants-manager.test.ts +123 -0
  346. package/test-integration/claude-code.test.ts +98 -0
  347. package/test-integration/conversation-history.test.ts +205 -0
  348. package/test-integration/conversation.test.ts +137 -0
  349. package/test-integration/elevenlabs.test.ts +55 -0
  350. package/test-integration/google-services.test.ts +80 -0
  351. package/test-integration/helpers.ts +89 -0
  352. package/test-integration/openai-codex.test.ts +93 -0
  353. package/test-integration/runpod.test.ts +58 -0
  354. package/test-integration/server-endpoints.test.ts +97 -0
  355. package/test-integration/skills-library.test.ts +157 -0
  356. package/test-integration/telegram.test.ts +46 -0
  357. package/tsconfig.json +58 -0
  358. package/uv.lock +8 -0
@@ -0,0 +1,492 @@
1
+ import { z } from 'zod'
2
+ import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
+ import { dirname, resolve, isAbsolute } from 'path';
4
+ import { State } from '../../state.js';
5
+ import { features, Feature } from '../feature.js'
6
+
7
+ type LsFilesOptions = {
8
+ cached?: boolean;
9
+ deleted?: boolean;
10
+ modified?: boolean;
11
+ others?: boolean;
12
+ ignored?: boolean;
13
+ status?: boolean;
14
+ includeIgnored?: boolean;
15
+ exclude?: string | string[];
16
+ baseDir?: string;
17
+ }
18
+
19
+ const GitStateSchema = FeatureStateSchema.extend({
20
+ /** Absolute path to the Git repository root directory */
21
+ repoRoot: z.string().optional().describe('Absolute path to the Git repository root directory'),
22
+ })
23
+
24
+ type GitState = z.infer<typeof GitStateSchema>
25
+
26
+ /**
27
+ * The Git feature provides utilities for interacting with Git repositories.
28
+ *
29
+ * This feature allows you to check repository status, list files, get branch information,
30
+ * and access Git metadata for projects within a Git repository.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const git = container.feature('git')
35
+ *
36
+ * if (git.isRepo) {
37
+ * console.log(`Current branch: ${git.branch}`)
38
+ * console.log(`Repository root: ${git.repoRoot}`)
39
+ *
40
+ * const allFiles = await git.lsFiles()
41
+ * const modifiedFiles = await git.lsFiles({ modified: true })
42
+ * }
43
+ * ```
44
+ *
45
+ * @extends Feature
46
+ */
47
+ export class Git extends Feature {
48
+ static override shortcut = 'features.git' as const
49
+ static override stateSchema = GitStateSchema
50
+ static override optionsSchema = FeatureOptionsSchema
51
+ override state: State<GitState> = new State()
52
+
53
+ /**
54
+ * Lists files in the Git repository using git ls-files command.
55
+ *
56
+ * This method provides a flexible interface to the git ls-files command,
57
+ * allowing you to filter files by various criteria such as cached, deleted,
58
+ * modified, untracked, and ignored files.
59
+ *
60
+ * @param {LsFilesOptions} [options={}] - Options to control which files are listed
61
+ * @param {boolean} [options.cached=false] - Show cached/staged files
62
+ * @param {boolean} [options.deleted=false] - Show deleted files
63
+ * @param {boolean} [options.modified=false] - Show modified files
64
+ * @param {boolean} [options.others=false] - Show untracked files
65
+ * @param {boolean} [options.ignored=false] - Show ignored files
66
+ * @param {boolean} [options.status=false] - Show file status information
67
+ * @param {boolean} [options.includeIgnored=false] - Include ignored files when showing others
68
+ * @param {string | string[]} [options.exclude] - Patterns to exclude from results
69
+ * @param {string} [options.baseDir=''] - Base directory to list files from
70
+ * @returns {Promise<string[]>} Promise that resolves to an array of file paths
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * // Get all tracked files
75
+ * const allFiles = await git.lsFiles()
76
+ *
77
+ * // Get only modified files
78
+ * const modified = await git.lsFiles({ modified: true })
79
+ *
80
+ * // Get untracked files excluding certain patterns
81
+ * const untracked = await git.lsFiles({
82
+ * others: true,
83
+ * exclude: ['*.log', 'node_modules']
84
+ * })
85
+ * ```
86
+ */
87
+ async lsFiles(options: LsFilesOptions = {}) {
88
+ const {
89
+ cached = false,
90
+ deleted = false,
91
+ modified = false,
92
+ others = false,
93
+ ignored = false,
94
+ status = false,
95
+ baseDir = '',
96
+ includeIgnored = false
97
+ } = options || {}
98
+
99
+ const exclude = Array.isArray(options.exclude) ? options.exclude : [options.exclude || ''].filter(v => v?.length)
100
+
101
+ const flags = [
102
+ cached ? '--cached' : '',
103
+ deleted ? '--deleted' : '',
104
+ modified ? '--modified' : '',
105
+ others ? '--others' : '',
106
+ ignored ? '--ignored' : '',
107
+ status ? '-t' : '',
108
+ ].filter(v => v?.length).flat()
109
+
110
+ const gitIgnorePath = this.container.fs.findUp('.gitignore', { cwd: this.container.cwd })
111
+
112
+ if (others && exclude.length) {
113
+ flags.push(
114
+ ...exclude.map((p:string) =>['--exclude', p]).flat()
115
+ )
116
+ }
117
+
118
+ if (others && gitIgnorePath && !includeIgnored) {
119
+ flags.push(...['--exclude-from', gitIgnorePath])
120
+ }
121
+
122
+
123
+ return this.container.feature('proc').exec(`git ls-files ${baseDir} ${flags.join(' ')}`, {
124
+ cwd: this.repoRoot,
125
+ maxBuffer: 1024 * 1024 * 100,
126
+ }).trim().split("\n")
127
+ }
128
+
129
+ /**
130
+ * Gets the current Git branch name.
131
+ *
132
+ * @returns {string | null} The current branch name, or null if not in a Git repository
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const currentBranch = git.branch
137
+ * if (currentBranch) {
138
+ * console.log(`Currently on branch: ${currentBranch}`)
139
+ * }
140
+ * ```
141
+ */
142
+ get branch() {
143
+ if(!this.isRepo) { return null }
144
+ return this.container.feature('proc').exec('git branch').split("\n").filter(line => line.startsWith('*')).map(line => line.replace('*', '').trim()).pop()
145
+ }
146
+
147
+ /**
148
+ * Gets the current Git commit SHA hash.
149
+ *
150
+ * @returns {string | null} The current commit SHA, or null if not in a Git repository
151
+ *
152
+ * @example
153
+ * ```typescript
154
+ * const commitSha = git.sha
155
+ * if (commitSha) {
156
+ * console.log(`Current commit: ${commitSha}`)
157
+ * }
158
+ * ```
159
+ */
160
+ get sha() {
161
+ if(!this.isRepo) { return null }
162
+ return this.container.feature('proc').exec('git rev-parse HEAD', { cwd: this.repoRoot })
163
+ }
164
+
165
+ /**
166
+ * Checks if the current directory is within a Git repository.
167
+ *
168
+ * @returns {boolean} True if currently in a Git repository, false otherwise
169
+ *
170
+ * @example
171
+ * ```typescript
172
+ * if (git.isRepo) {
173
+ * console.log('This is a Git repository!')
174
+ * } else {
175
+ * console.log('Not in a Git repository')
176
+ * }
177
+ * ```
178
+ */
179
+ get isRepo() {
180
+ return !!this.repoRoot
181
+ }
182
+
183
+ /**
184
+ * Checks if the current working directory is the root of the Git repository.
185
+ *
186
+ * @returns {boolean} True if currently at the repository root, false otherwise
187
+ *
188
+ * @example
189
+ * ```typescript
190
+ * if (git.isRepoRoot) {
191
+ * console.log('At the repository root')
192
+ * } else {
193
+ * console.log('In a subdirectory of the repository')
194
+ * }
195
+ * ```
196
+ */
197
+ get isRepoRoot() {
198
+ return this.repoRoot == this.container.cwd
199
+ }
200
+
201
+ /**
202
+ * Gets the absolute path to the Git repository root directory.
203
+ *
204
+ * This method caches the repository root path for performance. It searches upward
205
+ * from the current directory to find the .git directory.
206
+ *
207
+ * @returns {string | null} The absolute path to the repository root, or null if not in a Git repository
208
+ *
209
+ * @example
210
+ * ```typescript
211
+ * const repoRoot = git.repoRoot
212
+ * if (repoRoot) {
213
+ * console.log(`Repository root: ${repoRoot}`)
214
+ * }
215
+ * ```
216
+ */
217
+ get repoRoot() {
218
+ if (this.state.has('repoRoot')) {
219
+ return this.state.get('repoRoot')
220
+ }
221
+
222
+ const repoRoot = this.container.fs.findUp('.git')
223
+
224
+ if(typeof repoRoot === 'string') {
225
+ this.state.set('repoRoot', dirname(repoRoot))
226
+ return dirname(repoRoot)
227
+ }
228
+
229
+ return null
230
+ }
231
+
232
+ /**
233
+ * Gets the latest commits from the repository.
234
+ *
235
+ * Returns an array of commit objects containing the title (first line of commit message),
236
+ * full message body, and author name for each commit.
237
+ *
238
+ * @param {number} [numberOfChanges=10] - The number of recent commits to return
239
+ * @returns {Promise<Array<{ title: string, message: string, author: string }>>} Array of commit objects
240
+ *
241
+ * @example
242
+ * ```typescript
243
+ * const changes = await git.getLatestChanges(5)
244
+ * for (const commit of changes) {
245
+ * console.log(`${commit.author}: ${commit.title}`)
246
+ * }
247
+ * ```
248
+ */
249
+ async getLatestChanges(numberOfChanges: number = 10) {
250
+ if (!this.isRepo) return []
251
+
252
+ const separator = '---COMMIT---'
253
+ const fieldSep = '---FIELD---'
254
+
255
+ const output = this.container.feature('proc').exec(
256
+ `git log -n ${numberOfChanges} --pretty=format:"%s${fieldSep}%b${fieldSep}%an${separator}"`,
257
+ { cwd: this.repoRoot }
258
+ )
259
+
260
+ return output
261
+ .split(separator)
262
+ .filter((entry: string) => entry.trim().length > 0)
263
+ .map((entry: string) => {
264
+ const [title = '', message = '', author = ''] = entry.split(fieldSep).map((s: string) => s.trim())
265
+ return { title, message, author }
266
+ })
267
+ }
268
+
269
+ /**
270
+ * Gets a lightweight commit log for one or more files.
271
+ *
272
+ * Returns the SHA and message for each commit that touched the given files,
273
+ * without the per-commit overhead of resolving which specific files matched.
274
+ * For richer per-file matching, see {@link getChangeHistoryForFiles}.
275
+ *
276
+ * @param {...string} files - File paths (absolute or relative to container.cwd)
277
+ * @returns {Array<{ sha: string, message: string }>} Array of commits
278
+ *
279
+ * @example
280
+ * ```typescript
281
+ * const log = git.fileLog('package.json')
282
+ * const log = git.fileLog('src/index.ts', 'src/helper.ts')
283
+ * for (const entry of log) {
284
+ * console.log(`${entry.sha.slice(0, 8)} ${entry.message}`)
285
+ * }
286
+ * ```
287
+ */
288
+ fileLog(...files: string[]) {
289
+ if (!this.isRepo || !files.length) return []
290
+
291
+ const proc = this.container.feature('proc')
292
+ const root = this.repoRoot!
293
+
294
+ const resolved = files.map(p =>
295
+ isAbsolute(p) ? p : resolve(this.container.cwd, p)
296
+ )
297
+
298
+ const separator = '---COMMIT---'
299
+ const fieldSep = '---FIELD---'
300
+
301
+ const output = proc.exec(
302
+ `git log --pretty=format:"%H${fieldSep}%s${separator}" -- ${resolved.map(p => `"${p}"`).join(' ')}`,
303
+ { cwd: root }
304
+ )
305
+
306
+ if (!output.trim()) return []
307
+
308
+ return output
309
+ .split(separator)
310
+ .filter((entry: string) => entry.trim().length > 0)
311
+ .map((entry: string) => {
312
+ const [sha = '', message = ''] = entry.split(fieldSep).map((s: string) => s.trim())
313
+ return { sha, message }
314
+ })
315
+ }
316
+
317
+ /**
318
+ * Gets the diff for a file between two refs.
319
+ *
320
+ * By default compares from the current HEAD to the given ref. You can
321
+ * supply both `compareTo` and `compareFrom` to diff between any two commits,
322
+ * branches, or tags.
323
+ *
324
+ * @param {string} file - File path (absolute or relative to container.cwd)
325
+ * @param {string} compareTo - The target ref (commit SHA, branch, tag) to compare to
326
+ * @param {string} [compareFrom] - The base ref to compare from (defaults to current HEAD)
327
+ * @returns {string} The diff output, or an empty string if there are no changes
328
+ *
329
+ * @example
330
+ * ```typescript
331
+ * // Diff package.json between HEAD and a specific commit
332
+ * const d = git.diff('package.json', 'abc1234')
333
+ *
334
+ * // Diff between two branches
335
+ * const d = git.diff('src/index.ts', 'feature-branch', 'main')
336
+ * ```
337
+ */
338
+ diff(file: string, compareTo: string, compareFrom?: string) {
339
+ if (!this.isRepo) return ''
340
+
341
+ const proc = this.container.feature('proc')
342
+ const root = this.repoRoot!
343
+ const from = compareFrom ?? this.sha!
344
+ const resolved = isAbsolute(file) ? file : resolve(this.container.cwd, file)
345
+
346
+ return proc.exec(
347
+ `git diff ${from} ${compareTo} -- "${resolved}"`,
348
+ { cwd: root }
349
+ ).trim()
350
+ }
351
+
352
+ /**
353
+ * Pretty prints a unified diff string to the terminal using colors.
354
+ *
355
+ * Parses the diff output and applies color coding:
356
+ * - File headers (`diff --git`, `---`, `+++`) are rendered bold
357
+ * - Hunk headers (`@@ ... @@`) are rendered in cyan
358
+ * - Added lines (`+`) are rendered in green
359
+ * - Removed lines (`-`) are rendered in red
360
+ * - Context lines are rendered dim
361
+ *
362
+ * Can be called with a raw diff string, or with the same arguments as
363
+ * {@link diff} to fetch and display in one step.
364
+ *
365
+ * @param diffOrFile - A raw diff string, or a file path to pass to {@link diff}
366
+ * @param compareTo - When diffOrFile is a file path, the target ref to compare to
367
+ * @param compareFrom - When diffOrFile is a file path, the base ref to compare from
368
+ * @returns The colorized diff string (also prints to stdout)
369
+ *
370
+ * @example
371
+ * ```typescript
372
+ * // Display a pre-fetched diff
373
+ * const raw = git.diff('src/index.ts', 'main')
374
+ * git.displayDiff(raw)
375
+ *
376
+ * // Fetch and display in one call
377
+ * git.displayDiff('src/index.ts', 'abc1234')
378
+ * ```
379
+ */
380
+ displayDiff(diffOrFile: string, compareTo?: string, compareFrom?: string): string {
381
+ const raw = compareTo
382
+ ? this.diff(diffOrFile, compareTo, compareFrom)
383
+ : diffOrFile
384
+
385
+ if (!raw.trim()) return ''
386
+
387
+ const { colors } = this.container.feature('ui')
388
+
389
+ const lines = raw.split('\n')
390
+ const colored = lines.map(line => {
391
+ if (line.startsWith('diff --git') || line.startsWith('index ')) {
392
+ return colors.bold(line)
393
+ }
394
+ if (line.startsWith('--- ') || line.startsWith('+++ ')) {
395
+ return colors.bold(line)
396
+ }
397
+ if (line.startsWith('@@')) {
398
+ return colors.cyan(line)
399
+ }
400
+ if (line.startsWith('+')) {
401
+ return colors.green(line)
402
+ }
403
+ if (line.startsWith('-')) {
404
+ return colors.red(line)
405
+ }
406
+ return colors.dim(line)
407
+ })
408
+
409
+ const output = colored.join('\n')
410
+ console.log(output)
411
+ return output
412
+ }
413
+
414
+ /**
415
+ * Gets the commit history for a set of files or glob patterns.
416
+ *
417
+ * Accepts absolute paths, relative paths (resolved from container.cwd),
418
+ * or glob patterns. Returns commits that touched any of the matched files,
419
+ * with each entry noting which of your queried files were in that commit.
420
+ *
421
+ * @param {...string} paths - File paths or glob patterns to get history for
422
+ * @returns {Array<{ sha: string, message: string, longMessage: string, filesMatched: string[] }>}
423
+ *
424
+ * @example
425
+ * ```typescript
426
+ * const history = git.getChangeHistoryForFiles('src/container.ts', 'src/helper.ts')
427
+ * const history = git.getChangeHistoryForFiles('src/node/features/*.ts')
428
+ * ```
429
+ */
430
+ getChangeHistoryForFiles(...paths: string[]) {
431
+ if (!this.isRepo || !paths.length) return []
432
+
433
+ const proc = this.container.feature('proc')
434
+ const root = this.repoRoot!
435
+
436
+ // resolve each path relative to cwd (globs stay as-is for git, but we resolve for matching)
437
+ const resolved = paths.map(p =>
438
+ isAbsolute(p) ? p : resolve(this.container.cwd, p)
439
+ )
440
+
441
+ const separator = '---COMMIT---'
442
+ const fieldSep = '---FIELD---'
443
+
444
+ const output = proc.exec(
445
+ `git log --pretty=format:"%H${fieldSep}%s${fieldSep}%b${separator}" -- ${resolved.map(p => `"${p}"`).join(' ')}`,
446
+ { cwd: root }
447
+ )
448
+
449
+ if (!output.trim()) return []
450
+
451
+ const commits = output
452
+ .split(separator)
453
+ .filter((entry: string) => entry.trim().length > 0)
454
+ .map((entry: string) => {
455
+ const [sha = '', message = '', longMessage = ''] = entry.split(fieldSep).map((s: string) => s.trim())
456
+ return { sha, message, longMessage }
457
+ })
458
+
459
+ // build matchers: Bun.Glob for patterns with wildcards, exact match otherwise
460
+ const matchers = resolved.map(p => {
461
+ const hasGlob = /[*?{}\[\]]/.test(p)
462
+ return {
463
+ original: p,
464
+ match: hasGlob
465
+ ? (file: string) => new Bun.Glob(p).match(file)
466
+ : (file: string) => file === p,
467
+ }
468
+ })
469
+
470
+ return commits.map(commit => {
471
+ const changedFiles = proc.exec(
472
+ `git diff-tree --no-commit-id --name-only -r ${commit.sha}`,
473
+ { cwd: root }
474
+ ).trim().split('\n').filter(Boolean)
475
+
476
+ const changedAbsolute = changedFiles.map(f => resolve(root, f))
477
+
478
+ const filesMatched = matchers
479
+ .filter(m => changedAbsolute.some(f => m.match(f)))
480
+ .map(m => m.original)
481
+
482
+ return {
483
+ sha: commit.sha,
484
+ message: commit.message,
485
+ longMessage: commit.longMessage,
486
+ filesMatched,
487
+ }
488
+ })
489
+ }
490
+ }
491
+
492
+ export default features.register('git', Git)