@samaksh/git-worker 0.0.0-stage → 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 (78) hide show
  1. package/README.md +114 -2
  2. package/dist/app.d.ts +2 -0
  3. package/dist/app.js +48 -0
  4. package/dist/cli/commands.d.ts +6 -0
  5. package/dist/cli/commands.js +111 -0
  6. package/dist/cli/index.d.ts +2 -0
  7. package/dist/cli/index.js +115 -0
  8. package/dist/config/defaults.d.ts +2 -0
  9. package/dist/config/defaults.js +44 -0
  10. package/dist/config/load.d.ts +3 -0
  11. package/dist/config/load.js +65 -0
  12. package/dist/config/schema.d.ts +200 -0
  13. package/dist/config/schema.js +45 -0
  14. package/dist/core/errors.d.ts +12 -0
  15. package/dist/core/errors.js +34 -0
  16. package/dist/core/pipeline.d.ts +3 -0
  17. package/dist/core/pipeline.js +14 -0
  18. package/dist/core/types.d.ts +153 -0
  19. package/dist/core/types.js +16 -0
  20. package/dist/index.d.ts +3 -0
  21. package/dist/index.js +3 -0
  22. package/dist/services/editor/index.d.ts +9 -0
  23. package/dist/services/editor/index.js +59 -0
  24. package/dist/services/git/ops.d.ts +30 -0
  25. package/dist/services/git/ops.js +139 -0
  26. package/dist/services/git/reader.d.ts +14 -0
  27. package/dist/services/git/reader.js +195 -0
  28. package/dist/services/report/index.d.ts +18 -0
  29. package/dist/services/report/index.js +68 -0
  30. package/dist/services/review/index.d.ts +30 -0
  31. package/dist/services/review/index.js +59 -0
  32. package/dist/services/state/index.d.ts +20 -0
  33. package/dist/services/state/index.js +49 -0
  34. package/dist/services/template/env.d.ts +23 -0
  35. package/dist/services/template/env.js +146 -0
  36. package/dist/services/template/interpolator.d.ts +30 -0
  37. package/dist/services/template/interpolator.js +75 -0
  38. package/dist/services/template/transform.d.ts +22 -0
  39. package/dist/services/template/transform.js +75 -0
  40. package/dist/services/template/validator.d.ts +5 -0
  41. package/dist/services/template/validator.js +68 -0
  42. package/dist/services/workspace/cache.d.ts +32 -0
  43. package/dist/services/workspace/cache.js +107 -0
  44. package/dist/services/workspace/manager.d.ts +44 -0
  45. package/dist/services/workspace/manager.js +301 -0
  46. package/dist/stages/apply.d.ts +3 -0
  47. package/dist/stages/apply.js +161 -0
  48. package/dist/stages/edit-template.d.ts +3 -0
  49. package/dist/stages/edit-template.js +198 -0
  50. package/dist/stages/eligibility.d.ts +3 -0
  51. package/dist/stages/eligibility.js +68 -0
  52. package/dist/stages/load-template.d.ts +3 -0
  53. package/dist/stages/load-template.js +168 -0
  54. package/dist/stages/plan.d.ts +3 -0
  55. package/dist/stages/plan.js +270 -0
  56. package/dist/stages/push.d.ts +3 -0
  57. package/dist/stages/push.js +99 -0
  58. package/dist/stages/report.d.ts +3 -0
  59. package/dist/stages/report.js +103 -0
  60. package/dist/stages/resolve-project.d.ts +3 -0
  61. package/dist/stages/resolve-project.js +294 -0
  62. package/dist/stages/review.d.ts +3 -0
  63. package/dist/stages/review.js +197 -0
  64. package/dist/stages/select.d.ts +13 -0
  65. package/dist/stages/select.js +265 -0
  66. package/dist/stages/sync.d.ts +3 -0
  67. package/dist/stages/sync.js +47 -0
  68. package/dist/utils/hash.d.ts +2 -0
  69. package/dist/utils/hash.js +7 -0
  70. package/dist/utils/ui.d.ts +21 -0
  71. package/dist/utils/ui.js +113 -0
  72. package/dist/utils/which.d.ts +5 -0
  73. package/dist/utils/which.js +16 -0
  74. package/docs/FEATURES.md +84 -0
  75. package/docs/SERVICES.md +57 -0
  76. package/docs/architecture.md +38 -0
  77. package/docs/usage.md +337 -0
  78. package/package.json +58 -4
