@snipcodeit/mgw 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/init.md +115 -5
- package/commands/issues.md +63 -1
- package/commands/milestone.md +43 -0
- package/commands/status.md +95 -1
- package/completions/mgw.bash +112 -0
- package/completions/mgw.fish +99 -0
- package/completions/mgw.zsh +142 -0
- package/dist/bin/mgw.cjs +99 -29
- package/dist/index-CXfe9U4l.cjs +1818 -0
- package/dist/lib/index.cjs +109 -8
- package/package.json +6 -1
- package/dist/claude-Dk1oVsaG.cjs +0 -622
package/dist/claude-Dk1oVsaG.cjs
DELETED
|
@@ -1,622 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var require$$0 = require('fs');
|
|
4
|
-
var require$$1 = require('path');
|
|
5
|
-
var require$$0$1 = require('child_process');
|
|
6
|
-
|
|
7
|
-
function getDefaultExportFromCjs(x) {
|
|
8
|
-
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
var state;
|
|
12
|
-
var hasRequiredState;
|
|
13
|
-
|
|
14
|
-
function requireState () {
|
|
15
|
-
if (hasRequiredState) return state;
|
|
16
|
-
hasRequiredState = 1;
|
|
17
|
-
const fs = require$$0;
|
|
18
|
-
const path = require$$1;
|
|
19
|
-
function getMgwDir() {
|
|
20
|
-
return path.join(process.cwd(), ".mgw");
|
|
21
|
-
}
|
|
22
|
-
function getActiveDir() {
|
|
23
|
-
return path.join(getMgwDir(), "active");
|
|
24
|
-
}
|
|
25
|
-
function getCompletedDir() {
|
|
26
|
-
return path.join(getMgwDir(), "completed");
|
|
27
|
-
}
|
|
28
|
-
function loadProjectState() {
|
|
29
|
-
const filePath = path.join(getMgwDir(), "project.json");
|
|
30
|
-
try {
|
|
31
|
-
const raw = fs.readFileSync(filePath, "utf-8");
|
|
32
|
-
return JSON.parse(raw);
|
|
33
|
-
} catch {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function writeProjectState(state) {
|
|
38
|
-
const mgwDir = getMgwDir();
|
|
39
|
-
if (!fs.existsSync(mgwDir)) {
|
|
40
|
-
fs.mkdirSync(mgwDir, { recursive: true });
|
|
41
|
-
}
|
|
42
|
-
const filePath = path.join(mgwDir, "project.json");
|
|
43
|
-
fs.writeFileSync(filePath, JSON.stringify(state, null, 2), "utf-8");
|
|
44
|
-
}
|
|
45
|
-
function loadActiveIssue(number) {
|
|
46
|
-
const activeDir = getActiveDir();
|
|
47
|
-
if (!fs.existsSync(activeDir)) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
const prefix = String(number) + "-";
|
|
51
|
-
let entries;
|
|
52
|
-
try {
|
|
53
|
-
entries = fs.readdirSync(activeDir);
|
|
54
|
-
} catch {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
const match = entries.find(
|
|
58
|
-
(f) => f.startsWith(prefix) && f.endsWith(".json")
|
|
59
|
-
);
|
|
60
|
-
if (!match) return null;
|
|
61
|
-
try {
|
|
62
|
-
const raw = fs.readFileSync(path.join(activeDir, match), "utf-8");
|
|
63
|
-
return JSON.parse(raw);
|
|
64
|
-
} catch {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
function mergeProjectState(newMilestones, newPhaseMap, newCurrentMilestone, activeGsdMilestone) {
|
|
69
|
-
const existing = loadProjectState();
|
|
70
|
-
if (!existing) {
|
|
71
|
-
throw new Error("No existing project state found. Cannot merge without a project.json.");
|
|
72
|
-
}
|
|
73
|
-
existing.milestones = (existing.milestones || []).concat(newMilestones);
|
|
74
|
-
existing.phase_map = Object.assign({}, newPhaseMap, existing.phase_map);
|
|
75
|
-
if (activeGsdMilestone !== void 0 && activeGsdMilestone !== null) {
|
|
76
|
-
existing.active_gsd_milestone = activeGsdMilestone;
|
|
77
|
-
} else {
|
|
78
|
-
if (!existing.active_gsd_milestone) {
|
|
79
|
-
existing.current_milestone = newCurrentMilestone;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
writeProjectState(existing);
|
|
83
|
-
return existing;
|
|
84
|
-
}
|
|
85
|
-
function migrateProjectState() {
|
|
86
|
-
const existing = loadProjectState();
|
|
87
|
-
if (!existing) return null;
|
|
88
|
-
let changed = false;
|
|
89
|
-
if (!existing.hasOwnProperty("active_gsd_milestone")) {
|
|
90
|
-
existing.active_gsd_milestone = null;
|
|
91
|
-
changed = true;
|
|
92
|
-
}
|
|
93
|
-
for (const m of existing.milestones || []) {
|
|
94
|
-
if (!m.hasOwnProperty("gsd_milestone_id")) {
|
|
95
|
-
m.gsd_milestone_id = null;
|
|
96
|
-
changed = true;
|
|
97
|
-
}
|
|
98
|
-
if (!m.hasOwnProperty("gsd_state")) {
|
|
99
|
-
m.gsd_state = null;
|
|
100
|
-
changed = true;
|
|
101
|
-
}
|
|
102
|
-
if (!m.hasOwnProperty("roadmap_archived_at")) {
|
|
103
|
-
m.roadmap_archived_at = null;
|
|
104
|
-
changed = true;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (changed) {
|
|
108
|
-
writeProjectState(existing);
|
|
109
|
-
}
|
|
110
|
-
const activeDir = getActiveDir();
|
|
111
|
-
if (fs.existsSync(activeDir)) {
|
|
112
|
-
let entries;
|
|
113
|
-
try {
|
|
114
|
-
entries = fs.readdirSync(activeDir);
|
|
115
|
-
} catch {
|
|
116
|
-
entries = [];
|
|
117
|
-
}
|
|
118
|
-
for (const file of entries) {
|
|
119
|
-
if (!file.endsWith(".json")) continue;
|
|
120
|
-
const filePath = path.join(activeDir, file);
|
|
121
|
-
let issueState;
|
|
122
|
-
try {
|
|
123
|
-
issueState = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
124
|
-
} catch {
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
let issueChanged = false;
|
|
128
|
-
if (!issueState.hasOwnProperty("retry_count")) {
|
|
129
|
-
issueState.retry_count = 0;
|
|
130
|
-
issueChanged = true;
|
|
131
|
-
}
|
|
132
|
-
if (!issueState.hasOwnProperty("dead_letter")) {
|
|
133
|
-
issueState.dead_letter = false;
|
|
134
|
-
issueChanged = true;
|
|
135
|
-
}
|
|
136
|
-
if (issueChanged) {
|
|
137
|
-
try {
|
|
138
|
-
fs.writeFileSync(filePath, JSON.stringify(issueState, null, 2), "utf-8");
|
|
139
|
-
} catch {
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return existing;
|
|
145
|
-
}
|
|
146
|
-
function resolveActiveMilestoneIndex(state) {
|
|
147
|
-
if (!state) return -1;
|
|
148
|
-
if (state.active_gsd_milestone) {
|
|
149
|
-
const idx = (state.milestones || []).findIndex(
|
|
150
|
-
(m) => m.gsd_milestone_id === state.active_gsd_milestone
|
|
151
|
-
);
|
|
152
|
-
return idx;
|
|
153
|
-
}
|
|
154
|
-
if (typeof state.current_milestone === "number") {
|
|
155
|
-
return state.current_milestone - 1;
|
|
156
|
-
}
|
|
157
|
-
return -1;
|
|
158
|
-
}
|
|
159
|
-
state = {
|
|
160
|
-
getMgwDir,
|
|
161
|
-
getActiveDir,
|
|
162
|
-
getCompletedDir,
|
|
163
|
-
loadProjectState,
|
|
164
|
-
writeProjectState,
|
|
165
|
-
loadActiveIssue,
|
|
166
|
-
mergeProjectState,
|
|
167
|
-
migrateProjectState,
|
|
168
|
-
resolveActiveMilestoneIndex
|
|
169
|
-
};
|
|
170
|
-
return state;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
var github;
|
|
174
|
-
var hasRequiredGithub;
|
|
175
|
-
|
|
176
|
-
function requireGithub () {
|
|
177
|
-
if (hasRequiredGithub) return github;
|
|
178
|
-
hasRequiredGithub = 1;
|
|
179
|
-
const { execSync } = require$$0$1;
|
|
180
|
-
function run(cmd) {
|
|
181
|
-
return execSync(cmd, {
|
|
182
|
-
encoding: "utf-8",
|
|
183
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
184
|
-
}).trim();
|
|
185
|
-
}
|
|
186
|
-
function getRepo() {
|
|
187
|
-
return run("gh repo view --json nameWithOwner -q .nameWithOwner");
|
|
188
|
-
}
|
|
189
|
-
function getIssue(number) {
|
|
190
|
-
const raw = run(
|
|
191
|
-
`gh issue view ${number} --json number,title,state,labels,milestone,assignees,body`
|
|
192
|
-
);
|
|
193
|
-
return JSON.parse(raw);
|
|
194
|
-
}
|
|
195
|
-
function listIssues(filters) {
|
|
196
|
-
const f = filters || {};
|
|
197
|
-
let cmd = "gh issue list --json number,title,state,labels,milestone,assignees";
|
|
198
|
-
if (f.label) cmd += ` --label ${JSON.stringify(f.label)}`;
|
|
199
|
-
if (f.milestone) cmd += ` --milestone ${JSON.stringify(f.milestone)}`;
|
|
200
|
-
if (f.assignee && f.assignee !== "all") cmd += ` --assignee ${JSON.stringify(f.assignee)}`;
|
|
201
|
-
if (f.state) cmd += ` --state ${f.state}`;
|
|
202
|
-
const raw = run(cmd);
|
|
203
|
-
return JSON.parse(raw);
|
|
204
|
-
}
|
|
205
|
-
function getMilestone(number) {
|
|
206
|
-
const repo = getRepo();
|
|
207
|
-
const raw = run(`gh api repos/${repo}/milestones/${number}`);
|
|
208
|
-
return JSON.parse(raw);
|
|
209
|
-
}
|
|
210
|
-
function getRateLimit() {
|
|
211
|
-
const raw = run("gh api rate_limit");
|
|
212
|
-
const data = JSON.parse(raw);
|
|
213
|
-
const core = data.resources.core;
|
|
214
|
-
return {
|
|
215
|
-
remaining: core.remaining,
|
|
216
|
-
limit: core.limit,
|
|
217
|
-
reset: core.reset
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
function closeMilestone(repo, number) {
|
|
221
|
-
const raw = run(
|
|
222
|
-
`gh api repos/${repo}/milestones/${number} --method PATCH -f state=closed`
|
|
223
|
-
);
|
|
224
|
-
return JSON.parse(raw);
|
|
225
|
-
}
|
|
226
|
-
function createRelease(repo, tag, title, opts) {
|
|
227
|
-
const o = opts || {};
|
|
228
|
-
let cmd = `gh release create ${JSON.stringify(tag)} --repo ${JSON.stringify(repo)} --title ${JSON.stringify(title)}`;
|
|
229
|
-
if (o.notes) cmd += ` --notes ${JSON.stringify(o.notes)}`;
|
|
230
|
-
if (o.draft) cmd += " --draft";
|
|
231
|
-
if (o.prerelease) cmd += " --prerelease";
|
|
232
|
-
return run(cmd);
|
|
233
|
-
}
|
|
234
|
-
function getProjectNodeId(owner, projectNumber) {
|
|
235
|
-
const userQuery = `'query($login: String!, $number: Int!) { user(login: $login) { projectV2(number: $number) { id } } }'`;
|
|
236
|
-
const orgQuery = `'query($login: String!, $number: Int!) { organization(login: $login) { projectV2(number: $number) { id } } }'`;
|
|
237
|
-
try {
|
|
238
|
-
const raw = run(
|
|
239
|
-
`gh api graphql -f query=${userQuery} -f login=${JSON.stringify(owner)} -F number=${projectNumber} --jq '.data.user.projectV2.id'`
|
|
240
|
-
);
|
|
241
|
-
if (raw && raw !== "null") return raw;
|
|
242
|
-
} catch (_) {
|
|
243
|
-
}
|
|
244
|
-
try {
|
|
245
|
-
const raw = run(
|
|
246
|
-
`gh api graphql -f query=${orgQuery} -f login=${JSON.stringify(owner)} -F number=${projectNumber} --jq '.data.organization.projectV2.id'`
|
|
247
|
-
);
|
|
248
|
-
if (raw && raw !== "null") return raw;
|
|
249
|
-
} catch (_) {
|
|
250
|
-
}
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
function findExistingBoard(owner, titlePattern) {
|
|
254
|
-
const pattern = titlePattern.toLowerCase();
|
|
255
|
-
try {
|
|
256
|
-
const raw = run(
|
|
257
|
-
`gh api graphql -f query='query($login: String!) { user(login: $login) { projectsV2(first: 20) { nodes { id number url title } } } }' -f login=${JSON.stringify(owner)} --jq '.data.user.projectsV2.nodes'`
|
|
258
|
-
);
|
|
259
|
-
const nodes = JSON.parse(raw);
|
|
260
|
-
const match = nodes.find((n) => n.title.toLowerCase().includes(pattern));
|
|
261
|
-
if (match) return { number: match.number, url: match.url, nodeId: match.id, title: match.title };
|
|
262
|
-
} catch (_) {
|
|
263
|
-
}
|
|
264
|
-
try {
|
|
265
|
-
const raw = run(
|
|
266
|
-
`gh api graphql -f query='query($login: String!) { organization(login: $login) { projectsV2(first: 20) { nodes { id number url title } } } }' -f login=${JSON.stringify(owner)} --jq '.data.organization.projectsV2.nodes'`
|
|
267
|
-
);
|
|
268
|
-
const nodes = JSON.parse(raw);
|
|
269
|
-
const match = nodes.find((n) => n.title.toLowerCase().includes(pattern));
|
|
270
|
-
if (match) return { number: match.number, url: match.url, nodeId: match.id, title: match.title };
|
|
271
|
-
} catch (_) {
|
|
272
|
-
}
|
|
273
|
-
return null;
|
|
274
|
-
}
|
|
275
|
-
function getProjectFields(owner, projectNumber) {
|
|
276
|
-
const query = `'query($login: String!, $number: Int!) { user(login: $login) { projectV2(number: $number) { fields(first: 20) { nodes { ... on ProjectV2SingleSelectField { id name options { id name } } ... on ProjectV2Field { id name dataType } } } } } }'`;
|
|
277
|
-
const orgQuery = `'query($login: String!, $number: Int!) { organization(login: $login) { projectV2(number: $number) { fields(first: 20) { nodes { ... on ProjectV2SingleSelectField { id name options { id name } } ... on ProjectV2Field { id name dataType } } } } } }'`;
|
|
278
|
-
let raw;
|
|
279
|
-
try {
|
|
280
|
-
raw = run(`gh api graphql -f query=${query} -f login=${JSON.stringify(owner)} -F number=${projectNumber} --jq '.data.user.projectV2.fields.nodes'`);
|
|
281
|
-
} catch (_) {
|
|
282
|
-
try {
|
|
283
|
-
raw = run(`gh api graphql -f query=${orgQuery} -f login=${JSON.stringify(owner)} -F number=${projectNumber} --jq '.data.organization.projectV2.fields.nodes'`);
|
|
284
|
-
} catch (_2) {
|
|
285
|
-
return null;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
const nodes = JSON.parse(raw);
|
|
289
|
-
const fields = {};
|
|
290
|
-
const statusNode = nodes.find((n) => n.name === "Status" && n.options);
|
|
291
|
-
if (statusNode) {
|
|
292
|
-
const stageMap = {
|
|
293
|
-
"new": "New",
|
|
294
|
-
"triaged": "Triaged",
|
|
295
|
-
"needs-info": "Needs Info",
|
|
296
|
-
"needs-security-review": "Needs Security Review",
|
|
297
|
-
"discussing": "Discussing",
|
|
298
|
-
"approved": "Approved",
|
|
299
|
-
"planning": "Planning",
|
|
300
|
-
"executing": "Executing",
|
|
301
|
-
"verifying": "Verifying",
|
|
302
|
-
"pr-created": "PR Created",
|
|
303
|
-
"done": "Done",
|
|
304
|
-
"failed": "Failed",
|
|
305
|
-
"blocked": "Blocked"
|
|
306
|
-
};
|
|
307
|
-
const nameToId = Object.fromEntries(statusNode.options.map((o) => [o.name, o.id]));
|
|
308
|
-
fields.status = {
|
|
309
|
-
field_id: statusNode.id,
|
|
310
|
-
field_name: "Status",
|
|
311
|
-
type: "SINGLE_SELECT",
|
|
312
|
-
options: Object.fromEntries(
|
|
313
|
-
Object.entries(stageMap).map(([stage, label]) => [stage, nameToId[label] || ""])
|
|
314
|
-
)
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
const aiNode = nodes.find((n) => n.name === "AI Agent State" && !n.options);
|
|
318
|
-
if (aiNode) {
|
|
319
|
-
fields.ai_agent_state = { field_id: aiNode.id, field_name: "AI Agent State", type: "TEXT" };
|
|
320
|
-
}
|
|
321
|
-
const phaseNode = nodes.find((n) => n.name === "Phase" && !n.options);
|
|
322
|
-
if (phaseNode) {
|
|
323
|
-
fields.phase = { field_id: phaseNode.id, field_name: "Phase", type: "TEXT" };
|
|
324
|
-
}
|
|
325
|
-
const routeNode = nodes.find((n) => n.name === "GSD Route" && n.options);
|
|
326
|
-
if (routeNode) {
|
|
327
|
-
const routeMap = {
|
|
328
|
-
"gsd:quick": "quick",
|
|
329
|
-
"gsd:quick --full": "quick --full",
|
|
330
|
-
"gsd:plan-phase": "plan-phase",
|
|
331
|
-
"gsd:new-milestone": "new-milestone"
|
|
332
|
-
};
|
|
333
|
-
const nameToId = Object.fromEntries(routeNode.options.map((o) => [o.name, o.id]));
|
|
334
|
-
fields.gsd_route = {
|
|
335
|
-
field_id: routeNode.id,
|
|
336
|
-
field_name: "GSD Route",
|
|
337
|
-
type: "SINGLE_SELECT",
|
|
338
|
-
options: Object.fromEntries(
|
|
339
|
-
Object.entries(routeMap).map(([route, label]) => [route, nameToId[label] || ""])
|
|
340
|
-
)
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
const milestoneNode = nodes.find((n) => n.name === "Milestone");
|
|
344
|
-
if (milestoneNode) {
|
|
345
|
-
fields.milestone = {
|
|
346
|
-
field_id: milestoneNode.id,
|
|
347
|
-
field_name: "Milestone",
|
|
348
|
-
type: milestoneNode.dataType || (milestoneNode.options ? "SINGLE_SELECT" : "TEXT")
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
return Object.keys(fields).length > 0 ? fields : null;
|
|
352
|
-
}
|
|
353
|
-
function createProject(owner, title) {
|
|
354
|
-
const raw = run(
|
|
355
|
-
`gh project create --owner ${JSON.stringify(owner)} --title ${JSON.stringify(title)} --format json`
|
|
356
|
-
);
|
|
357
|
-
const data = JSON.parse(raw);
|
|
358
|
-
return { number: data.number, url: data.url };
|
|
359
|
-
}
|
|
360
|
-
function addItemToProject(owner, projectNumber, issueUrl) {
|
|
361
|
-
return run(
|
|
362
|
-
`gh project item-add ${projectNumber} --owner ${JSON.stringify(owner)} --url ${JSON.stringify(issueUrl)}`
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
function postMilestoneStartAnnouncement(opts) {
|
|
366
|
-
const {
|
|
367
|
-
repo,
|
|
368
|
-
milestoneName,
|
|
369
|
-
boardUrl,
|
|
370
|
-
issues,
|
|
371
|
-
firstIssueNumber
|
|
372
|
-
} = opts;
|
|
373
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
374
|
-
const issueList = Array.isArray(issues) ? issues : [];
|
|
375
|
-
const issueRows = issueList.map((i) => {
|
|
376
|
-
const assignee = i.assignee ? `@${i.assignee}` : "\u2014";
|
|
377
|
-
return `| #${i.number} | ${i.title} | ${assignee} | \`${i.gsdRoute}\` |`;
|
|
378
|
-
}).join("\n");
|
|
379
|
-
const boardLine = boardUrl ? `**Board:** ${boardUrl}` : "**Board:** _(not configured)_";
|
|
380
|
-
const body = [
|
|
381
|
-
`> **MGW** \xB7 \`milestone-started\` \xB7 ${timestamp}`,
|
|
382
|
-
"",
|
|
383
|
-
`## Milestone Execution Started: ${milestoneName}`,
|
|
384
|
-
"",
|
|
385
|
-
boardLine,
|
|
386
|
-
"",
|
|
387
|
-
"### Issues in This Milestone",
|
|
388
|
-
"",
|
|
389
|
-
"| # | Title | Assignee | Route |",
|
|
390
|
-
"|---|-------|----------|-------|",
|
|
391
|
-
issueRows,
|
|
392
|
-
"",
|
|
393
|
-
`**${issueList.length} issue(s)** queued for autonomous execution. PRs will be posted on each issue as they complete.`,
|
|
394
|
-
"",
|
|
395
|
-
"---",
|
|
396
|
-
"*Auto-posted by MGW milestone orchestration*"
|
|
397
|
-
].join("\n");
|
|
398
|
-
const title = `[MGW] Milestone Started: ${milestoneName}`;
|
|
399
|
-
if (repo) {
|
|
400
|
-
try {
|
|
401
|
-
const [owner, repoName] = repo.split("/");
|
|
402
|
-
const repoMetaRaw = run(
|
|
403
|
-
`gh api graphql -f query='query { repository(owner: "${owner}", name: "${repoName}") { id discussionCategories(first: 20) { nodes { id name } } } }' --jq '.data.repository'`
|
|
404
|
-
);
|
|
405
|
-
const repoMeta = JSON.parse(repoMetaRaw);
|
|
406
|
-
const categories = repoMeta.discussionCategories && repoMeta.discussionCategories.nodes || [];
|
|
407
|
-
const announcements = categories.find((c) => c.name === "Announcements");
|
|
408
|
-
if (announcements) {
|
|
409
|
-
const repoId = repoMeta.id;
|
|
410
|
-
const categoryId = announcements.id;
|
|
411
|
-
const bodyEscaped = JSON.stringify(body);
|
|
412
|
-
const titleEscaped = JSON.stringify(title);
|
|
413
|
-
const resultRaw = run(
|
|
414
|
-
`gh api graphql -f query='mutation { createDiscussion(input: { repositoryId: ${JSON.stringify(repoId)}, categoryId: ${JSON.stringify(categoryId)}, title: ${titleEscaped}, body: ${bodyEscaped} }) { discussion { url } } }' --jq '.data.createDiscussion.discussion'`
|
|
415
|
-
);
|
|
416
|
-
const result = JSON.parse(resultRaw);
|
|
417
|
-
if (result && result.url) {
|
|
418
|
-
return { posted: true, method: "discussion", url: result.url };
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
} catch (_) {
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
if (firstIssueNumber && repo) {
|
|
425
|
-
try {
|
|
426
|
-
run(
|
|
427
|
-
`gh issue comment ${firstIssueNumber} --repo ${JSON.stringify(repo)} --body ${JSON.stringify(body)}`
|
|
428
|
-
);
|
|
429
|
-
return { posted: true, method: "comment", url: null };
|
|
430
|
-
} catch (_) {
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
return { posted: false, method: "none", url: null };
|
|
434
|
-
}
|
|
435
|
-
github = {
|
|
436
|
-
getRepo,
|
|
437
|
-
getIssue,
|
|
438
|
-
listIssues,
|
|
439
|
-
getMilestone,
|
|
440
|
-
getRateLimit,
|
|
441
|
-
closeMilestone,
|
|
442
|
-
createRelease,
|
|
443
|
-
getProjectNodeId,
|
|
444
|
-
findExistingBoard,
|
|
445
|
-
getProjectFields,
|
|
446
|
-
createProject,
|
|
447
|
-
addItemToProject,
|
|
448
|
-
postMilestoneStartAnnouncement
|
|
449
|
-
};
|
|
450
|
-
return github;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
var output;
|
|
454
|
-
var hasRequiredOutput;
|
|
455
|
-
|
|
456
|
-
function requireOutput () {
|
|
457
|
-
if (hasRequiredOutput) return output;
|
|
458
|
-
hasRequiredOutput = 1;
|
|
459
|
-
const IS_TTY = process.stdout.isTTY === true;
|
|
460
|
-
const IS_CI = Boolean(process.env.CI);
|
|
461
|
-
const USE_COLOR = IS_TTY && !IS_CI && !process.env.NO_COLOR;
|
|
462
|
-
const COLORS = {
|
|
463
|
-
reset: "\x1B[0m",
|
|
464
|
-
red: "\x1B[31m",
|
|
465
|
-
green: "\x1B[32m",
|
|
466
|
-
yellow: "\x1B[33m",
|
|
467
|
-
blue: "\x1B[34m",
|
|
468
|
-
cyan: "\x1B[36m",
|
|
469
|
-
bold: "\x1B[1m",
|
|
470
|
-
dim: "\x1B[2m"
|
|
471
|
-
};
|
|
472
|
-
function colorize(text, colorCode) {
|
|
473
|
-
if (!USE_COLOR) return text;
|
|
474
|
-
return `${colorCode}${text}${COLORS.reset}`;
|
|
475
|
-
}
|
|
476
|
-
function statusLine(icon, message) {
|
|
477
|
-
if (USE_COLOR) {
|
|
478
|
-
return `${icon} ${message}`;
|
|
479
|
-
}
|
|
480
|
-
return message;
|
|
481
|
-
}
|
|
482
|
-
function log(message) {
|
|
483
|
-
console.log(message);
|
|
484
|
-
}
|
|
485
|
-
function error(message) {
|
|
486
|
-
const prefix = colorize("error:", COLORS.red);
|
|
487
|
-
console.error(`${prefix} ${message}`);
|
|
488
|
-
}
|
|
489
|
-
function verbose(message, opts) {
|
|
490
|
-
if (opts && (opts.verbose || opts.debug)) {
|
|
491
|
-
console.log(colorize(` ${message}`, COLORS.dim));
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
function debug(message, opts) {
|
|
495
|
-
if (opts && opts.debug) {
|
|
496
|
-
console.log(colorize(`[debug] ${message}`, COLORS.dim));
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
function formatJson(data) {
|
|
500
|
-
return JSON.stringify(data, null, 2);
|
|
501
|
-
}
|
|
502
|
-
output = {
|
|
503
|
-
IS_TTY,
|
|
504
|
-
IS_CI,
|
|
505
|
-
USE_COLOR,
|
|
506
|
-
COLORS,
|
|
507
|
-
colorize,
|
|
508
|
-
statusLine,
|
|
509
|
-
log,
|
|
510
|
-
error,
|
|
511
|
-
verbose,
|
|
512
|
-
debug,
|
|
513
|
-
formatJson
|
|
514
|
-
};
|
|
515
|
-
return output;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
var claude;
|
|
519
|
-
var hasRequiredClaude;
|
|
520
|
-
|
|
521
|
-
function requireClaude () {
|
|
522
|
-
if (hasRequiredClaude) return claude;
|
|
523
|
-
hasRequiredClaude = 1;
|
|
524
|
-
const { execSync, spawn } = require$$0$1;
|
|
525
|
-
const path = require$$1;
|
|
526
|
-
const fs = require$$0;
|
|
527
|
-
function getCommandsDir() {
|
|
528
|
-
const dir = path.join(__dirname, "..", "commands");
|
|
529
|
-
if (!fs.existsSync(dir)) {
|
|
530
|
-
throw new Error(
|
|
531
|
-
`Commands directory not found at: ${dir}
|
|
532
|
-
This may indicate a corrupted installation. Try reinstalling mgw.`
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
return dir;
|
|
536
|
-
}
|
|
537
|
-
function assertClaudeAvailable() {
|
|
538
|
-
try {
|
|
539
|
-
execSync("claude --version", {
|
|
540
|
-
encoding: "utf-8",
|
|
541
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
542
|
-
});
|
|
543
|
-
} catch (err) {
|
|
544
|
-
if (err.code === "ENOENT") {
|
|
545
|
-
console.error(
|
|
546
|
-
"Error: claude CLI is not installed.\n\nInstall it with:\n npm install -g @anthropic-ai/claude-code\n\nThen run:\n claude login"
|
|
547
|
-
);
|
|
548
|
-
} else {
|
|
549
|
-
console.error(
|
|
550
|
-
"Error: claude CLI check failed.\nEnsure claude is installed and on your PATH."
|
|
551
|
-
);
|
|
552
|
-
}
|
|
553
|
-
process.exit(1);
|
|
554
|
-
}
|
|
555
|
-
try {
|
|
556
|
-
execSync("claude auth status", {
|
|
557
|
-
encoding: "utf-8",
|
|
558
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
559
|
-
});
|
|
560
|
-
} catch {
|
|
561
|
-
console.error(
|
|
562
|
-
"Error: claude CLI is not authenticated.\n\nRun:\n claude login\n\nThen retry your command."
|
|
563
|
-
);
|
|
564
|
-
process.exit(1);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
function invokeClaude(commandFile, userPrompt, opts) {
|
|
568
|
-
const o = opts || {};
|
|
569
|
-
const args = ["-p"];
|
|
570
|
-
if (commandFile) {
|
|
571
|
-
args.push("--system-prompt-file", commandFile);
|
|
572
|
-
}
|
|
573
|
-
if (o.model) {
|
|
574
|
-
args.push("--model", o.model);
|
|
575
|
-
}
|
|
576
|
-
if (o.json) {
|
|
577
|
-
args.push("--output-format", "json");
|
|
578
|
-
}
|
|
579
|
-
if (userPrompt) {
|
|
580
|
-
args.push(userPrompt);
|
|
581
|
-
}
|
|
582
|
-
if (o.dryRun) {
|
|
583
|
-
console.log("Would invoke: claude " + args.join(" "));
|
|
584
|
-
return Promise.resolve({ exitCode: 0, output: "" });
|
|
585
|
-
}
|
|
586
|
-
return new Promise((resolve, reject) => {
|
|
587
|
-
const stdio = o.quiet ? ["pipe", "pipe", "pipe"] : ["inherit", "inherit", "inherit"];
|
|
588
|
-
const child = spawn("claude", args, { stdio });
|
|
589
|
-
let output = "";
|
|
590
|
-
if (o.quiet) {
|
|
591
|
-
child.stdout.on("data", (chunk) => {
|
|
592
|
-
output += chunk.toString();
|
|
593
|
-
});
|
|
594
|
-
child.stderr.on("data", (chunk) => {
|
|
595
|
-
output += chunk.toString();
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
child.on("error", (err) => {
|
|
599
|
-
if (err.code === "ENOENT") {
|
|
600
|
-
reject(new Error("claude CLI not found. Install with: npm install -g @anthropic-ai/claude-code"));
|
|
601
|
-
} else {
|
|
602
|
-
reject(err);
|
|
603
|
-
}
|
|
604
|
-
});
|
|
605
|
-
child.on("close", (code) => {
|
|
606
|
-
resolve({ exitCode: code || 0, output });
|
|
607
|
-
});
|
|
608
|
-
});
|
|
609
|
-
}
|
|
610
|
-
claude = {
|
|
611
|
-
assertClaudeAvailable,
|
|
612
|
-
invokeClaude,
|
|
613
|
-
getCommandsDir
|
|
614
|
-
};
|
|
615
|
-
return claude;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
exports.getDefaultExportFromCjs = getDefaultExportFromCjs;
|
|
619
|
-
exports.requireClaude = requireClaude;
|
|
620
|
-
exports.requireGithub = requireGithub;
|
|
621
|
-
exports.requireOutput = requireOutput;
|
|
622
|
-
exports.requireState = requireState;
|