@snipcodeit/mgw 0.2.2 → 0.4.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/README.md +55 -5
- package/bin/mgw-install.cjs +121 -24
- package/commands/board/configure.md +205 -0
- package/commands/board/create.md +688 -0
- package/commands/board/show.md +221 -0
- package/commands/board/sync.md +461 -0
- package/commands/board/views.md +253 -0
- package/commands/board.md +23 -1543
- package/commands/context.md +183 -0
- package/commands/handoff.md +169 -0
- package/commands/issue.md +62 -0
- package/commands/milestone.md +42 -43
- package/commands/project.md +19 -0
- package/commands/review.md +222 -42
- package/commands/run/execute.md +820 -0
- package/commands/run/pr-create.md +324 -0
- package/commands/run/triage.md +510 -0
- package/commands/run/worktree.md +95 -0
- package/commands/run.md +23 -1547
- package/commands/sync.md +69 -0
- package/commands/workflows/gsd.md +1 -13
- package/dist/bin/mgw.cjs +107 -15
- package/dist/{index-BiwU0uWA.cjs → index-B-_JvYpz.cjs} +885 -69
- package/dist/lib/index.cjs +653 -155
- package/package.json +5 -2
package/dist/lib/index.cjs
CHANGED
|
@@ -1,12 +1,150 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index$1 = require('../index-
|
|
3
|
+
var index$1 = require('../index-B-_JvYpz.cjs');
|
|
4
4
|
var require$$0 = require('child_process');
|
|
5
5
|
var require$$1 = require('path');
|
|
6
|
-
var require$$
|
|
7
|
-
var require$$
|
|
6
|
+
var require$$3 = require('os');
|
|
7
|
+
var require$$2 = require('fs');
|
|
8
8
|
require('events');
|
|
9
9
|
|
|
10
|
+
var pipeline;
|
|
11
|
+
var hasRequiredPipeline;
|
|
12
|
+
|
|
13
|
+
function requirePipeline () {
|
|
14
|
+
if (hasRequiredPipeline) return pipeline;
|
|
15
|
+
hasRequiredPipeline = 1;
|
|
16
|
+
const STAGES = {
|
|
17
|
+
NEW: "new",
|
|
18
|
+
TRIAGED: "triaged",
|
|
19
|
+
NEEDS_INFO: "needs-info",
|
|
20
|
+
NEEDS_SECURITY_REVIEW: "needs-security-review",
|
|
21
|
+
DISCUSSING: "discussing",
|
|
22
|
+
APPROVED: "approved",
|
|
23
|
+
PLANNING: "planning",
|
|
24
|
+
DIAGNOSING: "diagnosing",
|
|
25
|
+
EXECUTING: "executing",
|
|
26
|
+
VERIFYING: "verifying",
|
|
27
|
+
PR_CREATED: "pr-created",
|
|
28
|
+
DONE: "done",
|
|
29
|
+
FAILED: "failed",
|
|
30
|
+
BLOCKED: "blocked"
|
|
31
|
+
};
|
|
32
|
+
const STAGE_SET = new Set(Object.values(STAGES));
|
|
33
|
+
const VALID_TRANSITIONS = {
|
|
34
|
+
[STAGES.NEW]: [STAGES.TRIAGED],
|
|
35
|
+
[STAGES.TRIAGED]: [STAGES.NEEDS_INFO, STAGES.NEEDS_SECURITY_REVIEW, STAGES.DISCUSSING, STAGES.APPROVED, STAGES.PLANNING, STAGES.DIAGNOSING],
|
|
36
|
+
[STAGES.NEEDS_INFO]: [STAGES.TRIAGED],
|
|
37
|
+
[STAGES.NEEDS_SECURITY_REVIEW]: [STAGES.TRIAGED, STAGES.APPROVED],
|
|
38
|
+
[STAGES.DISCUSSING]: [STAGES.TRIAGED, STAGES.APPROVED],
|
|
39
|
+
[STAGES.APPROVED]: [STAGES.PLANNING],
|
|
40
|
+
[STAGES.PLANNING]: [STAGES.EXECUTING],
|
|
41
|
+
[STAGES.DIAGNOSING]: [STAGES.PLANNING],
|
|
42
|
+
[STAGES.EXECUTING]: [STAGES.VERIFYING],
|
|
43
|
+
[STAGES.VERIFYING]: [STAGES.PR_CREATED, STAGES.EXECUTING],
|
|
44
|
+
[STAGES.PR_CREATED]: [STAGES.DONE],
|
|
45
|
+
[STAGES.DONE]: [],
|
|
46
|
+
[STAGES.FAILED]: [STAGES.NEW, STAGES.TRIAGED, STAGES.PLANNING, STAGES.EXECUTING],
|
|
47
|
+
[STAGES.BLOCKED]: [STAGES.NEW, STAGES.TRIAGED, STAGES.PLANNING, STAGES.EXECUTING]
|
|
48
|
+
};
|
|
49
|
+
for (const stage of Object.keys(VALID_TRANSITIONS)) {
|
|
50
|
+
if (stage !== STAGES.DONE && stage !== STAGES.FAILED && stage !== STAGES.BLOCKED) {
|
|
51
|
+
VALID_TRANSITIONS[stage].push(STAGES.FAILED, STAGES.BLOCKED);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const STAGE_ICONS = {
|
|
55
|
+
[STAGES.NEW]: "\u25CB",
|
|
56
|
+
[STAGES.TRIAGED]: "\u25C7",
|
|
57
|
+
[STAGES.NEEDS_INFO]: "?",
|
|
58
|
+
[STAGES.NEEDS_SECURITY_REVIEW]: "\u2691",
|
|
59
|
+
[STAGES.DISCUSSING]: "\u{1F4AC}",
|
|
60
|
+
[STAGES.APPROVED]: "\u2714",
|
|
61
|
+
[STAGES.PLANNING]: "\u25C6",
|
|
62
|
+
[STAGES.DIAGNOSING]: "\u{1F50D}",
|
|
63
|
+
[STAGES.EXECUTING]: "\u25C6",
|
|
64
|
+
[STAGES.VERIFYING]: "\u25C6",
|
|
65
|
+
[STAGES.PR_CREATED]: "\u2713",
|
|
66
|
+
[STAGES.DONE]: "\u2713",
|
|
67
|
+
[STAGES.FAILED]: "\u2717",
|
|
68
|
+
[STAGES.BLOCKED]: "\u2298"
|
|
69
|
+
};
|
|
70
|
+
const STAGE_LABELS = {
|
|
71
|
+
[STAGES.NEW]: "New",
|
|
72
|
+
[STAGES.TRIAGED]: "Triaged",
|
|
73
|
+
[STAGES.NEEDS_INFO]: "Needs Info",
|
|
74
|
+
[STAGES.NEEDS_SECURITY_REVIEW]: "Needs Security Review",
|
|
75
|
+
[STAGES.DISCUSSING]: "Discussing",
|
|
76
|
+
[STAGES.APPROVED]: "Approved",
|
|
77
|
+
[STAGES.PLANNING]: "Planning",
|
|
78
|
+
[STAGES.DIAGNOSING]: "Diagnosing",
|
|
79
|
+
[STAGES.EXECUTING]: "Executing",
|
|
80
|
+
[STAGES.VERIFYING]: "Verifying",
|
|
81
|
+
[STAGES.PR_CREATED]: "PR Created",
|
|
82
|
+
[STAGES.DONE]: "Done",
|
|
83
|
+
[STAGES.FAILED]: "Failed",
|
|
84
|
+
[STAGES.BLOCKED]: "Blocked"
|
|
85
|
+
};
|
|
86
|
+
function isValidStage(stage) {
|
|
87
|
+
return STAGE_SET.has(stage);
|
|
88
|
+
}
|
|
89
|
+
function isValidTransition(from, to) {
|
|
90
|
+
if (!isValidStage(from) || !isValidStage(to)) return false;
|
|
91
|
+
const allowed = VALID_TRANSITIONS[from];
|
|
92
|
+
return Array.isArray(allowed) && allowed.includes(to);
|
|
93
|
+
}
|
|
94
|
+
const _hooks = [];
|
|
95
|
+
function onTransition(fn) {
|
|
96
|
+
_hooks.push(fn);
|
|
97
|
+
return () => {
|
|
98
|
+
const idx = _hooks.indexOf(fn);
|
|
99
|
+
if (idx !== -1) _hooks.splice(idx, 1);
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function transitionStage(issueState, newStage, context) {
|
|
103
|
+
const currentStage = issueState && issueState.pipeline_stage || STAGES.NEW;
|
|
104
|
+
if (!isValidStage(newStage)) {
|
|
105
|
+
throw new Error(`Invalid target stage: "${newStage}"`);
|
|
106
|
+
}
|
|
107
|
+
if (currentStage === newStage) {
|
|
108
|
+
throw new Error(`Already at stage "${newStage}" \u2014 self-transitions are not allowed`);
|
|
109
|
+
}
|
|
110
|
+
if (!isValidTransition(currentStage, newStage)) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Invalid transition: "${currentStage}" \u2192 "${newStage}". Allowed targets: [${(VALID_TRANSITIONS[currentStage] || []).join(", ")}]`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
const newState = Object.assign({}, issueState, {
|
|
116
|
+
pipeline_stage: newStage,
|
|
117
|
+
last_transition: (/* @__PURE__ */ new Date()).toISOString(),
|
|
118
|
+
previous_stage: currentStage
|
|
119
|
+
});
|
|
120
|
+
const ctx = context || {};
|
|
121
|
+
for (const hook of _hooks) {
|
|
122
|
+
try {
|
|
123
|
+
hook(currentStage, newStage, ctx);
|
|
124
|
+
} catch (err) {
|
|
125
|
+
process.stderr.write(`[pipeline] hook error: ${err.message}
|
|
126
|
+
`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return newState;
|
|
130
|
+
}
|
|
131
|
+
function clearHooks() {
|
|
132
|
+
_hooks.length = 0;
|
|
133
|
+
}
|
|
134
|
+
pipeline = {
|
|
135
|
+
STAGES,
|
|
136
|
+
VALID_TRANSITIONS,
|
|
137
|
+
STAGE_ICONS,
|
|
138
|
+
STAGE_LABELS,
|
|
139
|
+
isValidStage,
|
|
140
|
+
isValidTransition,
|
|
141
|
+
transitionStage,
|
|
142
|
+
onTransition,
|
|
143
|
+
clearHooks
|
|
144
|
+
};
|
|
145
|
+
return pipeline;
|
|
146
|
+
}
|
|
147
|
+
|
|
10
148
|
var gsdAdapter;
|
|
11
149
|
var hasRequiredGsdAdapter;
|
|
12
150
|
|
|
@@ -15,24 +153,55 @@ function requireGsdAdapter () {
|
|
|
15
153
|
hasRequiredGsdAdapter = 1;
|
|
16
154
|
const { execSync } = require$$0;
|
|
17
155
|
const path = require$$1;
|
|
18
|
-
const os = require$$
|
|
19
|
-
const fs = require$$
|
|
156
|
+
const os = require$$3;
|
|
157
|
+
const fs = require$$2;
|
|
158
|
+
const { TimeoutError, GsdToolError } = index$1.requireErrors();
|
|
159
|
+
const { STAGES } = requirePipeline();
|
|
160
|
+
function resolveGsdRoot() {
|
|
161
|
+
if (process.env.GSD_TOOLS_PATH) {
|
|
162
|
+
return process.env.GSD_TOOLS_PATH;
|
|
163
|
+
}
|
|
164
|
+
const configPath = path.join(process.cwd(), ".mgw", "config.json");
|
|
165
|
+
if (fs.existsSync(configPath)) {
|
|
166
|
+
try {
|
|
167
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
168
|
+
if (config.gsd_path) {
|
|
169
|
+
return config.gsd_path;
|
|
170
|
+
}
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return path.join(os.homedir(), ".claude", "get-shit-done");
|
|
175
|
+
}
|
|
20
176
|
function getGsdToolsPath() {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
|
|
177
|
+
const root = resolveGsdRoot();
|
|
178
|
+
const toolPath = path.join(root, "bin", "gsd-tools.cjs");
|
|
179
|
+
if (fs.existsSync(toolPath)) {
|
|
180
|
+
return toolPath;
|
|
181
|
+
}
|
|
182
|
+
const checked = [];
|
|
183
|
+
if (process.env.GSD_TOOLS_PATH) {
|
|
184
|
+
checked.push(` GSD_TOOLS_PATH: ${path.join(process.env.GSD_TOOLS_PATH, "bin", "gsd-tools.cjs")}`);
|
|
185
|
+
}
|
|
186
|
+
const configPath = path.join(process.cwd(), ".mgw", "config.json");
|
|
187
|
+
if (fs.existsSync(configPath)) {
|
|
188
|
+
try {
|
|
189
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
190
|
+
if (config.gsd_path) {
|
|
191
|
+
checked.push(` .mgw/config.json gsd_path: ${path.join(config.gsd_path, "bin", "gsd-tools.cjs")}`);
|
|
192
|
+
}
|
|
193
|
+
} catch {
|
|
194
|
+
}
|
|
30
195
|
}
|
|
196
|
+
const defaultPath = path.join(os.homedir(), ".claude", "get-shit-done", "bin", "gsd-tools.cjs");
|
|
197
|
+
checked.push(` default: ${defaultPath}`);
|
|
31
198
|
throw new Error(
|
|
32
|
-
`GSD tools not found
|
|
33
|
-
|
|
199
|
+
`GSD tools not found. Checked:
|
|
200
|
+
${checked.join("\n")}
|
|
201
|
+
Set GSD_TOOLS_PATH or add gsd_path to .mgw/config.json`
|
|
34
202
|
);
|
|
35
203
|
}
|
|
204
|
+
const GSD_TIMEOUT_MS = 15e3;
|
|
36
205
|
function invokeGsdTool(command, args) {
|
|
37
206
|
const toolPath = getGsdToolsPath();
|
|
38
207
|
const argsStr = Array.isArray(args) ? args.map((a) => JSON.stringify(String(a))).join(" ") : "";
|
|
@@ -41,13 +210,21 @@ Ensure the get-shit-done framework is installed at ~/.claude/get-shit-done/`
|
|
|
41
210
|
try {
|
|
42
211
|
raw = execSync(cmd, {
|
|
43
212
|
encoding: "utf-8",
|
|
44
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
213
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
214
|
+
timeout: GSD_TIMEOUT_MS
|
|
45
215
|
}).trim();
|
|
46
216
|
} catch (err) {
|
|
217
|
+
if (err.killed) {
|
|
218
|
+
throw new TimeoutError(
|
|
219
|
+
`GSD tool timed out after ${GSD_TIMEOUT_MS / 1e3}s: ${command}`,
|
|
220
|
+
{ timeoutMs: GSD_TIMEOUT_MS, operation: `gsd-tools ${command}` }
|
|
221
|
+
);
|
|
222
|
+
}
|
|
47
223
|
const stderr = err.stderr ? err.stderr.trim() : "";
|
|
48
|
-
throw new
|
|
224
|
+
throw new GsdToolError(
|
|
49
225
|
`GSD tool command failed: ${command}
|
|
50
|
-
` + (stderr ? `stderr: ${stderr}` : `exit code: ${err.status}`)
|
|
226
|
+
` + (stderr ? `stderr: ${stderr}` : `exit code: ${err.status}`),
|
|
227
|
+
{ command, cause: err }
|
|
51
228
|
);
|
|
52
229
|
}
|
|
53
230
|
if (!raw) return null;
|
|
@@ -89,13 +266,13 @@ Ensure the get-shit-done framework is installed at ~/.claude/get-shit-done/`
|
|
|
89
266
|
if (/gsd-route:diagnose|needs-diagnosis/.test(labelStr)) {
|
|
90
267
|
return "diagnose";
|
|
91
268
|
}
|
|
92
|
-
if (stage ===
|
|
269
|
+
if (stage === STAGES.DIAGNOSING) {
|
|
93
270
|
return "diagnose";
|
|
94
271
|
}
|
|
95
|
-
if (stage ===
|
|
272
|
+
if (stage === STAGES.EXECUTING) {
|
|
96
273
|
return "execute-only";
|
|
97
274
|
}
|
|
98
|
-
if (stage ===
|
|
275
|
+
if (stage === STAGES.VERIFYING) {
|
|
99
276
|
return "verify-only";
|
|
100
277
|
}
|
|
101
278
|
if (projectState && projectState.milestones) ;
|
|
@@ -140,6 +317,7 @@ Ensure the get-shit-done framework is installed at ~/.claude/get-shit-done/`
|
|
|
140
317
|
return { activeMilestone, currentPhase, planCount };
|
|
141
318
|
}
|
|
142
319
|
gsdAdapter = {
|
|
320
|
+
resolveGsdRoot,
|
|
143
321
|
getGsdToolsPath,
|
|
144
322
|
invokeGsdTool,
|
|
145
323
|
getTimestamp,
|
|
@@ -171,7 +349,7 @@ function requireTemplateLoader () {
|
|
|
171
349
|
if (hasRequiredTemplateLoader) return templateLoader.exports;
|
|
172
350
|
hasRequiredTemplateLoader = 1;
|
|
173
351
|
(function (module) {
|
|
174
|
-
const fs = require$$
|
|
352
|
+
const fs = require$$2;
|
|
175
353
|
const path = require$$1;
|
|
176
354
|
const VALID_GSD_ROUTES = [
|
|
177
355
|
"quick",
|
|
@@ -344,15 +522,15 @@ Checked: ` + path.join(__dirname, "..", "templates") + "\nChecked: " + path.join
|
|
|
344
522
|
const nextMatch = nextSectionMatch.exec(content);
|
|
345
523
|
const sectionEnd = nextMatch ? nextMatch.index : content.length;
|
|
346
524
|
const sectionText = content.slice(sectionStart, sectionEnd);
|
|
347
|
-
const goalMatch = sectionText.match(/\*\*Goal[
|
|
525
|
+
const goalMatch = sectionText.match(/\*\*Goal[:*]*\*?\*?[:\s]+([^\n]+)/);
|
|
348
526
|
const goal = goalMatch ? goalMatch[1].trim().replace(/\*+$/, "") : "";
|
|
349
|
-
const reqMatch = sectionText.match(/\*\*Requirements?[
|
|
527
|
+
const reqMatch = sectionText.match(/\*\*Requirements?[:*]*\*?\*?[:\s]+([\s\S]*?)(?=\n\*\*|\n###|$)/);
|
|
350
528
|
let requirements = [];
|
|
351
529
|
if (reqMatch) {
|
|
352
530
|
const reqText = reqMatch[1].trim();
|
|
353
531
|
requirements = reqText.split(/[,\n]+/).map((r) => r.trim().replace(/^[-*\s]+/, "").trim()).filter((r) => r.length > 0);
|
|
354
532
|
}
|
|
355
|
-
const scMatch = sectionText.match(/\*\*Success Criteria[
|
|
533
|
+
const scMatch = sectionText.match(/\*\*Success Criteria[:*]*\*?\*?[:\s]+([\s\S]*?)(?=\n\*\*|\n###|$)/);
|
|
356
534
|
let success_criteria = [];
|
|
357
535
|
if (scMatch) {
|
|
358
536
|
const scText = scMatch[1];
|
|
@@ -477,122 +655,19 @@ function requireTemplates () {
|
|
|
477
655
|
return templates;
|
|
478
656
|
}
|
|
479
657
|
|
|
480
|
-
var
|
|
481
|
-
var
|
|
658
|
+
var claude;
|
|
659
|
+
var hasRequiredClaude;
|
|
482
660
|
|
|
483
|
-
function
|
|
484
|
-
if (
|
|
485
|
-
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
"network timeout",
|
|
492
|
-
"econnreset",
|
|
493
|
-
"econnrefused",
|
|
494
|
-
"etimedout",
|
|
495
|
-
"socket hang up",
|
|
496
|
-
"worktree lock",
|
|
497
|
-
"model overload",
|
|
498
|
-
"rate limit",
|
|
499
|
-
"too many requests",
|
|
500
|
-
"service unavailable",
|
|
501
|
-
"bad gateway",
|
|
502
|
-
"gateway timeout"
|
|
503
|
-
];
|
|
504
|
-
const NEEDS_INFO_MESSAGE_PATTERNS = [
|
|
505
|
-
"ambiguous",
|
|
506
|
-
"missing required field",
|
|
507
|
-
"contradictory requirements",
|
|
508
|
-
"issue body"
|
|
509
|
-
];
|
|
510
|
-
function classifyFailure(error) {
|
|
511
|
-
if (!error || typeof error !== "object") {
|
|
512
|
-
return { class: "permanent", reason: "no error object provided" };
|
|
513
|
-
}
|
|
514
|
-
const status = error.status;
|
|
515
|
-
const message = (error.message || "").toLowerCase();
|
|
516
|
-
const code = (error.code || "").toLowerCase();
|
|
517
|
-
if (typeof status === "number") {
|
|
518
|
-
if (status === 429) {
|
|
519
|
-
return { class: "transient", reason: "rate limit (HTTP 429)" };
|
|
520
|
-
}
|
|
521
|
-
if (TRANSIENT_STATUS_CODES.has(status)) {
|
|
522
|
-
return { class: "transient", reason: `server error (HTTP ${status})` };
|
|
523
|
-
}
|
|
524
|
-
if (status === 403) {
|
|
525
|
-
return { class: "permanent", reason: "forbidden (HTTP 403 \u2014 non-rate-limit)" };
|
|
526
|
-
}
|
|
527
|
-
if (status >= 400 && status < 500) {
|
|
528
|
-
return { class: "permanent", reason: `client error (HTTP ${status})` };
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
if (code) {
|
|
532
|
-
const networkCodes = /* @__PURE__ */ new Set([
|
|
533
|
-
"econnreset",
|
|
534
|
-
"econnrefused",
|
|
535
|
-
"etimedout",
|
|
536
|
-
"enotfound",
|
|
537
|
-
"epipe"
|
|
538
|
-
]);
|
|
539
|
-
if (networkCodes.has(code)) {
|
|
540
|
-
return { class: "transient", reason: `network error (${code.toUpperCase()})` };
|
|
541
|
-
}
|
|
542
|
-
if (code === "enoent") {
|
|
543
|
-
return { class: "permanent", reason: "file not found (ENOENT) \u2014 GSD tools may be missing" };
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
for (const pattern of TRANSIENT_MESSAGE_PATTERNS) {
|
|
547
|
-
if (message.includes(pattern)) {
|
|
548
|
-
return { class: "transient", reason: `transient condition detected: "${pattern}"` };
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
for (const pattern of NEEDS_INFO_MESSAGE_PATTERNS) {
|
|
552
|
-
if (message.includes(pattern)) {
|
|
553
|
-
return { class: "needs-info", reason: `issue requires clarification: "${pattern}"` };
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
return {
|
|
557
|
-
class: "permanent",
|
|
558
|
-
reason: "unknown error \u2014 classified as permanent to prevent runaway retries"
|
|
559
|
-
};
|
|
560
|
-
}
|
|
561
|
-
function canRetry(issueState) {
|
|
562
|
-
if (!issueState || typeof issueState !== "object") return false;
|
|
563
|
-
if (issueState.dead_letter === true) return false;
|
|
564
|
-
const count = typeof issueState.retry_count === "number" ? issueState.retry_count : 0;
|
|
565
|
-
return count < MAX_RETRIES;
|
|
566
|
-
}
|
|
567
|
-
function incrementRetry(issueState) {
|
|
568
|
-
const current = typeof issueState.retry_count === "number" ? issueState.retry_count : 0;
|
|
569
|
-
return Object.assign({}, issueState, { retry_count: current + 1 });
|
|
570
|
-
}
|
|
571
|
-
function resetRetryState(issueState) {
|
|
572
|
-
return Object.assign({}, issueState, {
|
|
573
|
-
retry_count: 0,
|
|
574
|
-
last_failure_class: null,
|
|
575
|
-
dead_letter: false
|
|
576
|
-
});
|
|
577
|
-
}
|
|
578
|
-
function getBackoffMs(retryCount) {
|
|
579
|
-
const count = Math.max(0, Math.floor(retryCount));
|
|
580
|
-
const base = Math.min(BACKOFF_MAX_MS, BACKOFF_BASE_MS * Math.pow(2, count));
|
|
581
|
-
return Math.floor(Math.random() * (base + 1));
|
|
582
|
-
}
|
|
583
|
-
retry = {
|
|
584
|
-
// Constants
|
|
585
|
-
MAX_RETRIES,
|
|
586
|
-
BACKOFF_BASE_MS,
|
|
587
|
-
BACKOFF_MAX_MS,
|
|
588
|
-
// Core functions
|
|
589
|
-
classifyFailure,
|
|
590
|
-
canRetry,
|
|
591
|
-
incrementRetry,
|
|
592
|
-
resetRetryState,
|
|
593
|
-
getBackoffMs
|
|
661
|
+
function requireClaude () {
|
|
662
|
+
if (hasRequiredClaude) return claude;
|
|
663
|
+
hasRequiredClaude = 1;
|
|
664
|
+
const provider = index$1.requireProviderClaude();
|
|
665
|
+
claude = {
|
|
666
|
+
assertClaudeAvailable: provider.assertAvailable,
|
|
667
|
+
invokeClaude: provider.invoke,
|
|
668
|
+
getCommandsDir: provider.getCommandsDir
|
|
594
669
|
};
|
|
595
|
-
return
|
|
670
|
+
return claude;
|
|
596
671
|
}
|
|
597
672
|
|
|
598
673
|
var progress;
|
|
@@ -601,7 +676,8 @@ var hasRequiredProgress;
|
|
|
601
676
|
function requireProgress () {
|
|
602
677
|
if (hasRequiredProgress) return progress;
|
|
603
678
|
hasRequiredProgress = 1;
|
|
604
|
-
const {
|
|
679
|
+
const { USE_COLOR } = index$1.requireOutput();
|
|
680
|
+
const { STAGES } = requirePipeline();
|
|
605
681
|
const SUPPORTS_COLOR = USE_COLOR;
|
|
606
682
|
const C = {
|
|
607
683
|
reset: "\x1B[0m",
|
|
@@ -646,16 +722,16 @@ function requireProgress () {
|
|
|
646
722
|
}
|
|
647
723
|
function stageIcon(stage) {
|
|
648
724
|
switch (stage) {
|
|
649
|
-
case
|
|
650
|
-
case
|
|
725
|
+
case STAGES.DONE:
|
|
726
|
+
case STAGES.PR_CREATED:
|
|
651
727
|
return { icon: "\u2713", colored: col(C.green, "\u2713") };
|
|
652
|
-
case
|
|
653
|
-
case
|
|
654
|
-
case
|
|
728
|
+
case STAGES.EXECUTING:
|
|
729
|
+
case STAGES.PLANNING:
|
|
730
|
+
case STAGES.VERIFYING:
|
|
655
731
|
return { icon: "\u25C6", colored: col(C.blue, "\u25C6") };
|
|
656
|
-
case
|
|
732
|
+
case STAGES.FAILED:
|
|
657
733
|
return { icon: "\u2717", colored: col(C.red, "\u2717") };
|
|
658
|
-
case
|
|
734
|
+
case STAGES.BLOCKED:
|
|
659
735
|
return { icon: "\u2298", colored: col(C.yellow, "\u2298") };
|
|
660
736
|
default:
|
|
661
737
|
return { icon: "\u25CB", colored: col(C.dim, "\u25CB") };
|
|
@@ -673,8 +749,8 @@ ${header}
|
|
|
673
749
|
`);
|
|
674
750
|
if (issues && issues.length > 0) {
|
|
675
751
|
const parts = issues.map(({ number, pipeline_stage }) => {
|
|
676
|
-
const s = number === currentIssue && pipeline_stage !==
|
|
677
|
-
const { colored
|
|
752
|
+
const s = number === currentIssue && pipeline_stage !== STAGES.DONE ? STAGES.EXECUTING : pipeline_stage || STAGES.NEW;
|
|
753
|
+
const { colored } = stageIcon(s);
|
|
678
754
|
const numStr = SUPPORTS_COLOR ? `${C.dim}#${number}${C.reset}` : `#${number}`;
|
|
679
755
|
return `${numStr}${colored}`;
|
|
680
756
|
});
|
|
@@ -692,25 +768,448 @@ ${header}
|
|
|
692
768
|
return progress;
|
|
693
769
|
}
|
|
694
770
|
|
|
771
|
+
var issueContext;
|
|
772
|
+
var hasRequiredIssueContext;
|
|
773
|
+
|
|
774
|
+
function requireIssueContext () {
|
|
775
|
+
if (hasRequiredIssueContext) return issueContext;
|
|
776
|
+
hasRequiredIssueContext = 1;
|
|
777
|
+
const { execSync } = require$$0;
|
|
778
|
+
const fs = require$$2;
|
|
779
|
+
const path = require$$1;
|
|
780
|
+
const GH_TIMEOUT_MS = 3e4;
|
|
781
|
+
const BUDGET = {
|
|
782
|
+
vision: 2e3,
|
|
783
|
+
priorSummary: 500,
|
|
784
|
+
maxPriorSummaries: 5,
|
|
785
|
+
currentPlan: 4e3,
|
|
786
|
+
milestone: 1e3
|
|
787
|
+
};
|
|
788
|
+
const CACHE_TTL_MINUTES = 30;
|
|
789
|
+
function run(cmd) {
|
|
790
|
+
return execSync(cmd, {
|
|
791
|
+
encoding: "utf-8",
|
|
792
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
793
|
+
timeout: GH_TIMEOUT_MS
|
|
794
|
+
}).trim();
|
|
795
|
+
}
|
|
796
|
+
function getMgwDir() {
|
|
797
|
+
return path.join(process.cwd(), ".mgw");
|
|
798
|
+
}
|
|
799
|
+
function getCacheDir() {
|
|
800
|
+
const dir = path.join(getMgwDir(), "context-cache");
|
|
801
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
802
|
+
return dir;
|
|
803
|
+
}
|
|
804
|
+
function truncate(str, maxLen) {
|
|
805
|
+
if (!str || str.length <= maxLen) return str || "";
|
|
806
|
+
return str.slice(0, maxLen) + "...";
|
|
807
|
+
}
|
|
808
|
+
function parseMetadata(commentBody) {
|
|
809
|
+
const result = { type: null, phase: null, milestone: null, timestamp: null };
|
|
810
|
+
if (!commentBody) return result;
|
|
811
|
+
const match = commentBody.match(/<!--\s*(mgw:[^\n]*?)-->/);
|
|
812
|
+
if (!match) return result;
|
|
813
|
+
const header = match[1];
|
|
814
|
+
const typeMatch = header.match(/mgw:type=(\S+)/);
|
|
815
|
+
const phaseMatch = header.match(/mgw:phase=(\S+)/);
|
|
816
|
+
const milestoneMatch = header.match(/mgw:milestone=(\S+)/);
|
|
817
|
+
const timestampMatch = header.match(/mgw:timestamp=(\S+)/);
|
|
818
|
+
if (typeMatch) result.type = typeMatch[1];
|
|
819
|
+
if (phaseMatch) result.phase = parseInt(phaseMatch[1], 10);
|
|
820
|
+
if (milestoneMatch) result.milestone = parseInt(milestoneMatch[1], 10);
|
|
821
|
+
if (timestampMatch) result.timestamp = timestampMatch[1];
|
|
822
|
+
return result;
|
|
823
|
+
}
|
|
824
|
+
function formatWithMetadata(content, meta) {
|
|
825
|
+
const m = meta || {};
|
|
826
|
+
const ts = m.timestamp || (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
827
|
+
const parts = [];
|
|
828
|
+
if (m.type) parts.push(`mgw:type=${m.type}`);
|
|
829
|
+
if (m.phase != null) parts.push(`mgw:phase=${m.phase}`);
|
|
830
|
+
if (m.milestone != null) parts.push(`mgw:milestone=${m.milestone}`);
|
|
831
|
+
parts.push(`mgw:timestamp=${ts}`);
|
|
832
|
+
const header = `<!-- ${parts.join(" ")} -->`;
|
|
833
|
+
return `${header}
|
|
834
|
+
${content}`;
|
|
835
|
+
}
|
|
836
|
+
async function postPlanningComment(issueNumber, type, content, meta) {
|
|
837
|
+
const formatted = formatWithMetadata(content, { ...meta, type });
|
|
838
|
+
const tmpFile = path.join(require$$3.tmpdir(), `mgw-comment-${Date.now()}.md`);
|
|
839
|
+
try {
|
|
840
|
+
fs.writeFileSync(tmpFile, formatted, "utf-8");
|
|
841
|
+
run(`gh issue comment ${issueNumber} --body-file ${JSON.stringify(tmpFile)}`);
|
|
842
|
+
} finally {
|
|
843
|
+
try {
|
|
844
|
+
fs.unlinkSync(tmpFile);
|
|
845
|
+
} catch (_) {
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
async function findPlanningComments(issueNumber, type) {
|
|
850
|
+
const raw = run(
|
|
851
|
+
`gh issue view ${issueNumber} --json comments --jq '.comments'`
|
|
852
|
+
);
|
|
853
|
+
const comments = JSON.parse(raw);
|
|
854
|
+
const results = [];
|
|
855
|
+
for (const c of comments) {
|
|
856
|
+
const meta = parseMetadata(c.body);
|
|
857
|
+
if (meta.type === type) {
|
|
858
|
+
results.push({
|
|
859
|
+
body: c.body,
|
|
860
|
+
meta,
|
|
861
|
+
createdAt: c.createdAt || ""
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
return results;
|
|
866
|
+
}
|
|
867
|
+
async function findLatestComment(issueNumber, type) {
|
|
868
|
+
const comments = await findPlanningComments(issueNumber, type);
|
|
869
|
+
if (comments.length === 0) return null;
|
|
870
|
+
comments.sort((a, b) => {
|
|
871
|
+
const tsA = a.meta.timestamp || a.createdAt || "";
|
|
872
|
+
const tsB = b.meta.timestamp || b.createdAt || "";
|
|
873
|
+
return tsA.localeCompare(tsB);
|
|
874
|
+
});
|
|
875
|
+
return comments[comments.length - 1];
|
|
876
|
+
}
|
|
877
|
+
async function assembleMilestoneContext(milestoneNum) {
|
|
878
|
+
const cached = readCache(milestoneNum);
|
|
879
|
+
if (cached) return Object.values(cached.summaries);
|
|
880
|
+
const raw = run(
|
|
881
|
+
`gh issue list --milestone ${JSON.stringify(String(milestoneNum))} --state closed --json number,title --limit 100`
|
|
882
|
+
);
|
|
883
|
+
const issues = JSON.parse(raw);
|
|
884
|
+
const summaries = [];
|
|
885
|
+
for (const issue of issues) {
|
|
886
|
+
try {
|
|
887
|
+
const comment = await findLatestComment(issue.number, "summary");
|
|
888
|
+
if (comment) {
|
|
889
|
+
const bodyWithoutHeader = comment.body.replace(/<!--[\s\S]*?-->\n?/, "").trim();
|
|
890
|
+
summaries.push({
|
|
891
|
+
issueNumber: issue.number,
|
|
892
|
+
title: issue.title,
|
|
893
|
+
summary: truncate(bodyWithoutHeader, BUDGET.priorSummary)
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
} catch (_) {
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
writeCache(milestoneNum, summaries);
|
|
900
|
+
return summaries;
|
|
901
|
+
}
|
|
902
|
+
async function assembleIssueContext(issueNumber) {
|
|
903
|
+
const raw = run(
|
|
904
|
+
`gh issue view ${issueNumber} --json number,title,body,milestone,labels,state`
|
|
905
|
+
);
|
|
906
|
+
const issue = JSON.parse(raw);
|
|
907
|
+
let milestoneContext = [];
|
|
908
|
+
if (issue.milestone && issue.milestone.number) {
|
|
909
|
+
try {
|
|
910
|
+
milestoneContext = await assembleMilestoneContext(issue.milestone.number);
|
|
911
|
+
} catch (_) {
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
let planComment = null;
|
|
915
|
+
let summaryComment = null;
|
|
916
|
+
try {
|
|
917
|
+
planComment = await findLatestComment(issueNumber, "plan");
|
|
918
|
+
} catch (_) {
|
|
919
|
+
}
|
|
920
|
+
try {
|
|
921
|
+
summaryComment = await findLatestComment(issueNumber, "summary");
|
|
922
|
+
} catch (_) {
|
|
923
|
+
}
|
|
924
|
+
return { issue, milestoneContext, planComment, summaryComment };
|
|
925
|
+
}
|
|
926
|
+
async function fetchProjectVision() {
|
|
927
|
+
try {
|
|
928
|
+
const projectJsonPath = path.join(getMgwDir(), "project.json");
|
|
929
|
+
if (fs.existsSync(projectJsonPath)) {
|
|
930
|
+
const project = JSON.parse(fs.readFileSync(projectJsonPath, "utf-8"));
|
|
931
|
+
const projectNumber = project.project && project.project.project_board && project.project.project_board.number || "";
|
|
932
|
+
if (projectNumber) {
|
|
933
|
+
const owner = run("gh repo view --json owner -q .owner.login");
|
|
934
|
+
const readme = run(
|
|
935
|
+
`gh project view ${projectNumber} --owner ${owner} --json readme -q .readme`
|
|
936
|
+
);
|
|
937
|
+
if (readme && readme.length > 10) {
|
|
938
|
+
const visionMatch = readme.match(/##\s*Vision\s*\n([\s\S]*?)(?=\n##\s|\n$|$)/);
|
|
939
|
+
if (visionMatch && visionMatch[1].trim()) {
|
|
940
|
+
return visionMatch[1].trim();
|
|
941
|
+
}
|
|
942
|
+
const lines = readme.split("\n");
|
|
943
|
+
const bodyLines = lines.filter((l) => !l.startsWith("# "));
|
|
944
|
+
const body = bodyLines.join("\n").trim();
|
|
945
|
+
if (body) return body;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
} catch (_) {
|
|
950
|
+
}
|
|
951
|
+
try {
|
|
952
|
+
const projectJsonPath = path.join(getMgwDir(), "project.json");
|
|
953
|
+
if (fs.existsSync(projectJsonPath)) {
|
|
954
|
+
const project = JSON.parse(fs.readFileSync(projectJsonPath, "utf-8"));
|
|
955
|
+
const description = project.project && project.project.description || "";
|
|
956
|
+
if (description) return description;
|
|
957
|
+
const projectName = project.project && project.project.name || "";
|
|
958
|
+
if (projectName) return `Project: ${projectName}`;
|
|
959
|
+
}
|
|
960
|
+
} catch (_) {
|
|
961
|
+
}
|
|
962
|
+
try {
|
|
963
|
+
const visionBriefPath = path.join(getMgwDir(), "vision-brief.json");
|
|
964
|
+
if (fs.existsSync(visionBriefPath)) {
|
|
965
|
+
const brief = JSON.parse(fs.readFileSync(visionBriefPath, "utf-8"));
|
|
966
|
+
return brief.vision_summary || brief.description || "";
|
|
967
|
+
}
|
|
968
|
+
} catch (_) {
|
|
969
|
+
}
|
|
970
|
+
return "";
|
|
971
|
+
}
|
|
972
|
+
async function buildGSDPromptContext(opts) {
|
|
973
|
+
const o = opts || {};
|
|
974
|
+
const sections = [];
|
|
975
|
+
if (o.includeVision) {
|
|
976
|
+
try {
|
|
977
|
+
const vision = await fetchProjectVision();
|
|
978
|
+
if (vision) {
|
|
979
|
+
sections.push(`<vision>
|
|
980
|
+
${truncate(vision, BUDGET.vision)}
|
|
981
|
+
</vision>`);
|
|
982
|
+
}
|
|
983
|
+
} catch (_) {
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
if (o.milestone) {
|
|
987
|
+
try {
|
|
988
|
+
const milestoneRaw = run(
|
|
989
|
+
`gh api repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/milestones/${o.milestone} --jq '{title: .title, description: .description}'`
|
|
990
|
+
);
|
|
991
|
+
const milestoneData = JSON.parse(milestoneRaw);
|
|
992
|
+
const milestoneInfo = truncate(
|
|
993
|
+
`${milestoneData.title}
|
|
994
|
+
${milestoneData.description || ""}`,
|
|
995
|
+
BUDGET.milestone
|
|
996
|
+
);
|
|
997
|
+
sections.push(`<milestone>
|
|
998
|
+
${milestoneInfo}
|
|
999
|
+
</milestone>`);
|
|
1000
|
+
} catch (_) {
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
if (o.includePriorSummaries && o.milestone) {
|
|
1004
|
+
try {
|
|
1005
|
+
const summaries = await assembleMilestoneContext(o.milestone);
|
|
1006
|
+
const prior = summaries.filter((s) => o.issueNumber == null || s.issueNumber !== o.issueNumber).slice(-BUDGET.maxPriorSummaries);
|
|
1007
|
+
if (prior.length > 0) {
|
|
1008
|
+
const priorText = prior.map((s) => `### Issue #${s.issueNumber}: ${s.title}
|
|
1009
|
+
${s.summary}`).join("\n\n");
|
|
1010
|
+
sections.push(`<prior_phases>
|
|
1011
|
+
${priorText}
|
|
1012
|
+
</prior_phases>`);
|
|
1013
|
+
}
|
|
1014
|
+
} catch (_) {
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
if (o.includeCurrentPlan && o.issueNumber) {
|
|
1018
|
+
try {
|
|
1019
|
+
const planComment = await findLatestComment(o.issueNumber, "plan");
|
|
1020
|
+
if (planComment) {
|
|
1021
|
+
const planBody = planComment.body.replace(/<!--[\s\S]*?-->\n?/, "").trim();
|
|
1022
|
+
sections.push(`<current_phase>
|
|
1023
|
+
${truncate(planBody, BUDGET.currentPlan)}
|
|
1024
|
+
</current_phase>`);
|
|
1025
|
+
}
|
|
1026
|
+
} catch (_) {
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
if (sections.length === 0) return "";
|
|
1030
|
+
return `<mgw_context>
|
|
1031
|
+
|
|
1032
|
+
${sections.join("\n\n")}
|
|
1033
|
+
|
|
1034
|
+
</mgw_context>`;
|
|
1035
|
+
}
|
|
1036
|
+
async function safeContext(opts) {
|
|
1037
|
+
try {
|
|
1038
|
+
return await buildGSDPromptContext(opts);
|
|
1039
|
+
} catch (_) {
|
|
1040
|
+
return "";
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
function readCache(milestoneNum) {
|
|
1044
|
+
try {
|
|
1045
|
+
const cachePath = path.join(getCacheDir(), `milestone-${milestoneNum}.json`);
|
|
1046
|
+
if (!fs.existsSync(cachePath)) return null;
|
|
1047
|
+
const data = JSON.parse(fs.readFileSync(cachePath, "utf-8"));
|
|
1048
|
+
const cachedAt = new Date(data.cached_at);
|
|
1049
|
+
const now = /* @__PURE__ */ new Date();
|
|
1050
|
+
const ageMinutes = (now - cachedAt) / (1e3 * 60);
|
|
1051
|
+
if (ageMinutes > (data.ttl_minutes || CACHE_TTL_MINUTES)) return null;
|
|
1052
|
+
return data;
|
|
1053
|
+
} catch (_) {
|
|
1054
|
+
return null;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
function writeCache(milestoneNum, summaries) {
|
|
1058
|
+
try {
|
|
1059
|
+
const cachePath = path.join(getCacheDir(), `milestone-${milestoneNum}.json`);
|
|
1060
|
+
const summaryMap = {};
|
|
1061
|
+
for (const s of summaries) {
|
|
1062
|
+
summaryMap[String(s.issueNumber)] = s;
|
|
1063
|
+
}
|
|
1064
|
+
const data = {
|
|
1065
|
+
milestone: Number(milestoneNum),
|
|
1066
|
+
cached_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1067
|
+
ttl_minutes: CACHE_TTL_MINUTES,
|
|
1068
|
+
summaries: summaryMap
|
|
1069
|
+
};
|
|
1070
|
+
fs.writeFileSync(cachePath, JSON.stringify(data, null, 2), "utf-8");
|
|
1071
|
+
} catch (_) {
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
async function rebuildContextCache() {
|
|
1075
|
+
const cacheDir = getCacheDir();
|
|
1076
|
+
try {
|
|
1077
|
+
const files = fs.readdirSync(cacheDir);
|
|
1078
|
+
for (const f of files) {
|
|
1079
|
+
if (f.startsWith("milestone-") && f.endsWith(".json")) {
|
|
1080
|
+
fs.unlinkSync(path.join(cacheDir, f));
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
} catch (_) {
|
|
1084
|
+
}
|
|
1085
|
+
let milestones;
|
|
1086
|
+
try {
|
|
1087
|
+
const repo = run("gh repo view --json nameWithOwner -q .nameWithOwner");
|
|
1088
|
+
const raw = run(`gh api repos/${repo}/milestones?state=all --jq '.[].number'`);
|
|
1089
|
+
milestones = raw.split("\n").filter(Boolean).map(Number);
|
|
1090
|
+
} catch (_) {
|
|
1091
|
+
milestones = [];
|
|
1092
|
+
}
|
|
1093
|
+
let totalIssues = 0;
|
|
1094
|
+
for (const num of milestones) {
|
|
1095
|
+
try {
|
|
1096
|
+
const summaries = await assembleMilestoneContext(num);
|
|
1097
|
+
totalIssues += summaries.length;
|
|
1098
|
+
} catch (_) {
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
return { issueCount: totalIssues, milestoneCount: milestones.length };
|
|
1102
|
+
}
|
|
1103
|
+
async function updateProjectReadme() {
|
|
1104
|
+
try {
|
|
1105
|
+
const projectJsonPath = path.join(getMgwDir(), "project.json");
|
|
1106
|
+
if (!fs.existsSync(projectJsonPath)) return false;
|
|
1107
|
+
const project = JSON.parse(fs.readFileSync(projectJsonPath, "utf-8"));
|
|
1108
|
+
const board = project.project && project.project.project_board || {};
|
|
1109
|
+
const projectNumber = board.number;
|
|
1110
|
+
if (!projectNumber) return false;
|
|
1111
|
+
const owner = run("gh repo view --json owner -q .owner.login");
|
|
1112
|
+
const projectName = project.project && project.project.name || "";
|
|
1113
|
+
let visionSummary = "";
|
|
1114
|
+
try {
|
|
1115
|
+
const visionBriefPath = path.join(getMgwDir(), "vision-brief.json");
|
|
1116
|
+
if (fs.existsSync(visionBriefPath)) {
|
|
1117
|
+
const brief = JSON.parse(fs.readFileSync(visionBriefPath, "utf-8"));
|
|
1118
|
+
visionSummary = (brief.vision_summary || brief.description || "").slice(0, 500);
|
|
1119
|
+
}
|
|
1120
|
+
} catch (_) {
|
|
1121
|
+
}
|
|
1122
|
+
if (!visionSummary) {
|
|
1123
|
+
visionSummary = project.project && project.project.description || "Project initialized via MGW.";
|
|
1124
|
+
}
|
|
1125
|
+
const milestones = project.milestones || [];
|
|
1126
|
+
const tableLines = ["| # | Milestone | Issues | Status |", "|---|-----------|--------|--------|"];
|
|
1127
|
+
for (let i = 0; i < milestones.length; i++) {
|
|
1128
|
+
const m = milestones[i];
|
|
1129
|
+
const name = m.name || m.title || "Unnamed";
|
|
1130
|
+
const count = (m.issues || []).length;
|
|
1131
|
+
const doneCount = (m.issues || []).filter((iss) => iss.pipeline_stage === "done").length;
|
|
1132
|
+
const state = m.gsd_state || "planned";
|
|
1133
|
+
const progress = count > 0 ? ` (${doneCount}/${count})` : "";
|
|
1134
|
+
const stateLabel = state.charAt(0).toUpperCase() + state.slice(1);
|
|
1135
|
+
tableLines.push(`| ${i + 1} | ${name} | ${count}${progress} | ${stateLabel} |`);
|
|
1136
|
+
}
|
|
1137
|
+
const boardUrl = board.url || "";
|
|
1138
|
+
const readmeBody = `# ${projectName}
|
|
1139
|
+
|
|
1140
|
+
## Vision
|
|
1141
|
+
${visionSummary}
|
|
1142
|
+
|
|
1143
|
+
## Milestones
|
|
1144
|
+
${tableLines.join("\n")}
|
|
1145
|
+
|
|
1146
|
+
## Links
|
|
1147
|
+
- [Board](${boardUrl})`;
|
|
1148
|
+
const tmpFile = path.join(require("os").tmpdir(), `mgw-readme-${Date.now()}.md`);
|
|
1149
|
+
try {
|
|
1150
|
+
fs.writeFileSync(tmpFile, readmeBody, "utf-8");
|
|
1151
|
+
run(`gh project edit ${projectNumber} --owner ${owner} --readme "$(cat ${JSON.stringify(tmpFile)})"`);
|
|
1152
|
+
} finally {
|
|
1153
|
+
try {
|
|
1154
|
+
fs.unlinkSync(tmpFile);
|
|
1155
|
+
} catch (_) {
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
return true;
|
|
1159
|
+
} catch (_) {
|
|
1160
|
+
return false;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
issueContext = {
|
|
1164
|
+
parseMetadata,
|
|
1165
|
+
formatWithMetadata,
|
|
1166
|
+
postPlanningComment,
|
|
1167
|
+
findPlanningComments,
|
|
1168
|
+
findLatestComment,
|
|
1169
|
+
assembleMilestoneContext,
|
|
1170
|
+
assembleIssueContext,
|
|
1171
|
+
buildGSDPromptContext,
|
|
1172
|
+
safeContext,
|
|
1173
|
+
rebuildContextCache,
|
|
1174
|
+
fetchProjectVision,
|
|
1175
|
+
updateProjectReadme
|
|
1176
|
+
};
|
|
1177
|
+
return issueContext;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
695
1180
|
var lib;
|
|
696
1181
|
var hasRequiredLib;
|
|
697
1182
|
|
|
698
1183
|
function requireLib () {
|
|
699
1184
|
if (hasRequiredLib) return lib;
|
|
700
1185
|
hasRequiredLib = 1;
|
|
701
|
-
|
|
1186
|
+
const _exports = {
|
|
702
1187
|
...index$1.requireState(),
|
|
703
1188
|
...index$1.requireGithub(),
|
|
704
1189
|
...requireGsd(),
|
|
705
1190
|
...requireGsdAdapter(),
|
|
706
1191
|
...requireTemplates(),
|
|
707
1192
|
...index$1.requireOutput(),
|
|
708
|
-
...
|
|
709
|
-
...
|
|
1193
|
+
...requireClaude(),
|
|
1194
|
+
...index$1.requireProviderManager(),
|
|
1195
|
+
...index$1.requireRetry(),
|
|
710
1196
|
...index$1.requireSpinner(),
|
|
711
1197
|
...requireProgress(),
|
|
712
|
-
...
|
|
1198
|
+
...requirePipeline(),
|
|
1199
|
+
...index$1.requireErrors(),
|
|
1200
|
+
...index$1.requireLogger(),
|
|
1201
|
+
...requireIssueContext()
|
|
713
1202
|
};
|
|
1203
|
+
Object.defineProperty(_exports, "createIssuesBrowser", {
|
|
1204
|
+
configurable: true,
|
|
1205
|
+
enumerable: true,
|
|
1206
|
+
get() {
|
|
1207
|
+
const value = index$1.requireTui().createIssuesBrowser;
|
|
1208
|
+
Object.defineProperty(_exports, "createIssuesBrowser", { value, enumerable: true });
|
|
1209
|
+
return value;
|
|
1210
|
+
}
|
|
1211
|
+
});
|
|
1212
|
+
lib = _exports;
|
|
714
1213
|
return lib;
|
|
715
1214
|
}
|
|
716
1215
|
|
|
@@ -718,4 +1217,3 @@ var libExports = requireLib();
|
|
|
718
1217
|
var index = /*@__PURE__*/index$1.getDefaultExportFromCjs(libExports);
|
|
719
1218
|
|
|
720
1219
|
module.exports = index;
|
|
721
|
-
0&&(module.exports={getMgwDir,getActiveDir,getCompletedDir,loadProjectState,writeProjectState,loadActiveIssue,mergeProjectState,migrateProjectState,resolveActiveMilestoneIndex,getRepo,getIssue,listIssues,getMilestone,getRateLimit,closeMilestone,createRelease,getProjectNodeId,findExistingBoard,getProjectFields,createProject,addItemToProject,postMilestoneStartAnnouncement,getGsdToolsPath,invokeGsdTool,getTimestamp,generateSlug,resolveModel,historyDigest,roadmapAnalyze,selectGsdRoute,getGsdState,validate,getSchema,VALID_GSD_ROUTES,IS_TTY,IS_CI,USE_COLOR,COLORS,colorize,statusLine,log,error,verbose,debug,formatJson,assertClaudeAvailable,invokeClaude,getCommandsDir,MAX_RETRIES,BACKOFF_BASE_MS,BACKOFF_MAX_MS,classifyFailure,canRetry,incrementRetry,resetRetryState,getBackoffMs,createSpinner,withSpinner,SUPPORTS_SPINNER,renderProgressBar,printMilestoneProgress,stageIcon,SUPPORTS_COLOR,createIssuesBrowser});
|