@@ -0,0 +1,200 @@
1
+ import { z } from "zod";
2
+ export declare const AppConfigSchema: z.ZodObject<{
3
+ git: z.ZodObject<{
4
+ remote: z.ZodDefault<z.ZodString>;
5
+ fetchPrune: z.ZodDefault<z.ZodBoolean>;
6
+ cleanMode: z.ZodDefault<z.ZodEnum<["tracked-and-untracked", "tracked-only"]>>;
7
+ pushPolicy: z.ZodDefault<z.ZodEnum<["prompt", "always", "never"]>>;
8
+ commitMessage: z.ZodDefault<z.ZodString>;
9
+ }, "strip", z.ZodTypeAny, {
10
+ remote: string;
11
+ fetchPrune: boolean;
12
+ cleanMode: "tracked-and-untracked" | "tracked-only";
13
+ pushPolicy: "never" | "prompt" | "always";
14
+ commitMessage: string;
15
+ }, {
16
+ remote?: string | undefined;
17
+ fetchPrune?: boolean | undefined;
18
+ cleanMode?: "tracked-and-untracked" | "tracked-only" | undefined;
19
+ pushPolicy?: "never" | "prompt" | "always" | undefined;
20
+ commitMessage?: string | undefined;
21
+ }>;
22
+ workspace: z.ZodObject<{
23
+ root: z.ZodDefault<z.ZodString>;
24
+ lockTimeoutMs: z.ZodDefault<z.ZodNumber>;
25
+ }, "strip", z.ZodTypeAny, {
26
+ root: string;
27
+ lockTimeoutMs: number;
28
+ }, {
29
+ root?: string | undefined;
30
+ lockTimeoutMs?: number | undefined;
31
+ }>;
32
+ editor: z.ZodObject<{
33
+ name: z.ZodDefault<z.ZodString>;
34
+ editCommand: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
35
+ diffCommand: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
36
+ }, "strip", z.ZodTypeAny, {
37
+ name: string;
38
+ editCommand: string[];
39
+ diffCommand: string[];
40
+ }, {
41
+ name?: string | undefined;
42
+ editCommand?: string[] | undefined;
43
+ diffCommand?: string[] | undefined;
44
+ }>;
45
+ template: z.ZodObject<{
46
+ placeholderOpen: z.ZodDefault<z.ZodString>;
47
+ placeholderClose: z.ZodDefault<z.ZodString>;
48
+ missingValuePolicy: z.ZodDefault<z.ZodEnum<["prompt-each-branch"]>>;
49
+ emptyValueIsPresent: z.ZodDefault<z.ZodBoolean>;
50
+ unmanagedKeyPolicy: z.ZodDefault<z.ZodEnum<["preserve"]>>;
51
+ removedTemplateKeyPolicy: z.ZodDefault<z.ZodEnum<["preserve"]>>;
52
+ }, "strip", z.ZodTypeAny, {
53
+ placeholderOpen: string;
54
+ placeholderClose: string;
55
+ missingValuePolicy: "prompt-each-branch";
56
+ emptyValueIsPresent: boolean;
57
+ unmanagedKeyPolicy: "preserve";
58
+ removedTemplateKeyPolicy: "preserve";
59
+ }, {
60
+ placeholderOpen?: string | undefined;
61
+ placeholderClose?: string | undefined;
62
+ missingValuePolicy?: "prompt-each-branch" | undefined;
63
+ emptyValueIsPresent?: boolean | undefined;
64
+ unmanagedKeyPolicy?: "preserve" | undefined;
65
+ removedTemplateKeyPolicy?: "preserve" | undefined;
66
+ }>;
67
+ review: z.ZodObject<{
68
+ autoApprove: z.ZodDefault<z.ZodBoolean>;
69
+ readOnlySnapshots: z.ZodDefault<z.ZodBoolean>;
70
+ }, "strip", z.ZodTypeAny, {
71
+ autoApprove: boolean;
72
+ readOnlySnapshots: boolean;
73
+ }, {
74
+ autoApprove?: boolean | undefined;
75
+ readOnlySnapshots?: boolean | undefined;
76
+ }>;
77
+ execution: z.ZodObject<{
78
+ concurrency: z.ZodDefault<z.ZodNumber>;
79
+ stopOnBranchError: z.ZodDefault<z.ZodBoolean>;
80
+ }, "strip", z.ZodTypeAny, {
81
+ concurrency: number;
82
+ stopOnBranchError: boolean;
83
+ }, {
84
+ concurrency?: number | undefined;
85
+ stopOnBranchError?: boolean | undefined;
86
+ }>;
87
+ report: z.ZodObject<{
88
+ writeJson: z.ZodDefault<z.ZodBoolean>;
89
+ writeMarkdown: z.ZodDefault<z.ZodBoolean>;
90
+ redactValues: z.ZodDefault<z.ZodBoolean>;
91
+ warnOnSecrets: z.ZodDefault<z.ZodBoolean>;
92
+ }, "strip", z.ZodTypeAny, {
93
+ writeJson: boolean;
94
+ writeMarkdown: boolean;
95
+ redactValues: boolean;
96
+ warnOnSecrets: boolean;
97
+ }, {
98
+ writeJson?: boolean | undefined;
99
+ writeMarkdown?: boolean | undefined;
100
+ redactValues?: boolean | undefined;
101
+ warnOnSecrets?: boolean | undefined;
102
+ }>;
103
+ safety: z.ZodObject<{
104
+ rejectNoRemote: z.ZodDefault<z.ZodBoolean>;
105
+ saveOnPartialSuccess: z.ZodDefault<z.ZodBoolean>;
106
+ }, "strip", z.ZodTypeAny, {
107
+ rejectNoRemote: boolean;
108
+ saveOnPartialSuccess: boolean;
109
+ }, {
110
+ rejectNoRemote?: boolean | undefined;
111
+ saveOnPartialSuccess?: boolean | undefined;
112
+ }>;
113
+ }, "strip", z.ZodTypeAny, {
114
+ git: {
115
+ remote: string;
116
+ fetchPrune: boolean;
117
+ cleanMode: "tracked-and-untracked" | "tracked-only";
118
+ pushPolicy: "never" | "prompt" | "always";
119
+ commitMessage: string;
120
+ };
121
+ workspace: {
122
+ root: string;
123
+ lockTimeoutMs: number;
124
+ };
125
+ editor: {
126
+ name: string;
127
+ editCommand: string[];
128
+ diffCommand: string[];
129
+ };
130
+ template: {
131
+ placeholderOpen: string;
132
+ placeholderClose: string;
133
+ missingValuePolicy: "prompt-each-branch";
134
+ emptyValueIsPresent: boolean;
135
+ unmanagedKeyPolicy: "preserve";
136
+ removedTemplateKeyPolicy: "preserve";
137
+ };
138
+ review: {
139
+ autoApprove: boolean;
140
+ readOnlySnapshots: boolean;
141
+ };
142
+ execution: {
143
+ concurrency: number;
144
+ stopOnBranchError: boolean;
145
+ };
146
+ report: {
147
+ writeJson: boolean;
148
+ writeMarkdown: boolean;
149
+ redactValues: boolean;
150
+ warnOnSecrets: boolean;
151
+ };
152
+ safety: {
153
+ rejectNoRemote: boolean;
154
+ saveOnPartialSuccess: boolean;
155
+ };
156
+ }, {
157
+ git: {
158
+ remote?: string | undefined;
159
+ fetchPrune?: boolean | undefined;
160
+ cleanMode?: "tracked-and-untracked" | "tracked-only" | undefined;
161
+ pushPolicy?: "never" | "prompt" | "always" | undefined;
162
+ commitMessage?: string | undefined;
163
+ };
164
+ workspace: {
165
+ root?: string | undefined;
166
+ lockTimeoutMs?: number | undefined;
167
+ };
168
+ editor: {
169
+ name?: string | undefined;
170
+ editCommand?: string[] | undefined;
171
+ diffCommand?: string[] | undefined;
172
+ };
173
+ template: {
174
+ placeholderOpen?: string | undefined;
175
+ placeholderClose?: string | undefined;
176
+ missingValuePolicy?: "prompt-each-branch" | undefined;
177
+ emptyValueIsPresent?: boolean | undefined;
178
+ unmanagedKeyPolicy?: "preserve" | undefined;
179
+ removedTemplateKeyPolicy?: "preserve" | undefined;
180
+ };
181
+ review: {
182
+ autoApprove?: boolean | undefined;
183
+ readOnlySnapshots?: boolean | undefined;
184
+ };
185
+ execution: {
186
+ concurrency?: number | undefined;
187
+ stopOnBranchError?: boolean | undefined;
188
+ };
189
+ report: {
190
+ writeJson?: boolean | undefined;
191
+ writeMarkdown?: boolean | undefined;
192
+ redactValues?: boolean | undefined;
193
+ warnOnSecrets?: boolean | undefined;
194
+ };
195
+ safety: {
196
+ rejectNoRemote?: boolean | undefined;
197
+ saveOnPartialSuccess?: boolean | undefined;
198
+ };
199
+ }>;
200
+ export type AppConfig = z.infer<typeof AppConfigSchema>;
@@ -0,0 +1,45 @@
1
+ import { z } from "zod";
2
+ export const AppConfigSchema = z.object({
3
+ git: z.object({
4
+ remote: z.string().default("origin"),
5
+ fetchPrune: z.boolean().default(true),
6
+ cleanMode: z.enum(["tracked-and-untracked", "tracked-only"]).default("tracked-and-untracked"),
7
+ pushPolicy: z.enum(["prompt", "always", "never"]).default("prompt"),
8
+ commitMessage: z.string().default("chore: sync configuration"),
9
+ }),
10
+ workspace: z.object({
11
+ root: z.string().default(".git-worker"),
12
+ lockTimeoutMs: z.number().default(30000),
13
+ }),
14
+ editor: z.object({
15
+ name: z.string().default("code"),
16
+ editCommand: z.array(z.string()).default(["code", "--wait", "{file}"]),
17
+ diffCommand: z.array(z.string()).default(["code", "--diff", "{before}", "{after}"]),
18
+ }),
19
+ template: z.object({
20
+ placeholderOpen: z.string().default("{"),
21
+ placeholderClose: z.string().default("}"),
22
+ missingValuePolicy: z.enum(["prompt-each-branch"]).default("prompt-each-branch"),
23
+ emptyValueIsPresent: z.boolean().default(true),
24
+ unmanagedKeyPolicy: z.enum(["preserve"]).default("preserve"),
25
+ removedTemplateKeyPolicy: z.enum(["preserve"]).default("preserve"),
26
+ }),
27
+ review: z.object({
28
+ autoApprove: z.boolean().default(false),
29
+ readOnlySnapshots: z.boolean().default(true),
30
+ }),
31
+ execution: z.object({
32
+ concurrency: z.number().default(1),
33
+ stopOnBranchError: z.boolean().default(false),
34
+ }),
35
+ report: z.object({
36
+ writeJson: z.boolean().default(false),
37
+ writeMarkdown: z.boolean().default(false),
38
+ redactValues: z.boolean().default(true),
39
+ warnOnSecrets: z.boolean().default(true),
40
+ }),
41
+ safety: z.object({
42
+ rejectNoRemote: z.boolean().default(true),
43
+ saveOnPartialSuccess: z.boolean().default(true),
44
+ }),
45
+ });
@@ -0,0 +1,12 @@
1
+ import { AppError } from "./types.js";
2
+ export declare function configError(msg: string, details?: unknown): AppError;
3
+ export declare function gitError(msg: string, details?: unknown): AppError;
4
+ export declare function editorError(msg: string, details?: unknown): AppError;
5
+ export declare function templateError(msg: string, details?: unknown): AppError;
6
+ export declare function eligibilityError(msg: string, details?: unknown): AppError;
7
+ export declare function inputCancelledError(msg: string, details?: unknown): AppError;
8
+ export declare function stalePlanError(msg: string, details?: unknown): AppError;
9
+ export declare function applyError(msg: string, details?: unknown): AppError;
10
+ export declare function pushError(msg: string, details?: unknown): AppError;
11
+ export declare function workspaceError(msg: string, details?: unknown): AppError;
12
+ export declare function validationError(msg: string, details?: unknown): AppError;
@@ -0,0 +1,34 @@
1
+ import { AppError } from "./types.js";
2
+ export function configError(msg, details) {
3
+ return new AppError("CONFIG", msg, details);
4
+ }
5
+ export function gitError(msg, details) {
6
+ return new AppError("GIT", msg, details);
7
+ }
8
+ export function editorError(msg, details) {
9
+ return new AppError("EDITOR", msg, details);
10
+ }
11
+ export function templateError(msg, details) {
12
+ return new AppError("TEMPLATE", msg, details);
13
+ }
14
+ export function eligibilityError(msg, details) {
15
+ return new AppError("ELIGIBILITY", msg, details);
16
+ }
17
+ export function inputCancelledError(msg, details) {
18
+ return new AppError("INPUT_CANCELLED", msg, details);
19
+ }
20
+ export function stalePlanError(msg, details) {
21
+ return new AppError("STALE_PLAN", msg, details);
22
+ }
23
+ export function applyError(msg, details) {
24
+ return new AppError("APPLY", msg, details);
25
+ }
26
+ export function pushError(msg, details) {
27
+ return new AppError("PUSH", msg, details);
28
+ }
29
+ export function workspaceError(msg, details) {
30
+ return new AppError("WORKSPACE", msg, details);
31
+ }
32
+ export function validationError(msg, details) {
33
+ return new AppError("VALIDATION", msg, details);
34
+ }
@@ -0,0 +1,3 @@
1
+ import { Result, AppError } from "./types.js";
2
+ export type Stage<C, D> = (context: C, deps: D) => Promise<Result<C, AppError>>;
3
+ export declare function pipeline<C, D>(...stages: Stage<C, D>[]): Stage<C, D>;
@@ -0,0 +1,14 @@
1
+ import { ok } from "./types.js";
2
+ export function pipeline(...stages) {
3
+ return async (initial, deps) => {
4
+ let current = initial;
5
+ for (const stage of stages) {
6
+ const result = await stage(current, deps);
7
+ if (!result.ok) {
8
+ return result;
9
+ }
10
+ current = result.value;
11
+ }
12
+ return ok(current);
13
+ };
14
+ }
@@ -0,0 +1,153 @@
1
+ export type Result<T, E = AppError> = {
2
+ ok: true;
3
+ value: T;
4
+ } | {
5
+ ok: false;
6
+ error: E;
7
+ };
8
+ export type AppErrorCategory = "CONFIG" | "GIT" | "EDITOR" | "TEMPLATE" | "ELIGIBILITY" | "INPUT_CANCELLED" | "STALE_PLAN" | "APPLY" | "PUSH" | "WORKSPACE" | "VALIDATION";
9
+ export declare class AppError extends Error {
10
+ category: AppErrorCategory;
11
+ details?: unknown;
12
+ constructor(category: AppErrorCategory, message: string, details?: unknown);
13
+ }
14
+ export declare function ok<T>(value: T): Result<T>;
15
+ export declare function err<E extends AppError>(error: E): Result<never, E>;
16
+ export type BranchRef = {
17
+ name: string;
18
+ remote?: string;
19
+ sha: string;
20
+ isRemote: boolean;
21
+ };
22
+ export type TargetFile = {
23
+ path: string;
24
+ };
25
+ export type TemplateEngineType = "env-reconcile" | "source-of-truth";
26
+ export type TargetTemplate = {
27
+ path: string;
28
+ engine: TemplateEngineType;
29
+ raw: string;
30
+ parsedEnv?: TemplateDocument;
31
+ variables?: Array<{
32
+ name: string;
33
+ fallback?: string;
34
+ raw: string;
35
+ }>;
36
+ isNewFile?: boolean;
37
+ };
38
+ export type ManagedProject = {
39
+ id: string;
40
+ sourceRepoPath: string;
41
+ remoteUrl?: string;
42
+ managedRepoPath: string;
43
+ stagingDir: string;
44
+ stateDir: string;
45
+ reportsDir: string;
46
+ markerPath: string;
47
+ isCached?: boolean;
48
+ };
49
+ export type TemplateDocument = {
50
+ raw: string;
51
+ assignments: TemplateAssignment[];
52
+ placeholders: string[];
53
+ };
54
+ export type TemplateAssignment = {
55
+ key: string;
56
+ value: string;
57
+ isStatic: boolean;
58
+ isPlaceholder: boolean;
59
+ placeholderName?: string;
60
+ originalLine: string;
61
+ };
62
+ export type EnvEntry = {
63
+ key: string;
64
+ value: string;
65
+ lineIndex: number;
66
+ originalLine: string;
67
+ hasExport: boolean;
68
+ };
69
+ export type BranchContent = {
70
+ branch: BranchRef;
71
+ exists: boolean;
72
+ content?: string;
73
+ error?: string;
74
+ };
75
+ export type MissingValueRequest = {
76
+ branch: BranchRef;
77
+ key: string;
78
+ };
79
+ export type BranchPlan = {
80
+ branch: BranchRef;
81
+ baseCommitSha: string;
82
+ path: string;
83
+ beforeContent: string;
84
+ beforeContentHash: string;
85
+ afterContent: string;
86
+ afterContentHash: string;
87
+ diff: string;
88
+ planHash: string;
89
+ status: "changed" | "unchanged" | "error";
90
+ reason?: string;
91
+ addedKeys: string[];
92
+ updatedKeys: string[];
93
+ missingVars?: string[];
94
+ isNewFile?: boolean;
95
+ };
96
+ export type Approval = {
97
+ branchName: string;
98
+ approved: boolean;
99
+ planHash: string;
100
+ };
101
+ export type BranchResult = {
102
+ branch: BranchRef;
103
+ status: "skipped" | "rejected" | "unchanged" | "committed" | "failed";
104
+ commitSha?: string;
105
+ reason?: string;
106
+ };
107
+ export type PushResult = {
108
+ branch: BranchRef;
109
+ status: "pushed" | "skipped" | "failed";
110
+ reason?: string;
111
+ };
112
+ export type CliInput = {
113
+ repo?: string;
114
+ cached?: boolean;
115
+ branch?: string;
116
+ invertBranch?: boolean;
117
+ file?: string;
118
+ editor?: string;
119
+ commitMsg?: string;
120
+ push?: "prompt" | "always" | "never";
121
+ remote?: string;
122
+ autoApprove?: boolean;
123
+ ci?: boolean;
124
+ };
125
+ export type AppServices = {
126
+ git: import("../services/git/reader.js").GitReader;
127
+ gitOps: import("../services/git/ops.js").GitOps;
128
+ workspace: import("../services/workspace/manager.js").WorkspaceManager;
129
+ template: import("../services/template/transform.js").TemplateService;
130
+ state: import("../services/state/index.js").StateService;
131
+ editor: import("../services/editor/index.js").EditorService;
132
+ review: import("../services/review/index.js").ReviewService;
133
+ };
134
+ export type AppDeps = {
135
+ config: import("../config/schema.js").AppConfig;
136
+ services: AppServices;
137
+ };
138
+ export type RunContext = {
139
+ runId: string;
140
+ input: CliInput;
141
+ project?: ManagedProject;
142
+ selectedBranches?: BranchRef[];
143
+ target?: TargetFile;
144
+ targets?: TargetFile[];
145
+ eligible?: BranchRef[];
146
+ template?: TemplateDocument;
147
+ activeTemplate?: TargetTemplate;
148
+ templates?: TargetTemplate[];
149
+ plans?: BranchPlan[];
150
+ approvals?: Approval[];
151
+ results?: BranchResult[];
152
+ pushResults?: PushResult[];
153
+ };
@@ -0,0 +1,16 @@
1
+ export class AppError extends Error {
2
+ category;
3
+ details;
4
+ constructor(category, message, details) {
5
+ super(message);
6
+ this.name = "AppError";
7
+ this.category = category;
8
+ this.details = details;
9
+ }
10
+ }
11
+ export function ok(value) {
12
+ return { ok: true, value };
13
+ }
14
+ export function err(error) {
15
+ return { ok: false, error };
16
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./app.js";
2
+ export * from "./core/types.js";
3
+ export * from "./core/errors.js";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./app.js";
2
+ export * from "./core/types.js";
3
+ export * from "./core/errors.js";
@@ -0,0 +1,9 @@
1
+ import { Result } from "../../core/types.js";
2
+ import { AppConfig } from "../../config/schema.js";
3
+ export type EditorService = {
4
+ openForEdit(templatePath: string, config: AppConfig): Promise<Result<void>>;
5
+ openForDiff(beforePath: string, afterPath: string, config: AppConfig): Promise<Result<void>>;
6
+ };
7
+ export declare function openForEdit(templatePath: string, config: AppConfig): Promise<Result<void>>;
8
+ export declare function openForDiff(beforePath: string, afterPath: string, config: AppConfig): Promise<Result<void>>;
9
+ export declare function createEditorService(): EditorService;
@@ -0,0 +1,59 @@
1
+ import { ok, err } from "../../core/types.js";
2
+ import { editorError } from "../../core/errors.js";
3
+ import { spawn } from "child_process";
4
+ /**
5
+ * Substitute {file}, {before}, {after} placeholders in a command template.
6
+ */
7
+ function buildCommand(template, vars) {
8
+ return template.map((part) => {
9
+ let result = part;
10
+ for (const [k, v] of Object.entries(vars)) {
11
+ result = result.replace(`{${k}}`, v);
12
+ }
13
+ return result;
14
+ });
15
+ }
16
+ /**
17
+ * Spawn a command and wait for it to exit.
18
+ * Returns an error if the process exits with a non-zero code.
19
+ */
20
+ async function runCommand(cmd, description) {
21
+ return new Promise((resolve) => {
22
+ const [bin, ...args] = cmd;
23
+ const proc = spawn(bin, args, { stdio: "inherit" });
24
+ proc.on("error", (e) => {
25
+ if (e.code === "ENOENT") {
26
+ resolve(err(editorError(`Editor '${bin}' not found. Install it or set a different editor via --editor or git-worker.config.json.`)));
27
+ }
28
+ else {
29
+ resolve(err(editorError(`${description} failed: ${e.message}`)));
30
+ }
31
+ });
32
+ proc.on("close", (code) => {
33
+ if (code === 0) {
34
+ resolve(ok(undefined));
35
+ }
36
+ else {
37
+ // Some editors (like VS Code) return non-zero on cancel
38
+ resolve(ok(undefined));
39
+ }
40
+ });
41
+ });
42
+ }
43
+ export async function openForEdit(templatePath, config) {
44
+ const cmd = buildCommand(config.editor.editCommand, { file: templatePath });
45
+ return runCommand(cmd, "Editor");
46
+ }
47
+ export async function openForDiff(beforePath, afterPath, config) {
48
+ const cmd = buildCommand(config.editor.diffCommand, {
49
+ before: beforePath,
50
+ after: afterPath,
51
+ });
52
+ return runCommand(cmd, "Diff viewer");
53
+ }
54
+ export function createEditorService() {
55
+ return {
56
+ openForEdit,
57
+ openForDiff,
58
+ };
59
+ }
@@ -0,0 +1,30 @@
1
+ import { Result } from "../../core/types.js";
2
+ export type GitOps = {
3
+ ensureRepo(repoPath: string): Promise<Result<void>>;
4
+ checkoutBranch(repoPath: string, branchName: string): Promise<Result<void>>;
5
+ resetHard(repoPath: string): Promise<Result<void>>;
6
+ clean(repoPath: string, mode?: "tracked-and-untracked" | "tracked-only"): Promise<Result<void>>;
7
+ writeAndStage(repoPath: string, filePath: string, content: string): Promise<Result<void>>;
8
+ getStagedFiles(repoPath: string): Promise<Result<string[]>>;
9
+ commit(repoPath: string, message: string): Promise<Result<string>>;
10
+ push(repoPath: string, remote: string, branchName: string): Promise<Result<void>>;
11
+ getBranchSha(repoPath: string, branchName: string): Promise<Result<string>>;
12
+ };
13
+ export declare function ensureRepo(repoPath: string): Promise<Result<void>>;
14
+ export declare function checkoutBranch(repoPath: string, branchName: string): Promise<Result<void>>;
15
+ export declare function resetHard(repoPath: string): Promise<Result<void>>;
16
+ export declare function clean(repoPath: string, mode?: "tracked-and-untracked" | "tracked-only"): Promise<Result<void>>;
17
+ /**
18
+ * Atomically write a file (via temp file + rename) and stage it.
19
+ * Only stages the specific path — no glob or wildcard.
20
+ */
21
+ export declare function writeAndStage(repoPath: string, filePath: string, content: string): Promise<Result<void>>;
22
+ /**
23
+ * Returns the list of staged file paths.
24
+ * Used to verify only the intended path is staged before committing.
25
+ */
26
+ export declare function getStagedFiles(repoPath: string): Promise<Result<string[]>>;
27
+ export declare function commit(repoPath: string, message: string): Promise<Result<string>>;
28
+ export declare function push(repoPath: string, remote: string, branchName: string): Promise<Result<void>>;
29
+ export declare function getBranchSha(repoPath: string, branchName: string): Promise<Result<string>>;
30
+ export declare function createGitOps(): GitOps;