100xprompt-cli 0.1.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,35 @@
1
+ 100xprompt
2
+ Copyright (c) 2025 100xprompt
3
+
4
+ All rights reserved, except as required by the upstream licence reproduced
5
+ below. 100xprompt is a derivative work of opencode. MIT permits sublicensing
6
+ under different terms provided the original copyright and permission notice are
7
+ retained — that is the purpose of the second section of this file. See NOTICE
8
+ for the full list of third-party components bundled into the binary.
9
+
10
+
11
+ ================================================================================
12
+ UPSTREAM — opencode (https://github.com/sst/opencode)
13
+ ================================================================================
14
+
15
+ MIT License
16
+
17
+ Copyright (c) 2025 opencode
18
+
19
+ Permission is hereby granted, free of charge, to any person obtaining a copy
20
+ of this software and associated documentation files (the "Software"), to deal
21
+ in the Software without restriction, including without limitation the rights
22
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23
+ copies of the Software, and to permit persons to whom the Software is
24
+ furnished to do so, subject to the following conditions:
25
+
26
+ The above copyright notice and this permission notice shall be included in all
27
+ copies or substantial portions of the Software.
28
+
29
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35
+ SOFTWARE.
package/NOTICE ADDED
@@ -0,0 +1,68 @@
1
+ NOTICE — third-party components distributed with 100xprompt
2
+ ================================================================================
3
+
4
+ This file lists third-party material bundled into the 100xprompt binary or the
5
+ published npm packages. It is a companion to LICENSE, which carries the upstream
6
+ copyright notice for the opencode-derived portions of this software.
7
+
8
+ Where a licence is stated as "not verified", the entry is recorded from general
9
+ knowledge rather than from the upstream project's own licence file, and should
10
+ be confirmed before anyone relies on it. Recording an unverified attribution is
11
+ better than recording none; it is not a substitute for checking.
12
+
13
+
14
+ 1. DERIVED FROM
15
+ --------------------------------------------------------------------------------
16
+
17
+ opencode — https://github.com/sst/opencode — MIT
18
+ 100xprompt is a derivative work of opencode. Its copyright and permission
19
+ notice are reproduced in full in LICENSE.
20
+
21
+
22
+ 2. TREE-SITTER GRAMMARS (compiled into the binary as build assets)
23
+ --------------------------------------------------------------------------------
24
+
25
+ The syntax highlighter embeds compiled tree-sitter grammars and their query
26
+ files. Each comes from a separate upstream project with its own licence:
27
+
28
+ bash, go, javascript, json, python, rust, tsx, typescript
29
+ tree-sitter grammar projects (github.com/tree-sitter/*)
30
+ markdown, markdown_inline
31
+ github.com/tree-sitter-grammars/tree-sitter-markdown
32
+
33
+ The `.scm` query files vendored alongside each grammar originate from those same
34
+ projects, except the markdown highlight queries, which are 100xprompt's own —
35
+ see packages/tui/scripts/queries/. The runtime is web-tree-sitter (MIT).
36
+
37
+ Regenerate with: bun run --cwd packages/tui vendor-grammars
38
+
39
+
40
+ 3. TERMINAL COLOUR SCHEMES
41
+ --------------------------------------------------------------------------------
42
+
43
+ The bundled themes are ports of third-party colour schemes. The palettes are the
44
+ work of their original authors; only the mapping to our theme format is ours:
45
+
46
+ aura, ayu, catppuccin (frappe/macchiato), cobalt2, cursor, dracula,
47
+ everforest, flexoki, github, gruvbox, kanagawa, material, monokai,
48
+ nightowl, nord, one-dark, osaka-jade, palenight, rosepine, solarized,
49
+ synthwave84, tokyonight, vercel, vesper, zenburn
50
+
51
+ 100xprompt, lucent-orng, matrix, mercury, orng — original to this project.
52
+
53
+ Two need attention before redistribution and are flagged here rather than
54
+ quietly shipped:
55
+
56
+ tokyonight Apache-2.0, NOT MIT. Apache-2.0 carries NOTICE-file and
57
+ change-statement obligations that a blanket permissive
58
+ assumption does not satisfy.
59
+ monokai The original TextMate theme was never clearly licensed and the
60
+ name is claimed as a trademark.
61
+
62
+
63
+ 4. RUNTIME DEPENDENCIES
64
+ --------------------------------------------------------------------------------
65
+
66
+ Ordinary npm dependencies (react, react-reconciler, web-tree-sitter and others)
67
+ are not vendored. Their licences travel with their own packages in node_modules
68
+ and are not reproduced here.
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+ const { spawn } = require('child_process')
3
+ const path = require('path')
4
+ const fs = require('fs')
5
+
6
+ function getPlatformPackageName() {
7
+ const platform = process.platform
8
+ const arch = process.arch
9
+ const isMusl = process.report?.getReport?.()?.header?.glibcVersionRuntime === undefined
10
+
11
+ let osName
12
+ if (platform === 'darwin') osName = 'darwin'
13
+ else if (platform === 'linux') osName = 'linux'
14
+ else if (platform === 'win32') osName = 'windows'
15
+
16
+ let pkgName = `@100xprompt/cli-${osName}-${arch}`
17
+ if (platform === 'linux' && isMusl) {
18
+ pkgName = `@100xprompt/cli-linux-${arch}-musl`
19
+ }
20
+
21
+ return pkgName
22
+ }
23
+
24
+ function findBinary() {
25
+ const pkgName = getPlatformPackageName()
26
+ const possiblePaths = [
27
+ path.join(__dirname, '..', '..', pkgName, 'bin', '100xprompt'),
28
+ path.join(__dirname, '..', '..', pkgName, 'bin', '100xprompt.exe'),
29
+ path.join(__dirname, '..', '..', 'node_modules', pkgName, 'bin', '100xprompt'),
30
+ path.join(__dirname, '..', '..', 'node_modules', pkgName, 'bin', '100xprompt.exe'),
31
+ ]
32
+
33
+ for (const p of possiblePaths) {
34
+ if (fs.existsSync(p)) return p
35
+ }
36
+
37
+ try {
38
+ const pkgPath = require.resolve(pkgName + '/package.json')
39
+ const binPath = path.join(path.dirname(pkgPath), 'bin', '100xprompt')
40
+ if (fs.existsSync(binPath)) return binPath
41
+ if (fs.existsSync(binPath + '.exe')) return binPath + '.exe'
42
+ } catch {}
43
+
44
+ return null
45
+ }
46
+
47
+ const binaryPath = findBinary()
48
+ if (!binaryPath) {
49
+ console.error(`Could not find binary for platform: ${getPlatformPackageName()}`)
50
+ process.exit(1)
51
+ }
52
+
53
+ const child = spawn(binaryPath, process.argv.slice(2), {
54
+ stdio: 'inherit',
55
+ env: process.env
56
+ })
57
+
58
+ child.on('error', (err) => {
59
+ console.error('[100XPrompt] Failed to start:', err.message)
60
+ process.exit(1)
61
+ })
62
+
63
+ child.on('exit', (code, signal) => {
64
+ if (signal) {
65
+ console.error(`[100XPrompt] Process was killed with signal: ${signal}`)
66
+ process.exit(1)
67
+ }
68
+ process.exit(code || 0)
69
+ })
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "100xprompt-cli",
3
+ "version": "0.1.72",
4
+ "description": "Multi-model terminal coding agent. Route work across Claude, GLM, Gemini, DeepSeek and local models from one harness.",
5
+ "license": "UNLICENSED",
6
+ "author": "100XPrompt",
7
+ "homepage": "https://100xprompt.com",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/nipurn123/100xprompt.git",
11
+ "directory": "packages/100xprompt"
12
+ },
13
+ "keywords": [
14
+ "ai",
15
+ "agent",
16
+ "cli",
17
+ "coding-agent",
18
+ "llm",
19
+ "tui",
20
+ "100xprompt"
21
+ ],
22
+ "bin": {
23
+ "100xprompt": "bin/100xprompt.js"
24
+ },
25
+ "scripts": {
26
+ "postinstall": "bun ./postinstall.mjs || node ./postinstall.mjs"
27
+ },
28
+ "optionalDependencies": {
29
+ "@100xprompt/cli-linux-arm64": "0.1.72",
30
+ "@100xprompt/cli-linux-x64": "0.1.72",
31
+ "@100xprompt/cli-linux-x64-baseline": "0.1.72",
32
+ "@100xprompt/cli-linux-arm64-musl": "0.1.72",
33
+ "@100xprompt/cli-linux-x64-musl": "0.1.72",
34
+ "@100xprompt/cli-linux-x64-baseline-musl": "0.1.72",
35
+ "@100xprompt/cli-darwin-arm64": "0.1.72",
36
+ "@100xprompt/cli-darwin-x64": "0.1.72",
37
+ "@100xprompt/cli-darwin-x64-baseline": "0.1.72",
38
+ "@100xprompt/cli-windows-x64": "0.1.72",
39
+ "@100xprompt/cli-windows-x64-baseline": "0.1.72"
40
+ }
41
+ }
@@ -0,0 +1,372 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "fs"
4
+ import path from "path"
5
+ import os from "os"
6
+ import { execFileSync } from "child_process"
7
+ import { fileURLToPath } from "url"
8
+ import { createRequire } from "module"
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
11
+ const require = createRequire(import.meta.url)
12
+
13
+ function detectPlatformAndArch() {
14
+ // Map platform names
15
+ let platform
16
+ switch (os.platform()) {
17
+ case "darwin":
18
+ platform = "darwin"
19
+ break
20
+ case "linux":
21
+ platform = "linux"
22
+ break
23
+ case "win32":
24
+ platform = "windows"
25
+ break
26
+ default:
27
+ platform = os.platform()
28
+ break
29
+ }
30
+
31
+ // Map architecture names
32
+ let arch
33
+ switch (os.arch()) {
34
+ case "x64":
35
+ arch = "x64"
36
+ break
37
+ case "arm64":
38
+ arch = "arm64"
39
+ break
40
+ case "arm":
41
+ arch = "arm"
42
+ break
43
+ default:
44
+ arch = os.arch()
45
+ break
46
+ }
47
+
48
+ // glibc vs musl matters for the Linux build variant. When the runtime report
49
+ // exposes no glibc version, we're on a musl libc (Alpine, etc).
50
+ let isMusl = false
51
+ if (platform === "linux") {
52
+ try {
53
+ isMusl = process.report?.getReport?.()?.header?.glibcVersionRuntime === undefined
54
+ } catch {
55
+ isMusl = false
56
+ }
57
+ }
58
+
59
+ return { platform, arch, isMusl }
60
+ }
61
+
62
+ // Candidate platform-package names, in preference order. Mirrors the runtime
63
+ // wrapper (bin/100xprompt.js) and also probes the `-baseline` variant as a
64
+ // fallback for older CPUs without AVX2.
65
+ function candidatePackageNames() {
66
+ const { platform, arch, isMusl } = detectPlatformAndArch()
67
+ const base = `@100xprompt/cli-${platform}-${arch}`
68
+ const names = []
69
+ if (platform === "linux" && isMusl) {
70
+ names.push(`${base}-musl`, `${base}-baseline-musl`)
71
+ }
72
+ names.push(base, `${base}-baseline`)
73
+ return names
74
+ }
75
+
76
+ function findBinary() {
77
+ const { platform } = detectPlatformAndArch()
78
+ const binaryName = platform === "windows" ? "100xprompt.exe" : "100xprompt"
79
+
80
+ for (const packageName of candidatePackageNames()) {
81
+ // Probe common install layouts (sibling package, hoisted node_modules,
82
+ // and require.resolve) so we work under npm/bun/pnpm/yarn and global installs.
83
+ const candidates = []
84
+ try {
85
+ const packageJsonPath = require.resolve(`${packageName}/package.json`)
86
+ candidates.push(path.join(path.dirname(packageJsonPath), "bin", binaryName))
87
+ } catch {
88
+ /* not resolvable from here — fall through to path probing */
89
+ }
90
+ candidates.push(
91
+ path.join(__dirname, "..", packageName, "bin", binaryName),
92
+ path.join(__dirname, "node_modules", packageName, "bin", binaryName),
93
+ path.join(__dirname, "..", "node_modules", packageName, "bin", binaryName),
94
+ path.join(__dirname, "..", "..", packageName, "bin", binaryName),
95
+ path.join(__dirname, "..", "..", "node_modules", packageName, "bin", binaryName),
96
+ )
97
+
98
+ for (const binaryPath of candidates) {
99
+ if (fs.existsSync(binaryPath)) {
100
+ return { binaryPath, binaryName }
101
+ }
102
+ }
103
+ }
104
+
105
+ throw new Error(`Could not find platform binary (tried: ${candidatePackageNames().join(", ")})`)
106
+ }
107
+
108
+ // The bun-compiled binary must be executable for the wrapper's spawn() to run it
109
+ // without the user ever needing sudo. npm/tar can drop the +x bit on some
110
+ // installs, so re-assert it here. Best-effort: never throw.
111
+ function ensureExecutable(binaryPath) {
112
+ try {
113
+ fs.chmodSync(binaryPath, 0o755)
114
+ } catch (err) {
115
+ console.warn(`Could not set executable bit on ${binaryPath}: ${err.message}`)
116
+ }
117
+ }
118
+
119
+ // macOS kills unsigned/foreign-signed binaries with SIGKILL. Ad-hoc re-sign the
120
+ // binary in place so it runs without Gatekeeper intervention (and without sudo).
121
+ // Best-effort: codesign may be absent or the file read-only — never fail install.
122
+ function codesignDarwin(binaryPath) {
123
+ if (os.platform() !== "darwin") return
124
+ try {
125
+ execFileSync("codesign", ["--remove-signature", binaryPath], { stdio: "ignore" })
126
+ } catch {
127
+ /* nothing to remove — fine */
128
+ }
129
+ try {
130
+ execFileSync(
131
+ "codesign",
132
+ ["--sign", "-", "--force", "--preserve-metadata=entitlements,requirements,flags,runtime", binaryPath],
133
+ { stdio: "ignore" },
134
+ )
135
+ } catch (err) {
136
+ console.warn(`Could not ad-hoc sign ${binaryPath}: ${err.message}`)
137
+ }
138
+ }
139
+
140
+ function prepareBinDirectory(binaryName) {
141
+ const binDir = path.join(__dirname, "bin")
142
+ const targetPath = path.join(binDir, binaryName)
143
+
144
+ // Ensure bin directory exists
145
+ if (!fs.existsSync(binDir)) {
146
+ fs.mkdirSync(binDir, { recursive: true })
147
+ }
148
+
149
+ // Remove existing binary/symlink if it exists
150
+ if (fs.existsSync(targetPath) || fs.lstatSync(targetPath, { throwIfNoEntry: false })) {
151
+ try {
152
+ fs.unlinkSync(targetPath)
153
+ } catch {
154
+ /* ignore — overwritten below */
155
+ }
156
+ }
157
+
158
+ return { binDir, targetPath }
159
+ }
160
+
161
+ // Make the platform binary reachable at <pkg>/bin/<name>. Prefer a symlink; if
162
+ // the filesystem/permissions reject symlinks (Windows without dev mode, locked
163
+ // dirs), copy instead. The runtime wrapper resolves the binary on its own, so a
164
+ // failure here is non-fatal and must never break the npm install.
165
+ function linkBinary(sourcePath, binaryName) {
166
+ let targetPath
167
+ try {
168
+ ;({ targetPath } = prepareBinDirectory(binaryName))
169
+ } catch (err) {
170
+ console.warn(`Could not prepare bin directory: ${err.message}`)
171
+ return
172
+ }
173
+
174
+ try {
175
+ fs.symlinkSync(sourcePath, targetPath)
176
+ console.log(`100xprompt binary symlinked: ${targetPath} -> ${sourcePath}`)
177
+ } catch (symlinkErr) {
178
+ try {
179
+ fs.copyFileSync(sourcePath, targetPath)
180
+ ensureExecutable(targetPath)
181
+ console.log(`100xprompt binary copied: ${targetPath}`)
182
+ } catch (copyErr) {
183
+ // Wrapper still resolves the binary directly; just warn.
184
+ console.warn(`Could not link binary (symlink: ${symlinkErr.message}; copy: ${copyErr.message})`)
185
+ }
186
+ }
187
+ }
188
+
189
+ function createConfig() {
190
+ const home = os.homedir()
191
+ // Primary: XDG config path (what the app reads via xdg-basedir)
192
+ const xdgConfigHome = process.env.XDG_CONFIG_HOME || path.join(home, ".config")
193
+ const configDir = path.join(xdgConfigHome, "100xprompt")
194
+ const configPath = path.join(configDir, "100xprompt.json")
195
+ // Legacy fallback: also check ~/.100xprompt
196
+ const legacyConfigDir = path.join(home, ".100xprompt")
197
+ const legacyConfigPath = path.join(legacyConfigDir, "100xprompt.json")
198
+
199
+ const defaultConfig = {
200
+ $schema: "https://proxy.100xprompt.com/config.json",
201
+ compaction: {
202
+ auto: {
203
+ enabled: true,
204
+ // Base auto-compaction threshold. The code uses model-aware thresholds:
205
+ // Pro ~160k, Flash ~500k; this value is the fallback for other models.
206
+ compactionThreshold: 160000,
207
+ tailPreserveTurns: 5,
208
+ },
209
+ micro: {
210
+ enabled: true,
211
+ hotTailCount: 5,
212
+ },
213
+ },
214
+ }
215
+
216
+ try {
217
+ // Determine which config path to use:
218
+ // If legacy path exists but XDG path doesn't, use legacy for validation/merge
219
+ // Otherwise prefer XDG path (the app reads from both)
220
+ let activeConfigDir = configDir
221
+ let activeConfigPath = configPath
222
+ if (fs.existsSync(legacyConfigPath) && !fs.existsSync(configPath)) {
223
+ activeConfigDir = legacyConfigDir
224
+ activeConfigPath = legacyConfigPath
225
+ }
226
+
227
+ if (!fs.existsSync(activeConfigDir)) {
228
+ fs.mkdirSync(activeConfigDir, { recursive: true })
229
+ }
230
+
231
+ if (fs.existsSync(activeConfigPath)) {
232
+ // Validate existing config and merge missing defaults
233
+ try {
234
+ const raw = fs.readFileSync(activeConfigPath, "utf-8")
235
+ const existing = JSON.parse(raw)
236
+
237
+ let needsUpdate = false
238
+
239
+ // Ensure compaction section exists with all required fields
240
+ if (!existing.compaction) {
241
+ existing.compaction = defaultConfig.compaction
242
+ needsUpdate = true
243
+ } else {
244
+ if (!existing.compaction.auto) {
245
+ existing.compaction.auto = defaultConfig.compaction.auto
246
+ needsUpdate = true
247
+ } else {
248
+ // Merge individual auto fields if missing
249
+ for (const [key, value] of Object.entries(defaultConfig.compaction.auto)) {
250
+ if (existing.compaction.auto[key] === undefined) {
251
+ existing.compaction.auto[key] = value
252
+ needsUpdate = true
253
+ }
254
+ }
255
+ }
256
+ if (!existing.compaction.micro) {
257
+ existing.compaction.micro = defaultConfig.compaction.micro
258
+ needsUpdate = true
259
+ }
260
+ }
261
+
262
+ if (!existing.$schema) {
263
+ existing.$schema = defaultConfig.$schema
264
+ needsUpdate = true
265
+ }
266
+
267
+ if (needsUpdate) {
268
+ fs.writeFileSync(activeConfigPath, JSON.stringify(existing, null, 2))
269
+ try {
270
+ fs.chmodSync(activeConfigPath, 0o644)
271
+ } catch {}
272
+ console.log(`Config updated with missing defaults at ${activeConfigPath}`)
273
+ } else {
274
+ console.log(`Config already exists and is valid at ${activeConfigPath}, skipping.`)
275
+ }
276
+ } catch (parseError) {
277
+ // Existing file is corrupt — back it up and recreate
278
+ const backupPath = activeConfigPath + ".bak"
279
+ console.warn(`Config at ${activeConfigPath} is corrupt, backing up to ${backupPath} and recreating.`)
280
+ try {
281
+ fs.copyFileSync(activeConfigPath, backupPath)
282
+ } catch {}
283
+ fs.writeFileSync(activeConfigPath, JSON.stringify(defaultConfig, null, 2))
284
+ try {
285
+ fs.chmodSync(activeConfigPath, 0o644)
286
+ } catch {}
287
+ console.log(`Default config recreated at ${activeConfigPath}`)
288
+ }
289
+ return
290
+ }
291
+
292
+ fs.writeFileSync(activeConfigPath, JSON.stringify(defaultConfig, null, 2))
293
+ try {
294
+ fs.chmodSync(activeConfigPath, 0o644)
295
+ } catch {}
296
+ console.log(`Default config created at ${activeConfigPath}`)
297
+ } catch (error) {
298
+ console.warn(`Failed to create default config: ${error.message}`)
299
+ // Don't fail postinstall just because config creation failed
300
+ }
301
+ }
302
+
303
+ function buildBanner(color) {
304
+ const p = (code, s) => (color ? `\x1b[${code}m${s}\x1b[0m` : s)
305
+ const bold = (s) => p("1", s)
306
+ const dim = (s) => p("2", s)
307
+ const accent = (s) => p("38;5;215", s) // soft orange
308
+ const violet = (s) => p("38;5;141", s)
309
+ const cyan = (s) => p("38;5;117", s)
310
+ const green = (s) => p("38;5;114", s)
311
+ const pink = (s) => p("38;5;213", s)
312
+
313
+ const rule = violet(" " + "━".repeat(48))
314
+ const cmd = color ? `\x1b[1m\x1b[30m\x1b[48;5;215m 100xprompt \x1b[0m` : "100xprompt"
315
+
316
+ return [
317
+ "",
318
+ rule,
319
+ "",
320
+ ` ${accent("✦")} ${bold("100XPrompt Pro")} ${dim("· AI coding assistant in your terminal")}`,
321
+ "",
322
+ ` ${green("▸")} Run ${cmd} ${dim("to launch")}`,
323
+ ` ${dim(" then type your first prompt and hit enter")}`,
324
+ "",
325
+ ` ${dim("made with")} ${pink("♥")} ${dim("by the")} ${cyan("100xprompt")} ${dim("team")}`,
326
+ "",
327
+ rule,
328
+ "",
329
+ "",
330
+ ].join("\n")
331
+ }
332
+
333
+ function printBanner() {
334
+ // npm hides postinstall stdout by default, so write straight to the controlling
335
+ // terminal (/dev/tty) — that bypasses npm's capture and shows the banner on install.
336
+ try {
337
+ fs.writeFileSync("/dev/tty", buildBanner(true))
338
+ } catch {
339
+ process.stdout.write(buildBanner(!!process.stdout.isTTY))
340
+ }
341
+ }
342
+
343
+ async function main() {
344
+ // Binary setup is best-effort: the runtime wrapper (bin/100xprompt.js) resolves
345
+ // and launches the platform binary on its own, so postinstall must NEVER fail
346
+ // the npm install — a non-zero exit is exactly what pushes users to retry with
347
+ // sudo. We only normalize permissions/signing so the binary runs unprivileged.
348
+ try {
349
+ const { binaryPath, binaryName } = findBinary()
350
+ ensureExecutable(binaryPath) // fixes "permission denied" → no sudo needed to run
351
+ codesignDarwin(binaryPath) // prevents macOS SIGKILL of the unsigned binary
352
+ linkBinary(binaryPath, binaryName)
353
+ } catch (error) {
354
+ // Don't fail — the wrapper still finds the binary at runtime.
355
+ console.warn(`100xprompt binary setup skipped: ${error.message}`)
356
+ }
357
+
358
+ try {
359
+ createConfig()
360
+ } catch (error) {
361
+ console.warn(`Config setup skipped: ${error.message}`)
362
+ }
363
+
364
+ printBanner()
365
+ }
366
+
367
+ main().catch((error) => {
368
+ // Always exit 0: a failed postinstall blocks the whole install and is the
369
+ // reason users reach for sudo. Never let that happen.
370
+ console.warn("Postinstall completed with warnings:", error?.message ?? error)
371
+ process.exit(0)
372
+ })