@samaksh/git-worker 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GIT_WORKER_PROJECT_PLAN.md +499 -0
- package/README.md +114 -2
- package/demo/README.md +58 -0
- package/demo/index.ts +526 -0
- package/dist/app.d.ts +2 -0
- package/dist/app.js +48 -0
- package/dist/cli/commands.d.ts +6 -0
- package/dist/cli/commands.js +111 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +115 -0
- package/dist/config/defaults.d.ts +2 -0
- package/dist/config/defaults.js +44 -0
- package/dist/config/load.d.ts +3 -0
- package/dist/config/load.js +65 -0
- package/dist/config/schema.d.ts +200 -0
- package/dist/config/schema.js +45 -0
- package/dist/core/errors.d.ts +12 -0
- package/dist/core/errors.js +34 -0
- package/dist/core/pipeline.d.ts +3 -0
- package/dist/core/pipeline.js +14 -0
- package/dist/core/types.d.ts +153 -0
- package/dist/core/types.js +16 -0
- package/dist/services/editor/index.d.ts +9 -0
- package/dist/services/editor/index.js +59 -0
- package/dist/services/git/ops.d.ts +30 -0
- package/dist/services/git/ops.js +139 -0
- package/dist/services/git/reader.d.ts +14 -0
- package/dist/services/git/reader.js +124 -0
- package/dist/services/report/index.d.ts +18 -0
- package/dist/services/report/index.js +68 -0
- package/dist/services/review/index.d.ts +30 -0
- package/dist/services/review/index.js +59 -0
- package/dist/services/state/index.d.ts +20 -0
- package/dist/services/state/index.js +49 -0
- package/dist/services/template/env.d.ts +23 -0
- package/dist/services/template/env.js +146 -0
- package/dist/services/template/interpolator.d.ts +30 -0
- package/dist/services/template/interpolator.js +75 -0
- package/dist/services/template/transform.d.ts +22 -0
- package/dist/services/template/transform.js +75 -0
- package/dist/services/template/validator.d.ts +5 -0
- package/dist/services/template/validator.js +68 -0
- package/dist/services/workspace/cache.d.ts +32 -0
- package/dist/services/workspace/cache.js +107 -0
- package/dist/services/workspace/manager.d.ts +44 -0
- package/dist/services/workspace/manager.js +301 -0
- package/dist/stages/apply.d.ts +3 -0
- package/dist/stages/apply.js +161 -0
- package/dist/stages/edit-template.d.ts +3 -0
- package/dist/stages/edit-template.js +198 -0
- package/dist/stages/eligibility.d.ts +3 -0
- package/dist/stages/eligibility.js +44 -0
- package/dist/stages/load-template.d.ts +3 -0
- package/dist/stages/load-template.js +168 -0
- package/dist/stages/plan.d.ts +3 -0
- package/dist/stages/plan.js +270 -0
- package/dist/stages/push.d.ts +3 -0
- package/dist/stages/push.js +99 -0
- package/dist/stages/report.d.ts +3 -0
- package/dist/stages/report.js +103 -0
- package/dist/stages/resolve-project.d.ts +3 -0
- package/dist/stages/resolve-project.js +294 -0
- package/dist/stages/review.d.ts +3 -0
- package/dist/stages/review.js +197 -0
- package/dist/stages/select.d.ts +13 -0
- package/dist/stages/select.js +250 -0
- package/dist/stages/sync.d.ts +3 -0
- package/dist/stages/sync.js +47 -0
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.js +7 -0
- package/dist/utils/ui.d.ts +21 -0
- package/dist/utils/ui.js +113 -0
- package/dist/utils/which.d.ts +5 -0
- package/dist/utils/which.js +16 -0
- package/docs/FEATURES.md +84 -0
- package/docs/SERVICES.md +57 -0
- package/docs/architecture.md +38 -0
- package/docs/usage.md +337 -0
- package/package.json +48 -4
- package/src/app.ts +69 -0
- package/src/cli/commands.ts +142 -0
- package/src/cli/index.ts +123 -0
- package/src/config/defaults.ts +46 -0
- package/src/config/load.ts +70 -0
- package/src/config/schema.ts +48 -0
- package/src/core/errors.ts +45 -0
- package/src/core/pipeline.ts +17 -0
- package/src/core/types.ts +188 -0
- package/src/services/editor/index.ts +85 -0
- package/src/services/git/ops.ts +170 -0
- package/src/services/git/reader.ts +148 -0
- package/src/services/report/index.ts +104 -0
- package/src/services/review/index.ts +80 -0
- package/src/services/state/index.ts +77 -0
- package/src/services/template/env.ts +169 -0
- package/src/services/template/interpolator.ts +109 -0
- package/src/services/template/transform.ts +97 -0
- package/src/services/template/validator.ts +75 -0
- package/src/services/workspace/cache.ts +144 -0
- package/src/services/workspace/manager.ts +370 -0
- package/src/stages/apply.ts +194 -0
- package/src/stages/edit-template.ts +227 -0
- package/src/stages/eligibility.ts +67 -0
- package/src/stages/load-template.ts +188 -0
- package/src/stages/plan.ts +362 -0
- package/src/stages/push.ts +120 -0
- package/src/stages/report.ts +126 -0
- package/src/stages/resolve-project.ts +341 -0
- package/src/stages/review.ts +228 -0
- package/src/stages/select.ts +302 -0
- package/src/stages/sync.ts +63 -0
- package/src/utils/hash.ts +15 -0
- package/src/utils/ui.ts +137 -0
- package/src/utils/which.ts +18 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { run } from "../app.js";
|
|
4
|
+
import { handleCacheList, handleCacheClean } from "./commands.js";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program
|
|
8
|
+
.name("git-worker")
|
|
9
|
+
.description("Safe, composable CLI/TUI for applying reviewed file changes across many Git branches")
|
|
10
|
+
.version("0.1.0")
|
|
11
|
+
.option("-r, --repo <path>", "Repository path or remote URL (default: current directory)")
|
|
12
|
+
.option("-c, --cached", "Select from previously cloned and cached repositories")
|
|
13
|
+
.option("-b, --branch <pattern>", "Branch pattern — glob (uat/*) or regex (/uat.*/)")
|
|
14
|
+
.option("-i, --invert-branch", "Invert the branch selection pattern")
|
|
15
|
+
.option("-f, --file <path>", "Target file path inside the repository (e.g. .env)")
|
|
16
|
+
.option("-e, --editor <name>", "Editor to use: code (VS Code) or vim")
|
|
17
|
+
.option("-m, --commit-msg <message>", "Commit message")
|
|
18
|
+
.option("-p, --push <policy>", "Push policy: prompt (default) | always | never")
|
|
19
|
+
.option("--remote <name>", "Git remote name (default: origin)")
|
|
20
|
+
.option("--auto-approve", "Auto-approve all changes (skip interactive review)")
|
|
21
|
+
.option("--ci", "CI mode: non-interactive, fail on missing dynamic values")
|
|
22
|
+
.action(async (opts) => {
|
|
23
|
+
// Validate push policy
|
|
24
|
+
if (opts.push && !["prompt", "always", "never"].includes(opts.push)) {
|
|
25
|
+
console.error(chalk.red(` ✗ Invalid push policy '${opts.push}'. Must be: prompt | always | never`));
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
// Validate incompatible combinations
|
|
29
|
+
if (opts.ci && opts.push === "prompt") {
|
|
30
|
+
console.error(chalk.yellow(` ⚠ Warning: --ci with --push prompt will not push (no interactive prompt in CI). Use --push always or --push never.`));
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const result = await run({
|
|
34
|
+
repo: opts.repo,
|
|
35
|
+
cached: opts.cached ?? false,
|
|
36
|
+
branch: opts.branch,
|
|
37
|
+
invertBranch: opts.invertBranch ?? false,
|
|
38
|
+
file: opts.file,
|
|
39
|
+
editor: opts.editor,
|
|
40
|
+
commitMsg: opts.commitMsg,
|
|
41
|
+
push: opts.push,
|
|
42
|
+
remote: opts.remote,
|
|
43
|
+
autoApprove: opts.autoApprove ?? false,
|
|
44
|
+
ci: opts.ci ?? false,
|
|
45
|
+
});
|
|
46
|
+
if (!result.ok) {
|
|
47
|
+
const { error } = result;
|
|
48
|
+
if (error.category === "INPUT_CANCELLED") {
|
|
49
|
+
console.log(chalk.gray("\n Cancelled."));
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
console.error(chalk.red(`\n ✗ [${error.category}] ${error.message}`));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
console.error(chalk.red(`\n ✗ Fatal: ${e.message}`));
|
|
59
|
+
if (process.env.DEBUG) {
|
|
60
|
+
console.error(e.stack);
|
|
61
|
+
}
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
// ── Cache management subcommands ──────────────────────────────────────────
|
|
66
|
+
const cacheCmd = program
|
|
67
|
+
.command("cache")
|
|
68
|
+
.description("Manage git-worker's cached workspaces and repositories (~/.git-worker)");
|
|
69
|
+
cacheCmd
|
|
70
|
+
.command("list")
|
|
71
|
+
.alias("ls")
|
|
72
|
+
.description("List all cached projects")
|
|
73
|
+
.action(async () => {
|
|
74
|
+
try {
|
|
75
|
+
await handleCacheList();
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
console.error(chalk.red(` ✗ Error: ${e.message}`));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
cacheCmd
|
|
84
|
+
.command("clean [target]")
|
|
85
|
+
.alias("clear")
|
|
86
|
+
.description("Clean workspace cache (all or a specific project)")
|
|
87
|
+
.option("-a, --all", "Clean all cached projects")
|
|
88
|
+
.option("-y, --yes", "Skip confirmation prompt")
|
|
89
|
+
.action(async (target, opts) => {
|
|
90
|
+
try {
|
|
91
|
+
await handleCacheClean(target, opts);
|
|
92
|
+
process.exit(0);
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
console.error(chalk.red(` ✗ Error: ${e.message}`));
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
// Convenient top-level alias: git-worker clean [target] [-a]
|
|
100
|
+
program
|
|
101
|
+
.command("clean [target]")
|
|
102
|
+
.description("Shorthand to clean cached workspace(s)")
|
|
103
|
+
.option("-a, --all", "Clean all cached projects")
|
|
104
|
+
.option("-y, --yes", "Skip confirmation prompt")
|
|
105
|
+
.action(async (target, opts) => {
|
|
106
|
+
try {
|
|
107
|
+
await handleCacheClean(target, opts);
|
|
108
|
+
process.exit(0);
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
console.error(chalk.red(` ✗ Error: ${e.message}`));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
program.parse();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const DEFAULT_CONFIG = {
|
|
2
|
+
git: {
|
|
3
|
+
remote: "origin",
|
|
4
|
+
fetchPrune: true,
|
|
5
|
+
cleanMode: "tracked-and-untracked",
|
|
6
|
+
pushPolicy: "prompt",
|
|
7
|
+
commitMessage: "chore: sync configuration",
|
|
8
|
+
},
|
|
9
|
+
workspace: {
|
|
10
|
+
root: ".git-worker",
|
|
11
|
+
lockTimeoutMs: 30000,
|
|
12
|
+
},
|
|
13
|
+
editor: {
|
|
14
|
+
name: "code",
|
|
15
|
+
editCommand: ["code", "--wait", "{file}"],
|
|
16
|
+
diffCommand: ["code", "--diff", "{before}", "{after}"],
|
|
17
|
+
},
|
|
18
|
+
template: {
|
|
19
|
+
placeholderOpen: "{",
|
|
20
|
+
placeholderClose: "}",
|
|
21
|
+
missingValuePolicy: "prompt-each-branch",
|
|
22
|
+
emptyValueIsPresent: true,
|
|
23
|
+
unmanagedKeyPolicy: "preserve",
|
|
24
|
+
removedTemplateKeyPolicy: "preserve",
|
|
25
|
+
},
|
|
26
|
+
review: {
|
|
27
|
+
autoApprove: false,
|
|
28
|
+
readOnlySnapshots: true,
|
|
29
|
+
},
|
|
30
|
+
execution: {
|
|
31
|
+
concurrency: 1,
|
|
32
|
+
stopOnBranchError: false,
|
|
33
|
+
},
|
|
34
|
+
report: {
|
|
35
|
+
writeJson: false,
|
|
36
|
+
writeMarkdown: false,
|
|
37
|
+
redactValues: true,
|
|
38
|
+
warnOnSecrets: true,
|
|
39
|
+
},
|
|
40
|
+
safety: {
|
|
41
|
+
rejectNoRemote: true,
|
|
42
|
+
saveOnPartialSuccess: true,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { AppConfigSchema } from "./schema.js";
|
|
2
|
+
import { DEFAULT_CONFIG } from "./defaults.js";
|
|
3
|
+
import { existsSync, readFileSync } from "fs";
|
|
4
|
+
import { join, resolve } from "path";
|
|
5
|
+
function loadProjectConfig(repoPath) {
|
|
6
|
+
const candidates = [
|
|
7
|
+
join(resolve(repoPath), "git-worker.config.json"),
|
|
8
|
+
join(resolve(repoPath), ".git-worker.config.json"),
|
|
9
|
+
];
|
|
10
|
+
for (const candidate of candidates) {
|
|
11
|
+
if (existsSync(candidate)) {
|
|
12
|
+
try {
|
|
13
|
+
const raw = readFileSync(candidate, "utf-8");
|
|
14
|
+
return JSON.parse(raw);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// ignore malformed
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
function deepMerge(base, override) {
|
|
24
|
+
const result = { ...base };
|
|
25
|
+
for (const key of Object.keys(override)) {
|
|
26
|
+
const overrideVal = override[key];
|
|
27
|
+
if (overrideVal === undefined)
|
|
28
|
+
continue;
|
|
29
|
+
if (typeof overrideVal === "object" &&
|
|
30
|
+
overrideVal !== null &&
|
|
31
|
+
!Array.isArray(overrideVal) &&
|
|
32
|
+
typeof result[key] === "object" &&
|
|
33
|
+
result[key] !== null) {
|
|
34
|
+
result[key] = deepMerge(result[key], overrideVal);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
result[key] = overrideVal;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
export function loadConfig(cliInput) {
|
|
43
|
+
// Load project config from repo directory
|
|
44
|
+
const repoPath = cliInput.repo || process.cwd();
|
|
45
|
+
const projectConfig = loadProjectConfig(repoPath);
|
|
46
|
+
// Merge: defaults < project config < CLI overrides
|
|
47
|
+
const cliOverrides = {
|
|
48
|
+
git: {
|
|
49
|
+
...DEFAULT_CONFIG.git,
|
|
50
|
+
...(cliInput.push ? { pushPolicy: cliInput.push } : {}),
|
|
51
|
+
...(cliInput.commitMsg ? { commitMessage: cliInput.commitMsg } : {}),
|
|
52
|
+
...(cliInput.remote ? { remote: cliInput.remote } : {}),
|
|
53
|
+
},
|
|
54
|
+
editor: {
|
|
55
|
+
...DEFAULT_CONFIG.editor,
|
|
56
|
+
...(cliInput.editor ? { name: cliInput.editor } : {}),
|
|
57
|
+
},
|
|
58
|
+
review: {
|
|
59
|
+
...DEFAULT_CONFIG.review,
|
|
60
|
+
...(cliInput.autoApprove !== undefined ? { autoApprove: cliInput.autoApprove } : {}),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
const merged = deepMerge(deepMerge(DEFAULT_CONFIG, projectConfig), cliOverrides);
|
|
64
|
+
return Object.freeze(AppConfigSchema.parse(merged));
|
|
65
|
+
}
|
|
@@ -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,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
|
+
}
|