codex-orchestrator 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.
Files changed (122) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +94 -0
  3. package/dist/src/cli.d.ts +3 -0
  4. package/dist/src/cli.d.ts.map +1 -0
  5. package/dist/src/cli.js +253 -0
  6. package/dist/src/cli.js.map +1 -0
  7. package/dist/src/codex/command-adapter.d.ts +27 -0
  8. package/dist/src/codex/command-adapter.d.ts.map +1 -0
  9. package/dist/src/codex/command-adapter.js +49 -0
  10. package/dist/src/codex/command-adapter.js.map +1 -0
  11. package/dist/src/config/constants.d.ts +6 -0
  12. package/dist/src/config/constants.d.ts.map +1 -0
  13. package/dist/src/config/constants.js +13 -0
  14. package/dist/src/config/constants.js.map +1 -0
  15. package/dist/src/config/schema.d.ts +87 -0
  16. package/dist/src/config/schema.d.ts.map +1 -0
  17. package/dist/src/config/schema.js +180 -0
  18. package/dist/src/config/schema.js.map +1 -0
  19. package/dist/src/git/worktree.d.ts +50 -0
  20. package/dist/src/git/worktree.d.ts.map +1 -0
  21. package/dist/src/git/worktree.js +139 -0
  22. package/dist/src/git/worktree.js.map +1 -0
  23. package/dist/src/github/gh-cli.d.ts +11 -0
  24. package/dist/src/github/gh-cli.d.ts.map +1 -0
  25. package/dist/src/github/gh-cli.js +28 -0
  26. package/dist/src/github/gh-cli.js.map +1 -0
  27. package/dist/src/github/gh-issue-adapter.d.ts +15 -0
  28. package/dist/src/github/gh-issue-adapter.d.ts.map +1 -0
  29. package/dist/src/github/gh-issue-adapter.js +193 -0
  30. package/dist/src/github/gh-issue-adapter.js.map +1 -0
  31. package/dist/src/github/gh-pull-request-adapter.d.ts +9 -0
  32. package/dist/src/github/gh-pull-request-adapter.d.ts.map +1 -0
  33. package/dist/src/github/gh-pull-request-adapter.js +39 -0
  34. package/dist/src/github/gh-pull-request-adapter.js.map +1 -0
  35. package/dist/src/github/issues.d.ts +80 -0
  36. package/dist/src/github/issues.d.ts.map +1 -0
  37. package/dist/src/github/issues.js +122 -0
  38. package/dist/src/github/issues.js.map +1 -0
  39. package/dist/src/github/pull-requests.d.ts +24 -0
  40. package/dist/src/github/pull-requests.d.ts.map +1 -0
  41. package/dist/src/github/pull-requests.js +21 -0
  42. package/dist/src/github/pull-requests.js.map +1 -0
  43. package/dist/src/index.d.ts +31 -0
  44. package/dist/src/index.d.ts.map +1 -0
  45. package/dist/src/index.js +17 -0
  46. package/dist/src/index.js.map +1 -0
  47. package/dist/src/package-info.d.ts +6 -0
  48. package/dist/src/package-info.d.ts.map +1 -0
  49. package/dist/src/package-info.js +13 -0
  50. package/dist/src/package-info.js.map +1 -0
  51. package/dist/src/process/command.d.ts +15 -0
  52. package/dist/src/process/command.d.ts.map +1 -0
  53. package/dist/src/process/command.js +34 -0
  54. package/dist/src/process/command.js.map +1 -0
  55. package/dist/src/runner/command-utils.d.ts +5 -0
  56. package/dist/src/runner/command-utils.d.ts.map +1 -0
  57. package/dist/src/runner/command-utils.js +19 -0
  58. package/dist/src/runner/command-utils.js.map +1 -0
  59. package/dist/src/runner/issue-state-machine.d.ts +39 -0
  60. package/dist/src/runner/issue-state-machine.d.ts.map +1 -0
  61. package/dist/src/runner/issue-state-machine.js +119 -0
  62. package/dist/src/runner/issue-state-machine.js.map +1 -0
  63. package/dist/src/runner/issue-tree.d.ts +62 -0
  64. package/dist/src/runner/issue-tree.d.ts.map +1 -0
  65. package/dist/src/runner/issue-tree.js +376 -0
  66. package/dist/src/runner/issue-tree.js.map +1 -0
  67. package/dist/src/runner/local-state.d.ts +31 -0
  68. package/dist/src/runner/local-state.d.ts.map +1 -0
  69. package/dist/src/runner/local-state.js +125 -0
  70. package/dist/src/runner/local-state.js.map +1 -0
  71. package/dist/src/runner/plan-auto-command.d.ts +30 -0
  72. package/dist/src/runner/plan-auto-command.d.ts.map +1 -0
  73. package/dist/src/runner/plan-auto-command.js +611 -0
  74. package/dist/src/runner/plan-auto-command.js.map +1 -0
  75. package/dist/src/runner/prompt.d.ts +108 -0
  76. package/dist/src/runner/prompt.d.ts.map +1 -0
  77. package/dist/src/runner/prompt.js +322 -0
  78. package/dist/src/runner/prompt.js.map +1 -0
  79. package/dist/src/runner/recovery.d.ts +24 -0
  80. package/dist/src/runner/recovery.d.ts.map +1 -0
  81. package/dist/src/runner/recovery.js +62 -0
  82. package/dist/src/runner/recovery.js.map +1 -0
  83. package/dist/src/runner/safety.d.ts +14 -0
  84. package/dist/src/runner/safety.d.ts.map +1 -0
  85. package/dist/src/runner/safety.js +46 -0
  86. package/dist/src/runner/safety.js.map +1 -0
  87. package/dist/src/runner/scoped-auto-command.d.ts +29 -0
  88. package/dist/src/runner/scoped-auto-command.d.ts.map +1 -0
  89. package/dist/src/runner/scoped-auto-command.js +262 -0
  90. package/dist/src/runner/scoped-auto-command.js.map +1 -0
  91. package/dist/src/runner/status-command.d.ts +17 -0
  92. package/dist/src/runner/status-command.d.ts.map +1 -0
  93. package/dist/src/runner/status-command.js +72 -0
  94. package/dist/src/runner/status-command.js.map +1 -0
  95. package/dist/src/setup/github-label-adapter.d.ts +13 -0
  96. package/dist/src/setup/github-label-adapter.d.ts.map +1 -0
  97. package/dist/src/setup/github-label-adapter.js +28 -0
  98. package/dist/src/setup/github-label-adapter.js.map +1 -0
  99. package/dist/src/setup/labels.d.ts +27 -0
  100. package/dist/src/setup/labels.d.ts.map +1 -0
  101. package/dist/src/setup/labels.js +42 -0
  102. package/dist/src/setup/labels.js.map +1 -0
  103. package/dist/src/setup/project-config.d.ts +15 -0
  104. package/dist/src/setup/project-config.d.ts.map +1 -0
  105. package/dist/src/setup/project-config.js +124 -0
  106. package/dist/src/setup/project-config.js.map +1 -0
  107. package/dist/src/setup/setup-command.d.ts +22 -0
  108. package/dist/src/setup/setup-command.d.ts.map +1 -0
  109. package/dist/src/setup/setup-command.js +104 -0
  110. package/dist/src/setup/setup-command.js.map +1 -0
  111. package/dist/src/setup/workflows.d.ts +11 -0
  112. package/dist/src/setup/workflows.d.ts.map +1 -0
  113. package/dist/src/setup/workflows.js +60 -0
  114. package/dist/src/setup/workflows.js.map +1 -0
  115. package/package.json +45 -0
  116. package/prompts/setup-skill.md +11 -0
  117. package/prompts/workflows/breakdown-review.md +4 -0
  118. package/prompts/workflows/issue-breakdown.md +4 -0
  119. package/prompts/workflows/issue-tree-orchestration.md +4 -0
  120. package/prompts/workflows/prd.md +4 -0
  121. package/prompts/workflows/scoped-implementation.md +4 -0
  122. package/prompts/workflows/triage.md +4 -0
