@snipcodeit/mgw 0.3.0 → 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/bin/mgw-install.cjs +121 -24
- package/commands/board/create.md +192 -0
- package/commands/board/sync.md +44 -0
- package/commands/board/views.md +23 -0
- package/commands/context.md +183 -0
- package/commands/handoff.md +169 -0
- package/commands/issue.md +62 -0
- package/commands/milestone.md +37 -5
- package/commands/project.md +19 -0
- package/commands/run/execute.md +150 -5
- package/commands/run/pr-create.md +42 -0
- package/commands/run/worktree.md +41 -0
- package/commands/sync.md +69 -0
- package/dist/bin/mgw.cjs +14 -11
- package/dist/{index-s7v-ifd0.cjs → index-B-_JvYpz.cjs} +217 -22
- package/dist/lib/index.cjs +474 -19
- package/package.json +1 -1
package/dist/lib/index.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
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
10
|
var pipeline;
|
|
@@ -153,24 +153,52 @@ function requireGsdAdapter () {
|
|
|
153
153
|
hasRequiredGsdAdapter = 1;
|
|
154
154
|
const { execSync } = require$$0;
|
|
155
155
|
const path = require$$1;
|
|
156
|
-
const os = require$$
|
|
157
|
-
const fs = require$$
|
|
156
|
+
const os = require$$3;
|
|
157
|
+
const fs = require$$2;
|
|
158
158
|
const { TimeoutError, GsdToolError } = index$1.requireErrors();
|
|
159
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
|
+
}
|
|
160
176
|
function getGsdToolsPath() {
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
"bin",
|
|
166
|
-
"gsd-tools.cjs"
|
|
167
|
-
);
|
|
168
|
-
if (fs.existsSync(standard)) {
|
|
169
|
-
return standard;
|
|
177
|
+
const root = resolveGsdRoot();
|
|
178
|
+
const toolPath = path.join(root, "bin", "gsd-tools.cjs");
|
|
179
|
+
if (fs.existsSync(toolPath)) {
|
|
180
|
+
return toolPath;
|
|
170
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
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const defaultPath = path.join(os.homedir(), ".claude", "get-shit-done", "bin", "gsd-tools.cjs");
|
|
197
|
+
checked.push(` default: ${defaultPath}`);
|
|
171
198
|
throw new Error(
|
|
172
|
-
`GSD tools not found
|
|
173
|
-
|
|
199
|
+
`GSD tools not found. Checked:
|
|
200
|
+
${checked.join("\n")}
|
|
201
|
+
Set GSD_TOOLS_PATH or add gsd_path to .mgw/config.json`
|
|
174
202
|
);
|
|
175
203
|
}
|
|
176
204
|
const GSD_TIMEOUT_MS = 15e3;
|
|
@@ -289,6 +317,7 @@ Ensure the get-shit-done framework is installed at ~/.claude/get-shit-done/`
|
|
|
289
317
|
return { activeMilestone, currentPhase, planCount };
|
|
290
318
|
}
|
|
291
319
|
gsdAdapter = {
|
|
320
|
+
resolveGsdRoot,
|
|
292
321
|
getGsdToolsPath,
|
|
293
322
|
invokeGsdTool,
|
|
294
323
|
getTimestamp,
|
|
@@ -320,7 +349,7 @@ function requireTemplateLoader () {
|
|
|
320
349
|
if (hasRequiredTemplateLoader) return templateLoader.exports;
|
|
321
350
|
hasRequiredTemplateLoader = 1;
|
|
322
351
|
(function (module) {
|
|
323
|
-
const fs = require$$
|
|
352
|
+
const fs = require$$2;
|
|
324
353
|
const path = require$$1;
|
|
325
354
|
const VALID_GSD_ROUTES = [
|
|
326
355
|
"quick",
|
|
@@ -626,6 +655,21 @@ function requireTemplates () {
|
|
|
626
655
|
return templates;
|
|
627
656
|
}
|
|
628
657
|
|
|
658
|
+
var claude;
|
|
659
|
+
var hasRequiredClaude;
|
|
660
|
+
|
|
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
|
|
669
|
+
};
|
|
670
|
+
return claude;
|
|
671
|
+
}
|
|
672
|
+
|
|
629
673
|
var progress;
|
|
630
674
|
var hasRequiredProgress;
|
|
631
675
|
|
|
@@ -724,6 +768,415 @@ ${header}
|
|
|
724
768
|
return progress;
|
|
725
769
|
}
|
|
726
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
|
+
|
|
727
1180
|
var lib;
|
|
728
1181
|
var hasRequiredLib;
|
|
729
1182
|
|
|
@@ -737,13 +1190,15 @@ function requireLib () {
|
|
|
737
1190
|
...requireGsdAdapter(),
|
|
738
1191
|
...requireTemplates(),
|
|
739
1192
|
...index$1.requireOutput(),
|
|
740
|
-
...
|
|
1193
|
+
...requireClaude(),
|
|
1194
|
+
...index$1.requireProviderManager(),
|
|
741
1195
|
...index$1.requireRetry(),
|
|
742
1196
|
...index$1.requireSpinner(),
|
|
743
1197
|
...requireProgress(),
|
|
744
1198
|
...requirePipeline(),
|
|
745
1199
|
...index$1.requireErrors(),
|
|
746
|
-
...index$1.requireLogger()
|
|
1200
|
+
...index$1.requireLogger(),
|
|
1201
|
+
...requireIssueContext()
|
|
747
1202
|
};
|
|
748
1203
|
Object.defineProperty(_exports, "createIssuesBrowser", {
|
|
749
1204
|
configurable: true,
|