@nemus-cli/nemus 0.2.1

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.
Files changed (320) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/LICENSE +21 -0
  3. package/README.md +275 -0
  4. package/assets/README.md +53 -0
  5. package/assets/banner.png +0 -0
  6. package/assets/banner.svg +66 -0
  7. package/assets/favicon-32.png +0 -0
  8. package/assets/favicon-64.png +0 -0
  9. package/assets/icon-256.png +0 -0
  10. package/assets/icon.svg +23 -0
  11. package/assets/logo-256.png +0 -0
  12. package/assets/logo-512.png +0 -0
  13. package/assets/logo-wordmark.svg +35 -0
  14. package/assets/logo.svg +32 -0
  15. package/bin/workspace.js +130 -0
  16. package/dist/cli/ai-prompt.js +421 -0
  17. package/dist/cli/dashboard/AgentList.js +61 -0
  18. package/dist/cli/dashboard/DashboardSidebar.js +280 -0
  19. package/dist/cli/dashboard/HelpFooter.js +73 -0
  20. package/dist/cli/dashboard/StatusBadge.js +50 -0
  21. package/dist/cli/dashboard/index.js +11 -0
  22. package/dist/cli/dashboard/types.js +2 -0
  23. package/dist/cli/tui.js +48 -0
  24. package/dist/commands/ai-prompt.js +22 -0
  25. package/dist/commands/analyze-deps.js +138 -0
  26. package/dist/commands/archive.js +113 -0
  27. package/dist/commands/branch/create.js +119 -0
  28. package/dist/commands/branch/index.js +104 -0
  29. package/dist/commands/branch/merge.js +90 -0
  30. package/dist/commands/branch/rebase.js +84 -0
  31. package/dist/commands/branch/switch.js +165 -0
  32. package/dist/commands/cache/index.js +80 -0
  33. package/dist/commands/cache/manager.js +198 -0
  34. package/dist/commands/cleanup.js +153 -0
  35. package/dist/commands/configure-claude.js +55 -0
  36. package/dist/commands/configure.js +231 -0
  37. package/dist/commands/create.js +204 -0
  38. package/dist/commands/dashboard/hook-handler.js +162 -0
  39. package/dist/commands/dashboard/index.js +258 -0
  40. package/dist/commands/dashboard/launcher.js +40 -0
  41. package/dist/commands/dashboard/session-picker.js +106 -0
  42. package/dist/commands/dashboard/sidebar.js +39 -0
  43. package/dist/commands/dashboard/workspace-picker.js +95 -0
  44. package/dist/commands/delete.js +194 -0
  45. package/dist/commands/deprecated-aliases.js +44 -0
  46. package/dist/commands/diff.js +129 -0
  47. package/dist/commands/doctor.js +141 -0
  48. package/dist/commands/generate-docs.js +77 -0
  49. package/dist/commands/ghq-status.js +72 -0
  50. package/dist/commands/go.js +154 -0
  51. package/dist/commands/history.js +124 -0
  52. package/dist/commands/list.js +191 -0
  53. package/dist/commands/mcp/index.js +69 -0
  54. package/dist/commands/migrate.js +296 -0
  55. package/dist/commands/remove-repo.js +217 -0
  56. package/dist/commands/report-bug.js +112 -0
  57. package/dist/commands/run.js +147 -0
  58. package/dist/commands/save-context.js +171 -0
  59. package/dist/commands/sessions.js +129 -0
  60. package/dist/commands/status.js +138 -0
  61. package/dist/commands/suite/create.js +212 -0
  62. package/dist/commands/suite/delete.js +64 -0
  63. package/dist/commands/suite/export.js +126 -0
  64. package/dist/commands/suite/import.js +128 -0
  65. package/dist/commands/suite/index.js +94 -0
  66. package/dist/commands/suite/list.js +49 -0
  67. package/dist/commands/suite/use.js +227 -0
  68. package/dist/commands/sync.js +117 -0
  69. package/dist/commands/update.js +203 -0
  70. package/dist/mcp/install.js +550 -0
  71. package/dist/mcp/server.js +398 -0
  72. package/dist/mcp/tools.js +922 -0
  73. package/dist/pi-extensions/permission-sync.ts.template +41 -0
  74. package/dist/pi-extensions/permissions-gate.ts.template +69 -0
  75. package/dist/pi-extensions/pr-fix-trigger.ts.template +124 -0
  76. package/dist/pi-extensions/review-reminder.ts.template +58 -0
  77. package/dist/pi-extensions/workspace-input-status.ts.template +919 -0
  78. package/dist/program.js +136 -0
  79. package/dist/scripts/workspace-table.py +394 -0
  80. package/dist/types/dashboard.js +11 -0
  81. package/dist/types/index.js +2 -0
  82. package/dist/utils/agent-config.js +263 -0
  83. package/dist/utils/agent-state.js +178 -0
  84. package/dist/utils/banner.js +24 -0
  85. package/dist/utils/branch-operations.js +232 -0
  86. package/dist/utils/bug-report.js +355 -0
  87. package/dist/utils/cache.js +168 -0
  88. package/dist/utils/claude-integration.js +617 -0
  89. package/dist/utils/claude-sessions.js +260 -0
  90. package/dist/utils/cleanup-operations.js +120 -0
  91. package/dist/utils/colors.js +26 -0
  92. package/dist/utils/command-helpers.js +44 -0
  93. package/dist/utils/config.js +150 -0
  94. package/dist/utils/context-file.js +33 -0
  95. package/dist/utils/dashboard-hooks.js +172 -0
  96. package/dist/utils/dependency-analyzer.js +234 -0
  97. package/dist/utils/diff-operations.js +80 -0
  98. package/dist/utils/doc-generator.js +184 -0
  99. package/dist/utils/ghq-integration.js +332 -0
  100. package/dist/utils/git-operations.js +237 -0
  101. package/dist/utils/git-status.js +187 -0
  102. package/dist/utils/github.js +68 -0
  103. package/dist/utils/health-checks.js +345 -0
  104. package/dist/utils/history.js +150 -0
  105. package/dist/utils/hooks.js +100 -0
  106. package/dist/utils/logger.js +40 -0
  107. package/dist/utils/permission-sync.js +681 -0
  108. package/dist/utils/pi-extensions.js +224 -0
  109. package/dist/utils/progress.js +92 -0
  110. package/dist/utils/prompts.js +279 -0
  111. package/dist/utils/repo-resolver.js +260 -0
  112. package/dist/utils/retry.js +65 -0
  113. package/dist/utils/run-operations.js +30 -0
  114. package/dist/utils/suite.js +159 -0
  115. package/dist/utils/sync-operations.js +107 -0
  116. package/dist/utils/tmux-dashboard.js +184 -0
  117. package/dist/utils/validation.js +153 -0
  118. package/dist/utils/version-check.js +121 -0
  119. package/dist/utils/workspace-meta.js +185 -0
  120. package/install-shell-integration.sh +532 -0
  121. package/install.sh +401 -0
  122. package/package.json +90 -0
  123. package/remove-shell-block.awk +44 -0
  124. package/scripts/postinstall.js +85 -0
  125. package/skills/analyze-deps.md +23 -0
  126. package/skills/archive-workspace.md +26 -0
  127. package/skills/branch-create.md +18 -0
  128. package/skills/create-workspace.md +39 -0
  129. package/skills/delete-workspace.md +37 -0
  130. package/skills/list-org-repos.md +26 -0
  131. package/skills/list-suites.md +24 -0
  132. package/skills/list-workspaces.md +27 -0
  133. package/skills/refresh-workspace-docs.md +89 -0
  134. package/skills/remove-repo.md +25 -0
  135. package/skills/run-command.md +23 -0
  136. package/skills/save-context.md +68 -0
  137. package/skills/search-repos.md +31 -0
  138. package/skills/snapshot-list.md +18 -0
  139. package/skills/snapshot-save.md +12 -0
  140. package/skills/switch-branch.md +18 -0
  141. package/skills/update-cache.md +21 -0
  142. package/skills/update-workspace.md +66 -0
  143. package/skills/workspace-cleanup.md +18 -0
  144. package/skills/workspace-diff.md +23 -0
  145. package/skills/workspace-doctor.md +23 -0
  146. package/skills/workspace-info.md +45 -0
  147. package/skills/workspace-manager/SKILL.md +201 -0
  148. package/skills/workspace-manager/references/ai-prompt.md +44 -0
  149. package/skills/workspace-manager/references/analyze-deps.md +45 -0
  150. package/skills/workspace-manager/references/archive-workspace.md +38 -0
  151. package/skills/workspace-manager/references/branch-create.md +31 -0
  152. package/skills/workspace-manager/references/branch-merge.md +22 -0
  153. package/skills/workspace-manager/references/branch-rebase.md +22 -0
  154. package/skills/workspace-manager/references/cache-clear.md +18 -0
  155. package/skills/workspace-manager/references/cache-info.md +22 -0
  156. package/skills/workspace-manager/references/configure-claude.md +31 -0
  157. package/skills/workspace-manager/references/configure.md +35 -0
  158. package/skills/workspace-manager/references/create-workspace.md +45 -0
  159. package/skills/workspace-manager/references/dashboard.md +61 -0
  160. package/skills/workspace-manager/references/delete-workspace.md +33 -0
  161. package/skills/workspace-manager/references/generate-docs.md +36 -0
  162. package/skills/workspace-manager/references/ghq-status.md +28 -0
  163. package/skills/workspace-manager/references/go.md +33 -0
  164. package/skills/workspace-manager/references/history.md +41 -0
  165. package/skills/workspace-manager/references/list-org-repos.md +26 -0
  166. package/skills/workspace-manager/references/list-suites.md +21 -0
  167. package/skills/workspace-manager/references/list-workspaces.md +27 -0
  168. package/skills/workspace-manager/references/mcp.md +49 -0
  169. package/skills/workspace-manager/references/remove-repo.md +26 -0
  170. package/skills/workspace-manager/references/run-command.md +44 -0
  171. package/skills/workspace-manager/references/search-repos.md +24 -0
  172. package/skills/workspace-manager/references/sessions.md +27 -0
  173. package/skills/workspace-manager/references/suite-create.md +22 -0
  174. package/skills/workspace-manager/references/suite-delete.md +21 -0
  175. package/skills/workspace-manager/references/suite-export.md +19 -0
  176. package/skills/workspace-manager/references/suite-import.md +19 -0
  177. package/skills/workspace-manager/references/suite-use.md +27 -0
  178. package/skills/workspace-manager/references/switch-branch.md +25 -0
  179. package/skills/workspace-manager/references/update-cache.md +22 -0
  180. package/skills/workspace-manager/references/update-workspace.md +50 -0
  181. package/skills/workspace-manager/references/workspace-cleanup.md +33 -0
  182. package/skills/workspace-manager/references/workspace-diff.md +27 -0
  183. package/skills/workspace-manager/references/workspace-doctor.md +27 -0
  184. package/skills/workspace-manager/references/workspace-info.md +33 -0
  185. package/skills/workspace-manager/references/workspace-status.md +43 -0
  186. package/skills/workspace-manager/references/workspace-sync.md +26 -0
  187. package/skills/workspace-status.md +26 -0
  188. package/skills/workspace-sync.md +23 -0
  189. package/src/cli/ai-prompt.test.ts +667 -0
  190. package/src/cli/ai-prompt.ts +421 -0
  191. package/src/cli/dashboard/AgentList.tsx +44 -0
  192. package/src/cli/dashboard/DashboardSidebar.tsx +291 -0
  193. package/src/cli/dashboard/HelpFooter.tsx +22 -0
  194. package/src/cli/dashboard/StatusBadge.tsx +23 -0
  195. package/src/cli/dashboard/index.ts +5 -0
  196. package/src/cli/dashboard/types.ts +16 -0
  197. package/src/cli/tui.tsx +51 -0
  198. package/src/cli/workspace-cli.test.ts +30 -0
  199. package/src/commands/ai-prompt.ts +21 -0
  200. package/src/commands/analyze-deps.ts +119 -0
  201. package/src/commands/archive.ts +114 -0
  202. package/src/commands/branch/create.ts +94 -0
  203. package/src/commands/branch/index.ts +74 -0
  204. package/src/commands/branch/merge.ts +61 -0
  205. package/src/commands/branch/rebase.ts +54 -0
  206. package/src/commands/branch/switch.ts +150 -0
  207. package/src/commands/cache/index.ts +51 -0
  208. package/src/commands/cache/manager.ts +168 -0
  209. package/src/commands/cleanup.ts +132 -0
  210. package/src/commands/configure-claude.ts +55 -0
  211. package/src/commands/configure.ts +204 -0
  212. package/src/commands/create.ts +196 -0
  213. package/src/commands/dashboard/hook-handler.ts +138 -0
  214. package/src/commands/dashboard/index.ts +248 -0
  215. package/src/commands/dashboard/launcher.ts +42 -0
  216. package/src/commands/dashboard/session-picker.ts +73 -0
  217. package/src/commands/dashboard/sidebar.js +39 -0
  218. package/src/commands/dashboard/workspace-picker.ts +62 -0
  219. package/src/commands/delete.test.ts +250 -0
  220. package/src/commands/delete.ts +171 -0
  221. package/src/commands/deprecated-aliases.ts +55 -0
  222. package/src/commands/diff.ts +106 -0
  223. package/src/commands/doctor.ts +110 -0
  224. package/src/commands/generate-docs.ts +45 -0
  225. package/src/commands/ghq-status.ts +70 -0
  226. package/src/commands/go.ts +125 -0
  227. package/src/commands/history.ts +136 -0
  228. package/src/commands/list.test.ts +219 -0
  229. package/src/commands/list.ts +176 -0
  230. package/src/commands/mcp/index.ts +39 -0
  231. package/src/commands/migrate.ts +277 -0
  232. package/src/commands/remove-repo.ts +194 -0
  233. package/src/commands/report-bug.ts +88 -0
  234. package/src/commands/run.ts +125 -0
  235. package/src/commands/save-context.test.ts +113 -0
  236. package/src/commands/save-context.ts +141 -0
  237. package/src/commands/sessions.ts +99 -0
  238. package/src/commands/status.ts +111 -0
  239. package/src/commands/suite/create.ts +240 -0
  240. package/src/commands/suite/delete.ts +65 -0
  241. package/src/commands/suite/export.ts +97 -0
  242. package/src/commands/suite/import.ts +100 -0
  243. package/src/commands/suite/index.ts +66 -0
  244. package/src/commands/suite/list.ts +54 -0
  245. package/src/commands/suite/use.ts +232 -0
  246. package/src/commands/sync.ts +97 -0
  247. package/src/commands/update.ts +197 -0
  248. package/src/mcp/install.ts +529 -0
  249. package/src/mcp/server.ts +579 -0
  250. package/src/mcp/tools.test.ts +545 -0
  251. package/src/mcp/tools.ts +1019 -0
  252. package/src/mcp/update-workspace.test.ts +220 -0
  253. package/src/pi-extensions/permission-sync.ts.template +41 -0
  254. package/src/pi-extensions/permissions-gate.ts.template +69 -0
  255. package/src/pi-extensions/pr-fix-trigger.ts.template +124 -0
  256. package/src/pi-extensions/review-reminder.ts.template +58 -0
  257. package/src/pi-extensions/workspace-input-status.ts.template +919 -0
  258. package/src/program.ts +112 -0
  259. package/src/scripts/workspace-table.py +394 -0
  260. package/src/types/dashboard.ts +29 -0
  261. package/src/types/index.ts +109 -0
  262. package/src/types/ink.d.ts +171 -0
  263. package/src/utils/agent-config.test.ts +303 -0
  264. package/src/utils/agent-config.ts +264 -0
  265. package/src/utils/agent-state.test.ts +166 -0
  266. package/src/utils/agent-state.ts +135 -0
  267. package/src/utils/banner.ts +24 -0
  268. package/src/utils/branch-operations.ts +237 -0
  269. package/src/utils/bug-report.test.ts +174 -0
  270. package/src/utils/bug-report.ts +355 -0
  271. package/src/utils/cache.ts +150 -0
  272. package/src/utils/claude-integration.test.ts +381 -0
  273. package/src/utils/claude-integration.ts +649 -0
  274. package/src/utils/claude-sessions.test.ts +262 -0
  275. package/src/utils/claude-sessions.ts +251 -0
  276. package/src/utils/cleanup-operations.ts +94 -0
  277. package/src/utils/colors.ts +25 -0
  278. package/src/utils/command-helpers.ts +46 -0
  279. package/src/utils/config.test.ts +203 -0
  280. package/src/utils/config.ts +133 -0
  281. package/src/utils/context-file.ts +36 -0
  282. package/src/utils/dashboard-hooks.test.ts +168 -0
  283. package/src/utils/dashboard-hooks.ts +163 -0
  284. package/src/utils/dependency-analyzer.ts +243 -0
  285. package/src/utils/diff-operations.ts +59 -0
  286. package/src/utils/doc-generator.ts +187 -0
  287. package/src/utils/ghq-integration.test.ts +195 -0
  288. package/src/utils/ghq-integration.ts +317 -0
  289. package/src/utils/git-operations.test.ts +265 -0
  290. package/src/utils/git-operations.ts +233 -0
  291. package/src/utils/git-status.ts +171 -0
  292. package/src/utils/github.ts +74 -0
  293. package/src/utils/health-checks.ts +359 -0
  294. package/src/utils/history.ts +140 -0
  295. package/src/utils/hooks.ts +92 -0
  296. package/src/utils/logger.ts +37 -0
  297. package/src/utils/permission-sync.test.ts +593 -0
  298. package/src/utils/permission-sync.ts +720 -0
  299. package/src/utils/pi-extensions.test.ts +182 -0
  300. package/src/utils/pi-extensions.ts +226 -0
  301. package/src/utils/progress.ts +73 -0
  302. package/src/utils/prompts.test.ts +169 -0
  303. package/src/utils/prompts.ts +296 -0
  304. package/src/utils/repo-resolver.test.ts +313 -0
  305. package/src/utils/repo-resolver.ts +294 -0
  306. package/src/utils/retry.test.ts +124 -0
  307. package/src/utils/retry.ts +85 -0
  308. package/src/utils/run-operations.ts +38 -0
  309. package/src/utils/suite.test.ts +256 -0
  310. package/src/utils/suite.ts +144 -0
  311. package/src/utils/sync-operations.ts +87 -0
  312. package/src/utils/tmux-dashboard.ts +193 -0
  313. package/src/utils/validation.test.ts +139 -0
  314. package/src/utils/validation.ts +128 -0
  315. package/src/utils/version-check.test.ts +108 -0
  316. package/src/utils/version-check.ts +96 -0
  317. package/src/utils/workspace-meta.ts +173 -0
  318. package/sync-permissions.sh +137 -0
  319. package/tsconfig.json +20 -0
  320. package/uninstall.sh +162 -0