@@ -0,0 +1,180 @@
1
+ import { forbiddenRuntimeKeys, labelKeys, labelPreparationPolicies, workflowKeys, workflowSources, } from './constants.js';
2
+ export function validateConfig(input) {
3
+ const errors = [];
4
+ const root = asObject(input);
5
+ if (!root) {
6
+ return { ok: false, errors: ['config must be an object'] };
7
+ }
8
+ for (const forbiddenKey of forbiddenRuntimeKeys) {
9
+ if (forbiddenKey in root) {
10
+ errors.push(`${forbiddenKey} is runtime state and must not be committed config`);
11
+ }
12
+ }
13
+ expectLiteral(root, 'version', 1, errors);
14
+ const github = expectObject(root, 'github', errors);
15
+ const runner = expectObject(root, 'runner', errors);
16
+ const codex = expectObject(root, 'codex', errors);
17
+ const project = expectObject(root, 'project', errors);
18
+ const workflows = expectObject(root, 'workflows', errors);
19
+ const checks = expectObject(root, 'checks', errors);
20
+ const deny = expectObject(root, 'deny', errors);
21
+ const branches = expectObject(root, 'branches', errors);
22
+ const pullRequests = expectObject(root, 'pullRequests', errors);
23
+ const issueClassification = expectObject(root, 'issueClassification', errors);
24
+ if (github) {
25
+ expectString(github, 'github.owner', errors);
26
+ expectString(github, 'github.repo', errors);
27
+ expectUnion(github, 'github.prepareLabels', labelPreparationPolicies, errors);
28
+ const labels = expectObject(github, 'github.labels', errors);
29
+ if (labels) {
30
+ for (const labelKey of labelKeys) {
31
+ validateLabel(labels, `github.labels.${labelKey}`, errors);
32
+ }
33
+ }
34
+ }
35
+ if (runner) {
36
+ expectString(runner, 'runner.workspaceRoot', errors);
37
+ expectParallelLimit(runner, errors);
38
+ expectString(runner, 'runner.stateDir', errors);
39
+ }
40
+ if (codex) {
41
+ expectLiteral(codex, 'codex.adapter', 'codex-cli', errors);
42
+ expectString(codex, 'codex.command', errors);
43
+ expectStringArray(codex, 'codex.args', errors);
44
+ expectLiteral(codex, 'codex.promptFileEnv', 'CODEX_ORCHESTRATOR_PROMPT_FILE', errors);
45
+ expectLiteral(codex, 'codex.reportFileEnv', 'CODEX_ORCHESTRATOR_REPORT_FILE', errors);
46
+ }
47
+ if (project) {
48
+ expectLiteral(project, 'project.configDir', '.codex-orchestrator', errors);
49
+ expectLiteral(project, 'project.promptsDir', '.codex-orchestrator/prompts', errors);
50
+ }
51
+ if (workflows) {
52
+ for (const workflowKey of workflowKeys) {
53
+ validateWorkflow(workflows, `workflows.${workflowKey}`, errors);
54
+ }
55
+ }
56
+ if (checks) {
57
+ validateChecks(checks, errors);
58
+ }
59
+ if (deny) {
60
+ expectStringArray(deny, 'deny.secretFiles', errors);
61
+ expectBoolean(deny, 'deny.destructiveDbOrCache', errors);
62
+ expectBoolean(deny, 'deny.productionDeployOrRelease', errors);
63
+ expectStringArray(deny, 'deny.additionalPathGlobs', errors);
64
+ }
65
+ if (branches) {
66
+ expectString(branches, 'branches.base', errors);
67
+ expectString(branches, 'branches.scopedIssue', errors);
68
+ expectString(branches, 'branches.issueTree', errors);
69
+ }
70
+ if (pullRequests) {
71
+ expectString(pullRequests, 'pullRequests.scopedIssueTitle', errors);
72
+ expectString(pullRequests, 'pullRequests.issueTreeTitle', errors);
73
+ }
74
+ if (issueClassification) {
75
+ expectStringArray(issueClassification, 'issueClassification.promotionCriteria', errors);
76
+ expectLiteral(issueClassification, 'issueClassification.clarificationGate', 'block-and-comment', errors);
77
+ }
78
+ if (errors.length > 0) {
79
+ return { ok: false, errors };
80
+ }
81
+ return { ok: true, value: input };
82
+ }
83
+ function asObject(value) {
84
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
85
+ return undefined;
86
+ }
87
+ return value;
88
+ }
89
+ function readPath(parent, path) {
90
+ const lastKey = path.split('.').at(-1);
91
+ return lastKey ? parent[lastKey] : undefined;
92
+ }
93
+ function expectObject(parent, path, errors) {
94
+ const value = readPath(parent, path);
95
+ const objectValue = asObject(value);
96
+ if (!objectValue) {
97
+ errors.push(`${path} must be an object`);
98
+ return undefined;
99
+ }
100
+ return objectValue;
101
+ }
102
+ function expectString(parent, path, errors) {
103
+ const value = readPath(parent, path);
104
+ if (typeof value !== 'string' || value.length === 0) {
105
+ errors.push(`${path} must be a non-empty string`);
106
+ return undefined;
107
+ }
108
+ return value;
109
+ }
110
+ function expectBoolean(parent, path, errors) {
111
+ const value = readPath(parent, path);
112
+ if (typeof value !== 'boolean') {
113
+ errors.push(`${path} must be a boolean`);
114
+ return undefined;
115
+ }
116
+ return value;
117
+ }
118
+ function expectLiteral(parent, path, expected, errors) {
119
+ const value = readPath(parent, path);
120
+ if (value !== expected) {
121
+ errors.push(`${path} must be ${expected}`);
122
+ return undefined;
123
+ }
124
+ return expected;
125
+ }
126
+ function expectUnion(parent, path, expected, errors) {
127
+ const value = readPath(parent, path);
128
+ if (typeof value !== 'string' || !expected.includes(value)) {
129
+ errors.push(`${path} must be one of ${expected.join(', ')}`);
130
+ return undefined;
131
+ }
132
+ return value;
133
+ }
134
+ function expectParallelLimit(parent, errors) {
135
+ const value = readPath(parent, 'runner.maxParallelChildren');
136
+ if (!Number.isInteger(value) || typeof value !== 'number' || value < 1 || value > 3) {
137
+ errors.push('runner.maxParallelChildren must be an integer between 1 and 3');
138
+ return undefined;
139
+ }
140
+ return value;
141
+ }
142
+ function expectStringArray(parent, path, errors) {
143
+ const value = readPath(parent, path);
144
+ if (!Array.isArray(value) || value.some((item) => typeof item !== 'string' || item.length === 0)) {
145
+ errors.push(`${path} must be an array of non-empty strings`);
146
+ return undefined;
147
+ }
148
+ return value;
149
+ }
150
+ function validateLabel(parent, path, errors) {
151
+ const label = expectObject(parent, path, errors);
152
+ if (!label) {
153
+ return;
154
+ }
155
+ expectString(label, `${path}.name`, errors);
156
+ expectString(label, `${path}.color`, errors);
157
+ expectString(label, `${path}.description`, errors);
158
+ }
159
+ function validateWorkflow(parent, path, errors) {
160
+ const workflow = expectObject(parent, path, errors);
161
+ if (!workflow) {
162
+ return;
163
+ }
164
+ const source = expectUnion(workflow, `${path}.source`, workflowSources, errors);
165
+ expectString(workflow, `${path}.skillName`, errors);
166
+ if (source === 'existing-skill' || source === 'package-owned-skill') {
167
+ expectString(workflow, `${path}.skillPath`, errors);
168
+ }
169
+ if (source === 'package-owned-prompt-fallback') {
170
+ expectString(workflow, `${path}.promptPath`, errors);
171
+ }
172
+ }
173
+ function validateChecks(checks, errors) {
174
+ for (const [name, command] of Object.entries(checks)) {
175
+ if (name.length === 0 || typeof command !== 'string' || command.length === 0) {
176
+ errors.push('checks must map non-empty names to non-empty shell commands');
177
+ }
178
+ }
179
+ }
180
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/config/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import type { ProcessExecutor } from '../process/command.js';
2
+ export interface BranchTemplateValues {
3
+ issueNumber?: number;
4
+ parentIssueNumber?: number;
5
+ }
6
+ export interface CreateIssueWorktreeInput {
7
+ targetRoot: string;
8
+ workspacePath: string;
9
+ branchName: string;
10
+ baseBranch: string;
11
+ }
12
+ export interface CommitAllInput {
13
+ worktreePath: string;
14
+ message: string;
15
+ }
16
+ export interface PushBranchInput {
17
+ worktreePath: string;
18
+ branchName: string;
19
+ }
20
+ export interface MergeBranchInput {
21
+ worktreePath: string;
22
+ branchName: string;
23
+ message: string;
24
+ }
25
+ export interface RemoveWorktreeInput {
26
+ targetRoot: string;
27
+ worktreePath: string;
28
+ }
29
+ export declare class GitMergeConflictError extends Error {
30
+ readonly worktreePath: string;
31
+ readonly branchName: string;
32
+ readonly stdout: string;
33
+ readonly stderr: string;
34
+ constructor(worktreePath: string, branchName: string, stdout: string, stderr: string);
35
+ }
36
+ export declare class GitWorktreeManager {
37
+ private readonly executor;
38
+ constructor(executor?: ProcessExecutor);
39
+ getHead(worktreePath: string): Promise<string>;
40
+ createIssueWorktree(input: CreateIssueWorktreeInput): Promise<void>;
41
+ listChangedFiles(worktreePath: string): Promise<string[]>;
42
+ commitAll(input: CommitAllInput): Promise<void>;
43
+ pushBranch(input: PushBranchInput): Promise<void>;
44
+ mergeBranch(input: MergeBranchInput): Promise<void>;
45
+ abortMerge(worktreePath: string): Promise<void>;
46
+ removeWorktree(input: RemoveWorktreeInput): Promise<void>;
47
+ private git;
48
+ }
49
+ export declare function renderBranchTemplate(template: string, values: BranchTemplateValues): string;
50
+ //# sourceMappingURL=worktree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree.d.ts","sourceRoot":"","sources":["../../../src/git/worktree.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG7D,MAAM,WAAW,oBAAoB;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,qBAAsB,SAAQ,KAAK;aAE5B,YAAY,EAAE,MAAM;aACpB,UAAU,EAAE,MAAM;aAClB,MAAM,EAAE,MAAM;aACd,MAAM,EAAE,MAAM;gBAHd,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM;CAKjC;AAED,qBAAa,kBAAkB;IACV,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,GAAE,eAAwC;IAEzE,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK9C,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAcnE,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAKzD,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB/C,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAcjD,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBnD,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;YAIxD,GAAG;CAOlB;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAI3F"}
@@ -0,0 +1,139 @@
1
+ import { mkdir } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { defaultProcessExecutor } from '../process/command.js';
4
+ export class GitMergeConflictError extends Error {
5
+ worktreePath;
6
+ branchName;
7
+ stdout;
8
+ stderr;
9
+ constructor(worktreePath, branchName, stdout, stderr) {
10
+ super(`git merge failed for ${branchName} in ${worktreePath}`);
11
+ this.worktreePath = worktreePath;
12
+ this.branchName = branchName;
13
+ this.stdout = stdout;
14
+ this.stderr = stderr;
15
+ this.name = 'GitMergeConflictError';
16
+ }
17
+ }
18
+ export class GitWorktreeManager {
19
+ executor;
20
+ constructor(executor = defaultProcessExecutor) {
21
+ this.executor = executor;
22
+ }
23
+ async getHead(worktreePath) {
24
+ const result = await this.git(['-C', worktreePath, 'rev-parse', 'HEAD']);
25
+ return result.stdout.trim();
26
+ }
27
+ async createIssueWorktree(input) {
28
+ await mkdir(dirname(input.workspacePath), { recursive: true });
29
+ await this.git([
30
+ '-C',
31
+ input.targetRoot,
32
+ 'worktree',
33
+ 'add',
34
+ '-b',
35
+ input.branchName,
36
+ input.workspacePath,
37
+ input.baseBranch,
38
+ ]);
39
+ }
40
+ async listChangedFiles(worktreePath) {
41
+ const result = await this.git(['-C', worktreePath, 'status', '--porcelain=v1', '-z']);
42
+ return parsePorcelainChangedFiles(result.stdout);
43
+ }
44
+ async commitAll(input) {
45
+ await this.git(['-C', input.worktreePath, 'add', '--all']);
46
+ await this.git([
47
+ '-C',
48
+ input.worktreePath,
49
+ '-c',
50
+ 'core.hooksPath=/dev/null',
51
+ '-c',
52
+ 'user.name=codex-orchestrator',
53
+ '-c',
54
+ 'user.email=codex-orchestrator@users.noreply.github.com',
55
+ 'commit',
56
+ '--no-verify',
57
+ '-m',
58
+ input.message,
59
+ ]);
60
+ }
61
+ async pushBranch(input) {
62
+ await this.git([
63
+ '-C',
64
+ input.worktreePath,
65
+ '-c',
66
+ 'core.hooksPath=/dev/null',
67
+ 'push',
68
+ '--no-verify',
69
+ '-u',
70
+ 'origin',
71
+ input.branchName,
72
+ ]);
73
+ }
74
+ async mergeBranch(input) {
75
+ const result = await this.executor('git', [
76
+ '-C',
77
+ input.worktreePath,
78
+ '-c',
79
+ 'core.hooksPath=/dev/null',
80
+ '-c',
81
+ 'user.name=codex-orchestrator',
82
+ '-c',
83
+ 'user.email=codex-orchestrator@users.noreply.github.com',
84
+ 'merge',
85
+ '--no-ff',
86
+ input.branchName,
87
+ '-m',
88
+ input.message,
89
+ ]);
90
+ if (result.exitCode !== 0) {
91
+ throw new GitMergeConflictError(input.worktreePath, input.branchName, result.stdout, result.stderr);
92
+ }
93
+ }
94
+ async abortMerge(worktreePath) {
95
+ await this.git(['-C', worktreePath, 'merge', '--abort']);
96
+ }
97
+ async removeWorktree(input) {
98
+ await this.git(['-C', input.targetRoot, 'worktree', 'remove', input.worktreePath]);
99
+ }
100
+ async git(args) {
101
+ const result = await this.executor('git', args);
102
+ if (result.exitCode !== 0) {
103
+ throw new Error(`git command failed: git ${args.join(' ')}\n${result.stderr}`);
104
+ }
105
+ return result;
106
+ }
107
+ }
108
+ export function renderBranchTemplate(template, values) {
109
+ return template
110
+ .replaceAll('${issueNumber}', values.issueNumber === undefined ? '' : String(values.issueNumber))
111
+ .replaceAll('${parentIssueNumber}', values.parentIssueNumber === undefined ? '' : String(values.parentIssueNumber));
112
+ }
113
+ function parsePorcelainChangedFiles(output) {
114
+ const entries = output.split('\0').filter(Boolean);
115
+ const files = [];
116
+ for (let index = 0; index < entries.length; index += 1) {
117
+ const entry = entries[index] ?? '';
118
+ const status = entry.slice(0, 2);
119
+ const path = entry.slice(3);
120
+ if (status.startsWith('R') || status.startsWith('C')) {
121
+ const nextPath = entries[index + 1];
122
+ if (path) {
123
+ files.push(normalizePath(path));
124
+ }
125
+ if (nextPath) {
126
+ files.push(normalizePath(nextPath));
127
+ index += 1;
128
+ }
129
+ }
130
+ else if (path) {
131
+ files.push(normalizePath(path));
132
+ }
133
+ }
134
+ return Array.from(new Set(files));
135
+ }
136
+ function normalizePath(path) {
137
+ return path.replaceAll('\\', '/');
138
+ }
139
+ //# sourceMappingURL=worktree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../../src/git/worktree.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface CommandResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ }
5
+ export type CommandExecutor = (file: string, args: string[]) => Promise<CommandResult>;
6
+ export interface CommandExecutionError extends Error {
7
+ code?: number;
8
+ stderr?: string;
9
+ }
10
+ export declare function defaultGhExecutor(file: string, args: string[]): Promise<CommandResult>;
11
+ //# sourceMappingURL=gh-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gh-cli.d.ts","sourceRoot":"","sources":["../../../src/github/gh-cli.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;AAEvF,MAAM,WAAW,qBAAsB,SAAQ,KAAK;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAsB5F"}
@@ -0,0 +1,28 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ const execFileAsync = promisify(execFile);
4
+ export async function defaultGhExecutor(file, args) {
5
+ try {
6
+ const result = await execFileAsync(file, args);
7
+ return {
8
+ stdout: result.stdout,
9
+ stderr: result.stderr,
10
+ };
11
+ }
12
+ catch (error) {
13
+ const message = error instanceof Error ? error.message : 'unknown error';
14
+ const commandError = new Error(`Failed to run ${file} ${args.join(' ')}: ${message}`);
15
+ if (typeof error === 'object' && error !== null) {
16
+ const code = error.code;
17
+ const stderr = error.stderr;
18
+ if (typeof code === 'number') {
19
+ commandError.code = code;
20
+ }
21
+ if (typeof stderr === 'string') {
22
+ commandError.stderr = stderr;
23
+ }
24
+ }
25
+ throw commandError;
26
+ }
27
+ }
28
+ //# sourceMappingURL=gh-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gh-cli.js","sourceRoot":"","sources":["../../../src/github/gh-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAc1C,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,IAAc;IAClE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC/C,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,MAAM,EAAE,MAAM,CAAC,MAAM;SACtB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACzE,MAAM,YAAY,GAAG,IAAI,KAAK,CAAC,iBAAiB,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,OAAO,EAAE,CAA0B,CAAC;QAC/G,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YAChD,MAAM,IAAI,GAAI,KAA4B,CAAC,IAAI,CAAC;YAChD,MAAM,MAAM,GAAI,KAA8B,CAAC,MAAM,CAAC;YACtD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC7B,YAAY,CAAC,IAAI,GAAG,IAAI,CAAC;YAC3B,CAAC;YACD,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;gBAC/B,YAAY,CAAC,MAAM,GAAG,MAAM,CAAC;YAC/B,CAAC;QACH,CAAC;QACD,MAAM,YAAY,CAAC;IACrB,CAAC;AACH,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { CommandExecutor } from './gh-cli.js';
2
+ import type { CreateIssueInput, GitHubIssue, GitHubIssueAdapter, UpdateIssueInput } from './issues.js';
3
+ export declare class GhCliIssueAdapter implements GitHubIssueAdapter {
4
+ private readonly repo;
5
+ private readonly executor;
6
+ constructor(owner: string, repo: string, executor?: CommandExecutor);
7
+ listOpenIssuesWithAnyLabel(labels: string[]): Promise<GitHubIssue[]>;
8
+ getIssue(number: number): Promise<GitHubIssue | undefined>;
9
+ createIssue(input: CreateIssueInput): Promise<GitHubIssue>;
10
+ updateIssue(issueNumber: number, input: UpdateIssueInput): Promise<GitHubIssue>;
11
+ addLabels(issueNumber: number, labels: string[]): Promise<void>;
12
+ removeLabels(issueNumber: number, labels: string[]): Promise<void>;
13
+ postComment(issueNumber: number, body: string): Promise<void>;
14
+ }
15
+ //# sourceMappingURL=gh-issue-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gh-issue-adapter.d.ts","sourceRoot":"","sources":["../../../src/github/gh-issue-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAyB,eAAe,EAAE,MAAM,aAAa,CAAC;AAE1E,OAAO,KAAK,EACV,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAMlB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAIrB,qBAAa,iBAAkB,YAAW,kBAAkB;IAC1D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;gBAExB,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,eAAmC;IAKhF,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAsBpE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAoB1D,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;IAkB1D,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;IAsB/E,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/D,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAMlE,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG3E"}
@@ -0,0 +1,193 @@
1
+ import { defaultGhExecutor } from './gh-cli.js';
2
+ const issueJsonFields = 'number,title,body,url,state,labels,comments,closedByPullRequestsReferences';
3
+ export class GhCliIssueAdapter {
4
+ repo;
5
+ executor;
6
+ constructor(owner, repo, executor = defaultGhExecutor) {
7
+ this.repo = `${owner}/${repo}`;
8
+ this.executor = executor;
9
+ }
10
+ async listOpenIssuesWithAnyLabel(labels) {
11
+ const wanted = new Set(labels);
12
+ const result = await this.executor('gh', [
13
+ 'issue',
14
+ 'list',
15
+ '--repo',
16
+ this.repo,
17
+ '--state',
18
+ 'open',
19
+ '--limit',
20
+ '1000',
21
+ '--json',
22
+ issueJsonFields,
23
+ ]);
24
+ const parsed = JSON.parse(result.stdout);
25
+ return parsed
26
+ .map(normalizeIssue)
27
+ .filter((issue) => issue.labels.some((label) => wanted.has(label.name)))
28
+ .sort((left, right) => left.number - right.number);
29
+ }
30
+ async getIssue(number) {
31
+ try {
32
+ const result = await this.executor('gh', [
33
+ 'issue',
34
+ 'view',
35
+ String(number),
36
+ '--repo',
37
+ this.repo,
38
+ '--json',
39
+ issueJsonFields,
40
+ ]);
41
+ return normalizeIssue(JSON.parse(result.stdout));
42
+ }
43
+ catch (error) {
44
+ if (isIssueNotFound(error)) {
45
+ return undefined;
46
+ }
47
+ throw error;
48
+ }
49
+ }
50
+ async createIssue(input) {
51
+ const args = ['issue', 'create', '--repo', this.repo, '--title', input.title, '--body', input.body];
52
+ for (const label of input.labels) {
53
+ args.push('--label', label);
54
+ }
55
+ const result = await this.executor('gh', args);
56
+ const match = result.stdout.match(/\/issues\/(\d+)/);
57
+ if (!match?.[1]) {
58
+ throw new Error('gh issue create did not return an issue URL');
59
+ }
60
+ const issueNumber = Number(match[1]);
61
+ const issue = await this.getIssue(issueNumber);
62
+ if (!issue) {
63
+ throw new Error(`Created issue #${issueNumber} was not found`);
64
+ }
65
+ return issue;
66
+ }
67
+ async updateIssue(issueNumber, input) {
68
+ const args = ['issue', 'edit', String(issueNumber), '--repo', this.repo];
69
+ if (input.title !== undefined) {
70
+ args.push('--title', input.title);
71
+ }
72
+ if (input.body !== undefined) {
73
+ args.push('--body', input.body);
74
+ }
75
+ for (const label of input.addLabels ?? []) {
76
+ args.push('--add-label', label);
77
+ }
78
+ for (const label of input.removeLabels ?? []) {
79
+ args.push('--remove-label', label);
80
+ }
81
+ await this.executor('gh', args);
82
+ const issue = await this.getIssue(issueNumber);
83
+ if (!issue) {
84
+ throw new Error(`Updated issue #${issueNumber} was not found`);
85
+ }
86
+ return issue;
87
+ }
88
+ async addLabels(issueNumber, labels) {
89
+ for (const label of labels) {
90
+ await this.executor('gh', ['issue', 'edit', String(issueNumber), '--repo', this.repo, '--add-label', label]);
91
+ }
92
+ }
93
+ async removeLabels(issueNumber, labels) {
94
+ for (const label of labels) {
95
+ await this.executor('gh', ['issue', 'edit', String(issueNumber), '--repo', this.repo, '--remove-label', label]);
96
+ }
97
+ }
98
+ async postComment(issueNumber, body) {
99
+ await this.executor('gh', ['issue', 'comment', String(issueNumber), '--repo', this.repo, '--body', body]);
100
+ }
101
+ }
102
+ function isIssueNotFound(error) {
103
+ if (!(error instanceof Error)) {
104
+ return false;
105
+ }
106
+ const commandError = error;
107
+ return commandError.code === 1 && /(not found|could not resolve to an issue or pull request)/i.test(commandError.stderr ?? '');
108
+ }
109
+ function normalizeIssue(input) {
110
+ if (typeof input !== 'object' || input === null) {
111
+ throw new Error('GitHub issue payload must be an object');
112
+ }
113
+ const record = input;
114
+ const number = readNumber(record, 'number');
115
+ const title = readString(record, 'title');
116
+ const body = readString(record, 'body');
117
+ const url = readString(record, 'url');
118
+ const state = readIssueState(record.state);
119
+ return {
120
+ number,
121
+ title,
122
+ body,
123
+ url,
124
+ state,
125
+ labels: readArray(record.labels).flatMap(normalizeLabel),
126
+ comments: readArray(record.comments).flatMap(normalizeComment),
127
+ closedByPullRequestsReferences: readArray(record.closedByPullRequestsReferences).flatMap(normalizePullRequestLink),
128
+ };
129
+ }
130
+ function normalizeLabel(input) {
131
+ if (typeof input !== 'object' || input === null) {
132
+ return [];
133
+ }
134
+ const name = input.name;
135
+ return typeof name === 'string' ? [{ name }] : [];
136
+ }
137
+ function normalizeComment(input) {
138
+ if (typeof input !== 'object' || input === null) {
139
+ return [];
140
+ }
141
+ const record = input;
142
+ const id = typeof record.id === 'string' ? record.id : '';
143
+ const url = typeof record.url === 'string' ? record.url : '';
144
+ const body = typeof record.body === 'string' ? record.body : '';
145
+ const createdAt = typeof record.createdAt === 'string' ? record.createdAt : '';
146
+ const author = typeof record.author === 'object' && record.author !== null ? record.author : {};
147
+ const login = typeof author.login === 'string' ? author.login : '';
148
+ const authorAssociation = typeof record.authorAssociation === 'string' ? record.authorAssociation : '';
149
+ return [{ id, url, body, createdAt, author: { login }, authorAssociation }];
150
+ }
151
+ function normalizePullRequestLink(input) {
152
+ if (typeof input !== 'object' || input === null) {
153
+ return [];
154
+ }
155
+ const record = input;
156
+ return [
157
+ {
158
+ number: readNumber(record, 'number'),
159
+ url: readString(record, 'url'),
160
+ state: readPullRequestState(record.state),
161
+ },
162
+ ];
163
+ }
164
+ function readArray(input) {
165
+ return Array.isArray(input) ? input : [];
166
+ }
167
+ function readNumber(record, key) {
168
+ const value = record[key];
169
+ if (typeof value !== 'number') {
170
+ throw new Error(`GitHub issue payload ${key} must be a number`);
171
+ }
172
+ return value;
173
+ }
174
+ function readString(record, key) {
175
+ const value = record[key];
176
+ if (typeof value !== 'string') {
177
+ throw new Error(`GitHub issue payload ${key} must be a string`);
178
+ }
179
+ return value;
180
+ }
181
+ function readIssueState(value) {
182
+ if (value === 'OPEN' || value === 'CLOSED') {
183
+ return value;
184
+ }
185
+ throw new Error('GitHub issue payload state must be OPEN or CLOSED');
186
+ }
187
+ function readPullRequestState(value) {
188
+ if (value === 'OPEN' || value === 'CLOSED' || value === 'MERGED') {
189
+ return value;
190
+ }
191
+ throw new Error('GitHub pull request payload state must be OPEN, CLOSED, or MERGED');
192
+ }
193
+ //# sourceMappingURL=gh-issue-adapter.js.map