forge-cc 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/.forge.json +5 -0
- package/AGENTS.md +42 -0
- package/README.md +283 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +148 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/loader.d.ts +2 -0
- package/dist/config/loader.js +44 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +57 -0
- package/dist/config/schema.js +15 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/gates/index.d.ts +11 -0
- package/dist/gates/index.js +106 -0
- package/dist/gates/index.js.map +1 -0
- package/dist/gates/lint-gate.d.ts +2 -0
- package/dist/gates/lint-gate.js +66 -0
- package/dist/gates/lint-gate.js.map +1 -0
- package/dist/gates/prd-gate.d.ts +7 -0
- package/dist/gates/prd-gate.js +193 -0
- package/dist/gates/prd-gate.js.map +1 -0
- package/dist/gates/runtime-gate.d.ts +5 -0
- package/dist/gates/runtime-gate.js +99 -0
- package/dist/gates/runtime-gate.js.map +1 -0
- package/dist/gates/tests-gate.d.ts +2 -0
- package/dist/gates/tests-gate.js +116 -0
- package/dist/gates/tests-gate.js.map +1 -0
- package/dist/gates/types-gate.d.ts +2 -0
- package/dist/gates/types-gate.js +59 -0
- package/dist/gates/types-gate.js.map +1 -0
- package/dist/gates/visual-gate.d.ts +6 -0
- package/dist/gates/visual-gate.js +118 -0
- package/dist/gates/visual-gate.js.map +1 -0
- package/dist/go/auto-chain.d.ts +107 -0
- package/dist/go/auto-chain.js +303 -0
- package/dist/go/auto-chain.js.map +1 -0
- package/dist/go/executor.d.ts +130 -0
- package/dist/go/executor.js +409 -0
- package/dist/go/executor.js.map +1 -0
- package/dist/go/finalize.d.ts +58 -0
- package/dist/go/finalize.js +200 -0
- package/dist/go/finalize.js.map +1 -0
- package/dist/go/linear-sync.d.ts +75 -0
- package/dist/go/linear-sync.js +239 -0
- package/dist/go/linear-sync.js.map +1 -0
- package/dist/go/verify-loop.d.ts +47 -0
- package/dist/go/verify-loop.js +172 -0
- package/dist/go/verify-loop.js.map +1 -0
- package/dist/hooks/pre-commit.d.ts +5 -0
- package/dist/hooks/pre-commit.js +69 -0
- package/dist/hooks/pre-commit.js.map +1 -0
- package/dist/linear/client.d.ts +108 -0
- package/dist/linear/client.js +388 -0
- package/dist/linear/client.js.map +1 -0
- package/dist/linear/issues.d.ts +20 -0
- package/dist/linear/issues.js +39 -0
- package/dist/linear/issues.js.map +1 -0
- package/dist/linear/milestones.d.ts +11 -0
- package/dist/linear/milestones.js +32 -0
- package/dist/linear/milestones.js.map +1 -0
- package/dist/linear/projects.d.ts +16 -0
- package/dist/linear/projects.js +50 -0
- package/dist/linear/projects.js.map +1 -0
- package/dist/reporter/human.d.ts +2 -0
- package/dist/reporter/human.js +63 -0
- package/dist/reporter/human.js.map +1 -0
- package/dist/reporter/json.d.ts +2 -0
- package/dist/reporter/json.js +4 -0
- package/dist/reporter/json.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +109 -0
- package/dist/server.js.map +1 -0
- package/dist/spec/generator.d.ts +14 -0
- package/dist/spec/generator.js +206 -0
- package/dist/spec/generator.js.map +1 -0
- package/dist/spec/interview.d.ts +104 -0
- package/dist/spec/interview.js +342 -0
- package/dist/spec/interview.js.map +1 -0
- package/dist/spec/linear-sync.d.ts +48 -0
- package/dist/spec/linear-sync.js +125 -0
- package/dist/spec/linear-sync.js.map +1 -0
- package/dist/spec/scanner.d.ts +45 -0
- package/dist/spec/scanner.js +473 -0
- package/dist/spec/scanner.js.map +1 -0
- package/dist/spec/templates.d.ts +345 -0
- package/dist/spec/templates.js +86 -0
- package/dist/spec/templates.js.map +1 -0
- package/dist/state/reader.d.ts +29 -0
- package/dist/state/reader.js +116 -0
- package/dist/state/reader.js.map +1 -0
- package/dist/state/writer.d.ts +60 -0
- package/dist/state/writer.js +222 -0
- package/dist/state/writer.js.map +1 -0
- package/dist/types.d.ts +64 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/browser.d.ts +10 -0
- package/dist/utils/browser.js +89 -0
- package/dist/utils/browser.js.map +1 -0
- package/hooks/pre-commit-verify.js +103 -0
- package/package.json +68 -0
- package/skills/README.md +33 -0
- package/skills/forge-go.md +332 -0
- package/skills/forge-spec.md +251 -0
- package/skills/forge-triage.md +133 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Final Milestone Detection and PR Creation
|
|
3
|
+
*
|
|
4
|
+
* Handles the transition from "last milestone complete" to "PR ready for review".
|
|
5
|
+
* Uses `gh pr create` to open a pull request with a forge verification report
|
|
6
|
+
* in the body. Designed to be called by the execution engine after the final
|
|
7
|
+
* milestone passes verification.
|
|
8
|
+
*/
|
|
9
|
+
import { execSync } from "node:child_process";
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// buildPRTitle
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
/**
|
|
14
|
+
* Generate a PR title summarizing all completed milestones.
|
|
15
|
+
*
|
|
16
|
+
* Examples:
|
|
17
|
+
* - `feat: my-project — Milestone 1 complete`
|
|
18
|
+
* - `feat: my-project — Milestones 1-5 complete`
|
|
19
|
+
*/
|
|
20
|
+
export function buildPRTitle(project, milestoneCount) {
|
|
21
|
+
if (milestoneCount <= 0) {
|
|
22
|
+
return `feat: ${project} — implementation complete`;
|
|
23
|
+
}
|
|
24
|
+
if (milestoneCount === 1) {
|
|
25
|
+
return `feat: ${project} — Milestone 1 complete`;
|
|
26
|
+
}
|
|
27
|
+
return `feat: ${project} — Milestones 1-${milestoneCount} complete`;
|
|
28
|
+
}
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// buildVerificationSection
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
/**
|
|
33
|
+
* Format a PipelineResult into a PR-ready verification section.
|
|
34
|
+
*
|
|
35
|
+
* Includes gate-level pass/fail status with error counts and
|
|
36
|
+
* duration information. Designed to be embedded in a PR body.
|
|
37
|
+
*/
|
|
38
|
+
export function buildVerificationSection(result) {
|
|
39
|
+
const lines = [];
|
|
40
|
+
const status = result.passed ? "PASSED" : "FAILED";
|
|
41
|
+
lines.push(`**Status:** ${status}`);
|
|
42
|
+
lines.push(`**Iterations:** ${result.iteration}/${result.maxIterations}`);
|
|
43
|
+
lines.push("");
|
|
44
|
+
// Gate results table
|
|
45
|
+
lines.push("| Gate | Status | Duration | Details |");
|
|
46
|
+
lines.push("|------|--------|----------|---------|");
|
|
47
|
+
for (const gate of result.gates) {
|
|
48
|
+
const statusIcon = gate.passed ? "Pass" : "Fail";
|
|
49
|
+
const duration = `${(gate.duration_ms / 1000).toFixed(1)}s`;
|
|
50
|
+
let details = "";
|
|
51
|
+
if (!gate.passed && gate.errors.length > 0) {
|
|
52
|
+
details = `${gate.errors.length} error${gate.errors.length === 1 ? "" : "s"}`;
|
|
53
|
+
}
|
|
54
|
+
else if (gate.passed && gate.warnings.length > 0) {
|
|
55
|
+
details = `${gate.warnings.length} warning${gate.warnings.length === 1 ? "" : "s"}`;
|
|
56
|
+
}
|
|
57
|
+
else if (gate.passed) {
|
|
58
|
+
details = "Clean";
|
|
59
|
+
}
|
|
60
|
+
lines.push(`| ${gate.gate} | ${statusIcon} | ${duration} | ${details} |`);
|
|
61
|
+
}
|
|
62
|
+
lines.push("");
|
|
63
|
+
// Error details (collapsed for readability)
|
|
64
|
+
const failedGates = result.gates.filter((g) => !g.passed && g.errors.length > 0);
|
|
65
|
+
if (failedGates.length > 0) {
|
|
66
|
+
lines.push("<details>");
|
|
67
|
+
lines.push("<summary>Error Details</summary>");
|
|
68
|
+
lines.push("");
|
|
69
|
+
for (const gate of failedGates) {
|
|
70
|
+
lines.push(`**${gate.gate}:**`);
|
|
71
|
+
for (const err of gate.errors) {
|
|
72
|
+
const loc = err.file
|
|
73
|
+
? `${err.file}${err.line ? `:${err.line}` : ""}`
|
|
74
|
+
: "";
|
|
75
|
+
const prefix = loc ? `\`${loc}\`: ` : "";
|
|
76
|
+
lines.push(`- ${prefix}${err.message}`);
|
|
77
|
+
}
|
|
78
|
+
lines.push("");
|
|
79
|
+
}
|
|
80
|
+
lines.push("</details>");
|
|
81
|
+
lines.push("");
|
|
82
|
+
}
|
|
83
|
+
return lines.join("\n");
|
|
84
|
+
}
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// buildPRBody
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
/**
|
|
89
|
+
* Assemble the complete PR body from milestones, verification, and metadata.
|
|
90
|
+
*/
|
|
91
|
+
function buildPRBody(options) {
|
|
92
|
+
const lines = [];
|
|
93
|
+
// Summary section — milestone checklist
|
|
94
|
+
lines.push("## Summary");
|
|
95
|
+
lines.push("");
|
|
96
|
+
for (const m of options.milestones) {
|
|
97
|
+
const checkbox = m.success ? "[x]" : "[ ]";
|
|
98
|
+
lines.push(`- ${checkbox} M${m.number}: ${m.name}`);
|
|
99
|
+
}
|
|
100
|
+
lines.push("");
|
|
101
|
+
// Verification report section
|
|
102
|
+
lines.push("## Verification Report");
|
|
103
|
+
lines.push("");
|
|
104
|
+
if (options.verificationReport) {
|
|
105
|
+
lines.push(options.verificationReport);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
lines.push("_No verification report available._");
|
|
109
|
+
}
|
|
110
|
+
lines.push("");
|
|
111
|
+
// Details section
|
|
112
|
+
lines.push("## Details");
|
|
113
|
+
lines.push(`- **Branch:** ${options.branch}`);
|
|
114
|
+
if (options.commitSha) {
|
|
115
|
+
lines.push(`- **Commit:** ${options.commitSha.slice(0, 8)}`);
|
|
116
|
+
}
|
|
117
|
+
lines.push("");
|
|
118
|
+
// Footer
|
|
119
|
+
lines.push("---");
|
|
120
|
+
lines.push("Generated with [forge-cc](https://github.com/troyhoffman/forge-cc)");
|
|
121
|
+
return lines.join("\n");
|
|
122
|
+
}
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// createPullRequest
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
/**
|
|
127
|
+
* Create a GitHub pull request using the `gh` CLI.
|
|
128
|
+
*
|
|
129
|
+
* Builds a structured PR body with milestone status, verification report,
|
|
130
|
+
* and metadata, then calls `gh pr create`. If `gh` is not installed or the
|
|
131
|
+
* command fails, returns a descriptive error result instead of throwing.
|
|
132
|
+
*/
|
|
133
|
+
export function createPullRequest(options) {
|
|
134
|
+
const baseBranch = options.baseBranch ?? "main";
|
|
135
|
+
const body = buildPRBody(options);
|
|
136
|
+
// Check that gh CLI is available
|
|
137
|
+
try {
|
|
138
|
+
execSync("gh --version", { cwd: options.projectDir, stdio: "pipe" });
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return {
|
|
142
|
+
url: "",
|
|
143
|
+
number: 0,
|
|
144
|
+
title: options.title,
|
|
145
|
+
created: false,
|
|
146
|
+
error: "GitHub CLI (gh) is not installed or not in PATH. " +
|
|
147
|
+
"Install it from https://cli.github.com/ and run `gh auth login`.",
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// Create the PR
|
|
151
|
+
try {
|
|
152
|
+
// Write body to a temp approach using stdin to avoid shell escaping issues
|
|
153
|
+
const output = execSync(`gh pr create --title ${JSON.stringify(options.title)} --base ${JSON.stringify(baseBranch)} --body ${JSON.stringify(body)}`, {
|
|
154
|
+
cwd: options.projectDir,
|
|
155
|
+
encoding: "utf-8",
|
|
156
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
157
|
+
}).trim();
|
|
158
|
+
// gh pr create outputs the PR URL on success
|
|
159
|
+
const url = output.trim();
|
|
160
|
+
const prNumberMatch = url.match(/\/pull\/(\d+)/);
|
|
161
|
+
const prNumber = prNumberMatch ? parseInt(prNumberMatch[1], 10) : 0;
|
|
162
|
+
return {
|
|
163
|
+
url,
|
|
164
|
+
number: prNumber,
|
|
165
|
+
title: options.title,
|
|
166
|
+
created: true,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
171
|
+
// Check for common failure modes
|
|
172
|
+
if (message.includes("already exists")) {
|
|
173
|
+
return {
|
|
174
|
+
url: "",
|
|
175
|
+
number: 0,
|
|
176
|
+
title: options.title,
|
|
177
|
+
created: false,
|
|
178
|
+
error: `A pull request already exists for branch '${options.branch}'. Close or merge the existing PR first.`,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (message.includes("not authenticated") ||
|
|
182
|
+
message.includes("auth login")) {
|
|
183
|
+
return {
|
|
184
|
+
url: "",
|
|
185
|
+
number: 0,
|
|
186
|
+
title: options.title,
|
|
187
|
+
created: false,
|
|
188
|
+
error: "GitHub CLI is not authenticated. Run `gh auth login` to authenticate.",
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
url: "",
|
|
193
|
+
number: 0,
|
|
194
|
+
title: options.title,
|
|
195
|
+
created: false,
|
|
196
|
+
error: `Failed to create PR: ${message}`,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=finalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finalize.js","sourceRoot":"","sources":["../../src/go/finalize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linear Status Sync for Execution Engine
|
|
3
|
+
*
|
|
4
|
+
* Bridges the milestone executor with the Linear project management client.
|
|
5
|
+
* Manages issue and project state transitions during the go execution flow:
|
|
6
|
+
*
|
|
7
|
+
* - Milestone start: issues -> In Progress, project -> In Progress
|
|
8
|
+
* - Mid-execution: progress comments on milestone issues
|
|
9
|
+
* - Milestone complete: issues -> In Review (last milestone) or progress comment
|
|
10
|
+
*
|
|
11
|
+
* All operations degrade gracefully — the execution engine must never fail
|
|
12
|
+
* because Linear is unavailable or misconfigured.
|
|
13
|
+
*/
|
|
14
|
+
export interface LinearSyncOptions {
|
|
15
|
+
projectId: string;
|
|
16
|
+
milestoneNumber: number;
|
|
17
|
+
milestoneName: string;
|
|
18
|
+
apiKey?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface MilestoneStartSync {
|
|
21
|
+
linearMilestoneId: string | null;
|
|
22
|
+
issuesUpdated: number;
|
|
23
|
+
projectUpdated: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface MilestoneCompleteOptions extends LinearSyncOptions {
|
|
26
|
+
isLastMilestone: boolean;
|
|
27
|
+
prUrl?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface MilestoneCompleteSync {
|
|
30
|
+
issuesUpdated: number;
|
|
31
|
+
projectUpdated: boolean;
|
|
32
|
+
finalState: string;
|
|
33
|
+
}
|
|
34
|
+
export interface ProgressCommentOptions {
|
|
35
|
+
projectId: string;
|
|
36
|
+
milestoneNumber: number;
|
|
37
|
+
milestoneName: string;
|
|
38
|
+
message: string;
|
|
39
|
+
apiKey?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Execute a Linear operation safely — catches LinearClientError and logs
|
|
43
|
+
* a warning instead of crashing. The execution engine should never fail
|
|
44
|
+
* because Linear is unavailable.
|
|
45
|
+
*
|
|
46
|
+
* Returns the function's result on success, or null on failure.
|
|
47
|
+
*/
|
|
48
|
+
export declare function syncLinearSafe<T>(fn: () => Promise<T>): Promise<T | null>;
|
|
49
|
+
/**
|
|
50
|
+
* Sync Linear state when a milestone starts execution.
|
|
51
|
+
*
|
|
52
|
+
* - Finds the Linear milestone by name
|
|
53
|
+
* - Transitions all milestone issues to "In Progress"
|
|
54
|
+
* - Transitions the project to "In Progress" (if not already)
|
|
55
|
+
*
|
|
56
|
+
* Degrades gracefully if Linear is unavailable or the milestone is not found.
|
|
57
|
+
*/
|
|
58
|
+
export declare function syncMilestoneStart(options: LinearSyncOptions): Promise<MilestoneStartSync>;
|
|
59
|
+
/**
|
|
60
|
+
* Sync Linear state when a milestone completes.
|
|
61
|
+
*
|
|
62
|
+
* - If NOT the last milestone: adds a progress comment to each issue
|
|
63
|
+
* - If IS the last milestone: transitions all project issues to "In Review",
|
|
64
|
+
* transitions the project to "In Review"
|
|
65
|
+
*
|
|
66
|
+
* Degrades gracefully if Linear is unavailable.
|
|
67
|
+
*/
|
|
68
|
+
export declare function syncMilestoneComplete(options: MilestoneCompleteOptions): Promise<MilestoneCompleteSync>;
|
|
69
|
+
/**
|
|
70
|
+
* Add a progress comment to all issues in a milestone.
|
|
71
|
+
* Used during execution to keep Linear updated with wave progress.
|
|
72
|
+
*
|
|
73
|
+
* Degrades gracefully if Linear is unavailable or the milestone is not found.
|
|
74
|
+
*/
|
|
75
|
+
export declare function addMilestoneProgressComment(options: ProgressCommentOptions): Promise<void>;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linear Status Sync for Execution Engine
|
|
3
|
+
*
|
|
4
|
+
* Bridges the milestone executor with the Linear project management client.
|
|
5
|
+
* Manages issue and project state transitions during the go execution flow:
|
|
6
|
+
*
|
|
7
|
+
* - Milestone start: issues -> In Progress, project -> In Progress
|
|
8
|
+
* - Mid-execution: progress comments on milestone issues
|
|
9
|
+
* - Milestone complete: issues -> In Review (last milestone) or progress comment
|
|
10
|
+
*
|
|
11
|
+
* All operations degrade gracefully — the execution engine must never fail
|
|
12
|
+
* because Linear is unavailable or misconfigured.
|
|
13
|
+
*/
|
|
14
|
+
import { LinearClient, LinearClientError } from "../linear/client.js";
|
|
15
|
+
import { transitionProject } from "../linear/projects.js";
|
|
16
|
+
import { transitionMilestoneIssues } from "../linear/issues.js";
|
|
17
|
+
import { findMilestoneByName } from "../linear/milestones.js";
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Safe Wrapper
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
/**
|
|
22
|
+
* Execute a Linear operation safely — catches LinearClientError and logs
|
|
23
|
+
* a warning instead of crashing. The execution engine should never fail
|
|
24
|
+
* because Linear is unavailable.
|
|
25
|
+
*
|
|
26
|
+
* Returns the function's result on success, or null on failure.
|
|
27
|
+
*/
|
|
28
|
+
export async function syncLinearSafe(fn) {
|
|
29
|
+
try {
|
|
30
|
+
return await fn();
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (error instanceof LinearClientError) {
|
|
34
|
+
console.warn(`[linear-sync] Linear operation failed: ${error.message}`);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
// Re-throw unexpected errors — they indicate bugs, not Linear issues
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Client Creation
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
/**
|
|
45
|
+
* Attempt to create a LinearClient. Returns null and logs a warning
|
|
46
|
+
* if no API key is available (graceful degradation).
|
|
47
|
+
*/
|
|
48
|
+
function createClientSafe(apiKey) {
|
|
49
|
+
try {
|
|
50
|
+
return new LinearClient(apiKey);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error instanceof LinearClientError) {
|
|
54
|
+
console.warn(`[linear-sync] Linear client unavailable: ${error.message}`);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Milestone Start
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
/**
|
|
64
|
+
* Sync Linear state when a milestone starts execution.
|
|
65
|
+
*
|
|
66
|
+
* - Finds the Linear milestone by name
|
|
67
|
+
* - Transitions all milestone issues to "In Progress"
|
|
68
|
+
* - Transitions the project to "In Progress" (if not already)
|
|
69
|
+
*
|
|
70
|
+
* Degrades gracefully if Linear is unavailable or the milestone is not found.
|
|
71
|
+
*/
|
|
72
|
+
export async function syncMilestoneStart(options) {
|
|
73
|
+
const noopResult = {
|
|
74
|
+
linearMilestoneId: null,
|
|
75
|
+
issuesUpdated: 0,
|
|
76
|
+
projectUpdated: false,
|
|
77
|
+
};
|
|
78
|
+
const client = createClientSafe(options.apiKey);
|
|
79
|
+
if (!client)
|
|
80
|
+
return noopResult;
|
|
81
|
+
const result = await syncLinearSafe(async () => {
|
|
82
|
+
// Find the milestone in Linear
|
|
83
|
+
const milestone = await findMilestoneByName(client, options.projectId, options.milestoneName);
|
|
84
|
+
if (!milestone) {
|
|
85
|
+
console.warn(`[linear-sync] Milestone "${options.milestoneName}" not found in project ${options.projectId}. Skipping issue transitions.`);
|
|
86
|
+
// Still try to transition the project
|
|
87
|
+
let projectUpdated = false;
|
|
88
|
+
try {
|
|
89
|
+
await transitionProject(client, options.projectId, "In Progress");
|
|
90
|
+
projectUpdated = true;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Project may already be In Progress or beyond — that's fine
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
linearMilestoneId: null,
|
|
97
|
+
issuesUpdated: 0,
|
|
98
|
+
projectUpdated,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// Transition milestone issues to In Progress
|
|
102
|
+
const { updated } = await transitionMilestoneIssues(client, options.projectId, milestone.id, "In Progress");
|
|
103
|
+
// Transition project to In Progress
|
|
104
|
+
let projectUpdated = false;
|
|
105
|
+
try {
|
|
106
|
+
await transitionProject(client, options.projectId, "In Progress");
|
|
107
|
+
projectUpdated = true;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Project may already be In Progress or beyond — that's fine
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
linearMilestoneId: milestone.id,
|
|
114
|
+
issuesUpdated: updated,
|
|
115
|
+
projectUpdated,
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
return result ?? noopResult;
|
|
119
|
+
}
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// Milestone Complete
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
/**
|
|
124
|
+
* Sync Linear state when a milestone completes.
|
|
125
|
+
*
|
|
126
|
+
* - If NOT the last milestone: adds a progress comment to each issue
|
|
127
|
+
* - If IS the last milestone: transitions all project issues to "In Review",
|
|
128
|
+
* transitions the project to "In Review"
|
|
129
|
+
*
|
|
130
|
+
* Degrades gracefully if Linear is unavailable.
|
|
131
|
+
*/
|
|
132
|
+
export async function syncMilestoneComplete(options) {
|
|
133
|
+
const noopResult = {
|
|
134
|
+
issuesUpdated: 0,
|
|
135
|
+
projectUpdated: false,
|
|
136
|
+
finalState: "unknown",
|
|
137
|
+
};
|
|
138
|
+
const client = createClientSafe(options.apiKey);
|
|
139
|
+
if (!client)
|
|
140
|
+
return noopResult;
|
|
141
|
+
const result = await syncLinearSafe(async () => {
|
|
142
|
+
if (!options.isLastMilestone) {
|
|
143
|
+
// Mid-project milestone — add progress comments
|
|
144
|
+
const milestone = await findMilestoneByName(client, options.projectId, options.milestoneName);
|
|
145
|
+
if (milestone) {
|
|
146
|
+
const issues = await client.listIssues({
|
|
147
|
+
projectId: options.projectId,
|
|
148
|
+
milestoneId: milestone.id,
|
|
149
|
+
});
|
|
150
|
+
const commentBody = `Milestone ${options.milestoneNumber} complete. Moving to next milestone.`;
|
|
151
|
+
for (const issue of issues) {
|
|
152
|
+
await client.createComment(issue.id, commentBody);
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
issuesUpdated: issues.length,
|
|
156
|
+
projectUpdated: false,
|
|
157
|
+
finalState: "In Progress",
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
issuesUpdated: 0,
|
|
162
|
+
projectUpdated: false,
|
|
163
|
+
finalState: "In Progress",
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
// Last milestone — move everything to In Review
|
|
167
|
+
// Get ALL project issues (not just this milestone's)
|
|
168
|
+
const allIssues = await client.listIssues({
|
|
169
|
+
projectId: options.projectId,
|
|
170
|
+
});
|
|
171
|
+
let updatedCount = 0;
|
|
172
|
+
for (const issue of allIssues) {
|
|
173
|
+
if (issue.state !== "In Review" && issue.state !== "Done") {
|
|
174
|
+
try {
|
|
175
|
+
await client.updateIssue(issue.id, { state: "In Review" });
|
|
176
|
+
updatedCount++;
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Some issues may not support this transition — skip them
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Add PR link comment if available
|
|
184
|
+
if (options.prUrl) {
|
|
185
|
+
for (const issue of allIssues) {
|
|
186
|
+
try {
|
|
187
|
+
await client.createComment(issue.id, `PR created: ${options.prUrl}`);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// Comment failures are non-critical
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Transition project to In Review
|
|
195
|
+
let projectUpdated = false;
|
|
196
|
+
try {
|
|
197
|
+
await transitionProject(client, options.projectId, "In Review");
|
|
198
|
+
projectUpdated = true;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
// Project may already be In Review or beyond — that's fine
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
issuesUpdated: updatedCount,
|
|
205
|
+
projectUpdated,
|
|
206
|
+
finalState: "In Review",
|
|
207
|
+
};
|
|
208
|
+
});
|
|
209
|
+
return result ?? noopResult;
|
|
210
|
+
}
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
// Progress Comments
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
/**
|
|
215
|
+
* Add a progress comment to all issues in a milestone.
|
|
216
|
+
* Used during execution to keep Linear updated with wave progress.
|
|
217
|
+
*
|
|
218
|
+
* Degrades gracefully if Linear is unavailable or the milestone is not found.
|
|
219
|
+
*/
|
|
220
|
+
export async function addMilestoneProgressComment(options) {
|
|
221
|
+
const client = createClientSafe(options.apiKey);
|
|
222
|
+
if (!client)
|
|
223
|
+
return;
|
|
224
|
+
await syncLinearSafe(async () => {
|
|
225
|
+
const milestone = await findMilestoneByName(client, options.projectId, options.milestoneName);
|
|
226
|
+
if (!milestone) {
|
|
227
|
+
console.warn(`[linear-sync] Milestone "${options.milestoneName}" not found. Skipping progress comment.`);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const issues = await client.listIssues({
|
|
231
|
+
projectId: options.projectId,
|
|
232
|
+
milestoneId: milestone.id,
|
|
233
|
+
});
|
|
234
|
+
for (const issue of issues) {
|
|
235
|
+
await client.createComment(issue.id, options.message);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=linear-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-sync.js","sourceRoot":"","sources":["../../src/go/linear-sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ForgeConfig, GateError, PipelineResult } from "../types.js";
|
|
2
|
+
/** Options for the self-healing verification loop */
|
|
3
|
+
export interface VerifyLoopOptions {
|
|
4
|
+
projectDir: string;
|
|
5
|
+
config: ForgeConfig;
|
|
6
|
+
/** Override config.maxIterations */
|
|
7
|
+
maxIterations?: number;
|
|
8
|
+
/** Called after each pipeline run with the iteration number and result */
|
|
9
|
+
onIteration?: (iteration: number, result: PipelineResult) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Called when verification fails, giving the caller a chance to fix errors.
|
|
12
|
+
* Returns true if a fix was attempted (loop will re-verify).
|
|
13
|
+
* Returns false or is absent to re-run verification without external fix.
|
|
14
|
+
*/
|
|
15
|
+
onFixAttempt?: (iteration: number, errors: GateError[]) => Promise<boolean>;
|
|
16
|
+
}
|
|
17
|
+
/** Result from the complete verification loop */
|
|
18
|
+
export interface VerifyLoopResult {
|
|
19
|
+
passed: boolean;
|
|
20
|
+
iterations: number;
|
|
21
|
+
maxIterations: number;
|
|
22
|
+
/** All pipeline results across every iteration */
|
|
23
|
+
results: PipelineResult[];
|
|
24
|
+
/** The last pipeline result */
|
|
25
|
+
finalResult: PipelineResult;
|
|
26
|
+
/** Gate names that still fail after all iterations */
|
|
27
|
+
failedGates: string[];
|
|
28
|
+
/** Human-readable summary of remaining errors */
|
|
29
|
+
errorSummary: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Self-healing verification loop.
|
|
33
|
+
*
|
|
34
|
+
* Runs the forge verification pipeline, and on failure either invokes the
|
|
35
|
+
* `onFixAttempt` callback (so the caller can spawn a fix agent) or simply
|
|
36
|
+
* re-runs verification. Loops until the pipeline passes or max iterations
|
|
37
|
+
* are exhausted.
|
|
38
|
+
*/
|
|
39
|
+
export declare function runVerifyLoop(options: VerifyLoopOptions): Promise<VerifyLoopResult>;
|
|
40
|
+
/**
|
|
41
|
+
* Format gate errors into a structured prompt for a fix agent.
|
|
42
|
+
*
|
|
43
|
+
* The output is designed to be directly usable in an AI agent prompt:
|
|
44
|
+
* it includes file paths, line numbers, error messages, and remediation
|
|
45
|
+
* hints so the agent can locate and fix issues without extra searching.
|
|
46
|
+
*/
|
|
47
|
+
export declare function formatErrorsForAgent(result: PipelineResult): string;
|