@@ -0,0 +1,355 @@
1
+ "use strict";
2
+ /**
3
+ * Bug reporting — capture runtime errors and file them as GitHub issues
4
+ * against the project repo.
5
+ *
6
+ * Design goals (see PR discussion):
7
+ * - Sanitize: never leak home dir, usernames, emails, or token-like strings.
8
+ * - Deduplicate: one issue per distinct error signature (search before create).
9
+ * - Safe: only file genuine tool errors; skip clearly-environmental ones unless
10
+ * forced. Auto-filing is opt-in via config (autoReportBugs).
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.BUG_LABEL = exports.LAST_ERROR_FILE = exports.BUG_REPORT_REPO = void 0;
47
+ exports.sanitize = sanitize;
48
+ exports.errorSignature = errorSignature;
49
+ exports.gatherEnv = gatherEnv;
50
+ exports.captureLastError = captureLastError;
51
+ exports.readLastError = readLastError;
52
+ exports.looksEnvironmental = looksEnvironmental;
53
+ exports.buildIssueTitle = buildIssueTitle;
54
+ exports.buildIssueBody = buildIssueBody;
55
+ exports.buildIssueListArgs = buildIssueListArgs;
56
+ exports.findExistingIssue = findExistingIssue;
57
+ exports.buildIssueCreateArgs = buildIssueCreateArgs;
58
+ exports.createIssue = createIssue;
59
+ exports.reportBug = reportBug;
60
+ const child_process_1 = require("child_process");
61
+ const os = __importStar(require("os"));
62
+ const fs = __importStar(require("fs"));
63
+ const path = __importStar(require("path"));
64
+ const crypto_1 = require("crypto");
65
+ const config_1 = require("./config");
66
+ /**
67
+ * Repo that bug issues are filed against. Overridable via NEMUS_BUG_REPORT_REPO
68
+ * so forks/downstreams route reports to their own tracker.
69
+ */
70
+ exports.BUG_REPORT_REPO = process.env.NEMUS_BUG_REPORT_REPO || 'me-public/nemus';
71
+ /** Where the most recent error is captured for `w report-bug`. */
72
+ exports.LAST_ERROR_FILE = path.join(config_1.CACHE_DIR, 'last-error.json');
73
+ /** Label applied to auto-filed bug issues (used for dedup search too). */
74
+ exports.BUG_LABEL = 'auto-reported';
75
+ /**
76
+ * Redact personally-identifiable / sensitive substrings from text:
77
+ * - absolute home dir -> ~
78
+ * - emails -> <email>
79
+ * - token-ish strings -> <redacted>
80
+ */
81
+ function sanitize(text) {
82
+ if (typeof text !== 'string' || !text)
83
+ return text || '';
84
+ let out = text;
85
+ const home = os.homedir();
86
+ if (home) {
87
+ // Replace the literal home path everywhere it appears.
88
+ out = out.split(home).join('~');
89
+ }
90
+ // /Users/<name>/ and /home/<name>/ (in case home didn't match, e.g. other users)
91
+ out = out.replace(/\/(Users|home)\/[^/\s"']+/g, '/$1/<user>');
92
+ // Emails
93
+ out = out.replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, '<email>');
94
+ // Bearer / token-like long alnum sequences (>= 24 chars with mixed case/digits)
95
+ out = out.replace(/\b(?=[A-Za-z0-9_-]*[A-Za-z])(?=[A-Za-z0-9_-]*[0-9])[A-Za-z0-9_-]{24,}\b/g, '<redacted>');
96
+ // GitHub tokens
97
+ out = out.replace(/\bgh[pousr]_[A-Za-z0-9]{20,}\b/g, '<redacted>');
98
+ // Defense-in-depth: neutralize shell metacharacters so that even if the
99
+ // text is ever interpolated into a shell (it shouldn't be — we use argv),
100
+ // command substitution can't fire. Also keeps issue bodies clean.
101
+ out = out.replace(/\$\(/g, '$​(').replace(/`/g, 'ˋ').replace(/\$\{/g, '$​{');
102
+ return out;
103
+ }
104
+ /**
105
+ * Compute a stable signature for an error so duplicates collapse to one issue.
106
+ * Uses the normalized message (numbers/paths stripped) + top stack frame.
107
+ */
108
+ function errorSignature(message, stack) {
109
+ const normMsg = sanitize(message || '')
110
+ .replace(/\d+/g, 'N') // collapse line numbers, counts, ports
111
+ .replace(/\s+/g, ' ')
112
+ .trim()
113
+ .slice(0, 200);
114
+ // First stack frame that points into our own code (most stable identifier).
115
+ let frame = '';
116
+ if (stack) {
117
+ const m = sanitize(stack).match(/at\s+([^\s(]+)\s*\(?([^\s)]+):\d+:\d+\)?/);
118
+ if (m)
119
+ frame = `${m[1]} ${m[2].replace(/\d+/g, 'N')}`;
120
+ }
121
+ return (0, crypto_1.createHash)('sha1').update(`${normMsg}||${frame}`).digest('hex').slice(0, 12);
122
+ }
123
+ /** Gather non-sensitive environment info for the report. */
124
+ function gatherEnv(version) {
125
+ return {
126
+ version,
127
+ node: process.version,
128
+ os: `${os.type()} ${os.release()}`,
129
+ arch: os.arch(),
130
+ };
131
+ }
132
+ /** Persist the most recent error so `w report-bug` can pick it up later. */
133
+ function captureLastError(err) {
134
+ try {
135
+ fs.mkdirSync(config_1.CACHE_DIR, { recursive: true });
136
+ // Sanitize before persisting so secrets/paths/emails never sit on disk
137
+ // (the issue body re-sanitizes too — sanitize is idempotent enough here).
138
+ const safe = {
139
+ ...err,
140
+ command: sanitize(err.command),
141
+ message: sanitize(err.message),
142
+ stack: err.stack ? sanitize(err.stack) : undefined,
143
+ };
144
+ fs.writeFileSync(exports.LAST_ERROR_FILE, JSON.stringify(safe, null, 2), 'utf-8');
145
+ }
146
+ catch {
147
+ /* non-critical */
148
+ }
149
+ }
150
+ /** Read the most recent captured error, if any. */
151
+ function readLastError() {
152
+ try {
153
+ const raw = JSON.parse(fs.readFileSync(exports.LAST_ERROR_FILE, 'utf-8'));
154
+ // Validate required fields so a malformed/old file can't crash the reporter.
155
+ if (!raw || typeof raw !== 'object' || typeof raw.message !== 'string' || !raw.message) {
156
+ return null;
157
+ }
158
+ return {
159
+ command: typeof raw.command === 'string' ? raw.command : 'unknown',
160
+ message: raw.message,
161
+ stack: typeof raw.stack === 'string' ? raw.stack : undefined,
162
+ timestamp: typeof raw.timestamp === 'string' ? raw.timestamp : new Date().toISOString(),
163
+ version: typeof raw.version === 'string' ? raw.version : undefined,
164
+ };
165
+ }
166
+ catch {
167
+ return null;
168
+ }
169
+ }
170
+ /** Heuristic: is this error clearly environmental (not a code bug)? */
171
+ function looksEnvironmental(message) {
172
+ return /ETIMEDOUT|ENOTFOUND|ECONNREFUSED|ECONNRESET|EACCES|EPERM|ENOSPC|network|rate.?limit|not authenticated|gh auth|sso|credential|token expired|command not found|No such file or directory/i
173
+ .test(message);
174
+ }
175
+ /** Build the GitHub issue title for a captured error. */
176
+ function buildIssueTitle(err) {
177
+ const sig = errorSignature(err.message, err.stack);
178
+ const shortMsg = sanitize(err.message).split('\n')[0].slice(0, 80);
179
+ return `[auto] ${shortMsg} (${sig})`;
180
+ }
181
+ /** Build the GitHub issue body (fully sanitized). */
182
+ function buildIssueBody(err, env) {
183
+ const sig = errorSignature(err.message, err.stack);
184
+ const stack = err.stack ? sanitize(err.stack).slice(0, 4000) : '(no stack captured)';
185
+ return [
186
+ `**Auto-reported by workspace-manager.**`,
187
+ ``,
188
+ `**Signature:** \`${sig}\``,
189
+ `**Command:** \`${sanitize(err.command)}\``,
190
+ `**When:** ${err.timestamp}`,
191
+ ``,
192
+ `### Error`,
193
+ '```',
194
+ sanitize(err.message).slice(0, 1000),
195
+ '```',
196
+ ``,
197
+ `### Stack`,
198
+ '```',
199
+ stack,
200
+ '```',
201
+ ``,
202
+ `### Environment`,
203
+ `| | |`,
204
+ `|---|---|`,
205
+ `| workspace-manager | ${env.version} |`,
206
+ `| node | ${env.node} |`,
207
+ `| os | ${env.os} |`,
208
+ `| arch | ${env.arch} |`,
209
+ ``,
210
+ `<sub>Paths, usernames, emails, and token-like strings are redacted automatically.</sub>`,
211
+ ].join('\n');
212
+ }
213
+ /**
214
+ * Build the argv for listing candidate duplicate issues. Exported for testing
215
+ * so we can assert no shell metacharacters / values are passed inertly.
216
+ */
217
+ function buildIssueListArgs() {
218
+ return [
219
+ 'issue', 'list',
220
+ '--repo', exports.BUG_REPORT_REPO,
221
+ '--state', 'open',
222
+ '--label', exports.BUG_LABEL,
223
+ '--json', 'url,title',
224
+ '--limit', '200',
225
+ ];
226
+ }
227
+ /**
228
+ * Find an existing open auto-reported issue with the same signature.
229
+ *
230
+ * Lists recent auto-reported issues and filters client-side on the
231
+ * parenthesized signature in the title. We do NOT rely on GitHub's
232
+ * free-text --search matching a hex substring, which is unreliable.
233
+ *
234
+ * Returns { ok:true, url } on a successful lookup (url=null = no duplicate),
235
+ * or { ok:false } if the lookup could not be performed.
236
+ */
237
+ function findExistingIssue(signature) {
238
+ try {
239
+ const out = (0, child_process_1.execFileSync)('gh', buildIssueListArgs(), {
240
+ encoding: 'utf-8',
241
+ timeout: 20000,
242
+ stdio: ['ignore', 'pipe', 'ignore'],
243
+ });
244
+ const issues = JSON.parse(out);
245
+ // Match the signature only where we put it: parenthesized at the end of
246
+ // the title ("[auto] … (<sig>)"). Avoids matching an unrelated issue whose
247
+ // free text happens to contain the same 12-hex substring.
248
+ const match = issues.find((i) => i.title.includes(`(${signature})`));
249
+ return { ok: true, url: match ? match.url : null };
250
+ }
251
+ catch {
252
+ return { ok: false };
253
+ }
254
+ }
255
+ /**
256
+ * Ensure the auto-report label exists (read-or-create, best-effort).
257
+ * Avoids re-writing the label on every issue by checking existence first.
258
+ */
259
+ function ensureLabel() {
260
+ try {
261
+ const out = (0, child_process_1.execFileSync)('gh', ['label', 'list', '--repo', exports.BUG_REPORT_REPO, '--json', 'name'], {
262
+ encoding: 'utf-8', timeout: 15000, stdio: ['ignore', 'pipe', 'ignore'],
263
+ });
264
+ const labels = JSON.parse(out);
265
+ if (labels.some((l) => l.name === exports.BUG_LABEL))
266
+ return; // already exists
267
+ }
268
+ catch {
269
+ // couldn't list — fall through and try to create
270
+ }
271
+ try {
272
+ (0, child_process_1.execFileSync)('gh', [
273
+ 'label', 'create', exports.BUG_LABEL,
274
+ '--repo', exports.BUG_REPORT_REPO,
275
+ '--color', 'B60205',
276
+ '--description', 'Auto-reported by workspace-manager',
277
+ ], { encoding: 'utf-8', timeout: 15000, stdio: 'ignore' });
278
+ }
279
+ catch {
280
+ /* may already exist or no perms — ignore */
281
+ }
282
+ }
283
+ /**
284
+ * Build the argv for creating an issue. Exported for testing: a title/body
285
+ * containing shell metacharacters ($(…), backticks) must be passed as inert
286
+ * argv elements, never interpolated into a shell.
287
+ */
288
+ function buildIssueCreateArgs(title, body) {
289
+ return [
290
+ 'issue', 'create',
291
+ '--repo', exports.BUG_REPORT_REPO,
292
+ '--title', title,
293
+ '--body', body,
294
+ '--label', exports.BUG_LABEL,
295
+ ];
296
+ }
297
+ /**
298
+ * Create a bug issue. Returns the created issue URL, or null on failure.
299
+ *
300
+ * Uses execFileSync with an argv array — NO shell — so title/body (derived
301
+ * from arbitrary error text) can never trigger command substitution.
302
+ */
303
+ function createIssue(title, body) {
304
+ try {
305
+ ensureLabel();
306
+ const url = (0, child_process_1.execFileSync)('gh', buildIssueCreateArgs(title, body), {
307
+ encoding: 'utf-8',
308
+ timeout: 30000,
309
+ stdio: ['ignore', 'pipe', 'pipe'],
310
+ }).trim();
311
+ const m = url.match(/https?:\/\/\S+/);
312
+ return m ? m[0] : url || null;
313
+ }
314
+ catch {
315
+ return null;
316
+ }
317
+ }
318
+ /**
319
+ * File (or dedupe) a bug report for a captured error.
320
+ *
321
+ * @param err The captured error.
322
+ * @param version Tool version string.
323
+ * @param opts.force Report even if the error looks environmental.
324
+ */
325
+ function reportBug(err, version, opts = {}) {
326
+ if (!opts.force && looksEnvironmental(err.message)) {
327
+ return {
328
+ status: 'skipped',
329
+ reason: 'Error looks environmental (network/auth/credentials), not a code bug. Use --force to report anyway.',
330
+ };
331
+ }
332
+ // gh must be available + authenticated.
333
+ try {
334
+ (0, child_process_1.execFileSync)('gh', ['auth', 'status'], { timeout: 15000, stdio: 'ignore' });
335
+ }
336
+ catch {
337
+ return { status: 'failed', reason: 'GitHub CLI (gh) not available or not authenticated.' };
338
+ }
339
+ const sig = errorSignature(err.message, err.stack);
340
+ const dedup = findExistingIssue(sig);
341
+ if (!dedup.ok) {
342
+ return {
343
+ status: 'failed',
344
+ reason: 'Could not check for existing reports (gh issue list failed). Not filing, to avoid creating a duplicate. Please retry.',
345
+ };
346
+ }
347
+ if (dedup.url) {
348
+ return { status: 'duplicate', url: dedup.url };
349
+ }
350
+ const env = gatherEnv(version);
351
+ const url = createIssue(buildIssueTitle(err), buildIssueBody(err, env));
352
+ if (!url)
353
+ return { status: 'failed', reason: 'Failed to create the GitHub issue.' };
354
+ return { status: 'created', url };
355
+ }
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getCachedRepos = getCachedRepos;
37
+ exports.setCachedRepos = setCachedRepos;
38
+ exports.clearCache = clearCache;
39
+ exports.getCacheStats = getCacheStats;
40
+ const fs = __importStar(require("fs/promises"));
41
+ const path = __importStar(require("path"));
42
+ const config_1 = require("./config");
43
+ const logger_1 = require("./logger");
44
+ const colors_1 = require("./colors");
45
+ const REPOS_CACHE_FILE = path.join(config_1.CACHE_DIR, 'repos-cache.json');
46
+ const DEFAULT_TTL = 86400000; // 24 hours in milliseconds
47
+ /**
48
+ * Ensure cache directory exists
49
+ */
50
+ async function ensureCacheDir() {
51
+ try {
52
+ await fs.mkdir(config_1.CACHE_DIR, { recursive: true });
53
+ }
54
+ catch (error) {
55
+ (0, logger_1.logWarning)('Failed to create cache directory');
56
+ }
57
+ }
58
+ /**
59
+ * Check if cache is valid (not expired)
60
+ */
61
+ function isCacheValid(entry) {
62
+ const now = Date.now();
63
+ const age = now - entry.timestamp;
64
+ return age < entry.ttl;
65
+ }
66
+ /**
67
+ * Get cache age in human-readable format
68
+ */
69
+ function getCacheAge(timestamp) {
70
+ const ageMs = Date.now() - timestamp;
71
+ const ageMinutes = Math.floor(ageMs / 60000);
72
+ const ageHours = Math.floor(ageMinutes / 60);
73
+ const ageDays = Math.floor(ageHours / 24);
74
+ if (ageDays > 0) {
75
+ return `${ageDays} day${ageDays > 1 ? 's' : ''} ago`;
76
+ }
77
+ else if (ageHours > 0) {
78
+ return `${ageHours} hour${ageHours > 1 ? 's' : ''} ago`;
79
+ }
80
+ else if (ageMinutes > 0) {
81
+ return `${ageMinutes} minute${ageMinutes > 1 ? 's' : ''} ago`;
82
+ }
83
+ else {
84
+ return 'just now';
85
+ }
86
+ }
87
+ /**
88
+ * Read cached repositories
89
+ */
90
+ async function getCachedRepos(options = {}) {
91
+ const { force = false, org } = options;
92
+ if (force) {
93
+ (0, logger_1.logInfo)('Skipping cache (forced refresh)');
94
+ return null;
95
+ }
96
+ try {
97
+ const content = await fs.readFile(REPOS_CACHE_FILE, 'utf-8');
98
+ const entry = JSON.parse(content);
99
+ if (org && entry.org !== org) {
100
+ (0, logger_1.logInfo)(`Cache is for org "${entry.org || 'unknown'}", but current org is "${org}". Refreshing...`);
101
+ return null;
102
+ }
103
+ if (isCacheValid(entry)) {
104
+ const age = getCacheAge(entry.timestamp);
105
+ (0, logger_1.logInfo)(`Using cached repositories (${(0, colors_1.colorize)(String(entry.data.length), 'cyan')} repos, cached ${age})`);
106
+ return entry.data;
107
+ }
108
+ else {
109
+ (0, logger_1.logInfo)('Cache expired, fetching fresh data...');
110
+ return null;
111
+ }
112
+ }
113
+ catch (error) {
114
+ // Cache file doesn't exist or is invalid
115
+ return null;
116
+ }
117
+ }
118
+ /**
119
+ * Write repositories to cache
120
+ */
121
+ async function setCachedRepos(repos, ttl = DEFAULT_TTL, org) {
122
+ try {
123
+ await ensureCacheDir();
124
+ const entry = {
125
+ data: repos,
126
+ timestamp: Date.now(),
127
+ ttl,
128
+ org,
129
+ };
130
+ await fs.writeFile(REPOS_CACHE_FILE, JSON.stringify(entry, null, 2), 'utf-8');
131
+ (0, logger_1.logInfo)(`Cached ${(0, colors_1.colorize)(String(repos.length), 'cyan')} repositories (TTL: ${Math.floor(ttl / 60000)} minutes)`);
132
+ }
133
+ catch (error) {
134
+ (0, logger_1.logWarning)('Failed to cache repositories');
135
+ }
136
+ }
137
+ /**
138
+ * Clear the cache
139
+ */
140
+ async function clearCache() {
141
+ try {
142
+ await fs.unlink(REPOS_CACHE_FILE);
143
+ (0, logger_1.logInfo)('Cache cleared successfully');
144
+ }
145
+ catch (error) {
146
+ // Cache file doesn't exist, nothing to clear
147
+ (0, logger_1.logInfo)('No cache to clear');
148
+ }
149
+ }
150
+ /**
151
+ * Get cache statistics
152
+ */
153
+ async function getCacheStats() {
154
+ try {
155
+ const content = await fs.readFile(REPOS_CACHE_FILE, 'utf-8');
156
+ const entry = JSON.parse(content);
157
+ const stats = await fs.stat(REPOS_CACHE_FILE);
158
+ return {
159
+ exists: true,
160
+ age: getCacheAge(entry.timestamp),
161
+ repoCount: entry.data.length,
162
+ size: stats.size,
163
+ };
164
+ }
165
+ catch {
166
+ return { exists: false };
167
+ }
168
+ }