@build-astron-co/nimbus 0.2.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 (313) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +628 -0
  3. package/bin/nimbus +38 -0
  4. package/package.json +80 -0
  5. package/src/__tests__/app.test.ts +76 -0
  6. package/src/__tests__/audit.test.ts +877 -0
  7. package/src/__tests__/circuit-breaker.test.ts +116 -0
  8. package/src/__tests__/cli-run.test.ts +115 -0
  9. package/src/__tests__/context-manager.test.ts +502 -0
  10. package/src/__tests__/context.test.ts +242 -0
  11. package/src/__tests__/enterprise.test.ts +401 -0
  12. package/src/__tests__/generator.test.ts +433 -0
  13. package/src/__tests__/hooks.test.ts +582 -0
  14. package/src/__tests__/init.test.ts +436 -0
  15. package/src/__tests__/intent-parser.test.ts +229 -0
  16. package/src/__tests__/llm-router.test.ts +209 -0
  17. package/src/__tests__/lsp.test.ts +293 -0
  18. package/src/__tests__/modes.test.ts +336 -0
  19. package/src/__tests__/permissions.test.ts +338 -0
  20. package/src/__tests__/serve.test.ts +275 -0
  21. package/src/__tests__/sessions.test.ts +227 -0
  22. package/src/__tests__/sharing.test.ts +288 -0
  23. package/src/__tests__/snapshots.test.ts +581 -0
  24. package/src/__tests__/state-db.test.ts +334 -0
  25. package/src/__tests__/stream-with-tools.test.ts +732 -0
  26. package/src/__tests__/subagents.test.ts +176 -0
  27. package/src/__tests__/system-prompt.test.ts +169 -0
  28. package/src/__tests__/tool-converter.test.ts +256 -0
  29. package/src/__tests__/tool-schemas.test.ts +397 -0
  30. package/src/__tests__/tools.test.ts +143 -0
  31. package/src/__tests__/version.test.ts +49 -0
  32. package/src/agent/compaction-agent.ts +227 -0
  33. package/src/agent/context-manager.ts +435 -0
  34. package/src/agent/context.ts +427 -0
  35. package/src/agent/deploy-preview.ts +426 -0
  36. package/src/agent/index.ts +68 -0
  37. package/src/agent/loop.ts +717 -0
  38. package/src/agent/modes.ts +429 -0
  39. package/src/agent/permissions.ts +466 -0
  40. package/src/agent/subagents/base.ts +116 -0
  41. package/src/agent/subagents/cost.ts +51 -0
  42. package/src/agent/subagents/explore.ts +42 -0
  43. package/src/agent/subagents/general.ts +54 -0
  44. package/src/agent/subagents/index.ts +102 -0
  45. package/src/agent/subagents/infra.ts +59 -0
  46. package/src/agent/subagents/security.ts +69 -0
  47. package/src/agent/system-prompt.ts +436 -0
  48. package/src/app.ts +122 -0
  49. package/src/audit/activity-log.ts +290 -0
  50. package/src/audit/compliance-checker.ts +540 -0
  51. package/src/audit/cost-tracker.ts +318 -0
  52. package/src/audit/index.ts +23 -0
  53. package/src/audit/security-scanner.ts +596 -0
  54. package/src/auth/guard.ts +75 -0
  55. package/src/auth/index.ts +56 -0
  56. package/src/auth/oauth.ts +455 -0
  57. package/src/auth/providers.ts +470 -0
  58. package/src/auth/sso.ts +113 -0
  59. package/src/auth/store.ts +505 -0
  60. package/src/auth/types.ts +187 -0
  61. package/src/build.ts +141 -0
  62. package/src/cli/index.ts +16 -0
  63. package/src/cli/init.ts +854 -0
  64. package/src/cli/openapi-spec.ts +356 -0
  65. package/src/cli/run.ts +237 -0
  66. package/src/cli/serve-auth.ts +80 -0
  67. package/src/cli/serve.ts +462 -0
  68. package/src/cli/web.ts +67 -0
  69. package/src/cli.ts +1417 -0
  70. package/src/clients/core-engine-client.ts +227 -0
  71. package/src/clients/enterprise-client.ts +334 -0
  72. package/src/clients/generator-client.ts +351 -0
  73. package/src/clients/git-client.ts +627 -0
  74. package/src/clients/github-client.ts +410 -0
  75. package/src/clients/helm-client.ts +504 -0
  76. package/src/clients/index.ts +80 -0
  77. package/src/clients/k8s-client.ts +497 -0
  78. package/src/clients/llm-client.ts +161 -0
  79. package/src/clients/rest-client.ts +130 -0
  80. package/src/clients/service-discovery.ts +33 -0
  81. package/src/clients/terraform-client.ts +482 -0
  82. package/src/clients/tools-client.ts +1843 -0
  83. package/src/clients/ws-client.ts +115 -0
  84. package/src/commands/analyze/index.ts +352 -0
  85. package/src/commands/apply/helm.ts +473 -0
  86. package/src/commands/apply/index.ts +213 -0
  87. package/src/commands/apply/k8s.ts +454 -0
  88. package/src/commands/apply/terraform.ts +582 -0
  89. package/src/commands/ask.ts +167 -0
  90. package/src/commands/audit/index.ts +238 -0
  91. package/src/commands/auth-cloud.ts +294 -0
  92. package/src/commands/auth-list.ts +134 -0
  93. package/src/commands/auth-profile.ts +121 -0
  94. package/src/commands/auth-status.ts +141 -0
  95. package/src/commands/aws/ec2.ts +501 -0
  96. package/src/commands/aws/iam.ts +397 -0
  97. package/src/commands/aws/index.ts +133 -0
  98. package/src/commands/aws/lambda.ts +396 -0
  99. package/src/commands/aws/rds.ts +439 -0
  100. package/src/commands/aws/s3.ts +439 -0
  101. package/src/commands/aws/vpc.ts +393 -0
  102. package/src/commands/aws-discover.ts +649 -0
  103. package/src/commands/aws-terraform.ts +805 -0
  104. package/src/commands/azure/aks.ts +376 -0
  105. package/src/commands/azure/functions.ts +253 -0
  106. package/src/commands/azure/index.ts +116 -0
  107. package/src/commands/azure/storage.ts +478 -0
  108. package/src/commands/azure/vm.ts +355 -0
  109. package/src/commands/billing/index.ts +256 -0
  110. package/src/commands/chat.ts +314 -0
  111. package/src/commands/config.ts +346 -0
  112. package/src/commands/cost/cloud-cost-estimator.ts +266 -0
  113. package/src/commands/cost/estimator.ts +79 -0
  114. package/src/commands/cost/index.ts +594 -0
  115. package/src/commands/cost/parsers/terraform.ts +273 -0
  116. package/src/commands/cost/parsers/types.ts +25 -0
  117. package/src/commands/cost/pricing/aws.ts +544 -0
  118. package/src/commands/cost/pricing/azure.ts +499 -0
  119. package/src/commands/cost/pricing/gcp.ts +396 -0
  120. package/src/commands/cost/pricing/index.ts +40 -0
  121. package/src/commands/demo.ts +250 -0
  122. package/src/commands/doctor.ts +794 -0
  123. package/src/commands/drift/index.ts +439 -0
  124. package/src/commands/explain.ts +277 -0
  125. package/src/commands/feedback.ts +389 -0
  126. package/src/commands/fix.ts +324 -0
  127. package/src/commands/fs/index.ts +402 -0
  128. package/src/commands/gcp/compute.ts +325 -0
  129. package/src/commands/gcp/functions.ts +271 -0
  130. package/src/commands/gcp/gke.ts +438 -0
  131. package/src/commands/gcp/iam.ts +344 -0
  132. package/src/commands/gcp/index.ts +129 -0
  133. package/src/commands/gcp/storage.ts +284 -0
  134. package/src/commands/generate-helm.ts +1249 -0
  135. package/src/commands/generate-k8s.ts +1560 -0
  136. package/src/commands/generate-terraform.ts +1460 -0
  137. package/src/commands/gh/index.ts +863 -0
  138. package/src/commands/git/index.ts +1343 -0
  139. package/src/commands/helm/index.ts +1126 -0
  140. package/src/commands/help.ts +539 -0
  141. package/src/commands/history.ts +142 -0
  142. package/src/commands/import.ts +868 -0
  143. package/src/commands/index.ts +367 -0
  144. package/src/commands/init.ts +1046 -0
  145. package/src/commands/k8s/index.ts +1137 -0
  146. package/src/commands/login.ts +631 -0
  147. package/src/commands/logout.ts +83 -0
  148. package/src/commands/onboarding.ts +228 -0
  149. package/src/commands/plan/display.ts +279 -0
  150. package/src/commands/plan/index.ts +599 -0
  151. package/src/commands/preview.ts +452 -0
  152. package/src/commands/questionnaire.ts +1270 -0
  153. package/src/commands/resume.ts +55 -0
  154. package/src/commands/team/index.ts +346 -0
  155. package/src/commands/template.ts +232 -0
  156. package/src/commands/tf/index.ts +1034 -0
  157. package/src/commands/upgrade.ts +550 -0
  158. package/src/commands/usage/index.ts +134 -0
  159. package/src/commands/version.ts +170 -0
  160. package/src/compat/index.ts +2 -0
  161. package/src/compat/runtime.ts +12 -0
  162. package/src/compat/sqlite.ts +107 -0
  163. package/src/config/index.ts +17 -0
  164. package/src/config/manager.ts +530 -0
  165. package/src/config/safety-policy.ts +358 -0
  166. package/src/config/schema.ts +125 -0
  167. package/src/config/types.ts +527 -0
  168. package/src/context/context-db.ts +199 -0
  169. package/src/demo/index.ts +349 -0
  170. package/src/demo/scenarios/full-journey.ts +229 -0
  171. package/src/demo/scenarios/getting-started.ts +127 -0
  172. package/src/demo/scenarios/helm-release.ts +341 -0
  173. package/src/demo/scenarios/k8s-deployment.ts +194 -0
  174. package/src/demo/scenarios/terraform-vpc.ts +170 -0
  175. package/src/demo/types.ts +92 -0
  176. package/src/engine/cost-estimator.ts +438 -0
  177. package/src/engine/diagram-generator.ts +256 -0
  178. package/src/engine/drift-detector.ts +902 -0
  179. package/src/engine/executor.ts +1035 -0
  180. package/src/engine/index.ts +76 -0
  181. package/src/engine/orchestrator.ts +636 -0
  182. package/src/engine/planner.ts +720 -0
  183. package/src/engine/safety.ts +743 -0
  184. package/src/engine/verifier.ts +770 -0
  185. package/src/enterprise/audit.ts +348 -0
  186. package/src/enterprise/auth.ts +270 -0
  187. package/src/enterprise/billing.ts +822 -0
  188. package/src/enterprise/index.ts +17 -0
  189. package/src/enterprise/teams.ts +443 -0
  190. package/src/generator/best-practices.ts +1608 -0
  191. package/src/generator/helm.ts +630 -0
  192. package/src/generator/index.ts +37 -0
  193. package/src/generator/intent-parser.ts +514 -0
  194. package/src/generator/kubernetes.ts +976 -0
  195. package/src/generator/terraform.ts +1867 -0
  196. package/src/history/index.ts +8 -0
  197. package/src/history/manager.ts +322 -0
  198. package/src/history/types.ts +34 -0
  199. package/src/hooks/config.ts +432 -0
  200. package/src/hooks/engine.ts +391 -0
  201. package/src/hooks/index.ts +4 -0
  202. package/src/llm/auth-bridge.ts +198 -0
  203. package/src/llm/circuit-breaker.ts +140 -0
  204. package/src/llm/config-loader.ts +201 -0
  205. package/src/llm/cost-calculator.ts +171 -0
  206. package/src/llm/index.ts +8 -0
  207. package/src/llm/model-aliases.ts +115 -0
  208. package/src/llm/provider-registry.ts +63 -0
  209. package/src/llm/providers/anthropic.ts +433 -0
  210. package/src/llm/providers/bedrock.ts +477 -0
  211. package/src/llm/providers/google.ts +405 -0
  212. package/src/llm/providers/ollama.ts +767 -0
  213. package/src/llm/providers/openai-compatible.ts +340 -0
  214. package/src/llm/providers/openai.ts +328 -0
  215. package/src/llm/providers/openrouter.ts +338 -0
  216. package/src/llm/router.ts +1035 -0
  217. package/src/llm/types.ts +232 -0
  218. package/src/lsp/client.ts +298 -0
  219. package/src/lsp/languages.ts +116 -0
  220. package/src/lsp/manager.ts +278 -0
  221. package/src/mcp/client.ts +402 -0
  222. package/src/mcp/index.ts +5 -0
  223. package/src/mcp/manager.ts +133 -0
  224. package/src/nimbus.ts +214 -0
  225. package/src/plugins/index.ts +27 -0
  226. package/src/plugins/loader.ts +334 -0
  227. package/src/plugins/manager.ts +376 -0
  228. package/src/plugins/types.ts +284 -0
  229. package/src/scanners/cicd-scanner.ts +258 -0
  230. package/src/scanners/cloud-scanner.ts +466 -0
  231. package/src/scanners/framework-scanner.ts +469 -0
  232. package/src/scanners/iac-scanner.ts +388 -0
  233. package/src/scanners/index.ts +539 -0
  234. package/src/scanners/language-scanner.ts +276 -0
  235. package/src/scanners/package-manager-scanner.ts +277 -0
  236. package/src/scanners/types.ts +172 -0
  237. package/src/sessions/manager.ts +365 -0
  238. package/src/sessions/types.ts +44 -0
  239. package/src/sharing/sync.ts +296 -0
  240. package/src/sharing/viewer.ts +97 -0
  241. package/src/snapshots/index.ts +2 -0
  242. package/src/snapshots/manager.ts +530 -0
  243. package/src/state/artifacts.ts +147 -0
  244. package/src/state/audit.ts +137 -0
  245. package/src/state/billing.ts +240 -0
  246. package/src/state/checkpoints.ts +117 -0
  247. package/src/state/config.ts +67 -0
  248. package/src/state/conversations.ts +14 -0
  249. package/src/state/credentials.ts +154 -0
  250. package/src/state/db.ts +58 -0
  251. package/src/state/index.ts +26 -0
  252. package/src/state/messages.ts +115 -0
  253. package/src/state/projects.ts +123 -0
  254. package/src/state/schema.ts +236 -0
  255. package/src/state/sessions.ts +147 -0
  256. package/src/state/teams.ts +200 -0
  257. package/src/telemetry.ts +108 -0
  258. package/src/tools/aws-ops.ts +952 -0
  259. package/src/tools/azure-ops.ts +579 -0
  260. package/src/tools/file-ops.ts +593 -0
  261. package/src/tools/gcp-ops.ts +625 -0
  262. package/src/tools/git-ops.ts +773 -0
  263. package/src/tools/github-ops.ts +799 -0
  264. package/src/tools/helm-ops.ts +943 -0
  265. package/src/tools/index.ts +17 -0
  266. package/src/tools/k8s-ops.ts +819 -0
  267. package/src/tools/schemas/converter.ts +184 -0
  268. package/src/tools/schemas/devops.ts +612 -0
  269. package/src/tools/schemas/index.ts +73 -0
  270. package/src/tools/schemas/standard.ts +1144 -0
  271. package/src/tools/schemas/types.ts +705 -0
  272. package/src/tools/terraform-ops.ts +862 -0
  273. package/src/types/ambient.d.ts +193 -0
  274. package/src/types/config.ts +83 -0
  275. package/src/types/drift.ts +116 -0
  276. package/src/types/enterprise.ts +335 -0
  277. package/src/types/index.ts +20 -0
  278. package/src/types/plan.ts +44 -0
  279. package/src/types/request.ts +65 -0
  280. package/src/types/response.ts +54 -0
  281. package/src/types/service.ts +51 -0
  282. package/src/ui/App.tsx +997 -0
  283. package/src/ui/DeployPreview.tsx +169 -0
  284. package/src/ui/Header.tsx +68 -0
  285. package/src/ui/InputBox.tsx +350 -0
  286. package/src/ui/MessageList.tsx +585 -0
  287. package/src/ui/PermissionPrompt.tsx +151 -0
  288. package/src/ui/StatusBar.tsx +158 -0
  289. package/src/ui/ToolCallDisplay.tsx +409 -0
  290. package/src/ui/chat-ui.ts +853 -0
  291. package/src/ui/index.ts +33 -0
  292. package/src/ui/ink/index.ts +711 -0
  293. package/src/ui/streaming.ts +176 -0
  294. package/src/ui/types.ts +57 -0
  295. package/src/utils/analytics.ts +72 -0
  296. package/src/utils/cost-warning.ts +27 -0
  297. package/src/utils/env.ts +46 -0
  298. package/src/utils/errors.ts +69 -0
  299. package/src/utils/event-bus.ts +38 -0
  300. package/src/utils/index.ts +24 -0
  301. package/src/utils/logger.ts +171 -0
  302. package/src/utils/rate-limiter.ts +121 -0
  303. package/src/utils/service-auth.ts +49 -0
  304. package/src/utils/validation.ts +53 -0
  305. package/src/version.ts +4 -0
  306. package/src/watcher/index.ts +163 -0
  307. package/src/wizard/approval.ts +383 -0
  308. package/src/wizard/index.ts +25 -0
  309. package/src/wizard/prompts.ts +338 -0
  310. package/src/wizard/types.ts +171 -0
  311. package/src/wizard/ui.ts +556 -0
  312. package/src/wizard/wizard.ts +304 -0
  313. package/tsconfig.json +24 -0
