@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.
- package/CHANGELOG.md +86 -0
- package/LICENSE +21 -0
- package/README.md +275 -0
- package/assets/README.md +53 -0
- package/assets/banner.png +0 -0
- package/assets/banner.svg +66 -0
- package/assets/favicon-32.png +0 -0
- package/assets/favicon-64.png +0 -0
- package/assets/icon-256.png +0 -0
- package/assets/icon.svg +23 -0
- package/assets/logo-256.png +0 -0
- package/assets/logo-512.png +0 -0
- package/assets/logo-wordmark.svg +35 -0
- package/assets/logo.svg +32 -0
- package/bin/workspace.js +130 -0
- package/dist/cli/ai-prompt.js +421 -0
- package/dist/cli/dashboard/AgentList.js +61 -0
- package/dist/cli/dashboard/DashboardSidebar.js +280 -0
- package/dist/cli/dashboard/HelpFooter.js +73 -0
- package/dist/cli/dashboard/StatusBadge.js +50 -0
- package/dist/cli/dashboard/index.js +11 -0
- package/dist/cli/dashboard/types.js +2 -0
- package/dist/cli/tui.js +48 -0
- package/dist/commands/ai-prompt.js +22 -0
- package/dist/commands/analyze-deps.js +138 -0
- package/dist/commands/archive.js +113 -0
- package/dist/commands/branch/create.js +119 -0
- package/dist/commands/branch/index.js +104 -0
- package/dist/commands/branch/merge.js +90 -0
- package/dist/commands/branch/rebase.js +84 -0
- package/dist/commands/branch/switch.js +165 -0
- package/dist/commands/cache/index.js +80 -0
- package/dist/commands/cache/manager.js +198 -0
- package/dist/commands/cleanup.js +153 -0
- package/dist/commands/configure-claude.js +55 -0
- package/dist/commands/configure.js +231 -0
- package/dist/commands/create.js +204 -0
- package/dist/commands/dashboard/hook-handler.js +162 -0
- package/dist/commands/dashboard/index.js +258 -0
- package/dist/commands/dashboard/launcher.js +40 -0
- package/dist/commands/dashboard/session-picker.js +106 -0
- package/dist/commands/dashboard/sidebar.js +39 -0
- package/dist/commands/dashboard/workspace-picker.js +95 -0
- package/dist/commands/delete.js +194 -0
- package/dist/commands/deprecated-aliases.js +44 -0
- package/dist/commands/diff.js +129 -0
- package/dist/commands/doctor.js +141 -0
- package/dist/commands/generate-docs.js +77 -0
- package/dist/commands/ghq-status.js +72 -0
- package/dist/commands/go.js +154 -0
- package/dist/commands/history.js +124 -0
- package/dist/commands/list.js +191 -0
- package/dist/commands/mcp/index.js +69 -0
- package/dist/commands/migrate.js +296 -0
- package/dist/commands/remove-repo.js +217 -0
- package/dist/commands/report-bug.js +112 -0
- package/dist/commands/run.js +147 -0
- package/dist/commands/save-context.js +171 -0
- package/dist/commands/sessions.js +129 -0
- package/dist/commands/status.js +138 -0
- package/dist/commands/suite/create.js +212 -0
- package/dist/commands/suite/delete.js +64 -0
- package/dist/commands/suite/export.js +126 -0
- package/dist/commands/suite/import.js +128 -0
- package/dist/commands/suite/index.js +94 -0
- package/dist/commands/suite/list.js +49 -0
- package/dist/commands/suite/use.js +227 -0
- package/dist/commands/sync.js +117 -0
- package/dist/commands/update.js +203 -0
- package/dist/mcp/install.js +550 -0
- package/dist/mcp/server.js +398 -0
- package/dist/mcp/tools.js +922 -0
- package/dist/pi-extensions/permission-sync.ts.template +41 -0
- package/dist/pi-extensions/permissions-gate.ts.template +69 -0
- package/dist/pi-extensions/pr-fix-trigger.ts.template +124 -0
- package/dist/pi-extensions/review-reminder.ts.template +58 -0
- package/dist/pi-extensions/workspace-input-status.ts.template +919 -0
- package/dist/program.js +136 -0
- package/dist/scripts/workspace-table.py +394 -0
- package/dist/types/dashboard.js +11 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/agent-config.js +263 -0
- package/dist/utils/agent-state.js +178 -0
- package/dist/utils/banner.js +24 -0
- package/dist/utils/branch-operations.js +232 -0
- package/dist/utils/bug-report.js +355 -0
- package/dist/utils/cache.js +168 -0
- package/dist/utils/claude-integration.js +617 -0
- package/dist/utils/claude-sessions.js +260 -0
- package/dist/utils/cleanup-operations.js +120 -0
- package/dist/utils/colors.js +26 -0
- package/dist/utils/command-helpers.js +44 -0
- package/dist/utils/config.js +150 -0
- package/dist/utils/context-file.js +33 -0
- package/dist/utils/dashboard-hooks.js +172 -0
- package/dist/utils/dependency-analyzer.js +234 -0
- package/dist/utils/diff-operations.js +80 -0
- package/dist/utils/doc-generator.js +184 -0
- package/dist/utils/ghq-integration.js +332 -0
- package/dist/utils/git-operations.js +237 -0
- package/dist/utils/git-status.js +187 -0
- package/dist/utils/github.js +68 -0
- package/dist/utils/health-checks.js +345 -0
- package/dist/utils/history.js +150 -0
- package/dist/utils/hooks.js +100 -0
- package/dist/utils/logger.js +40 -0
- package/dist/utils/permission-sync.js +681 -0
- package/dist/utils/pi-extensions.js +224 -0
- package/dist/utils/progress.js +92 -0
- package/dist/utils/prompts.js +279 -0
- package/dist/utils/repo-resolver.js +260 -0
- package/dist/utils/retry.js +65 -0
- package/dist/utils/run-operations.js +30 -0
- package/dist/utils/suite.js +159 -0
- package/dist/utils/sync-operations.js +107 -0
- package/dist/utils/tmux-dashboard.js +184 -0
- package/dist/utils/validation.js +153 -0
- package/dist/utils/version-check.js +121 -0
- package/dist/utils/workspace-meta.js +185 -0
- package/install-shell-integration.sh +532 -0
- package/install.sh +401 -0
- package/package.json +90 -0
- package/remove-shell-block.awk +44 -0
- package/scripts/postinstall.js +85 -0
- package/skills/analyze-deps.md +23 -0
- package/skills/archive-workspace.md +26 -0
- package/skills/branch-create.md +18 -0
- package/skills/create-workspace.md +39 -0
- package/skills/delete-workspace.md +37 -0
- package/skills/list-org-repos.md +26 -0
- package/skills/list-suites.md +24 -0
- package/skills/list-workspaces.md +27 -0
- package/skills/refresh-workspace-docs.md +89 -0
- package/skills/remove-repo.md +25 -0
- package/skills/run-command.md +23 -0
- package/skills/save-context.md +68 -0
- package/skills/search-repos.md +31 -0
- package/skills/snapshot-list.md +18 -0
- package/skills/snapshot-save.md +12 -0
- package/skills/switch-branch.md +18 -0
- package/skills/update-cache.md +21 -0
- package/skills/update-workspace.md +66 -0
- package/skills/workspace-cleanup.md +18 -0
- package/skills/workspace-diff.md +23 -0
- package/skills/workspace-doctor.md +23 -0
- package/skills/workspace-info.md +45 -0
- package/skills/workspace-manager/SKILL.md +201 -0
- package/skills/workspace-manager/references/ai-prompt.md +44 -0
- package/skills/workspace-manager/references/analyze-deps.md +45 -0
- package/skills/workspace-manager/references/archive-workspace.md +38 -0
- package/skills/workspace-manager/references/branch-create.md +31 -0
- package/skills/workspace-manager/references/branch-merge.md +22 -0
- package/skills/workspace-manager/references/branch-rebase.md +22 -0
- package/skills/workspace-manager/references/cache-clear.md +18 -0
- package/skills/workspace-manager/references/cache-info.md +22 -0
- package/skills/workspace-manager/references/configure-claude.md +31 -0
- package/skills/workspace-manager/references/configure.md +35 -0
- package/skills/workspace-manager/references/create-workspace.md +45 -0
- package/skills/workspace-manager/references/dashboard.md +61 -0
- package/skills/workspace-manager/references/delete-workspace.md +33 -0
- package/skills/workspace-manager/references/generate-docs.md +36 -0
- package/skills/workspace-manager/references/ghq-status.md +28 -0
- package/skills/workspace-manager/references/go.md +33 -0
- package/skills/workspace-manager/references/history.md +41 -0
- package/skills/workspace-manager/references/list-org-repos.md +26 -0
- package/skills/workspace-manager/references/list-suites.md +21 -0
- package/skills/workspace-manager/references/list-workspaces.md +27 -0
- package/skills/workspace-manager/references/mcp.md +49 -0
- package/skills/workspace-manager/references/remove-repo.md +26 -0
- package/skills/workspace-manager/references/run-command.md +44 -0
- package/skills/workspace-manager/references/search-repos.md +24 -0
- package/skills/workspace-manager/references/sessions.md +27 -0
- package/skills/workspace-manager/references/suite-create.md +22 -0
- package/skills/workspace-manager/references/suite-delete.md +21 -0
- package/skills/workspace-manager/references/suite-export.md +19 -0
- package/skills/workspace-manager/references/suite-import.md +19 -0
- package/skills/workspace-manager/references/suite-use.md +27 -0
- package/skills/workspace-manager/references/switch-branch.md +25 -0
- package/skills/workspace-manager/references/update-cache.md +22 -0
- package/skills/workspace-manager/references/update-workspace.md +50 -0
- package/skills/workspace-manager/references/workspace-cleanup.md +33 -0
- package/skills/workspace-manager/references/workspace-diff.md +27 -0
- package/skills/workspace-manager/references/workspace-doctor.md +27 -0
- package/skills/workspace-manager/references/workspace-info.md +33 -0
- package/skills/workspace-manager/references/workspace-status.md +43 -0
- package/skills/workspace-manager/references/workspace-sync.md +26 -0
- package/skills/workspace-status.md +26 -0
- package/skills/workspace-sync.md +23 -0
- package/src/cli/ai-prompt.test.ts +667 -0
- package/src/cli/ai-prompt.ts +421 -0
- package/src/cli/dashboard/AgentList.tsx +44 -0
- package/src/cli/dashboard/DashboardSidebar.tsx +291 -0
- package/src/cli/dashboard/HelpFooter.tsx +22 -0
- package/src/cli/dashboard/StatusBadge.tsx +23 -0
- package/src/cli/dashboard/index.ts +5 -0
- package/src/cli/dashboard/types.ts +16 -0
- package/src/cli/tui.tsx +51 -0
- package/src/cli/workspace-cli.test.ts +30 -0
- package/src/commands/ai-prompt.ts +21 -0
- package/src/commands/analyze-deps.ts +119 -0
- package/src/commands/archive.ts +114 -0
- package/src/commands/branch/create.ts +94 -0
- package/src/commands/branch/index.ts +74 -0
- package/src/commands/branch/merge.ts +61 -0
- package/src/commands/branch/rebase.ts +54 -0
- package/src/commands/branch/switch.ts +150 -0
- package/src/commands/cache/index.ts +51 -0
- package/src/commands/cache/manager.ts +168 -0
- package/src/commands/cleanup.ts +132 -0
- package/src/commands/configure-claude.ts +55 -0
- package/src/commands/configure.ts +204 -0
- package/src/commands/create.ts +196 -0
- package/src/commands/dashboard/hook-handler.ts +138 -0
- package/src/commands/dashboard/index.ts +248 -0
- package/src/commands/dashboard/launcher.ts +42 -0
- package/src/commands/dashboard/session-picker.ts +73 -0
- package/src/commands/dashboard/sidebar.js +39 -0
- package/src/commands/dashboard/workspace-picker.ts +62 -0
- package/src/commands/delete.test.ts +250 -0
- package/src/commands/delete.ts +171 -0
- package/src/commands/deprecated-aliases.ts +55 -0
- package/src/commands/diff.ts +106 -0
- package/src/commands/doctor.ts +110 -0
- package/src/commands/generate-docs.ts +45 -0
- package/src/commands/ghq-status.ts +70 -0
- package/src/commands/go.ts +125 -0
- package/src/commands/history.ts +136 -0
- package/src/commands/list.test.ts +219 -0
- package/src/commands/list.ts +176 -0
- package/src/commands/mcp/index.ts +39 -0
- package/src/commands/migrate.ts +277 -0
- package/src/commands/remove-repo.ts +194 -0
- package/src/commands/report-bug.ts +88 -0
- package/src/commands/run.ts +125 -0
- package/src/commands/save-context.test.ts +113 -0
- package/src/commands/save-context.ts +141 -0
- package/src/commands/sessions.ts +99 -0
- package/src/commands/status.ts +111 -0
- package/src/commands/suite/create.ts +240 -0
- package/src/commands/suite/delete.ts +65 -0
- package/src/commands/suite/export.ts +97 -0
- package/src/commands/suite/import.ts +100 -0
- package/src/commands/suite/index.ts +66 -0
- package/src/commands/suite/list.ts +54 -0
- package/src/commands/suite/use.ts +232 -0
- package/src/commands/sync.ts +97 -0
- package/src/commands/update.ts +197 -0
- package/src/mcp/install.ts +529 -0
- package/src/mcp/server.ts +579 -0
- package/src/mcp/tools.test.ts +545 -0
- package/src/mcp/tools.ts +1019 -0
- package/src/mcp/update-workspace.test.ts +220 -0
- package/src/pi-extensions/permission-sync.ts.template +41 -0
- package/src/pi-extensions/permissions-gate.ts.template +69 -0
- package/src/pi-extensions/pr-fix-trigger.ts.template +124 -0
- package/src/pi-extensions/review-reminder.ts.template +58 -0
- package/src/pi-extensions/workspace-input-status.ts.template +919 -0
- package/src/program.ts +112 -0
- package/src/scripts/workspace-table.py +394 -0
- package/src/types/dashboard.ts +29 -0
- package/src/types/index.ts +109 -0
- package/src/types/ink.d.ts +171 -0
- package/src/utils/agent-config.test.ts +303 -0
- package/src/utils/agent-config.ts +264 -0
- package/src/utils/agent-state.test.ts +166 -0
- package/src/utils/agent-state.ts +135 -0
- package/src/utils/banner.ts +24 -0
- package/src/utils/branch-operations.ts +237 -0
- package/src/utils/bug-report.test.ts +174 -0
- package/src/utils/bug-report.ts +355 -0
- package/src/utils/cache.ts +150 -0
- package/src/utils/claude-integration.test.ts +381 -0
- package/src/utils/claude-integration.ts +649 -0
- package/src/utils/claude-sessions.test.ts +262 -0
- package/src/utils/claude-sessions.ts +251 -0
- package/src/utils/cleanup-operations.ts +94 -0
- package/src/utils/colors.ts +25 -0
- package/src/utils/command-helpers.ts +46 -0
- package/src/utils/config.test.ts +203 -0
- package/src/utils/config.ts +133 -0
- package/src/utils/context-file.ts +36 -0
- package/src/utils/dashboard-hooks.test.ts +168 -0
- package/src/utils/dashboard-hooks.ts +163 -0
- package/src/utils/dependency-analyzer.ts +243 -0
- package/src/utils/diff-operations.ts +59 -0
- package/src/utils/doc-generator.ts +187 -0
- package/src/utils/ghq-integration.test.ts +195 -0
- package/src/utils/ghq-integration.ts +317 -0
- package/src/utils/git-operations.test.ts +265 -0
- package/src/utils/git-operations.ts +233 -0
- package/src/utils/git-status.ts +171 -0
- package/src/utils/github.ts +74 -0
- package/src/utils/health-checks.ts +359 -0
- package/src/utils/history.ts +140 -0
- package/src/utils/hooks.ts +92 -0
- package/src/utils/logger.ts +37 -0
- package/src/utils/permission-sync.test.ts +593 -0
- package/src/utils/permission-sync.ts +720 -0
- package/src/utils/pi-extensions.test.ts +182 -0
- package/src/utils/pi-extensions.ts +226 -0
- package/src/utils/progress.ts +73 -0
- package/src/utils/prompts.test.ts +169 -0
- package/src/utils/prompts.ts +296 -0
- package/src/utils/repo-resolver.test.ts +313 -0
- package/src/utils/repo-resolver.ts +294 -0
- package/src/utils/retry.test.ts +124 -0
- package/src/utils/retry.ts +85 -0
- package/src/utils/run-operations.ts +38 -0
- package/src/utils/suite.test.ts +256 -0
- package/src/utils/suite.ts +144 -0
- package/src/utils/sync-operations.ts +87 -0
- package/src/utils/tmux-dashboard.ts +193 -0
- package/src/utils/validation.test.ts +139 -0
- package/src/utils/validation.ts +128 -0
- package/src/utils/version-check.test.ts +108 -0
- package/src/utils/version-check.ts +96 -0
- package/src/utils/workspace-meta.ts +173 -0
- package/sync-permissions.sh +137 -0
- package/tsconfig.json +20 -0
- 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
|
+
}
|