@iloom/cli 0.9.1 → 0.10.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/LICENSE +1 -1
- package/README.md +179 -41
- package/dist/{BranchNamingService-K6XNWQ6C.js → BranchNamingService-ECJHBB67.js} +2 -2
- package/dist/ClaudeContextManager-QXX6ZFST.js +14 -0
- package/dist/ClaudeService-NJNK2SUH.js +13 -0
- package/dist/{GitHubService-O7T6CFAJ.js → GitHubService-MEHKHUQP.js} +4 -4
- package/dist/IssueTrackerFactory-NG53YX5S.js +14 -0
- package/dist/{LoomLauncher-3I47SUPV.js → LoomLauncher-L64HHS3T.js} +9 -9
- package/dist/{MetadataManager-W3C54UYT.js → MetadataManager-5QZSTKNN.js} +2 -2
- package/dist/{ProjectCapabilityDetector-N5L7T4IY.js → ProjectCapabilityDetector-5KSYUTBJ.js} +3 -3
- package/dist/{PromptTemplateManager-36YLQRHP.js → PromptTemplateManager-DULSVRRE.js} +2 -2
- package/dist/README.md +179 -41
- package/dist/{SettingsManager-QR7V2IW2.js → SettingsManager-BQDQA3FK.js} +4 -2
- package/dist/agents/iloom-artifact-reviewer.md +11 -0
- package/dist/agents/iloom-code-reviewer.md +14 -0
- package/dist/agents/iloom-issue-analyze-and-plan.md +55 -12
- package/dist/agents/iloom-issue-analyzer.md +49 -6
- package/dist/agents/iloom-issue-complexity-evaluator.md +47 -6
- package/dist/agents/iloom-issue-enhancer.md +86 -7
- package/dist/agents/iloom-issue-implementer.md +48 -7
- package/dist/agents/iloom-issue-planner.md +115 -62
- package/dist/{build-IC4CJRMP.js → build-5GO3XW26.js} +9 -9
- package/dist/{chunk-USSL2X4A.js → chunk-3D7WQM7I.js} +2 -2
- package/dist/chunk-4232AHNQ.js +35 -0
- package/dist/chunk-4232AHNQ.js.map +1 -0
- package/dist/{chunk-QN47QVBX.js → chunk-4WJNIR5O.js} +1 -1
- package/dist/chunk-4WJNIR5O.js.map +1 -0
- package/dist/{chunk-2JPXGGP4.js → chunk-5MWV33NN.js} +4 -4
- package/dist/{chunk-POU2UMWN.js → chunk-6EU6TCF6.js} +10 -10
- package/dist/chunk-6EU6TCF6.js.map +1 -0
- package/dist/{chunk-Y5O2ALDZ.js → chunk-FB47TIJG.js} +29 -11
- package/dist/chunk-FB47TIJG.js.map +1 -0
- package/dist/chunk-HEXKPKCK.js +1396 -0
- package/dist/chunk-HEXKPKCK.js.map +1 -0
- package/dist/{chunk-KAYXR544.js → chunk-J5S7DFYC.js} +2 -2
- package/dist/{chunk-OK7LUTRW.js → chunk-JO2LZ6EQ.js} +476 -5
- package/dist/chunk-JO2LZ6EQ.js.map +1 -0
- package/dist/{chunk-KBEIQP4G.js → chunk-KB64WNBZ.js} +43 -3
- package/dist/chunk-KB64WNBZ.js.map +1 -0
- package/dist/{chunk-Y5HSSIK2.js → chunk-KXDRI47U.js} +71 -13
- package/dist/chunk-KXDRI47U.js.map +1 -0
- package/dist/{chunk-HZXBHMVM.js → chunk-LXLMMXXY.js} +54 -14
- package/dist/chunk-LXLMMXXY.js.map +1 -0
- package/dist/{chunk-H6ST2TGP.js → chunk-MNHZB4Z2.js} +4 -4
- package/dist/{chunk-TL72BGP6.js → chunk-MORRVYPT.js} +2 -2
- package/dist/{chunk-TGRK3CHF.js → chunk-NRSWLOAZ.js} +8 -8
- package/dist/chunk-NRSWLOAZ.js.map +1 -0
- package/dist/{chunk-FO5GGFOV.js → chunk-ONQYPICO.js} +13 -5
- package/dist/chunk-ONQYPICO.js.map +1 -0
- package/dist/{chunk-7ZEHSSUP.js → chunk-P4O6EH46.js} +4 -4
- package/dist/chunk-QZWEJVWV.js +207 -0
- package/dist/chunk-QZWEJVWV.js.map +1 -0
- package/dist/chunk-RSYT7MVI.js +202 -0
- package/dist/chunk-RSYT7MVI.js.map +1 -0
- package/dist/{chunk-OAVJR4PM.js → chunk-RYWFS37M.js} +6 -6
- package/dist/chunk-RYWFS37M.js.map +1 -0
- package/dist/{chunk-B7U6OKUR.js → chunk-SF2P22EE.js} +11 -3
- package/dist/chunk-SF2P22EE.js.map +1 -0
- package/dist/{chunk-MZPRBNYC.js → chunk-SN3SQCFK.js} +10 -8
- package/dist/{chunk-MZPRBNYC.js.map → chunk-SN3SQCFK.js.map} +1 -1
- package/dist/{chunk-4ZIHFUPN.js → chunk-UD3WJDIV.js} +145 -107
- package/dist/chunk-UD3WJDIV.js.map +1 -0
- package/dist/{chunk-3P6J4IZZ.js → chunk-UKBAJ2QQ.js} +61 -7
- package/dist/chunk-UKBAJ2QQ.js.map +1 -0
- package/dist/{chunk-RD7OPXZK.js → chunk-UVD4CZKS.js} +3 -3
- package/dist/chunk-UWGVCXRF.js +207 -0
- package/dist/chunk-UWGVCXRF.js.map +1 -0
- package/dist/{chunk-JT5LZRMI.js → chunk-VECNX6VX.js} +2 -2
- package/dist/{chunk-TRUMP4DA.js → chunk-VG45TUYK.js} +75 -6
- package/dist/chunk-VG45TUYK.js.map +1 -0
- package/dist/{chunk-4GAJJUYS.js → chunk-VGGST52X.js} +2 -2
- package/dist/{chunk-4LKGCFGG.js → chunk-WWKOVDWC.js} +2 -2
- package/dist/{chunk-2HZX6AMR.js → chunk-WY4QBK43.js} +7 -7
- package/dist/chunk-WY4QBK43.js.map +1 -0
- package/dist/chunk-Y4YZTHZE.js +73 -0
- package/dist/chunk-Y4YZTHZE.js.map +1 -0
- package/dist/{chunk-VOGGLPG5.js → chunk-YQ57ORTV.js} +14 -1
- package/dist/chunk-YQ57ORTV.js.map +1 -0
- package/dist/{chunk-XFEK2X2D.js → chunk-YYAKPQBT.js} +73 -20
- package/dist/chunk-YYAKPQBT.js.map +1 -0
- package/dist/{chunk-NTTSUAVM.js → chunk-ZEWU5PZK.js} +2 -2
- package/dist/{chunk-5LVVQGB3.js → chunk-ZHPNZC75.js} +17 -17
- package/dist/chunk-ZHPNZC75.js.map +1 -0
- package/dist/{chunk-I3HMNWQQ.js → chunk-ZW2LKWWE.js} +9 -9
- package/dist/chunk-ZW2LKWWE.js.map +1 -0
- package/dist/{claude-TP2QO3BU.js → claude-P3NQR6IJ.js} +2 -2
- package/dist/{cleanup-D3CSRBBZ.js → cleanup-6UCPVMFG.js} +81 -32
- package/dist/cleanup-6UCPVMFG.js.map +1 -0
- package/dist/cli.js +640 -350
- package/dist/cli.js.map +1 -1
- package/dist/{commit-IWGT42XN.js → commit-L3EPY5QG.js} +23 -21
- package/dist/commit-L3EPY5QG.js.map +1 -0
- package/dist/{compile-EOWJORKO.js → compile-ZS4HYRX5.js} +9 -9
- package/dist/{contribute-WSJTV2RX.js → contribute-ORDDQGSL.js} +14 -6
- package/dist/contribute-ORDDQGSL.js.map +1 -0
- package/dist/{dev-server-Q6M62ATG.js → dev-server-FYZ2AQIH.js} +29 -15
- package/dist/dev-server-FYZ2AQIH.js.map +1 -0
- package/dist/{feedback-QPNDZQRV.js → feedback-TMBXSCM5.js} +15 -15
- package/dist/{git-W3XUIFTR.js → git-ET64COO3.js} +4 -4
- package/dist/hooks/iloom-hook.js +15 -0
- package/dist/ignite-CGOV3TD4.js +1393 -0
- package/dist/ignite-CGOV3TD4.js.map +1 -0
- package/dist/index.d.ts +397 -53
- package/dist/index.js +1178 -40
- package/dist/index.js.map +1 -1
- package/dist/{init-ALYWKNWG.js → init-GFQ5W7GK.js} +57 -21
- package/dist/init-GFQ5W7GK.js.map +1 -0
- package/dist/issues-T4ZZSPEG.js +179 -0
- package/dist/issues-T4ZZSPEG.js.map +1 -0
- package/dist/{lint-IHUH45OC.js → lint-6TQXDZ3T.js} +9 -9
- package/dist/mcp/issue-management-server.js +2472 -257
- package/dist/mcp/issue-management-server.js.map +1 -1
- package/dist/mcp/recap-server.js +144 -21
- package/dist/mcp/recap-server.js.map +1 -1
- package/dist/{neon-helpers-VVFFTLXE.js → neon-helpers-CQN2PB4S.js} +3 -3
- package/dist/neon-helpers-CQN2PB4S.js.map +1 -0
- package/dist/{open-KWOV2OFO.js → open-5QZGXQRF.js} +15 -15
- package/dist/open-5QZGXQRF.js.map +1 -0
- package/dist/{plan-BRJBFJHF.js → plan-U7ZQWLFY.js} +41 -25
- package/dist/plan-U7ZQWLFY.js.map +1 -0
- package/dist/{projects-LH362JZQ.js → projects-2UOXFLNZ.js} +4 -4
- package/dist/prompts/CLAUDE.md +62 -0
- package/dist/prompts/init-prompt.txt +386 -47
- package/dist/prompts/issue-prompt.txt +427 -54
- package/dist/prompts/plan-prompt.txt +97 -16
- package/dist/prompts/pr-prompt.txt +44 -1
- package/dist/prompts/regular-prompt.txt +42 -1
- package/dist/prompts/session-summary-prompt.txt +14 -0
- package/dist/prompts/swarm-orchestrator-prompt.txt +437 -0
- package/dist/{rebase-AJOJOZUG.js → rebase-DWIB77KV.js} +10 -10
- package/dist/{recap-GKJXMDXW.js → recap-MX63HAKV.js} +47 -19
- package/dist/recap-MX63HAKV.js.map +1 -0
- package/dist/{run-QEUVZF7J.js → run-O3TFNQFC.js} +15 -15
- package/dist/run-O3TFNQFC.js.map +1 -0
- package/dist/schema/package-iloom.schema.json +58 -0
- package/dist/schema/settings.schema.json +130 -15
- package/dist/{shell-DAAVG4YN.js → shell-G6VC2CYR.js} +14 -7
- package/dist/shell-G6VC2CYR.js.map +1 -0
- package/dist/{summary-ZKOA35PT.js → summary-FWHAX55O.js} +27 -25
- package/dist/summary-FWHAX55O.js.map +1 -0
- package/dist/{test-5GPWWO3P.js → test-F7JNJZYP.js} +9 -9
- package/dist/{test-git-EJUKDB7F.js → test-git-BTAOIUE2.js} +4 -4
- package/dist/test-jira-CHYNV33F.js +96 -0
- package/dist/test-jira-CHYNV33F.js.map +1 -0
- package/dist/{test-prefix-23TOBUXY.js → test-prefix-Q6TFSU6F.js} +4 -4
- package/dist/{test-webserver-CKROHFBQ.js → test-webserver-EONCG7E7.js} +6 -6
- package/dist/{vscode-6TOLFCI2.js → vscode-VA5X4P25.js} +7 -7
- package/package.json +5 -1
- package/dist/ClaudeContextManager-X2Y72GRL.js +0 -14
- package/dist/ClaudeService-7P32TTES.js +0 -13
- package/dist/chunk-2HZX6AMR.js.map +0 -1
- package/dist/chunk-3P6J4IZZ.js.map +0 -1
- package/dist/chunk-4ZIHFUPN.js.map +0 -1
- package/dist/chunk-5LVVQGB3.js.map +0 -1
- package/dist/chunk-B7U6OKUR.js.map +0 -1
- package/dist/chunk-ENGCJIYQ.js +0 -520
- package/dist/chunk-ENGCJIYQ.js.map +0 -1
- package/dist/chunk-FO5GGFOV.js.map +0 -1
- package/dist/chunk-HZXBHMVM.js.map +0 -1
- package/dist/chunk-I3HMNWQQ.js.map +0 -1
- package/dist/chunk-J7FJ6PUT.js +0 -121
- package/dist/chunk-J7FJ6PUT.js.map +0 -1
- package/dist/chunk-KBEIQP4G.js.map +0 -1
- package/dist/chunk-OAVJR4PM.js.map +0 -1
- package/dist/chunk-OK7LUTRW.js.map +0 -1
- package/dist/chunk-POU2UMWN.js.map +0 -1
- package/dist/chunk-QN47QVBX.js.map +0 -1
- package/dist/chunk-TGRK3CHF.js.map +0 -1
- package/dist/chunk-TRUMP4DA.js.map +0 -1
- package/dist/chunk-VOGGLPG5.js.map +0 -1
- package/dist/chunk-XFEK2X2D.js.map +0 -1
- package/dist/chunk-Y5HSSIK2.js.map +0 -1
- package/dist/chunk-Y5O2ALDZ.js.map +0 -1
- package/dist/cleanup-D3CSRBBZ.js.map +0 -1
- package/dist/commit-IWGT42XN.js.map +0 -1
- package/dist/contribute-WSJTV2RX.js.map +0 -1
- package/dist/dev-server-Q6M62ATG.js.map +0 -1
- package/dist/ignite-OPO6EDYT.js +0 -784
- package/dist/ignite-OPO6EDYT.js.map +0 -1
- package/dist/init-ALYWKNWG.js.map +0 -1
- package/dist/issues-L7TBUPXT.js +0 -116
- package/dist/issues-L7TBUPXT.js.map +0 -1
- package/dist/open-KWOV2OFO.js.map +0 -1
- package/dist/plan-BRJBFJHF.js.map +0 -1
- package/dist/recap-GKJXMDXW.js.map +0 -1
- package/dist/run-QEUVZF7J.js.map +0 -1
- package/dist/shell-DAAVG4YN.js.map +0 -1
- package/dist/summary-ZKOA35PT.js.map +0 -1
- /package/dist/{BranchNamingService-K6XNWQ6C.js.map → BranchNamingService-ECJHBB67.js.map} +0 -0
- /package/dist/{ClaudeContextManager-X2Y72GRL.js.map → ClaudeContextManager-QXX6ZFST.js.map} +0 -0
- /package/dist/{ClaudeService-7P32TTES.js.map → ClaudeService-NJNK2SUH.js.map} +0 -0
- /package/dist/{GitHubService-O7T6CFAJ.js.map → GitHubService-MEHKHUQP.js.map} +0 -0
- /package/dist/{MetadataManager-W3C54UYT.js.map → IssueTrackerFactory-NG53YX5S.js.map} +0 -0
- /package/dist/{LoomLauncher-3I47SUPV.js.map → LoomLauncher-L64HHS3T.js.map} +0 -0
- /package/dist/{ProjectCapabilityDetector-N5L7T4IY.js.map → MetadataManager-5QZSTKNN.js.map} +0 -0
- /package/dist/{PromptTemplateManager-36YLQRHP.js.map → ProjectCapabilityDetector-5KSYUTBJ.js.map} +0 -0
- /package/dist/{SettingsManager-QR7V2IW2.js.map → PromptTemplateManager-DULSVRRE.js.map} +0 -0
- /package/dist/{claude-TP2QO3BU.js.map → SettingsManager-BQDQA3FK.js.map} +0 -0
- /package/dist/{build-IC4CJRMP.js.map → build-5GO3XW26.js.map} +0 -0
- /package/dist/{chunk-USSL2X4A.js.map → chunk-3D7WQM7I.js.map} +0 -0
- /package/dist/{chunk-2JPXGGP4.js.map → chunk-5MWV33NN.js.map} +0 -0
- /package/dist/{chunk-KAYXR544.js.map → chunk-J5S7DFYC.js.map} +0 -0
- /package/dist/{chunk-H6ST2TGP.js.map → chunk-MNHZB4Z2.js.map} +0 -0
- /package/dist/{chunk-TL72BGP6.js.map → chunk-MORRVYPT.js.map} +0 -0
- /package/dist/{chunk-7ZEHSSUP.js.map → chunk-P4O6EH46.js.map} +0 -0
- /package/dist/{chunk-RD7OPXZK.js.map → chunk-UVD4CZKS.js.map} +0 -0
- /package/dist/{chunk-JT5LZRMI.js.map → chunk-VECNX6VX.js.map} +0 -0
- /package/dist/{chunk-4GAJJUYS.js.map → chunk-VGGST52X.js.map} +0 -0
- /package/dist/{chunk-4LKGCFGG.js.map → chunk-WWKOVDWC.js.map} +0 -0
- /package/dist/{chunk-NTTSUAVM.js.map → chunk-ZEWU5PZK.js.map} +0 -0
- /package/dist/{git-W3XUIFTR.js.map → claude-P3NQR6IJ.js.map} +0 -0
- /package/dist/{compile-EOWJORKO.js.map → compile-ZS4HYRX5.js.map} +0 -0
- /package/dist/{feedback-QPNDZQRV.js.map → feedback-TMBXSCM5.js.map} +0 -0
- /package/dist/{neon-helpers-VVFFTLXE.js.map → git-ET64COO3.js.map} +0 -0
- /package/dist/{lint-IHUH45OC.js.map → lint-6TQXDZ3T.js.map} +0 -0
- /package/dist/{projects-LH362JZQ.js.map → projects-2UOXFLNZ.js.map} +0 -0
- /package/dist/{rebase-AJOJOZUG.js.map → rebase-DWIB77KV.js.map} +0 -0
- /package/dist/{test-5GPWWO3P.js.map → test-F7JNJZYP.js.map} +0 -0
- /package/dist/{test-git-EJUKDB7F.js.map → test-git-BTAOIUE2.js.map} +0 -0
- /package/dist/{test-prefix-23TOBUXY.js.map → test-prefix-Q6TFSU6F.js.map} +0 -0
- /package/dist/{test-webserver-CKROHFBQ.js.map → test-webserver-EONCG7E7.js.map} +0 -0
- /package/dist/{vscode-6TOLFCI2.js.map → vscode-VA5X4P25.js.map} +0 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
IssueTrackerFactory
|
|
4
|
+
} from "./chunk-UKBAJ2QQ.js";
|
|
5
|
+
import {
|
|
6
|
+
getLinearIssueDependencies
|
|
7
|
+
} from "./chunk-HEXKPKCK.js";
|
|
8
|
+
import {
|
|
9
|
+
getIssueDependencies
|
|
10
|
+
} from "./chunk-VG45TUYK.js";
|
|
11
|
+
import {
|
|
12
|
+
getLogger
|
|
13
|
+
} from "./chunk-6MLEBAYZ.js";
|
|
14
|
+
import {
|
|
15
|
+
logger
|
|
16
|
+
} from "./chunk-VT4PDUYT.js";
|
|
17
|
+
|
|
18
|
+
// src/utils/list-children.ts
|
|
19
|
+
async function fetchChildIssues(parentIssueNumber, issueTracker, repo) {
|
|
20
|
+
logger.debug("Fetching child issues", { parentIssueNumber, provider: issueTracker.providerName, repo });
|
|
21
|
+
try {
|
|
22
|
+
return await issueTracker.getChildIssues(parentIssueNumber, repo);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
logger.warn(`Failed to fetch child issues for ${parentIssueNumber}`, { error });
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function findChildLooms(parentBranchName, metadataManager) {
|
|
29
|
+
logger.debug("Finding child looms", { parentBranchName });
|
|
30
|
+
const allMetadata = await metadataManager.listAllMetadata();
|
|
31
|
+
const childLooms = allMetadata.filter((metadata) => {
|
|
32
|
+
if (!metadata.parentLoom) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return metadata.parentLoom.branchName === parentBranchName;
|
|
36
|
+
});
|
|
37
|
+
logger.debug(`Found ${childLooms.length} child looms for parent: ${parentBranchName}`);
|
|
38
|
+
return childLooms;
|
|
39
|
+
}
|
|
40
|
+
function matchChildrenData(childIssues, childLooms) {
|
|
41
|
+
const issueToLoomMap = /* @__PURE__ */ new Map();
|
|
42
|
+
for (const loom of childLooms) {
|
|
43
|
+
for (const issueNum of loom.issue_numbers) {
|
|
44
|
+
issueToLoomMap.set(issueNum, loom);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const childIssueIds = new Set(childIssues.map((issue) => issue.id));
|
|
48
|
+
const matchedIssues = childIssues.map((issue) => {
|
|
49
|
+
const matchingLoom = issueToLoomMap.get(issue.id);
|
|
50
|
+
return {
|
|
51
|
+
id: issue.id,
|
|
52
|
+
title: issue.title,
|
|
53
|
+
url: issue.url,
|
|
54
|
+
state: issue.state,
|
|
55
|
+
hasActiveLoom: matchingLoom != null,
|
|
56
|
+
loomBranch: (matchingLoom == null ? void 0 : matchingLoom.branchName) ?? null
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
const matchedLooms = childLooms.map((loom) => {
|
|
60
|
+
const hasMatchingIssue = loom.issue_numbers.some((issueNum) => childIssueIds.has(issueNum));
|
|
61
|
+
return {
|
|
62
|
+
branch: loom.branchName ?? "",
|
|
63
|
+
issueNumbers: loom.issue_numbers,
|
|
64
|
+
hasMatchingIssue
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
const summary = {
|
|
68
|
+
totalIssues: matchedIssues.length,
|
|
69
|
+
issuesWithLooms: matchedIssues.filter((issue) => issue.hasActiveLoom).length,
|
|
70
|
+
totalLooms: matchedLooms.length,
|
|
71
|
+
orphanLooms: matchedLooms.filter((loom) => !loom.hasMatchingIssue).length
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
issues: matchedIssues,
|
|
75
|
+
looms: matchedLooms,
|
|
76
|
+
summary
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async function assembleChildrenData(parentLoom, metadataManager, issueTracker, repo) {
|
|
80
|
+
if (!parentLoom.issue_numbers || parentLoom.issue_numbers.length === 0) {
|
|
81
|
+
logger.debug("No issue_numbers on loom, skipping children fetch", {
|
|
82
|
+
branch: parentLoom.branchName
|
|
83
|
+
});
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
if (!parentLoom.branchName) {
|
|
87
|
+
logger.debug("No branchName on loom, skipping children fetch");
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const parentIssueNumber = parentLoom.issue_numbers[0];
|
|
91
|
+
if (parentIssueNumber === void 0) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const [childIssues, childLooms] = await Promise.all([
|
|
95
|
+
fetchChildIssues(parentIssueNumber, issueTracker, repo),
|
|
96
|
+
findChildLooms(parentLoom.branchName, metadataManager)
|
|
97
|
+
]);
|
|
98
|
+
return matchChildrenData(childIssues, childLooms);
|
|
99
|
+
}
|
|
100
|
+
async function fetchChildIssueDetails(parentIssueNumber, issueTracker, repo) {
|
|
101
|
+
const providerName = issueTracker.providerName;
|
|
102
|
+
logger.debug("Fetching child issue details", { parentIssueNumber, provider: providerName });
|
|
103
|
+
const childIssues = await fetchChildIssues(parentIssueNumber, issueTracker, repo);
|
|
104
|
+
if (childIssues.length === 0) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
const results = await Promise.allSettled(
|
|
108
|
+
childIssues.map(async (child) => {
|
|
109
|
+
try {
|
|
110
|
+
const fullIssue = await issueTracker.fetchIssue(child.id, repo);
|
|
111
|
+
return {
|
|
112
|
+
number: formatIssueNumber(child.id, providerName),
|
|
113
|
+
title: fullIssue.title,
|
|
114
|
+
body: fullIssue.body,
|
|
115
|
+
url: child.url
|
|
116
|
+
};
|
|
117
|
+
} catch {
|
|
118
|
+
return {
|
|
119
|
+
number: formatIssueNumber(child.id, providerName),
|
|
120
|
+
title: child.title,
|
|
121
|
+
body: "",
|
|
122
|
+
url: child.url
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
);
|
|
127
|
+
const details = [];
|
|
128
|
+
for (const result of results) {
|
|
129
|
+
if (result.status === "fulfilled") {
|
|
130
|
+
details.push(result.value);
|
|
131
|
+
} else {
|
|
132
|
+
logger.warn("Failed to fetch details for a child issue", { error: result.reason });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return details;
|
|
136
|
+
}
|
|
137
|
+
function formatIssueNumber(issueId, providerName) {
|
|
138
|
+
if (providerName === "github") {
|
|
139
|
+
return issueId.startsWith("#") ? issueId : `#${issueId}`;
|
|
140
|
+
}
|
|
141
|
+
return issueId;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/utils/dependency-map.ts
|
|
145
|
+
async function buildDependencyMap(childIssueIds, settings, repo) {
|
|
146
|
+
const providerName = IssueTrackerFactory.getProviderName(settings);
|
|
147
|
+
const childIdSet = new Set(childIssueIds);
|
|
148
|
+
const dependencyMap = {};
|
|
149
|
+
getLogger().debug("Building dependency map", { childIssueIds, provider: providerName });
|
|
150
|
+
for (const id of childIssueIds) {
|
|
151
|
+
dependencyMap[id] = [];
|
|
152
|
+
}
|
|
153
|
+
const results = await Promise.allSettled(
|
|
154
|
+
childIssueIds.map(async (childId) => {
|
|
155
|
+
const blockers = await fetchBlockedBy(childId, providerName, repo);
|
|
156
|
+
const siblingBlockers = blockers.filter((blockerId) => childIdSet.has(blockerId));
|
|
157
|
+
return { childId, siblingBlockers };
|
|
158
|
+
})
|
|
159
|
+
);
|
|
160
|
+
for (const result of results) {
|
|
161
|
+
if (result.status === "fulfilled") {
|
|
162
|
+
const { childId, siblingBlockers } = result.value;
|
|
163
|
+
if (siblingBlockers.length > 0) {
|
|
164
|
+
dependencyMap[childId] = siblingBlockers;
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
getLogger().warn(`Failed to fetch dependencies for a child issue`, { error: result.reason });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const formattedMap = {};
|
|
171
|
+
for (const [key, values] of Object.entries(dependencyMap)) {
|
|
172
|
+
const formattedKey = formatIssueNumber(key, providerName);
|
|
173
|
+
formattedMap[formattedKey] = values.map((v) => formatIssueNumber(v, providerName));
|
|
174
|
+
}
|
|
175
|
+
return formattedMap;
|
|
176
|
+
}
|
|
177
|
+
async function fetchBlockedBy(issueId, providerName, repo) {
|
|
178
|
+
switch (providerName) {
|
|
179
|
+
case "github": {
|
|
180
|
+
const issueNumber = parseInt(issueId, 10);
|
|
181
|
+
if (isNaN(issueNumber)) {
|
|
182
|
+
getLogger().warn(`Invalid GitHub issue number: ${issueId}`);
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
const deps = await getIssueDependencies(issueNumber, "blocked_by", repo);
|
|
186
|
+
return deps.map((dep) => dep.id);
|
|
187
|
+
}
|
|
188
|
+
case "linear": {
|
|
189
|
+
const result = await getLinearIssueDependencies(issueId, "blocked_by");
|
|
190
|
+
return result.blockedBy.map((dep) => dep.id);
|
|
191
|
+
}
|
|
192
|
+
case "jira":
|
|
193
|
+
getLogger().debug(`Jira dependency fetching not yet supported for issue ${issueId}`);
|
|
194
|
+
return [];
|
|
195
|
+
default:
|
|
196
|
+
getLogger().warn(`Unsupported provider for dependency fetching: ${providerName}`);
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export {
|
|
202
|
+
fetchChildIssues,
|
|
203
|
+
assembleChildrenData,
|
|
204
|
+
fetchChildIssueDetails,
|
|
205
|
+
buildDependencyMap
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=chunk-QZWEJVWV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/list-children.ts","../src/utils/dependency-map.ts"],"sourcesContent":["/**\n * List children utilities for the --children flag in il list command\n *\n * This module handles:\n * - Fetching child issues from GitHub/Linear APIs\n * - Finding child looms by scanning metadata for parentLoom.branchName match\n * - Matching child issues to child looms bidirectionally\n * - Computing summary statistics\n */\n\nimport { MetadataManager, type LoomMetadata } from '../lib/MetadataManager.js'\nimport type { IssueTracker } from '../lib/IssueTracker.js'\nimport { logger } from './logger.js'\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n\n/**\n * Information about a child issue from the issue tracker API\n */\nexport interface ChildIssueInfo {\n id: string\n title: string\n url: string\n state: string\n hasActiveLoom: boolean\n loomBranch: string | null\n}\n\n/**\n * Information about a child loom from metadata\n */\nexport interface ChildLoomInfo {\n branch: string\n issueNumbers: string[]\n hasMatchingIssue: boolean\n}\n\n/**\n * Summary statistics for children\n */\nexport interface ChildrenSummary {\n totalIssues: number\n issuesWithLooms: number\n totalLooms: number\n orphanLooms: number\n}\n\n/**\n * Complete children data for a parent loom\n */\nexport interface ChildrenData {\n issues: ChildIssueInfo[]\n looms: ChildLoomInfo[]\n summary: ChildrenSummary\n}\n\n// ============================================================================\n// Child Issue Fetching\n// ============================================================================\n\n/**\n * Raw child issue data from API (before matching with looms)\n */\ninterface RawChildIssue {\n id: string\n title: string\n url: string\n state: string\n}\n\n/**\n * Fetch child issues from the appropriate provider via IssueTracker interface\n *\n * Uses Promise.allSettled for fault tolerance - API failures return empty array\n * with a warning logged rather than crashing.\n *\n * @param parentIssueNumber - The issue number/identifier of the parent\n * @param issueTracker - IssueTracker instance to delegate to\n * @param repo - Optional repo in \"owner/repo\" format for GitHub\n * @returns Array of raw child issues, or empty array on failure\n */\nexport async function fetchChildIssues(\n parentIssueNumber: string,\n issueTracker: IssueTracker,\n repo?: string,\n): Promise<RawChildIssue[]> {\n logger.debug('Fetching child issues', { parentIssueNumber, provider: issueTracker.providerName, repo })\n\n try {\n return await issueTracker.getChildIssues(parentIssueNumber, repo)\n } catch (error) {\n logger.warn(`Failed to fetch child issues for ${parentIssueNumber}`, { error })\n return []\n }\n}\n\n// ============================================================================\n// Child Loom Detection\n// ============================================================================\n\n/**\n * Find child looms by scanning metadata for parentLoom.branchName match\n *\n * Scans all active loom metadata and filters to those whose parentLoom.branchName\n * matches the given parent branch name.\n *\n * @param parentBranchName - The branch name of the parent loom\n * @param metadataManager - MetadataManager instance for reading loom metadata\n * @returns Array of LoomMetadata for child looms\n */\nexport async function findChildLooms(\n parentBranchName: string,\n metadataManager: MetadataManager,\n): Promise<LoomMetadata[]> {\n logger.debug('Finding child looms', { parentBranchName })\n\n // Get all active loom metadata\n const allMetadata = await metadataManager.listAllMetadata()\n\n // Filter to looms where parentLoom.branchName matches\n const childLooms = allMetadata.filter((metadata) => {\n // Skip if no parentLoom field\n if (!metadata.parentLoom) {\n return false\n }\n\n // Match by parentLoom.branchName\n return metadata.parentLoom.branchName === parentBranchName\n })\n\n logger.debug(`Found ${childLooms.length} child looms for parent: ${parentBranchName}`)\n\n return childLooms\n}\n\n// ============================================================================\n// Matching Logic\n// ============================================================================\n\n/**\n * Match child issues to child looms and compute summary statistics\n *\n * Performs bidirectional matching:\n * - For each child issue: check if any child loom has that issue number\n * - For each child loom: check if any child issue matches its issue_numbers\n *\n * @param childIssues - Raw child issues from API\n * @param childLooms - Child loom metadata\n * @returns ChildrenData with matched issues, looms, and summary\n */\nexport function matchChildrenData(\n childIssues: RawChildIssue[],\n childLooms: LoomMetadata[],\n): ChildrenData {\n // Build a map of issue ID -> child loom for fast lookup\n const issueToLoomMap = new Map<string, LoomMetadata>()\n for (const loom of childLooms) {\n for (const issueNum of loom.issue_numbers) {\n issueToLoomMap.set(issueNum, loom)\n }\n }\n\n // Build a set of all issue IDs from child issues for fast lookup\n const childIssueIds = new Set(childIssues.map((issue) => issue.id))\n\n // Match child issues to looms\n const matchedIssues: ChildIssueInfo[] = childIssues.map((issue) => {\n const matchingLoom = issueToLoomMap.get(issue.id)\n return {\n id: issue.id,\n title: issue.title,\n url: issue.url,\n state: issue.state,\n hasActiveLoom: matchingLoom != null,\n loomBranch: matchingLoom?.branchName ?? null,\n }\n })\n\n // Match child looms to issues\n const matchedLooms: ChildLoomInfo[] = childLooms.map((loom) => {\n // Check if any of the loom's issue_numbers match a child issue\n const hasMatchingIssue = loom.issue_numbers.some((issueNum) => childIssueIds.has(issueNum))\n return {\n branch: loom.branchName ?? '',\n issueNumbers: loom.issue_numbers,\n hasMatchingIssue,\n }\n })\n\n // Compute summary statistics\n const summary: ChildrenSummary = {\n totalIssues: matchedIssues.length,\n issuesWithLooms: matchedIssues.filter((issue) => issue.hasActiveLoom).length,\n totalLooms: matchedLooms.length,\n orphanLooms: matchedLooms.filter((loom) => !loom.hasMatchingIssue).length,\n }\n\n return {\n issues: matchedIssues,\n looms: matchedLooms,\n summary,\n }\n}\n\n// ============================================================================\n// Orchestrator Function\n// ============================================================================\n\n/**\n * Assemble complete children data for a parent loom\n *\n * This is the main entry point that orchestrates:\n * 1. Fetching child issues from the API\n * 2. Finding child looms from metadata\n * 3. Matching and computing summary\n *\n * Returns null if the loom has no issue_numbers (nothing to fetch children for).\n * Uses Promise.allSettled internally for fault tolerance.\n *\n * @param parentLoom - The parent loom metadata\n * @param metadataManager - MetadataManager instance\n * @param issueTracker - IssueTracker instance for fetching child issues\n * @param repo - Optional repo in \"owner/repo\" format for GitHub\n * @returns ChildrenData or null if no parent issue to query\n */\nexport async function assembleChildrenData(\n parentLoom: LoomMetadata,\n metadataManager: MetadataManager,\n issueTracker: IssueTracker,\n repo?: string,\n): Promise<ChildrenData | null> {\n // Can't fetch children if there's no parent issue\n if (!parentLoom.issue_numbers || parentLoom.issue_numbers.length === 0) {\n logger.debug('No issue_numbers on loom, skipping children fetch', {\n branch: parentLoom.branchName,\n })\n return null\n }\n\n // Can't fetch children if no branch name (can't match child looms)\n if (!parentLoom.branchName) {\n logger.debug('No branchName on loom, skipping children fetch')\n return null\n }\n\n // Use the first issue number as the parent for child issue fetching\n // Safe to access [0] since we already checked length > 0 above\n const parentIssueNumber = parentLoom.issue_numbers[0]\n if (parentIssueNumber === undefined) {\n // This should never happen given the length check above, but satisfies TypeScript\n return null\n }\n\n // Fetch child issues and find child looms in parallel for performance\n const [childIssues, childLooms] = await Promise.all([\n fetchChildIssues(parentIssueNumber, issueTracker, repo),\n findChildLooms(parentLoom.branchName, metadataManager),\n ])\n\n // Match and return\n return matchChildrenData(childIssues, childLooms)\n}\n\n// ============================================================================\n// Child Issue Details (for epic metadata persistence)\n// ============================================================================\n\n/**\n * Child issue detail for persistence in epic metadata\n */\nexport interface ChildIssueDetail {\n number: string // Prefixed: \"#123\" for GitHub, \"ENG-123\" for Linear\n title: string\n body: string\n url: string\n}\n\n/**\n * Fetch child issue details with body content and properly-prefixed numbers\n *\n * Unlike fetchChildIssues (which returns minimal data for list display),\n * this function fetches full issue details including body/description\n * and formats the issue number with the appropriate provider prefix.\n *\n * @param parentIssueNumber - The parent issue number/identifier\n * @param issueTracker - IssueTracker instance for fetching full issue details\n * @param repo - Optional repo in \"owner/repo\" format for GitHub\n * @returns Array of child issue details, or empty array on failure\n */\nexport async function fetchChildIssueDetails(\n parentIssueNumber: string,\n issueTracker: IssueTracker,\n repo?: string,\n): Promise<ChildIssueDetail[]> {\n const providerName = issueTracker.providerName\n\n logger.debug('Fetching child issue details', { parentIssueNumber, provider: providerName })\n\n // First fetch the list of child issues (lightweight)\n const childIssues = await fetchChildIssues(parentIssueNumber, issueTracker, repo)\n\n if (childIssues.length === 0) {\n return []\n }\n\n // Fetch full details for each child in parallel\n const results = await Promise.allSettled(\n childIssues.map(async (child): Promise<ChildIssueDetail> => {\n try {\n const fullIssue = await issueTracker.fetchIssue(child.id, repo)\n return {\n number: formatIssueNumber(child.id, providerName),\n title: fullIssue.title,\n body: fullIssue.body,\n url: child.url,\n }\n } catch {\n // Fall back to data from child list if full fetch fails\n return {\n number: formatIssueNumber(child.id, providerName),\n title: child.title,\n body: '',\n url: child.url,\n }\n }\n }),\n )\n\n // Collect fulfilled results\n const details: ChildIssueDetail[] = []\n for (const result of results) {\n if (result.status === 'fulfilled') {\n details.push(result.value)\n } else {\n logger.warn('Failed to fetch details for a child issue', { error: result.reason })\n }\n }\n\n return details\n}\n\n/**\n * Format an issue number with the appropriate provider prefix\n *\n * @param issueId - Raw issue ID (e.g., \"123\" for GitHub, \"ENG-123\" for Linear)\n * @param providerName - Provider type\n * @returns Prefixed number: \"#123\" for GitHub, \"ENG-123\" for Linear (already prefixed)\n */\nexport function formatIssueNumber(issueId: string, providerName: string): string {\n if (providerName === 'github') {\n return issueId.startsWith('#') ? issueId : `#${issueId}`\n }\n // Linear and Jira identifiers are already prefixed (e.g., \"ENG-123\", \"PROJ-456\")\n return issueId\n}\n","/**\n * Dependency map builder for epic/swarm child issues\n *\n * Fetches dependency relationships between child issues from the\n * configured issue tracker provider and builds a DAG-compatible\n * dependency map.\n */\n\nimport { getIssueDependencies } from './github.js'\nimport { getLinearIssueDependencies } from './linear.js'\nimport { IssueTrackerFactory } from '../lib/IssueTrackerFactory.js'\nimport type { IloomSettings } from '../lib/SettingsManager.js'\nimport { getLogger } from './logger-context.js'\nimport { formatIssueNumber } from './list-children.js'\n\n/**\n * Build a dependency map for a set of child issues\n *\n * For each child issue, fetches its \"blocked_by\" dependencies from the\n * issue tracker API. Only sibling dependencies (within the child issue set)\n * are included — external blockers are filtered out.\n *\n * @param childIssueIds - Array of child issue IDs (e.g., [\"101\", \"102\", \"103\"])\n * @param settings - IloomSettings to determine which provider to use\n * @param repo - Optional repo in \"owner/repo\" format for GitHub\n * @returns Dependency map: Record<string, string[]> where key is formatted issue number\n * (e.g., \"#101\" for GitHub, \"ENG-101\" for Linear) and value is array of\n * formatted issue numbers that block it, matching SwarmIssue.number format\n */\nexport async function buildDependencyMap(\n childIssueIds: string[],\n settings: IloomSettings,\n repo?: string,\n): Promise<Record<string, string[]>> {\n const providerName = IssueTrackerFactory.getProviderName(settings)\n const childIdSet = new Set(childIssueIds)\n const dependencyMap: Record<string, string[]> = {}\n\n getLogger().debug('Building dependency map', { childIssueIds, provider: providerName })\n\n // Initialize all children with empty dependency arrays\n for (const id of childIssueIds) {\n dependencyMap[id] = []\n }\n\n // Fetch dependencies for each child in parallel\n const results = await Promise.allSettled(\n childIssueIds.map(async (childId) => {\n const blockers = await fetchBlockedBy(childId, providerName, repo)\n // Filter to only sibling dependencies\n const siblingBlockers = blockers.filter((blockerId) => childIdSet.has(blockerId))\n return { childId, siblingBlockers }\n }),\n )\n\n // Process results\n for (const result of results) {\n if (result.status === 'fulfilled') {\n const { childId, siblingBlockers } = result.value\n if (siblingBlockers.length > 0) {\n dependencyMap[childId] = siblingBlockers\n }\n } else {\n getLogger().warn(`Failed to fetch dependencies for a child issue`, { error: result.reason })\n }\n }\n\n // Format keys and values to match SwarmIssue.number format (e.g., \"#101\" for GitHub)\n const formattedMap: Record<string, string[]> = {}\n for (const [key, values] of Object.entries(dependencyMap)) {\n const formattedKey = formatIssueNumber(key, providerName)\n formattedMap[formattedKey] = values.map((v) => formatIssueNumber(v, providerName))\n }\n return formattedMap\n}\n\n/**\n * Fetch \"blocked_by\" dependencies for a single issue from the appropriate provider\n */\nasync function fetchBlockedBy(\n issueId: string,\n providerName: string,\n repo?: string,\n): Promise<string[]> {\n switch (providerName) {\n case 'github': {\n const issueNumber = parseInt(issueId, 10)\n if (isNaN(issueNumber)) {\n getLogger().warn(`Invalid GitHub issue number: ${issueId}`)\n return []\n }\n const deps = await getIssueDependencies(issueNumber, 'blocked_by', repo)\n return deps.map((dep) => dep.id)\n }\n case 'linear': {\n const result = await getLinearIssueDependencies(issueId, 'blocked_by')\n return result.blockedBy.map((dep) => dep.id)\n }\n case 'jira':\n // Jira dependency fetching not yet implemented\n getLogger().debug(`Jira dependency fetching not yet supported for issue ${issueId}`)\n return []\n default:\n getLogger().warn(`Unsupported provider for dependency fetching: ${providerName}`)\n return []\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
logger
|
|
4
|
+
} from "./chunk-VT4PDUYT.js";
|
|
5
|
+
|
|
6
|
+
// src/lib/TelemetryManager.ts
|
|
7
|
+
import os from "os";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import nodeFs from "fs";
|
|
10
|
+
import fs from "fs-extra";
|
|
11
|
+
import { v4 as uuidv4 } from "uuid";
|
|
12
|
+
var DEFAULT_CONFIG = { enabled: true };
|
|
13
|
+
var CONFIG_FILE = "telemetry.json";
|
|
14
|
+
var ID_FILE = "telemetry-id";
|
|
15
|
+
var TelemetryManager = class {
|
|
16
|
+
constructor(configDir) {
|
|
17
|
+
const dir = configDir ?? path.join(os.homedir(), ".config", "iloom-ai");
|
|
18
|
+
this.configFilePath = path.join(dir, CONFIG_FILE);
|
|
19
|
+
this.idFilePath = path.join(dir, ID_FILE);
|
|
20
|
+
this.distinctId = this.readOrCreateDistinctId();
|
|
21
|
+
this.config = this.readConfig();
|
|
22
|
+
}
|
|
23
|
+
readOrCreateDistinctId() {
|
|
24
|
+
try {
|
|
25
|
+
const id = nodeFs.readFileSync(this.idFilePath, "utf8").trim();
|
|
26
|
+
if (id) return id;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
const code = error.code;
|
|
29
|
+
if (code !== "ENOENT") {
|
|
30
|
+
if (code === "EACCES" || code === "EPERM") {
|
|
31
|
+
logger.warn(`TelemetryManager: Permission denied reading ID file: ${code}`);
|
|
32
|
+
} else {
|
|
33
|
+
logger.debug(`TelemetryManager: Failed to read ID file: ${error}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const newId = uuidv4();
|
|
38
|
+
this.writeDistinctId(newId);
|
|
39
|
+
return newId;
|
|
40
|
+
}
|
|
41
|
+
writeDistinctId(id) {
|
|
42
|
+
try {
|
|
43
|
+
fs.ensureDirSync(path.dirname(this.idFilePath));
|
|
44
|
+
nodeFs.writeFileSync(this.idFilePath, id, "utf8");
|
|
45
|
+
} catch (error) {
|
|
46
|
+
const code = error.code;
|
|
47
|
+
if (code === "EACCES" || code === "EPERM") {
|
|
48
|
+
logger.warn(`TelemetryManager: Permission denied writing ID file: ${code}`);
|
|
49
|
+
} else {
|
|
50
|
+
logger.debug(`TelemetryManager: Failed to write ID file: ${error}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
readConfig() {
|
|
55
|
+
try {
|
|
56
|
+
const data = fs.readJsonSync(this.configFilePath);
|
|
57
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
58
|
+
throw new Error("Invalid config format: expected a JSON object");
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
enabled: typeof data.enabled === "boolean" ? data.enabled : true,
|
|
62
|
+
...typeof data.disclosed_at === "string" ? { disclosed_at: data.disclosed_at } : {},
|
|
63
|
+
...typeof data.last_version === "string" ? { last_version: data.last_version } : {}
|
|
64
|
+
};
|
|
65
|
+
} catch (error) {
|
|
66
|
+
const code = error.code;
|
|
67
|
+
if (code === "ENOENT") {
|
|
68
|
+
logger.debug("TelemetryManager: Config file not found, using defaults");
|
|
69
|
+
return { ...DEFAULT_CONFIG };
|
|
70
|
+
}
|
|
71
|
+
logger.warn(`TelemetryManager: Unexpected error reading config (${code ?? error}), disabling telemetry`);
|
|
72
|
+
return { ...DEFAULT_CONFIG, enabled: false };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
writeConfig() {
|
|
76
|
+
try {
|
|
77
|
+
const dir = path.dirname(this.configFilePath);
|
|
78
|
+
fs.ensureDirSync(dir);
|
|
79
|
+
const tmpPath = `${this.configFilePath}.${process.pid}.tmp`;
|
|
80
|
+
const data = JSON.stringify(this.config, null, 2);
|
|
81
|
+
nodeFs.writeFileSync(tmpPath, data, "utf8");
|
|
82
|
+
nodeFs.renameSync(tmpPath, this.configFilePath);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
const code = error.code;
|
|
85
|
+
if (code === "EACCES" || code === "EPERM") {
|
|
86
|
+
logger.warn(`TelemetryManager: Permission denied writing config: ${code}`);
|
|
87
|
+
} else {
|
|
88
|
+
logger.debug(`TelemetryManager: Failed to write config: ${error}`);
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const tmpPath = `${this.configFilePath}.${process.pid}.tmp`;
|
|
92
|
+
nodeFs.unlinkSync(tmpPath);
|
|
93
|
+
} catch (cleanupError) {
|
|
94
|
+
const cleanupCode = cleanupError.code;
|
|
95
|
+
if (cleanupCode !== "ENOENT") {
|
|
96
|
+
logger.debug(`TelemetryManager: Failed to clean up temp file: ${cleanupError}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
getDistinctId() {
|
|
102
|
+
return this.distinctId;
|
|
103
|
+
}
|
|
104
|
+
isEnabled() {
|
|
105
|
+
return this.config.enabled;
|
|
106
|
+
}
|
|
107
|
+
enable() {
|
|
108
|
+
this.config.enabled = true;
|
|
109
|
+
this.writeConfig();
|
|
110
|
+
}
|
|
111
|
+
disable() {
|
|
112
|
+
this.config.enabled = false;
|
|
113
|
+
this.writeConfig();
|
|
114
|
+
}
|
|
115
|
+
getStatus() {
|
|
116
|
+
return { enabled: this.isEnabled(), distinctId: this.getDistinctId() };
|
|
117
|
+
}
|
|
118
|
+
hasBeenDisclosed() {
|
|
119
|
+
return this.config.disclosed_at !== void 0 && this.config.disclosed_at !== "";
|
|
120
|
+
}
|
|
121
|
+
markDisclosed() {
|
|
122
|
+
this.config.disclosed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
123
|
+
this.writeConfig();
|
|
124
|
+
}
|
|
125
|
+
getLastVersion() {
|
|
126
|
+
return this.config.last_version ?? null;
|
|
127
|
+
}
|
|
128
|
+
setLastVersion(version) {
|
|
129
|
+
if (this.config.last_version === version) return;
|
|
130
|
+
this.config.last_version = version;
|
|
131
|
+
this.writeConfig();
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// src/lib/TelemetryService.ts
|
|
136
|
+
import { PostHog } from "posthog-node";
|
|
137
|
+
var POSTHOG_API_KEY = "phc_H9IRi41nQuIXs6fthwCZJ4wi6jIs2LWQkUanMSdqmj";
|
|
138
|
+
var POSTHOG_HOST = "https://us.i.posthog.com";
|
|
139
|
+
var SHUTDOWN_TIMEOUT_MS = 1e3;
|
|
140
|
+
var _TelemetryService = class _TelemetryService {
|
|
141
|
+
constructor(manager) {
|
|
142
|
+
this.client = null;
|
|
143
|
+
this.manager = manager ?? new TelemetryManager();
|
|
144
|
+
if (this.manager.isEnabled()) {
|
|
145
|
+
try {
|
|
146
|
+
this.client = new PostHog(POSTHOG_API_KEY, { host: POSTHOG_HOST, flushAt: 1, flushInterval: 0 });
|
|
147
|
+
} catch (error) {
|
|
148
|
+
logger.debug(`TelemetryService: Failed to initialize PostHog: ${error}`);
|
|
149
|
+
this.client = null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
getManager() {
|
|
154
|
+
return this.manager;
|
|
155
|
+
}
|
|
156
|
+
static getInstance() {
|
|
157
|
+
_TelemetryService.instance ??= new _TelemetryService();
|
|
158
|
+
return _TelemetryService.instance;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Reset singleton instance. For testing only — does not flush pending events.
|
|
162
|
+
*/
|
|
163
|
+
static resetInstance() {
|
|
164
|
+
_TelemetryService.instance = null;
|
|
165
|
+
}
|
|
166
|
+
track(event, properties) {
|
|
167
|
+
if (!this.client) return;
|
|
168
|
+
try {
|
|
169
|
+
this.client.capture({
|
|
170
|
+
distinctId: this.manager.getDistinctId(),
|
|
171
|
+
event,
|
|
172
|
+
properties: { ...properties, source: "cli" }
|
|
173
|
+
});
|
|
174
|
+
} catch (error) {
|
|
175
|
+
logger.debug(`TelemetryService: track error: ${error}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async shutdown() {
|
|
179
|
+
if (!this.client) return;
|
|
180
|
+
let timeoutId;
|
|
181
|
+
try {
|
|
182
|
+
await Promise.race([
|
|
183
|
+
this.client.shutdown(),
|
|
184
|
+
new Promise((resolve) => {
|
|
185
|
+
timeoutId = globalThis.setTimeout(resolve, SHUTDOWN_TIMEOUT_MS);
|
|
186
|
+
})
|
|
187
|
+
]);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
logger.debug(`TelemetryService: Shutdown error: ${error}`);
|
|
190
|
+
} finally {
|
|
191
|
+
if (timeoutId) globalThis.clearTimeout(timeoutId);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
_TelemetryService.instance = null;
|
|
196
|
+
var TelemetryService = _TelemetryService;
|
|
197
|
+
|
|
198
|
+
export {
|
|
199
|
+
TelemetryManager,
|
|
200
|
+
TelemetryService
|
|
201
|
+
};
|
|
202
|
+
//# sourceMappingURL=chunk-RSYT7MVI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/TelemetryManager.ts","../src/lib/TelemetryService.ts"],"sourcesContent":["import os from 'os'\nimport path from 'path'\nimport nodeFs from 'node:fs'\nimport fs from 'fs-extra'\nimport { v4 as uuidv4 } from 'uuid'\nimport { logger } from '../utils/logger.js'\nimport type { TelemetryConfig } from '../types/telemetry.js'\n\nconst DEFAULT_CONFIG: TelemetryConfig = { enabled: true }\nconst CONFIG_FILE = 'telemetry.json'\nconst ID_FILE = 'telemetry-id'\n\nexport class TelemetryManager {\n\tprivate configFilePath: string\n\tprivate idFilePath: string\n\tprivate distinctId: string\n\tprivate config: TelemetryConfig\n\n\tconstructor(configDir?: string) {\n\t\tconst dir = configDir ?? path.join(os.homedir(), '.config', 'iloom-ai')\n\t\tthis.configFilePath = path.join(dir, CONFIG_FILE)\n\t\tthis.idFilePath = path.join(dir, ID_FILE)\n\t\tthis.distinctId = this.readOrCreateDistinctId()\n\t\tthis.config = this.readConfig()\n\t}\n\n\tprivate readOrCreateDistinctId(): string {\n\t\t// 1. Try to read existing telemetry-id file\n\t\ttry {\n\t\t\tconst id = nodeFs.readFileSync(this.idFilePath, 'utf8').trim()\n\t\t\tif (id) return id\n\t\t} catch (error) {\n\t\t\tconst code = (error as NodeJS.ErrnoException).code\n\t\t\tif (code !== 'ENOENT') {\n\t\t\t\tif (code === 'EACCES' || code === 'EPERM') {\n\t\t\t\t\tlogger.warn(`TelemetryManager: Permission denied reading ID file: ${code}`)\n\t\t\t\t} else {\n\t\t\t\t\tlogger.debug(`TelemetryManager: Failed to read ID file: ${error}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// 2. Generate new ID and write it\n\t\tconst newId = uuidv4()\n\t\tthis.writeDistinctId(newId)\n\t\treturn newId\n\t}\n\n\tprivate writeDistinctId(id: string): void {\n\t\ttry {\n\t\t\tfs.ensureDirSync(path.dirname(this.idFilePath))\n\t\t\tnodeFs.writeFileSync(this.idFilePath, id, 'utf8')\n\t\t} catch (error) {\n\t\t\tconst code = (error as NodeJS.ErrnoException).code\n\t\t\tif (code === 'EACCES' || code === 'EPERM') {\n\t\t\t\tlogger.warn(`TelemetryManager: Permission denied writing ID file: ${code}`)\n\t\t\t} else {\n\t\t\t\tlogger.debug(`TelemetryManager: Failed to write ID file: ${error}`)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate readConfig(): TelemetryConfig {\n\t\ttry {\n\t\t\tconst data = fs.readJsonSync(this.configFilePath)\n\t\t\tif (!data || typeof data !== 'object' || Array.isArray(data)) {\n\t\t\t\tthrow new Error('Invalid config format: expected a JSON object')\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tenabled: typeof data.enabled === 'boolean' ? data.enabled : true,\n\t\t\t\t...(typeof data.disclosed_at === 'string' ? { disclosed_at: data.disclosed_at } : {}),\n\t\t\t\t...(typeof data.last_version === 'string' ? { last_version: data.last_version } : {}),\n\t\t\t}\n\t\t} catch (error: unknown) {\n\t\t\tconst code = (error as NodeJS.ErrnoException).code\n\t\t\tif (code === 'ENOENT') {\n\t\t\t\tlogger.debug('TelemetryManager: Config file not found, using defaults')\n\t\t\t\treturn { ...DEFAULT_CONFIG }\n\t\t\t}\n\t\t\t// Corrupted/unreadable file: disable telemetry to respect user opt-out\n\t\t\tlogger.warn(`TelemetryManager: Unexpected error reading config (${code ?? error}), disabling telemetry`)\n\t\t\treturn { ...DEFAULT_CONFIG, enabled: false }\n\t\t}\n\t}\n\n\tprivate writeConfig(): void {\n\t\ttry {\n\t\t\tconst dir = path.dirname(this.configFilePath)\n\t\t\tfs.ensureDirSync(dir)\n\t\t\t// Atomic write: write to a temp file in the same directory, then rename.\n\t\t\t// renameSync is atomic on the same filesystem, so concurrent readers will\n\t\t\t// either see the old file or the new file, never a partially-written one.\n\t\t\tconst tmpPath = `${this.configFilePath}.${process.pid}.tmp`\n\t\t\tconst data = JSON.stringify(this.config, null, 2)\n\t\t\tnodeFs.writeFileSync(tmpPath, data, 'utf8')\n\t\t\tnodeFs.renameSync(tmpPath, this.configFilePath)\n\t\t} catch (error: unknown) {\n\t\t\tconst code = (error as NodeJS.ErrnoException).code\n\t\t\tif (code === 'EACCES' || code === 'EPERM') {\n\t\t\t\tlogger.warn(`TelemetryManager: Permission denied writing config: ${code}`)\n\t\t\t} else {\n\t\t\t\tlogger.debug(`TelemetryManager: Failed to write config: ${error}`)\n\t\t\t}\n\t\t\t// Clean up temp file if it exists\n\t\t\ttry {\n\t\t\t\tconst tmpPath = `${this.configFilePath}.${process.pid}.tmp`\n\t\t\t\tnodeFs.unlinkSync(tmpPath)\n\t\t\t} catch (cleanupError: unknown) {\n\t\t\t\tconst cleanupCode = (cleanupError as NodeJS.ErrnoException).code\n\t\t\t\tif (cleanupCode !== 'ENOENT') {\n\t\t\t\t\tlogger.debug(`TelemetryManager: Failed to clean up temp file: ${cleanupError}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tgetDistinctId(): string {\n\t\treturn this.distinctId\n\t}\n\n\tisEnabled(): boolean {\n\t\treturn this.config.enabled\n\t}\n\n\tenable(): void {\n\t\tthis.config.enabled = true\n\t\tthis.writeConfig()\n\t}\n\n\tdisable(): void {\n\t\tthis.config.enabled = false\n\t\tthis.writeConfig()\n\t}\n\n\tgetStatus(): { enabled: boolean; distinctId: string } {\n\t\treturn { enabled: this.isEnabled(), distinctId: this.getDistinctId() }\n\t}\n\n\thasBeenDisclosed(): boolean {\n\t\treturn this.config.disclosed_at !== undefined && this.config.disclosed_at !== ''\n\t}\n\n\tmarkDisclosed(): void {\n\t\tthis.config.disclosed_at = new Date().toISOString()\n\t\tthis.writeConfig()\n\t}\n\n\tgetLastVersion(): string | null {\n\t\treturn this.config.last_version ?? null\n\t}\n\n\tsetLastVersion(version: string): void {\n\t\tif (this.config.last_version === version) return\n\t\tthis.config.last_version = version\n\t\tthis.writeConfig()\n\t}\n}\n","import { PostHog } from 'posthog-node'\nimport { TelemetryManager } from './TelemetryManager.js'\nimport { logger } from '../utils/logger.js'\nimport type { TelemetryEventMap, TelemetryEventName } from '../types/telemetry.js'\n\nconst POSTHOG_API_KEY = 'phc_H9IRi41nQuIXs6fthwCZJ4wi6jIs2LWQkUanMSdqmj'\nconst POSTHOG_HOST = 'https://us.i.posthog.com'\nconst SHUTDOWN_TIMEOUT_MS = 1000\n\nexport class TelemetryService {\n\tprivate static instance: TelemetryService | null = null\n\tprivate client: PostHog | null = null\n\tprivate manager: TelemetryManager\n\n\tconstructor(manager?: TelemetryManager) {\n\t\tthis.manager = manager ?? new TelemetryManager()\n\t\tif (this.manager.isEnabled()) {\n\t\t\ttry {\n\t\t\t\tthis.client = new PostHog(POSTHOG_API_KEY, { host: POSTHOG_HOST, flushAt: 1, flushInterval: 0 })\n\t\t\t} catch (error) {\n\t\t\t\tlogger.debug(`TelemetryService: Failed to initialize PostHog: ${error}`)\n\t\t\t\tthis.client = null\n\t\t\t}\n\t\t}\n\t}\n\n\tgetManager(): TelemetryManager {\n\t\treturn this.manager\n\t}\n\n\tstatic getInstance(): TelemetryService {\n\t\tTelemetryService.instance ??= new TelemetryService()\n\t\treturn TelemetryService.instance\n\t}\n\n\t/**\n\t * Reset singleton instance. For testing only — does not flush pending events.\n\t */\n\tstatic resetInstance(): void {\n\t\tTelemetryService.instance = null\n\t}\n\n\ttrack<K extends TelemetryEventName>(event: K, properties: TelemetryEventMap[K]): void\n\ttrack(event: string, properties?: Record<string, unknown>): void\n\ttrack(event: string, properties?: Record<string, unknown>): void {\n\t\tif (!this.client) return\n\t\ttry {\n\t\t\tthis.client.capture({\n\t\t\t\tdistinctId: this.manager.getDistinctId(),\n\t\t\t\tevent,\n\t\t\t\tproperties: { ...properties, source: 'cli' },\n\t\t\t})\n\t\t} catch (error) {\n\t\t\tlogger.debug(`TelemetryService: track error: ${error}`)\n\t\t}\n\t}\n\n\tasync shutdown(): Promise<void> {\n\t\tif (!this.client) return\n\t\tlet timeoutId: NodeJS.Timeout | undefined\n\t\ttry {\n\t\t\tawait Promise.race([\n\t\t\t\tthis.client.shutdown(),\n\t\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\t\ttimeoutId = globalThis.setTimeout(resolve, SHUTDOWN_TIMEOUT_MS)\n\t\t\t\t}),\n\t\t\t])\n\t\t} catch (error) {\n\t\t\tlogger.debug(`TelemetryService: Shutdown error: ${error}`)\n\t\t} finally {\n\t\t\tif (timeoutId) globalThis.clearTimeout(timeoutId)\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ClaudeService
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SN3SQCFK.js";
|
|
5
5
|
import {
|
|
6
6
|
logger
|
|
7
7
|
} from "./chunk-VT4PDUYT.js";
|
|
@@ -19,11 +19,11 @@ var ClaudeContextManager = class {
|
|
|
19
19
|
if (!context.workspacePath) {
|
|
20
20
|
throw new Error("Workspace path is required");
|
|
21
21
|
}
|
|
22
|
-
if (context.type === "issue" &&
|
|
23
|
-
throw new Error("Issue identifier
|
|
22
|
+
if (context.type === "issue" && context.identifier === void 0) {
|
|
23
|
+
throw new Error("Issue identifier is required");
|
|
24
24
|
}
|
|
25
|
-
if (context.type === "pr" &&
|
|
26
|
-
throw new Error("PR identifier
|
|
25
|
+
if (context.type === "pr" && context.identifier === void 0) {
|
|
26
|
+
throw new Error("PR identifier is required");
|
|
27
27
|
}
|
|
28
28
|
logger.debug("Context prepared", { context });
|
|
29
29
|
}
|
|
@@ -63,4 +63,4 @@ var ClaudeContextManager = class {
|
|
|
63
63
|
export {
|
|
64
64
|
ClaudeContextManager
|
|
65
65
|
};
|
|
66
|
-
//# sourceMappingURL=chunk-
|
|
66
|
+
//# sourceMappingURL=chunk-RYWFS37M.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/ClaudeContextManager.ts"],"sourcesContent":["import { ClaudeService, ClaudeWorkflowOptions } from './ClaudeService.js'\nimport { PromptTemplateManager } from './PromptTemplateManager.js'\nimport { logger } from '../utils/logger.js'\n\nexport interface ClaudeContext {\n\ttype: 'issue' | 'pr' | 'regular'\n\tidentifier: number | string\n\ttitle?: string\n\tworkspacePath: string\n\tport?: number\n\tbranchName?: string\n\toneShot?: import('../types/index.js').OneShotMode\n\tsetArguments?: string[] // Raw --set arguments to forward\n\texecutablePath?: string // Executable path to use for spin command\n}\n\nexport class ClaudeContextManager {\n\tprivate claudeService: ClaudeService\n\n\tconstructor(claudeService?: ClaudeService, _promptTemplateManager?: PromptTemplateManager, settingsManager?: import('./SettingsManager.js').SettingsManager) {\n\t\tthis.claudeService = claudeService ?? new ClaudeService(undefined, settingsManager)\n\t\t// promptTemplateManager is accepted for dependency injection but not used yet\n\t\t// Will be used in Issue #11 for .claude-context.md generation\n\t}\n\n\t/**\n\t * Prepare context for Claude launch\n\t * Placeholder for future .claude-context.md generation (Issue #11)\n\t */\n\tasync prepareContext(context: ClaudeContext): Promise<void> {\n\t\t// Validate context object\n\t\tif (!context.workspacePath) {\n\t\t\tthrow new Error('Workspace path is required')\n\t\t}\n\n\t\tif (context.type === 'issue' && context.identifier === undefined) {\n\t\t\tthrow new Error('Issue identifier is required')\n\t\t}\n\n\t\tif (context.type === 'pr' && context.identifier === undefined) {\n\t\t\tthrow new Error('PR identifier is required')\n\t\t}\n\n\t\tlogger.debug('Context prepared', { context })\n\t\t// Future: Generate .claude-context.md file in workspace\n\t}\n\n\t/**\n\t * Launch Claude with the prepared context\n\t */\n\tasync launchWithContext(context: ClaudeContext, headless: boolean = false): Promise<string | void> {\n\t\t// Prepare context first\n\t\tawait this.prepareContext(context)\n\n\t\t// Convert ClaudeContext to ClaudeWorkflowOptions\n\t\tconst workflowOptions: ClaudeWorkflowOptions = {\n\t\t\ttype: context.type,\n\t\t\tworkspacePath: context.workspacePath,\n\t\t\t...(context.port !== undefined && { port: context.port }),\n\t\t\theadless,\n\t\t\toneShot: context.oneShot ?? 'default',\n\t\t}\n\n\t\t// Add optional title if present\n\t\tif (context.title !== undefined) {\n\t\t\tworkflowOptions.title = context.title\n\t\t}\n\n\t\t// Add optional branch name if present\n\t\tif (context.branchName !== undefined) {\n\t\t\tworkflowOptions.branchName = context.branchName\n\t\t}\n\n\t\t// Add optional setArguments if present\n\t\tif (context.setArguments !== undefined) {\n\t\t\tworkflowOptions.setArguments = context.setArguments\n\t\t}\n\n\t\t// Add optional executablePath if present\n\t\tif (context.executablePath !== undefined) {\n\t\t\tworkflowOptions.executablePath = context.executablePath\n\t\t}\n\n\t\t// Set issue or PR number based on type\n\t\tif (context.type === 'issue') {\n\t\t\tworkflowOptions.issueNumber = context.identifier as number\n\t\t} else if (context.type === 'pr') {\n\t\t\tworkflowOptions.prNumber = context.identifier as number\n\t\t}\n\n\t\t// Delegate to Claude service\n\t\treturn this.claudeService.launchForWorkflow(workflowOptions)\n\t}\n}\n"],"mappings":";;;;;;;;;AAgBO,IAAM,uBAAN,MAA2B;AAAA,EAGjC,YAAY,eAA+B,wBAAgD,iBAAkE;AAC5J,SAAK,gBAAgB,iBAAiB,IAAI,cAAc,QAAW,eAAe;AAAA,EAGnF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAAe,SAAuC;AAE3D,QAAI,CAAC,QAAQ,eAAe;AAC3B,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC7C;AAEA,QAAI,QAAQ,SAAS,WAAW,QAAQ,eAAe,QAAW;AACjE,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAC/C;AAEA,QAAI,QAAQ,SAAS,QAAQ,QAAQ,eAAe,QAAW;AAC9D,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC5C;AAEA,WAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC;AAAA,EAE7C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBAAkB,SAAwB,WAAoB,OAA+B;AAElG,UAAM,KAAK,eAAe,OAAO;AAGjC,UAAM,kBAAyC;AAAA,MAC9C,MAAM,QAAQ;AAAA,MACd,eAAe,QAAQ;AAAA,MACvB,GAAI,QAAQ,SAAS,UAAa,EAAE,MAAM,QAAQ,KAAK;AAAA,MACvD;AAAA,MACA,SAAS,QAAQ,WAAW;AAAA,IAC7B;AAGA,QAAI,QAAQ,UAAU,QAAW;AAChC,sBAAgB,QAAQ,QAAQ;AAAA,IACjC;AAGA,QAAI,QAAQ,eAAe,QAAW;AACrC,sBAAgB,aAAa,QAAQ;AAAA,IACtC;AAGA,QAAI,QAAQ,iBAAiB,QAAW;AACvC,sBAAgB,eAAe,QAAQ;AAAA,IACxC;AAGA,QAAI,QAAQ,mBAAmB,QAAW;AACzC,sBAAgB,iBAAiB,QAAQ;AAAA,IAC1C;AAGA,QAAI,QAAQ,SAAS,SAAS;AAC7B,sBAAgB,cAAc,QAAQ;AAAA,IACvC,WAAW,QAAQ,SAAS,MAAM;AACjC,sBAAgB,WAAW,QAAQ;AAAA,IACpC;AAGA,WAAO,KAAK,cAAc,kBAAkB,eAAe;AAAA,EAC5D;AACD;","names":[]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
PromptTemplateManager,
|
|
4
4
|
buildReviewTemplateVariables
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-4WJNIR5O.js";
|
|
6
6
|
import {
|
|
7
7
|
logger
|
|
8
8
|
} from "./chunk-VT4PDUYT.js";
|
|
@@ -191,7 +191,15 @@ var AgentManager = class {
|
|
|
191
191
|
model: agentSettings.model
|
|
192
192
|
};
|
|
193
193
|
} else if (!agents[agentName]) {
|
|
194
|
-
|
|
194
|
+
const RUNTIME_GENERATED_AGENTS = ["iloom-swarm-worker"];
|
|
195
|
+
if (!RUNTIME_GENERATED_AGENTS.includes(agentName)) {
|
|
196
|
+
const agentFile = path.join(this.agentDir, `${agentName}.md`);
|
|
197
|
+
try {
|
|
198
|
+
accessSync(agentFile);
|
|
199
|
+
} catch {
|
|
200
|
+
logger.warn(`Settings reference unknown agent: ${agentName}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
195
203
|
}
|
|
196
204
|
}
|
|
197
205
|
}
|
|
@@ -266,4 +274,4 @@ var AgentManager = class {
|
|
|
266
274
|
export {
|
|
267
275
|
AgentManager
|
|
268
276
|
};
|
|
269
|
-
//# sourceMappingURL=chunk-
|
|
277
|
+
//# sourceMappingURL=chunk-SF2P22EE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/AgentManager.ts","../src/utils/MarkdownAgentParser.ts"],"sourcesContent":["import { readFile } from 'fs/promises'\nimport { accessSync } from 'fs'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport fg from 'fast-glob'\nimport { MarkdownAgentParser } from '../utils/MarkdownAgentParser.js'\nimport { logger } from '../utils/logger.js'\nimport type { IloomSettings } from './SettingsManager.js'\nimport { PromptTemplateManager, TemplateVariables, buildReviewTemplateVariables } from './PromptTemplateManager.js'\n\n// Agent schema interface\nexport interface AgentConfig {\n\tdescription: string\n\tprompt: string\n\ttools?: string[] // Optional - when omitted, agent inherits all tools from parent\n\tmodel: string\n\tcolor?: string\n}\n\n// Container for all loaded agents (keyed by agent name without extension)\nexport interface AgentConfigs {\n\t[agentName: string]: AgentConfig\n}\n\nexport class AgentManager {\n\tprivate agentDir: string\n\tprivate templateManager: PromptTemplateManager\n\n\tconstructor(agentDir?: string, templateManager?: PromptTemplateManager) {\n\t\tthis.templateManager = templateManager ?? new PromptTemplateManager()\n\t\tif (agentDir) {\n\t\t\tthis.agentDir = agentDir\n\t\t} else {\n\t\t\t// Find agents relative to package installation\n\t\t\t// Same pattern as PromptTemplateManager\n\t\t\t// When running from dist/, agents are copied to dist/agents/\n\t\t\tconst currentFileUrl = import.meta.url\n\t\t\tconst currentFilePath = fileURLToPath(currentFileUrl)\n\t\t\tconst distDir = path.dirname(currentFilePath)\n\n\t\t\t// Walk up to find the agents directory\n\t\t\tlet agentDirPath = path.join(distDir, 'agents')\n\t\t\tlet currentDir = distDir\n\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst candidatePath = path.join(currentDir, 'agents')\n\t\t\t\ttry {\n\t\t\t\t\taccessSync(candidatePath)\n\t\t\t\t\tagentDirPath = candidatePath\n\t\t\t\t\tbreak\n\t\t\t\t} catch {\n\t\t\t\t\tcurrentDir = path.dirname(currentDir)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.agentDir = agentDirPath\n\t\t\tlogger.debug('AgentManager initialized', { agentDir: this.agentDir })\n\t\t}\n\t}\n\n\t/**\n\t * Load agent configuration files from markdown (.md) format\n\t * Optionally apply model overrides from settings and template variable substitution\n\t * Throws error if agents directory doesn't exist or files are malformed\n\t * @param settings - Optional project settings with per-agent model overrides\n\t * @param templateVariables - Optional variables for template substitution in agent prompts\n\t * @param patterns - Optional glob patterns to filter which agents to load (default: ['*.md'])\n\t * Supports negation patterns like ['*.md', '!iloom-framework-detector.md']\n\t */\n\tasync loadAgents(\n\t\tsettings?: IloomSettings,\n\t\ttemplateVariables?: TemplateVariables,\n\t\tpatterns: string[] = ['*.md']\n\t): Promise<AgentConfigs> {\n\t\t// Use fast-glob to filter agent files based on patterns\n\t\tconst agentFiles = await fg(patterns, {\n\t\t\tcwd: this.agentDir,\n\t\t\tonlyFiles: true,\n\t\t})\n\n\t\tconst agents: AgentConfigs = {}\n\n\t\tfor (const filename of agentFiles) {\n\t\t\tconst agentPath = path.join(this.agentDir, filename)\n\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(agentPath, 'utf-8')\n\n\t\t\t\t// Parse markdown with frontmatter\n\t\t\t\tconst parsed = this.parseMarkdownAgent(content, filename)\n\t\t\t\tconst agentConfig = parsed.config\n\t\t\t\tconst agentName = parsed.name\n\n\t\t\t\t// Validate required fields\n\t\t\t\tthis.validateAgentConfig(agentConfig, agentName)\n\n\t\t\t\tagents[agentName] = agentConfig\n\t\t\t\tlogger.debug(`Loaded agent: ${agentName}`)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Failed to load agent from ${filename}`, { error })\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to load agent from ${filename}: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// Apply template variable substitution to agent prompts if variables provided\n\t\tif (templateVariables) {\n\t\t\t// Extract review config from settings and add to template variables\n\t\t\tObject.assign(templateVariables, buildReviewTemplateVariables(settings?.agents))\n\n\t\t\tfor (const [agentName, agentConfig] of Object.entries(agents)) {\n\t\t\t\tagents[agentName] = {\n\t\t\t\t\t...agentConfig,\n\t\t\t\t\tprompt: this.templateManager.substituteVariables(agentConfig.prompt, templateVariables),\n\t\t\t\t}\n\t\t\t\tlogger.debug(`Applied template substitution to agent: ${agentName}`)\n\t\t\t}\n\t\t}\n\n\t\t// Apply settings overrides if provided\n\t\tif (settings?.agents) {\n\t\t\tfor (const [agentName, agentSettings] of Object.entries(settings.agents)) {\n\t\t\t\tif (agents[agentName] && agentSettings.model) {\n\t\t\t\t\tlogger.debug(`Overriding model for ${agentName}: ${agents[agentName].model} -> ${agentSettings.model}`)\n\t\t\t\t\tagents[agentName] = {\n\t\t\t\t\t\t...agents[agentName],\n\t\t\t\t\t\tmodel: agentSettings.model,\n\t\t\t\t\t}\n\t\t\t\t} else if (!agents[agentName]) {\n\t\t\t\t\t// Skip warning for runtime-generated agents (e.g., swarm worker)\n\t\t\t\t\tconst RUNTIME_GENERATED_AGENTS = ['iloom-swarm-worker']\n\t\t\t\t\tif (!RUNTIME_GENERATED_AGENTS.includes(agentName)) {\n\t\t\t\t\t\t// Only warn if the agent file doesn't exist at all (typo in settings)\n\t\t\t\t\t\t// Skip warning if the agent exists but wasn't loaded due to pattern filtering\n\t\t\t\t\t\tconst agentFile = path.join(this.agentDir, `${agentName}.md`)\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\taccessSync(agentFile)\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tlogger.warn(`Settings reference unknown agent: ${agentName}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn agents\n\t}\n\n\t/**\n\t * Validate agent configuration has required fields\n\t * Note: tools is optional - when omitted, agent inherits all tools from parent\n\t */\n\tprivate validateAgentConfig(config: AgentConfig, agentName: string): void {\n\t\tconst requiredFields: (keyof AgentConfig)[] = ['description', 'prompt', 'model']\n\n\t\tfor (const field of requiredFields) {\n\t\t\tif (!config[field]) {\n\t\t\t\tthrow new Error(`Agent ${agentName} missing required field: ${field}`)\n\t\t\t}\n\t\t}\n\n\t\t// Tools is optional, but if present must be an array\n\t\tif (config.tools !== undefined && !Array.isArray(config.tools)) {\n\t\t\tthrow new Error(`Agent ${agentName} tools must be an array`)\n\t\t}\n\t}\n\n\t/**\n\t * Parse markdown agent file with YAML frontmatter\n\t * @param content - Raw markdown file content\n\t * @param filename - Original filename for error messages\n\t * @returns Parsed agent config and name\n\t */\n\tprivate parseMarkdownAgent(content: string, filename: string): { config: AgentConfig; name: string } {\n\t\ttry {\n\t\t\t// Parse frontmatter using custom parser\n\t\t\tconst { data, content: markdownBody } = MarkdownAgentParser.parse(content)\n\n\t\t\t// Validate frontmatter has required fields\n\t\t\tif (!data.name) {\n\t\t\t\tthrow new Error('Missing required field: name')\n\t\t\t}\n\t\t\tif (!data.description) {\n\t\t\t\tthrow new Error('Missing required field: description')\n\t\t\t}\n\t\t\t// Note: tools is now optional - when omitted, agent inherits all tools from parent\n\t\t\tif (!data.model) {\n\t\t\t\tthrow new Error('Missing required field: model')\n\t\t\t}\n\n\t\t\t// Parse tools from comma-separated string to array (only if tools field is present)\n\t\t\tlet tools: string[] | undefined\n\t\t\tif (data.tools) {\n\t\t\t\ttools = data.tools\n\t\t\t\t\t.split(',')\n\t\t\t\t\t.map((tool: string) => tool.trim())\n\t\t\t\t\t.filter((tool: string) => tool.length > 0)\n\t\t\t}\n\n\t\t\t// Validate model and warn if non-standard\n\t\t\tconst validModels = ['sonnet', 'opus', 'haiku']\n\t\t\tif (!validModels.includes(data.model)) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Agent ${data.name} uses model \"${data.model}\" which may not be recognized by Claude CLI, and your workflow may fail or produce unexpected results. ` +\n\t\t\t\t\t\t`Valid values are: ${validModels.join(', ')}`\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Construct AgentConfig\n\t\t\tconst config: AgentConfig = {\n\t\t\t\tdescription: data.description,\n\t\t\t\tprompt: markdownBody.trim(),\n\t\t\t\tmodel: data.model,\n\t\t\t\t...(tools && { tools }),\n\t\t\t\t...(data.color && { color: data.color }),\n\t\t\t}\n\n\t\t\treturn { config, name: data.name }\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to parse markdown agent ${filename}: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Format loaded agents for Claude CLI --agents flag\n\t * Returns object suitable for JSON.stringify\n\t */\n\tformatForCli(agents: AgentConfigs): Record<string, unknown> {\n\t\t// The agents object is already in the correct format\n\t\t// Just return it - launchClaude will JSON.stringify it\n\t\treturn agents as Record<string, unknown>\n\t}\n}\n","/**\n * Custom YAML frontmatter parser for agent markdown files\n * Replaces gray-matter dependency with lightweight custom implementation\n */\n\ninterface ParseResult {\n\tdata: Record<string, string>\n\tcontent: string\n}\n\nexport class MarkdownAgentParser {\n\t/**\n\t * Parse markdown content with YAML frontmatter\n\t * @param content - Raw markdown file content\n\t * @returns Object with parsed frontmatter data and markdown body content\n\t * @throws Error if frontmatter is malformed or missing\n\t */\n\tstatic parse(content: string): ParseResult {\n\t\tconst lines = content.split('\\n')\n\n\t\t// Check for opening frontmatter delimiter\n\t\tif (lines[0]?.trim() !== '---') {\n\t\t\tthrow new Error('Missing opening frontmatter delimiter (---)')\n\t\t}\n\n\t\t// Find closing frontmatter delimiter\n\t\tlet closingDelimiterIndex = -1\n\t\tfor (let i = 1; i < lines.length; i++) {\n\t\t\tif (lines[i]?.trim() === '---') {\n\t\t\t\tclosingDelimiterIndex = i\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif (closingDelimiterIndex === -1) {\n\t\t\tthrow new Error('Missing closing frontmatter delimiter (---)')\n\t\t}\n\n\t\t// Extract frontmatter lines (between the delimiters)\n\t\tconst frontmatterLines = lines.slice(1, closingDelimiterIndex)\n\n\t\t// Extract markdown body (after closing delimiter)\n\t\tconst bodyLines = lines.slice(closingDelimiterIndex + 1)\n\t\tconst markdownBody = bodyLines.join('\\n')\n\n\t\t// Parse YAML frontmatter into key-value pairs\n\t\tconst data = this.parseYaml(frontmatterLines.join('\\n'))\n\n\t\treturn {\n\t\t\tdata,\n\t\t\tcontent: markdownBody,\n\t\t}\n\t}\n\n\t/**\n\t * Parse simplified YAML into key-value object\n\t * Supports:\n\t * - Simple key: value pairs\n\t * - Multiline values with | indicator\n\t * - Values with special characters and newlines\n\t *\n\t * @param yaml - YAML string to parse\n\t * @returns Object with parsed key-value pairs\n\t */\n\tprivate static parseYaml(yaml: string): Record<string, string> {\n\t\tconst result: Record<string, string> = {}\n\t\tconst lines = yaml.split('\\n')\n\t\tlet currentKey: string | null = null\n\t\tlet currentValue: string[] = []\n\t\tlet isMultiline = false\n\n\t\tconst finalizeCurrent = (): void => {\n\t\t\tif (currentKey && currentValue.length > 0) {\n\t\t\t\tresult[currentKey] = currentValue.join('\\n').trim()\n\t\t\t\tcurrentKey = null\n\t\t\t\tcurrentValue = []\n\t\t\t}\n\t\t}\n\n\t\tfor (const line of lines) {\n\t\t\t// Skip empty lines when not in multiline mode\n\t\t\tif (!isMultiline && line.trim() === '') {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Check if this is a new key-value pair\n\t\t\tconst keyValueMatch = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)\\s*:\\s*(.*)$/)\n\n\t\t\tif (keyValueMatch && !isMultiline) {\n\t\t\t\t// Finalize previous key if exists\n\t\t\t\tfinalizeCurrent()\n\n\t\t\t\tconst [, key, value] = keyValueMatch\n\t\t\t\tif (!key || value === undefined) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcurrentKey = key\n\n\t\t\t\t// Check for multiline indicator\n\t\t\t\tif (value.trim() === '|') {\n\t\t\t\t\tisMultiline = true\n\t\t\t\t\tcurrentValue = []\n\t\t\t\t} else {\n\t\t\t\t\t// Single line value\n\t\t\t\t\tcurrentValue = [value]\n\t\t\t\t\tfinalizeCurrent()\n\t\t\t\t\tisMultiline = false\n\t\t\t\t}\n\t\t\t} else if (isMultiline && currentKey) {\n\t\t\t\t// Continuation of multiline value\n\t\t\t\t// Check if we've returned to normal indentation (new key)\n\t\t\t\tif (line.match(/^[a-zA-Z_][a-zA-Z0-9_-]*\\s*:/) && !line.startsWith(' ')) {\n\t\t\t\t\t// End of multiline, this is a new key\n\t\t\t\t\tfinalizeCurrent()\n\t\t\t\t\tisMultiline = false\n\n\t\t\t\t\t// Process this line as a new key\n\t\t\t\t\tconst match = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)\\s*:\\s*(.*)$/)\n\t\t\t\t\tif (match) {\n\t\t\t\t\t\tconst [, key, value] = match\n\t\t\t\t\t\tif (key && value !== undefined) {\n\t\t\t\t\t\t\tcurrentKey = key\n\t\t\t\t\t\t\tcurrentValue = [value]\n\t\t\t\t\t\t\tfinalizeCurrent()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Remove leading spaces (common indentation) from multiline values\n\t\t\t\t\tconst trimmedLine = line.replace(/^ {2}/, '')\n\t\t\t\t\tcurrentValue.push(trimmedLine)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Finalize last key\n\t\tfinalizeCurrent()\n\n\t\treturn result\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
PromptTemplateManager
|
|
4
|
-
} from "./chunk-QN47QVBX.js";
|
|
5
|
-
import {
|
|
6
|
-
SettingsManager
|
|
7
|
-
} from "./chunk-XFEK2X2D.js";
|
|
8
2
|
import {
|
|
9
3
|
detectClaudeCli,
|
|
10
4
|
launchClaude,
|
|
11
5
|
launchClaudeInNewTerminalWindow
|
|
12
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ONQYPICO.js";
|
|
7
|
+
import {
|
|
8
|
+
PromptTemplateManager
|
|
9
|
+
} from "./chunk-4WJNIR5O.js";
|
|
10
|
+
import {
|
|
11
|
+
SettingsManager
|
|
12
|
+
} from "./chunk-YYAKPQBT.js";
|
|
13
13
|
import {
|
|
14
14
|
logger
|
|
15
15
|
} from "./chunk-VT4PDUYT.js";
|
|
@@ -65,6 +65,8 @@ var ClaudeService = class {
|
|
|
65
65
|
if (port !== void 0) {
|
|
66
66
|
variables.PORT = port;
|
|
67
67
|
}
|
|
68
|
+
const isVscodeMode = process.env.ILOOM_VSCODE === "1";
|
|
69
|
+
variables.IS_VSCODE_MODE = isVscodeMode;
|
|
68
70
|
const prompt = await this.templateManager.getPrompt(type, variables);
|
|
69
71
|
const permissionMode = this.getPermissionModeForWorkflow(type);
|
|
70
72
|
if (permissionMode === "bypassPermissions") {
|
|
@@ -119,4 +121,4 @@ var ClaudeService = class {
|
|
|
119
121
|
export {
|
|
120
122
|
ClaudeService
|
|
121
123
|
};
|
|
122
|
-
//# sourceMappingURL=chunk-
|
|
124
|
+
//# sourceMappingURL=chunk-SN3SQCFK.js.map
|