@@ -0,0 +1,773 @@
1
+ /**
2
+ * Git Operations — Embedded tool (stripped HTTP wrappers)
3
+ *
4
+ * Copied from services/git-tools-service/src/git/operations.ts
5
+ * Provides direct git operations for the embedded CLI binary.
6
+ */
7
+
8
+ import simpleGit, {
9
+ type SimpleGit,
10
+ type SimpleGitOptions,
11
+ type StatusResult,
12
+ type LogResult,
13
+ } from 'simple-git';
14
+ import { logger } from '../utils';
15
+
16
+ export interface GitCloneOptions {
17
+ url: string;
18
+ path: string;
19
+ branch?: string;
20
+ depth?: number;
21
+ }
22
+
23
+ export interface GitCommitOptions {
24
+ message: string;
25
+ amend?: boolean;
26
+ allowEmpty?: boolean;
27
+ }
28
+
29
+ export interface GitPushOptions {
30
+ remote?: string;
31
+ branch?: string;
32
+ force?: boolean;
33
+ setUpstream?: boolean;
34
+ }
35
+
36
+ export interface GitPullOptions {
37
+ remote?: string;
38
+ branch?: string;
39
+ rebase?: boolean;
40
+ }
41
+
42
+ export interface GitBranchOptions {
43
+ name: string;
44
+ checkout?: boolean;
45
+ startPoint?: string;
46
+ }
47
+
48
+ export interface GitMergeOptions {
49
+ branch: string;
50
+ noFf?: boolean;
51
+ squash?: boolean;
52
+ message?: string;
53
+ }
54
+
55
+ export interface GitLogOptions {
56
+ maxCount?: number;
57
+ from?: string;
58
+ to?: string;
59
+ file?: string;
60
+ }
61
+
62
+ export interface GitDiffOptions {
63
+ cached?: boolean;
64
+ nameOnly?: boolean;
65
+ from?: string;
66
+ to?: string;
67
+ }
68
+
69
+ export interface GitStashOptions {
70
+ command: 'push' | 'pop' | 'list' | 'drop' | 'apply' | 'clear';
71
+ message?: string;
72
+ index?: number;
73
+ }
74
+
75
+ export interface CherryPickOptions {
76
+ noCommit?: boolean;
77
+ edit?: boolean;
78
+ signoff?: boolean;
79
+ strategy?: string;
80
+ }
81
+
82
+ export interface RebaseOptions {
83
+ interactive?: boolean;
84
+ onto?: string;
85
+ preserveMerges?: boolean;
86
+ strategy?: string;
87
+ strategyOption?: string;
88
+ }
89
+
90
+ export interface TagOptions {
91
+ message?: string;
92
+ annotated?: boolean;
93
+ force?: boolean;
94
+ commit?: string;
95
+ }
96
+
97
+ export class GitOperations {
98
+ private git: SimpleGit;
99
+ private repoPath: string;
100
+
101
+ constructor(repoPath: string = process.cwd()) {
102
+ this.repoPath = repoPath;
103
+ const options: Partial<SimpleGitOptions> = {
104
+ baseDir: repoPath,
105
+ binary: 'git',
106
+ maxConcurrentProcesses: 6,
107
+ trimmed: true,
108
+ };
109
+ this.git = simpleGit(options);
110
+ }
111
+
112
+ /**
113
+ * Clone a repository
114
+ */
115
+ async clone(options: GitCloneOptions): Promise<{ success: boolean; path: string }> {
116
+ logger.info(`Cloning repository from ${options.url} to ${options.path}`);
117
+
118
+ const cloneOptions: string[] = [];
119
+ if (options.branch) {
120
+ cloneOptions.push('--branch', options.branch);
121
+ }
122
+ if (options.depth) {
123
+ cloneOptions.push('--depth', options.depth.toString());
124
+ }
125
+
126
+ await simpleGit().clone(options.url, options.path, cloneOptions);
127
+
128
+ return { success: true, path: options.path };
129
+ }
130
+
131
+ /**
132
+ * Get repository status
133
+ */
134
+ async status(): Promise<StatusResult> {
135
+ logger.info(`Getting git status for ${this.repoPath}`);
136
+ return await this.git.status();
137
+ }
138
+
139
+ /**
140
+ * Add files to staging
141
+ */
142
+ async add(files: string | string[] = '.'): Promise<{ success: boolean; files: string[] }> {
143
+ const fileList = Array.isArray(files) ? files : [files];
144
+ logger.info(`Staging files: ${fileList.join(', ')}`);
145
+
146
+ await this.git.add(fileList);
147
+
148
+ return { success: true, files: fileList };
149
+ }
150
+
151
+ /**
152
+ * Commit staged changes
153
+ */
154
+ async commit(
155
+ options: GitCommitOptions
156
+ ): Promise<{ success: boolean; hash: string; summary: string }> {
157
+ logger.info(`Committing with message: ${options.message}`);
158
+
159
+ const commitOptions: string[] = [];
160
+ if (options.amend) {
161
+ commitOptions.push('--amend');
162
+ }
163
+ if (options.allowEmpty) {
164
+ commitOptions.push('--allow-empty');
165
+ }
166
+
167
+ const result = await this.git.commit(options.message, undefined, { '--': commitOptions });
168
+
169
+ return {
170
+ success: true,
171
+ hash: result.commit,
172
+ summary: result.summary
173
+ ? `${result.summary.changes} changes, ${result.summary.insertions} insertions, ${result.summary.deletions} deletions`
174
+ : 'Committed',
175
+ };
176
+ }
177
+
178
+ /**
179
+ * Push to remote
180
+ */
181
+ async push(
182
+ options: GitPushOptions = {}
183
+ ): Promise<{ success: boolean; remote: string; branch: string }> {
184
+ const remote = options.remote || 'origin';
185
+ logger.info(`Pushing to ${remote}${options.branch ? `/${options.branch}` : ''}`);
186
+
187
+ const pushOptions: string[] = [];
188
+ if (options.force) {
189
+ pushOptions.push('--force');
190
+ }
191
+ if (options.setUpstream) {
192
+ pushOptions.push('--set-upstream');
193
+ }
194
+
195
+ await this.git.push(remote, options.branch, pushOptions);
196
+
197
+ return { success: true, remote, branch: options.branch || 'current' };
198
+ }
199
+
200
+ /**
201
+ * Pull from remote
202
+ */
203
+ async pull(options: GitPullOptions = {}): Promise<{ success: boolean; summary: string }> {
204
+ const remote = options.remote || 'origin';
205
+ logger.info(`Pulling from ${remote}${options.branch ? `/${options.branch}` : ''}`);
206
+
207
+ const pullOptions: Record<string, string | null> = {};
208
+ if (options.rebase) {
209
+ pullOptions['--rebase'] = null;
210
+ }
211
+
212
+ const result = await this.git.pull(remote, options.branch, pullOptions);
213
+
214
+ return {
215
+ success: true,
216
+ summary: result.summary
217
+ ? `${result.summary.changes} changes, ${result.summary.insertions} insertions, ${result.summary.deletions} deletions`
218
+ : 'Already up to date',
219
+ };
220
+ }
221
+
222
+ /**
223
+ * Create a new branch
224
+ */
225
+ async createBranch(options: GitBranchOptions): Promise<{ success: boolean; branch: string }> {
226
+ logger.info(`Creating branch: ${options.name}`);
227
+
228
+ if (options.checkout) {
229
+ if (options.startPoint) {
230
+ await this.git.checkoutBranch(options.name, options.startPoint);
231
+ } else {
232
+ await this.git.checkoutLocalBranch(options.name);
233
+ }
234
+ } else {
235
+ await this.git.branch([options.name, options.startPoint || 'HEAD']);
236
+ }
237
+
238
+ return { success: true, branch: options.name };
239
+ }
240
+
241
+ /**
242
+ * List branches
243
+ */
244
+ async listBranches(
245
+ showRemote: boolean = false
246
+ ): Promise<{ current: string; branches: string[] }> {
247
+ logger.info('Listing branches');
248
+
249
+ const result = await this.git.branch(showRemote ? ['-a'] : []);
250
+
251
+ return {
252
+ current: result.current,
253
+ branches: result.all,
254
+ };
255
+ }
256
+
257
+ /**
258
+ * Checkout a branch or commit
259
+ */
260
+ async checkout(
261
+ target: string,
262
+ create: boolean = false
263
+ ): Promise<{ success: boolean; target: string }> {
264
+ logger.info(`Checking out: ${target}`);
265
+
266
+ if (create) {
267
+ await this.git.checkoutLocalBranch(target);
268
+ } else {
269
+ await this.git.checkout(target);
270
+ }
271
+
272
+ return { success: true, target };
273
+ }
274
+
275
+ /**
276
+ * Get diff
277
+ */
278
+ async diff(options: GitDiffOptions = {}): Promise<{ diff: string; files: string[] }> {
279
+ logger.info('Getting diff');
280
+
281
+ const diffArgs: string[] = [];
282
+ if (options.cached) {
283
+ diffArgs.push('--cached');
284
+ }
285
+ if (options.nameOnly) {
286
+ diffArgs.push('--name-only');
287
+ }
288
+ if (options.from) {
289
+ diffArgs.push(options.from);
290
+ }
291
+ if (options.to) {
292
+ diffArgs.push(options.to);
293
+ }
294
+
295
+ const diff = await this.git.diff(diffArgs);
296
+ const files = options.nameOnly ? diff.split('\n').filter(f => f) : [];
297
+
298
+ return { diff, files };
299
+ }
300
+
301
+ /**
302
+ * Get commit log
303
+ */
304
+ async log(options: GitLogOptions = {}): Promise<LogResult> {
305
+ logger.info('Getting commit log');
306
+
307
+ const logOptions: any = {};
308
+ if (options.maxCount) {
309
+ logOptions.maxCount = options.maxCount;
310
+ }
311
+ if (options.from) {
312
+ logOptions.from = options.from;
313
+ }
314
+ if (options.to) {
315
+ logOptions.to = options.to;
316
+ }
317
+ if (options.file) {
318
+ logOptions.file = options.file;
319
+ }
320
+
321
+ return await this.git.log(logOptions);
322
+ }
323
+
324
+ /**
325
+ * Merge a branch
326
+ */
327
+ async merge(options: GitMergeOptions): Promise<{ success: boolean; result: string }> {
328
+ logger.info(`Merging branch: ${options.branch}`);
329
+
330
+ const mergeArgs: string[] = [options.branch];
331
+ if (options.noFf) {
332
+ mergeArgs.unshift('--no-ff');
333
+ }
334
+ if (options.squash) {
335
+ mergeArgs.unshift('--squash');
336
+ }
337
+ if (options.message) {
338
+ mergeArgs.unshift('-m', options.message);
339
+ }
340
+
341
+ const result = await this.git.merge(mergeArgs);
342
+
343
+ return {
344
+ success: true,
345
+ result: result.result || 'Merged successfully',
346
+ };
347
+ }
348
+
349
+ /**
350
+ * Stash operations
351
+ */
352
+ async stash(options: GitStashOptions): Promise<{ success: boolean; result: string }> {
353
+ logger.info(`Stash operation: ${options.command}`);
354
+
355
+ let result: string;
356
+
357
+ switch (options.command) {
358
+ case 'push': {
359
+ const pushArgs = options.message ? ['-m', options.message] : [];
360
+ result = await this.git.stash(['push', ...pushArgs]);
361
+ break;
362
+ }
363
+ case 'pop':
364
+ result = await this.git.stash([
365
+ 'pop',
366
+ ...(options.index !== undefined ? [options.index.toString()] : []),
367
+ ]);
368
+ break;
369
+ case 'apply':
370
+ result = await this.git.stash([
371
+ 'apply',
372
+ ...(options.index !== undefined ? [options.index.toString()] : []),
373
+ ]);
374
+ break;
375
+ case 'drop':
376
+ result = await this.git.stash([
377
+ 'drop',
378
+ ...(options.index !== undefined ? [options.index.toString()] : []),
379
+ ]);
380
+ break;
381
+ case 'list':
382
+ result = await this.git.stash(['list']);
383
+ break;
384
+ case 'clear':
385
+ result = await this.git.stash(['clear']);
386
+ break;
387
+ default:
388
+ throw new Error(`Unknown stash command: ${options.command}`);
389
+ }
390
+
391
+ return { success: true, result: result || 'Stash operation completed' };
392
+ }
393
+
394
+ /**
395
+ * Get current branch name
396
+ */
397
+ async currentBranch(): Promise<string> {
398
+ const result = await this.git.revparse(['--abbrev-ref', 'HEAD']);
399
+ return result.trim();
400
+ }
401
+
402
+ /**
403
+ * Check if repository is clean
404
+ */
405
+ async isClean(): Promise<boolean> {
406
+ const status = await this.status();
407
+ return status.isClean();
408
+ }
409
+
410
+ /**
411
+ * Reset to a commit
412
+ */
413
+ async reset(
414
+ target: string,
415
+ mode: 'soft' | 'mixed' | 'hard' = 'mixed'
416
+ ): Promise<{ success: boolean }> {
417
+ logger.info(`Resetting to ${target} with mode ${mode}`);
418
+ await this.git.reset([`--${mode}`, target]);
419
+ return { success: true };
420
+ }
421
+
422
+ /**
423
+ * Fetch from remote
424
+ */
425
+ async fetch(remote: string = 'origin', prune: boolean = false): Promise<{ success: boolean }> {
426
+ logger.info(`Fetching from ${remote}`);
427
+ if (prune) {
428
+ await this.git.fetch(remote, ['--prune']);
429
+ } else {
430
+ await this.git.fetch(remote);
431
+ }
432
+ return { success: true };
433
+ }
434
+
435
+ /**
436
+ * Get remote URL
437
+ */
438
+ async getRemoteUrl(remote: string = 'origin'): Promise<string | null> {
439
+ try {
440
+ const remotes = await this.git.getRemotes(true);
441
+ const targetRemote = remotes.find(r => r.name === remote);
442
+ return targetRemote?.refs?.fetch || null;
443
+ } catch {
444
+ return null;
445
+ }
446
+ }
447
+
448
+ /**
449
+ * Check if path is a git repository
450
+ */
451
+ async isRepo(): Promise<boolean> {
452
+ return await this.git.checkIsRepo();
453
+ }
454
+
455
+ /**
456
+ * Initialize a new repository
457
+ */
458
+ async init(bare: boolean = false): Promise<{ success: boolean }> {
459
+ logger.info(`Initializing git repository in ${this.repoPath}`);
460
+ await this.git.init(bare);
461
+ return { success: true };
462
+ }
463
+
464
+ /**
465
+ * Cherry-pick a commit
466
+ */
467
+ async cherryPick(
468
+ commit: string,
469
+ options: CherryPickOptions = {}
470
+ ): Promise<{ success: boolean; result: string }> {
471
+ logger.info(`Cherry-picking commit: ${commit}`);
472
+
473
+ const cherryPickArgs: string[] = [commit];
474
+
475
+ if (options.noCommit) {
476
+ cherryPickArgs.unshift('--no-commit');
477
+ }
478
+
479
+ if (options.edit) {
480
+ cherryPickArgs.unshift('-e');
481
+ }
482
+
483
+ if (options.signoff) {
484
+ cherryPickArgs.unshift('-s');
485
+ }
486
+
487
+ if (options.strategy) {
488
+ cherryPickArgs.unshift('-X', options.strategy);
489
+ }
490
+
491
+ // Use raw to execute cherry-pick
492
+ const result = await this.git.raw(['cherry-pick', ...cherryPickArgs]);
493
+
494
+ return {
495
+ success: true,
496
+ result: result || 'Cherry-pick completed successfully',
497
+ };
498
+ }
499
+
500
+ /**
501
+ * Rebase onto a target branch
502
+ */
503
+ async rebase(
504
+ target: string,
505
+ options: RebaseOptions = {}
506
+ ): Promise<{ success: boolean; result: string }> {
507
+ logger.info(`Rebasing onto: ${target}`);
508
+
509
+ const rebaseArgs: string[] = [];
510
+
511
+ if (options.interactive) {
512
+ // Note: Interactive rebase requires a terminal, so we skip it in automation
513
+ logger.warn('Interactive rebase not supported in automation mode');
514
+ }
515
+
516
+ if (options.onto) {
517
+ rebaseArgs.push('--onto', options.onto);
518
+ }
519
+
520
+ if (options.preserveMerges) {
521
+ rebaseArgs.push('--preserve-merges');
522
+ }
523
+
524
+ if (options.strategy) {
525
+ rebaseArgs.push('-s', options.strategy);
526
+ }
527
+
528
+ if (options.strategyOption) {
529
+ rebaseArgs.push('-X', options.strategyOption);
530
+ }
531
+
532
+ rebaseArgs.push(target);
533
+
534
+ const result = await this.git.rebase(rebaseArgs);
535
+
536
+ return {
537
+ success: true,
538
+ result: result || 'Rebase completed successfully',
539
+ };
540
+ }
541
+
542
+ /**
543
+ * Continue a rebase after resolving conflicts
544
+ */
545
+ async rebaseContinue(): Promise<{ success: boolean; result: string }> {
546
+ logger.info('Continuing rebase');
547
+ const result = await this.git.rebase(['--continue']);
548
+ return {
549
+ success: true,
550
+ result: result || 'Rebase continued successfully',
551
+ };
552
+ }
553
+
554
+ /**
555
+ * Abort a rebase in progress
556
+ */
557
+ async rebaseAbort(): Promise<{ success: boolean }> {
558
+ logger.info('Aborting rebase');
559
+ await this.git.rebase(['--abort']);
560
+ return { success: true };
561
+ }
562
+
563
+ /**
564
+ * Skip a commit during rebase
565
+ */
566
+ async rebaseSkip(): Promise<{ success: boolean; result: string }> {
567
+ logger.info('Skipping commit during rebase');
568
+ const result = await this.git.rebase(['--skip']);
569
+ return {
570
+ success: true,
571
+ result: result || 'Commit skipped',
572
+ };
573
+ }
574
+
575
+ /**
576
+ * Create a tag
577
+ */
578
+ async tag(name: string, options: TagOptions = {}): Promise<{ success: boolean; tag: string }> {
579
+ logger.info(`Creating tag: ${name}`);
580
+
581
+ const tagArgs: string[] = [];
582
+
583
+ if (options.annotated || options.message) {
584
+ tagArgs.push('-a');
585
+ }
586
+
587
+ if (options.message) {
588
+ tagArgs.push('-m', options.message);
589
+ }
590
+
591
+ if (options.force) {
592
+ tagArgs.push('-f');
593
+ }
594
+
595
+ tagArgs.push(name);
596
+
597
+ if (options.commit) {
598
+ tagArgs.push(options.commit);
599
+ }
600
+
601
+ await this.git.tag(tagArgs);
602
+
603
+ return { success: true, tag: name };
604
+ }
605
+
606
+ /**
607
+ * Delete a tag
608
+ */
609
+ async deleteTag(name: string, remote?: string): Promise<{ success: boolean }> {
610
+ logger.info(`Deleting tag: ${name}`);
611
+
612
+ // Delete locally
613
+ await this.git.tag(['-d', name]);
614
+
615
+ // Delete from remote if specified
616
+ if (remote) {
617
+ await this.git.push(remote, `:refs/tags/${name}`);
618
+ }
619
+
620
+ return { success: true };
621
+ }
622
+
623
+ /**
624
+ * List tags
625
+ */
626
+ async listTags(pattern?: string): Promise<string[]> {
627
+ logger.info('Listing tags');
628
+
629
+ const args = pattern ? ['-l', pattern] : [];
630
+ const result = await this.git.tags(args);
631
+
632
+ return result.all;
633
+ }
634
+
635
+ /**
636
+ * Push tags to remote
637
+ */
638
+ async pushTags(remote: string = 'origin', tagName?: string): Promise<{ success: boolean }> {
639
+ logger.info(`Pushing tags to ${remote}`);
640
+
641
+ if (tagName) {
642
+ await this.git.push(remote, `refs/tags/${tagName}`);
643
+ } else {
644
+ await this.git.pushTags(remote);
645
+ }
646
+
647
+ return { success: true };
648
+ }
649
+
650
+ /**
651
+ * Show information about a tag
652
+ */
653
+ async showTag(name: string): Promise<{ success: boolean; info: string }> {
654
+ logger.info(`Showing tag: ${name}`);
655
+
656
+ const result = await this.git.show(['--no-patch', name]);
657
+
658
+ return { success: true, info: result };
659
+ }
660
+
661
+ /**
662
+ * Check if there are conflicts
663
+ */
664
+ async hasConflicts(): Promise<boolean> {
665
+ const status = await this.status();
666
+ return status.conflicted.length > 0;
667
+ }
668
+
669
+ /**
670
+ * Get list of conflicted files
671
+ */
672
+ async getConflicts(): Promise<string[]> {
673
+ const status = await this.status();
674
+ return status.conflicted;
675
+ }
676
+
677
+ /**
678
+ * Abort cherry-pick in progress
679
+ */
680
+ async cherryPickAbort(): Promise<{ success: boolean }> {
681
+ logger.info('Aborting cherry-pick');
682
+ await this.git.raw(['cherry-pick', '--abort']);
683
+ return { success: true };
684
+ }
685
+
686
+ /**
687
+ * Continue cherry-pick after resolving conflicts
688
+ */
689
+ async cherryPickContinue(): Promise<{ success: boolean; result: string }> {
690
+ logger.info('Continuing cherry-pick');
691
+ const result = await this.git.raw(['cherry-pick', '--continue']);
692
+ return {
693
+ success: true,
694
+ result: result || 'Cherry-pick continued',
695
+ };
696
+ }
697
+
698
+ /**
699
+ * Show a specific commit
700
+ */
701
+ async showCommit(commit: string): Promise<string> {
702
+ logger.info(`Showing commit: ${commit}`);
703
+ return await this.git.show([commit]);
704
+ }
705
+
706
+ /**
707
+ * Get the short hash for a ref
708
+ */
709
+ async getShortHash(ref: string = 'HEAD'): Promise<string> {
710
+ const result = await this.git.revparse(['--short', ref]);
711
+ return result.trim();
712
+ }
713
+
714
+ /**
715
+ * Get the full hash for a ref
716
+ */
717
+ async getFullHash(ref: string = 'HEAD'): Promise<string> {
718
+ const result = await this.git.revparse([ref]);
719
+ return result.trim();
720
+ }
721
+
722
+ /**
723
+ * Get the commit count between two refs
724
+ */
725
+ async getCommitCount(from: string, to: string = 'HEAD'): Promise<number> {
726
+ const result = await this.git.raw(['rev-list', '--count', `${from}..${to}`]);
727
+ return parseInt(result.trim(), 10);
728
+ }
729
+
730
+ /**
731
+ * Revert a commit
732
+ */
733
+ async revert(
734
+ commit: string,
735
+ options: { noCommit?: boolean; noEdit?: boolean } = {}
736
+ ): Promise<{ success: boolean; result: string }> {
737
+ logger.info(`Reverting commit: ${commit}`);
738
+
739
+ const revertArgs: string[] = [];
740
+
741
+ if (options.noCommit) {
742
+ revertArgs.push('--no-commit');
743
+ }
744
+
745
+ if (options.noEdit) {
746
+ revertArgs.push('--no-edit');
747
+ }
748
+
749
+ revertArgs.push(commit);
750
+
751
+ const result = await this.git.raw(['revert', ...revertArgs]);
752
+
753
+ return {
754
+ success: true,
755
+ result: result || 'Revert completed successfully',
756
+ };
757
+ }
758
+
759
+ /**
760
+ * Blame a file
761
+ */
762
+ async blame(file: string, options?: { startLine?: number; endLine?: number }): Promise<string> {
763
+ logger.info(`Getting blame for: ${file}`);
764
+
765
+ const args = [];
766
+ if (options?.startLine && options?.endLine) {
767
+ args.push(`-L${options.startLine},${options.endLine}`);
768
+ }
769
+ args.push(file);
770
+
771
+ return await this.git.raw(['blame', ...args]);
772
+ }
773
+ }