@cg3/equip 0.2.22 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +26 -10
  3. package/bin/equip.js +159 -68
  4. package/demo/README.md +1 -1
  5. package/dist/index.d.ts +74 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +175 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/lib/cli.d.ts +22 -0
  10. package/dist/lib/cli.d.ts.map +1 -0
  11. package/dist/lib/cli.js +148 -0
  12. package/dist/lib/cli.js.map +1 -0
  13. package/dist/lib/commands/doctor.d.ts +2 -0
  14. package/dist/lib/commands/doctor.d.ts.map +1 -0
  15. package/dist/lib/commands/doctor.js +162 -0
  16. package/dist/lib/commands/doctor.js.map +1 -0
  17. package/dist/lib/commands/status.d.ts +2 -0
  18. package/dist/lib/commands/status.d.ts.map +1 -0
  19. package/dist/lib/commands/status.js +134 -0
  20. package/dist/lib/commands/status.js.map +1 -0
  21. package/dist/lib/commands/update.d.ts +2 -0
  22. package/dist/lib/commands/update.d.ts.map +1 -0
  23. package/dist/lib/commands/update.js +93 -0
  24. package/dist/lib/commands/update.js.map +1 -0
  25. package/dist/lib/detect.d.ts +12 -0
  26. package/dist/lib/detect.d.ts.map +1 -0
  27. package/dist/lib/detect.js +109 -0
  28. package/dist/lib/detect.js.map +1 -0
  29. package/dist/lib/hooks.d.ts +40 -0
  30. package/dist/lib/hooks.d.ts.map +1 -0
  31. package/dist/lib/hooks.js +226 -0
  32. package/dist/lib/hooks.js.map +1 -0
  33. package/dist/lib/mcp.d.ts +73 -0
  34. package/dist/lib/mcp.d.ts.map +1 -0
  35. package/dist/lib/mcp.js +418 -0
  36. package/dist/lib/mcp.js.map +1 -0
  37. package/dist/lib/platforms.d.ts +67 -0
  38. package/dist/lib/platforms.d.ts.map +1 -0
  39. package/dist/lib/platforms.js +353 -0
  40. package/dist/lib/platforms.js.map +1 -0
  41. package/dist/lib/rules.d.ts +35 -0
  42. package/dist/lib/rules.d.ts.map +1 -0
  43. package/dist/lib/rules.js +161 -0
  44. package/dist/lib/rules.js.map +1 -0
  45. package/dist/lib/state.d.ts +33 -0
  46. package/dist/lib/state.d.ts.map +1 -0
  47. package/dist/lib/state.js +130 -0
  48. package/dist/lib/state.js.map +1 -0
  49. package/package.json +19 -13
  50. package/registry.json +9 -0
  51. package/index.js +0 -244
  52. package/lib/cli.js +0 -99
  53. package/lib/detect.js +0 -242
  54. package/lib/hooks.js +0 -238
  55. package/lib/mcp.js +0 -503
  56. package/lib/platforms.js +0 -184
  57. package/lib/rules.js +0 -170
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ // State management — tracks what equip has installed across platforms.
3
+ // State file: ~/.equip/state.json
4
+ // Zero dependencies.
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.getEquipDir = getEquipDir;
40
+ exports.getStatePath = getStatePath;
41
+ exports.readState = readState;
42
+ exports.writeState = writeState;
43
+ exports.trackInstall = trackInstall;
44
+ exports.trackUninstall = trackUninstall;
45
+ exports.markUpdated = markUpdated;
46
+ const fs = __importStar(require("fs"));
47
+ const path = __importStar(require("path"));
48
+ const os = __importStar(require("os"));
49
+ // ─── Paths ──────────────────────────────────────────────────
50
+ const EQUIP_DIR = path.join(os.homedir(), ".equip");
51
+ const STATE_PATH = path.join(EQUIP_DIR, "state.json");
52
+ function getEquipDir() { return EQUIP_DIR; }
53
+ function getStatePath() { return STATE_PATH; }
54
+ // ─── Read / Write ───────────────────────────────────────────
55
+ function readState() {
56
+ try {
57
+ const raw = fs.readFileSync(STATE_PATH, "utf-8");
58
+ return JSON.parse(raw);
59
+ }
60
+ catch {
61
+ return {
62
+ equipVersion: "",
63
+ lastUpdated: "",
64
+ tools: {},
65
+ };
66
+ }
67
+ }
68
+ function writeState(state) {
69
+ fs.mkdirSync(EQUIP_DIR, { recursive: true });
70
+ const tmp = STATE_PATH + ".tmp";
71
+ fs.writeFileSync(tmp, JSON.stringify(state, null, 2) + "\n");
72
+ fs.renameSync(tmp, STATE_PATH);
73
+ }
74
+ // ─── Tool Tracking ──────────────────────────────────────────
75
+ /**
76
+ * Record that a tool was installed on a platform.
77
+ */
78
+ function trackInstall(toolName, pkg, platformId, record) {
79
+ const state = readState();
80
+ const version = getEquipVersion();
81
+ if (!state.tools[toolName]) {
82
+ state.tools[toolName] = {
83
+ package: pkg,
84
+ installedAt: new Date().toISOString(),
85
+ platforms: {},
86
+ };
87
+ }
88
+ state.tools[toolName].platforms[platformId] = record;
89
+ state.tools[toolName].updatedAt = new Date().toISOString();
90
+ state.equipVersion = version;
91
+ writeState(state);
92
+ }
93
+ /**
94
+ * Remove a tool's platform record from state.
95
+ */
96
+ function trackUninstall(toolName, platformId) {
97
+ const state = readState();
98
+ if (!state.tools[toolName])
99
+ return;
100
+ if (platformId) {
101
+ delete state.tools[toolName].platforms[platformId];
102
+ if (Object.keys(state.tools[toolName].platforms).length === 0) {
103
+ delete state.tools[toolName];
104
+ }
105
+ }
106
+ else {
107
+ delete state.tools[toolName];
108
+ }
109
+ writeState(state);
110
+ }
111
+ /**
112
+ * Mark equip as freshly updated.
113
+ */
114
+ function markUpdated() {
115
+ const state = readState();
116
+ state.equipVersion = getEquipVersion();
117
+ state.lastUpdated = new Date().toISOString();
118
+ writeState(state);
119
+ }
120
+ // ─── Helpers ────────────────────────────────────────────────
121
+ function getEquipVersion() {
122
+ try {
123
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "..", "package.json"), "utf-8"));
124
+ return pkg.version;
125
+ }
126
+ catch {
127
+ return "unknown";
128
+ }
129
+ }
130
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/lib/state.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,26 +1,28 @@
1
1
  {
2
2
  "name": "@cg3/equip",
3
- "version": "0.2.22",
3
+ "version": "0.4.0",
4
4
  "description": "Universal MCP + behavioral rules installer for AI coding agents",
5
- "main": "index.js",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
6
7
  "bin": {
7
8
  "equip": "bin/equip.js"
8
9
  },
9
10
  "exports": {
10
- ".": "./index.js",
11
- "./detect": "./lib/detect.js",
12
- "./mcp": "./lib/mcp.js",
13
- "./rules": "./lib/rules.js",
14
- "./platforms": "./lib/platforms.js",
15
- "./cli": "./lib/cli.js",
11
+ ".": { "types": "./dist/index.d.ts", "require": "./dist/index.js" },
12
+ "./dist/*": "./dist/*",
16
13
  "./package.json": "./package.json"
17
14
  },
18
15
  "files": [
19
- "index.js",
16
+ "dist/",
20
17
  "bin/",
21
- "lib/",
22
- "demo/"
18
+ "demo/",
19
+ "registry.json"
23
20
  ],
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "test": "tsc && node --test test/equip.test.js",
24
+ "prepublishOnly": "npm run build"
25
+ },
24
26
  "keywords": [
25
27
  "mcp",
26
28
  "ai",
@@ -35,13 +37,17 @@
35
37
  "codex",
36
38
  "gemini"
37
39
  ],
38
- "author": "CG3 LLC",
40
+ "author": "Charles Mulic",
39
41
  "license": "MIT",
40
42
  "repository": {
41
43
  "type": "git",
42
- "url": "https://github.com/cg3-llc/equip"
44
+ "url": "https://github.com/CharlesMulic/equip"
43
45
  },
44
46
  "engines": {
45
47
  "node": ">=18"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^25.5.0",
51
+ "typescript": "^6.0.2"
46
52
  }
47
53
  }
package/registry.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$comment": "Tool registry for npx @cg3/equip <shorthand>. Submit a PR to add your tool.",
4
+ "prior": {
5
+ "package": "@cg3/prior-node",
6
+ "command": "setup",
7
+ "description": "Prior — agent-centric shared knowledge base"
8
+ }
9
+ }
package/index.js DELETED
@@ -1,244 +0,0 @@
1
- // @cg3/equip — Universal MCP + behavioral rules installer for AI coding agents.
2
- // Zero dependencies. Works with Claude Code, Cursor, Windsurf, VS Code, Cline, Roo Code, Codex, Gemini CLI.
3
-
4
- "use strict";
5
-
6
- const path = require("path");
7
- const os = require("os");
8
-
9
- const { detectPlatforms, whichSync, dirExists, fileExists } = require("./lib/detect");
10
- const { readMcpEntry, buildHttpConfig, buildHttpConfigWithAuth, buildStdioConfig, installMcp, installMcpJson, installMcpToml, uninstallMcp, updateMcpKey, parseTomlServerEntry, parseTomlSubTables, buildTomlEntry, removeTomlEntry } = require("./lib/mcp");
11
- const { parseRulesVersion, installRules, uninstallRules, markerPatterns } = require("./lib/rules");
12
- const { getHookCapabilities, installHooks, uninstallHooks, hasHooks, buildHooksConfig } = require("./lib/hooks");
13
- const { createManualPlatform, platformName, KNOWN_PLATFORMS } = require("./lib/platforms");
14
- const cli = require("./lib/cli");
15
-
16
- /**
17
- * Equip — configure AI coding tools with your MCP server and behavioral rules.
18
- *
19
- * @example
20
- * const equip = new Equip({
21
- * name: "prior",
22
- * serverUrl: "https://api.cg3.io/mcp",
23
- * rules: { content: "...", version: "0.5.3", marker: "prior" },
24
- * });
25
- * const platforms = equip.detect();
26
- * await equip.install(platforms, { apiKey: "ask_xxx" });
27
- */
28
- class Equip {
29
- /**
30
- * @param {object} config
31
- * @param {string} config.name - Server name in MCP configs (e.g., "prior")
32
- * @param {string} config.serverUrl - Remote MCP server URL
33
- * @param {object} [config.rules] - Behavioral rules config
34
- * @param {string} config.rules.content - Rules markdown content (with markers)
35
- * @param {string} config.rules.version - Version string for marker tracking
36
- * @param {string} config.rules.marker - Marker name (e.g., "prior")
37
- * @param {string} [config.rules.fileName] - Standalone file name for file-based platforms (e.g., "prior.md")
38
- * @param {string[]} [config.rules.clipboardPlatforms] - Platforms using clipboard (default: ["cursor", "vscode"])
39
- * @param {object} [config.stdio] - Stdio transport config (alternative to HTTP)
40
- * @param {string} config.stdio.command - Command to run
41
- * @param {string[]} config.stdio.args - Command arguments
42
- * @param {string} config.stdio.envKey - Env var name for API key
43
- * @param {Array} [config.hooks] - Lifecycle hook definitions
44
- * @param {string} config.hooks[].event - Hook event name (e.g., "PostToolUseFailure")
45
- * @param {string} [config.hooks[].matcher] - Regex matcher for event filtering (e.g., "Bash")
46
- * @param {string} config.hooks[].script - Hook script content (Node.js)
47
- * @param {string} config.hooks[].name - Script filename (without .js extension)
48
- * @param {string} [config.hookDir] - Directory for hook scripts (default: ~/.${name}/hooks)
49
- */
50
- constructor(config) {
51
- if (!config.name) throw new Error("Equip: name is required");
52
- if (!config.serverUrl && !config.stdio) throw new Error("Equip: serverUrl or stdio is required");
53
-
54
- this.name = config.name;
55
- this.serverUrl = config.serverUrl;
56
- this.rules = config.rules || null;
57
- this.stdio = config.stdio || null;
58
- this.hookDefs = config.hooks || null;
59
- this.hookDir = config.hookDir || path.join(os.homedir(), `.${config.name}`, "hooks");
60
- }
61
-
62
- /**
63
- * Detect installed AI coding platforms.
64
- * @returns {Array<object>} Platform objects
65
- */
66
- detect() {
67
- return detectPlatforms(this.name);
68
- }
69
-
70
- /**
71
- * Build MCP config for a platform.
72
- * @param {string} platformId - Platform id
73
- * @param {string} apiKey - API key
74
- * @param {string} [transport="http"] - "http" or "stdio"
75
- * @returns {object} MCP config object
76
- */
77
- buildConfig(platformId, apiKey, transport = "http") {
78
- if (transport === "stdio" && this.stdio) {
79
- const env = { [this.stdio.envKey]: apiKey };
80
- return buildStdioConfig(this.stdio.command, this.stdio.args, env);
81
- }
82
- return buildHttpConfigWithAuth(this.serverUrl, apiKey, platformId);
83
- }
84
-
85
- /**
86
- * Install MCP config on a platform.
87
- * @param {object} platform - Platform object from detect()
88
- * @param {string} apiKey - API key
89
- * @param {object} [options] - { transport, dryRun }
90
- * @returns {{ success: boolean, method: string }}
91
- */
92
- installMcp(platform, apiKey, options = {}) {
93
- const { transport = "http", dryRun = false } = options;
94
- const config = this.buildConfig(platform.platform, apiKey, transport);
95
- return installMcp(platform, this.name, config, { dryRun, serverUrl: this.serverUrl });
96
- }
97
-
98
- /**
99
- * Uninstall MCP config from a platform.
100
- * @param {object} platform - Platform object
101
- * @param {boolean} [dryRun=false]
102
- * @returns {boolean}
103
- */
104
- uninstallMcp(platform, dryRun = false) {
105
- return uninstallMcp(platform, this.name, dryRun);
106
- }
107
-
108
- /**
109
- * Update API key in MCP config.
110
- * @param {object} platform - Platform object
111
- * @param {string} apiKey - New API key
112
- * @param {string} [transport="http"]
113
- * @returns {{ success: boolean, method: string }}
114
- */
115
- updateMcpKey(platform, apiKey, transport = "http") {
116
- const config = this.buildConfig(platform.platform, apiKey, transport);
117
- return updateMcpKey(platform, this.name, config);
118
- }
119
-
120
- /**
121
- * Install behavioral rules on a platform.
122
- * @param {object} platform - Platform object
123
- * @param {object} [options] - { dryRun }
124
- * @returns {{ action: string }}
125
- */
126
- installRules(platform, options = {}) {
127
- if (!this.rules) return { action: "skipped" };
128
- return installRules(platform, {
129
- content: this.rules.content,
130
- version: this.rules.version,
131
- marker: this.rules.marker,
132
- fileName: this.rules.fileName,
133
- clipboardPlatforms: this.rules.clipboardPlatforms,
134
- dryRun: options.dryRun || false,
135
- copyToClipboard: cli.copyToClipboard,
136
- });
137
- }
138
-
139
- /**
140
- * Uninstall behavioral rules from a platform.
141
- * @param {object} platform - Platform object
142
- * @param {boolean} [dryRun=false]
143
- * @returns {boolean}
144
- */
145
- uninstallRules(platform, dryRun = false) {
146
- if (!this.rules) return false;
147
- return uninstallRules(platform, {
148
- marker: this.rules.marker,
149
- fileName: this.rules.fileName,
150
- dryRun,
151
- });
152
- }
153
-
154
- /**
155
- * Check if MCP is configured on a platform.
156
- * @param {object} platform - Platform object
157
- * @returns {object|null} Existing MCP config or null
158
- */
159
- readMcp(platform) {
160
- return readMcpEntry(platform.configPath, platform.rootKey, this.name, platform.configFormat || "json");
161
- }
162
-
163
- /**
164
- * Install lifecycle hooks on a platform (if supported).
165
- * Uses hook definitions from constructor config.
166
- * @param {object} platform - Platform object from detect()
167
- * @param {object} [options] - { hookDir, dryRun }
168
- * @returns {{ installed: boolean, scripts: string[], hookDir: string } | null}
169
- */
170
- installHooks(platform, options = {}) {
171
- if (!this.hookDefs) return null;
172
- const opts = { ...options };
173
- if (this.hookDir && !opts.hookDir) opts.hookDir = this.hookDir;
174
- return installHooks(platform, this.hookDefs, opts);
175
- }
176
-
177
- /**
178
- * Uninstall hooks from a platform.
179
- * @param {object} platform - Platform object
180
- * @param {object} [options] - { hookDir, dryRun }
181
- * @returns {boolean}
182
- */
183
- uninstallHooks(platform, options = {}) {
184
- if (!this.hookDefs) return false;
185
- const opts = { ...options };
186
- if (this.hookDir && !opts.hookDir) opts.hookDir = this.hookDir;
187
- return uninstallHooks(platform, this.hookDefs, opts);
188
- }
189
-
190
- /**
191
- * Check if hooks are installed on a platform.
192
- * @param {object} platform - Platform object
193
- * @param {object} [options] - { hookDir }
194
- * @returns {boolean}
195
- */
196
- hasHooks(platform, options = {}) {
197
- if (!this.hookDefs) return false;
198
- const opts = { ...options };
199
- if (this.hookDir && !opts.hookDir) opts.hookDir = this.hookDir;
200
- return hasHooks(platform, this.hookDefs, opts);
201
- }
202
-
203
- /**
204
- * Check if a platform supports hooks and this instance has hook definitions.
205
- * @param {object} platform - Platform object
206
- * @returns {boolean}
207
- */
208
- supportsHooks(platform) {
209
- return !!this.hookDefs && this.hookDefs.length > 0 && !!getHookCapabilities(platform.platform);
210
- }
211
- }
212
-
213
- module.exports = {
214
- Equip,
215
- // Re-export primitives for advanced usage
216
- detectPlatforms,
217
- readMcpEntry,
218
- buildHttpConfig,
219
- buildHttpConfigWithAuth,
220
- buildStdioConfig,
221
- installMcp,
222
- installMcpJson,
223
- installMcpToml,
224
- uninstallMcp,
225
- updateMcpKey,
226
- parseTomlServerEntry,
227
- parseTomlSubTables,
228
- buildTomlEntry,
229
- removeTomlEntry,
230
- installRules,
231
- uninstallRules,
232
- parseRulesVersion,
233
- markerPatterns,
234
- createManualPlatform,
235
- platformName,
236
- KNOWN_PLATFORMS,
237
- // Hooks
238
- getHookCapabilities,
239
- installHooks,
240
- uninstallHooks,
241
- hasHooks,
242
- buildHooksConfig,
243
- cli,
244
- };
package/lib/cli.js DELETED
@@ -1,99 +0,0 @@
1
- // CLI output helpers, prompts, and clipboard.
2
- // Zero dependencies.
3
-
4
- "use strict";
5
-
6
- const os = require("os");
7
- const readline = require("readline");
8
-
9
- // ─── Colors ──────────────────────────────────────────────────
10
-
11
- const GREEN = "\x1b[32m";
12
- const RED = "\x1b[31m";
13
- const YELLOW = "\x1b[33m";
14
- const CYAN = "\x1b[36m";
15
- const BOLD = "\x1b[1m";
16
- const DIM = "\x1b[2m";
17
- const RESET = "\x1b[0m";
18
-
19
- // ─── Output ──────────────────────────────────────────────────
20
-
21
- function log(msg = "") { process.stderr.write(msg + "\n"); }
22
- function ok(msg) { log(` ${GREEN}✓${RESET} ${msg}`); }
23
- function fail(msg) { log(` ${RED}✗${RESET} ${msg}`); }
24
- function warn(msg) { log(` ${YELLOW}⚠${RESET} ${msg}`); }
25
- function info(msg) { log(` ${CYAN}ⓘ${RESET} ${msg}`); }
26
- function step(n, total, title) { log(`\n${BOLD}[${n}/${total}] ${title}${RESET}`); }
27
-
28
- // ─── Prompts ─────────────────────────────────────────────────
29
-
30
- function prompt(question) {
31
- return new Promise((resolve) => {
32
- const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
33
- rl.question(question, (answer) => { rl.close(); resolve(answer.trim()); });
34
- });
35
- }
36
-
37
- /**
38
- * Prompt that resolves on Enter (true) or Esc (false).
39
- * Falls back to readline if stdin isn't a TTY.
40
- */
41
- function promptEnterOrEsc(question) {
42
- return new Promise((resolve) => {
43
- process.stderr.write(question);
44
- if (!process.stdin.isTTY) {
45
- const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
46
- rl.question("", (answer) => { rl.close(); resolve(answer.toLowerCase() !== "n"); });
47
- return;
48
- }
49
- process.stdin.setRawMode(true);
50
- process.stdin.resume();
51
- process.stdin.setEncoding("utf-8");
52
- const onData = (key) => {
53
- if (key === "\x1b") {
54
- process.stdin.setRawMode(false); process.stdin.pause(); process.stdin.removeListener("data", onData);
55
- process.stderr.write("\n"); resolve(false);
56
- } else if (key === "\r" || key === "\n") {
57
- process.stdin.setRawMode(false); process.stdin.pause(); process.stdin.removeListener("data", onData);
58
- process.stderr.write("\n"); resolve(true);
59
- } else if (key === "\x03") {
60
- process.stdin.setRawMode(false); process.stdin.pause(); process.stdin.removeListener("data", onData);
61
- process.stderr.write("\n"); process.exit(0);
62
- }
63
- // else: ignore unrecognized keys, keep listening
64
- };
65
- process.stdin.on("data", onData);
66
- });
67
- }
68
-
69
- // ─── Clipboard ───────────────────────────────────────────────
70
-
71
- function copyToClipboard(text) {
72
- try {
73
- const cp = require("child_process");
74
- if (process.platform === "darwin") {
75
- cp.execSync("pbcopy", { input: text, timeout: 3000 });
76
- } else if (process.platform === "win32") {
77
- cp.execSync("clip", { input: text, timeout: 3000 });
78
- } else {
79
- try { cp.execSync("xclip -selection clipboard", { input: text, timeout: 3000 }); }
80
- catch { try { cp.execSync("xsel --clipboard --input", { input: text, timeout: 3000 }); }
81
- catch { cp.execSync("wl-copy", { input: text, timeout: 3000 }); } }
82
- }
83
- return true;
84
- } catch { return false; }
85
- }
86
-
87
- // ─── Utilities ───────────────────────────────────────────────
88
-
89
- function sanitizeError(msg) {
90
- return msg.replace(os.homedir(), "~");
91
- }
92
-
93
- module.exports = {
94
- GREEN, RED, YELLOW, CYAN, BOLD, DIM, RESET,
95
- log, ok, fail, warn, info, step,
96
- prompt, promptEnterOrEsc,
97
- copyToClipboard,
98
- sanitizeError,
99
- };