100xprism 2.3.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/LICENSE +21 -0
- package/README.md +196 -0
- package/VERSION +1 -0
- package/adapters/antigravity.sh +14 -0
- package/adapters/claude-code.sh +160 -0
- package/adapters/codex.sh +13 -0
- package/adapters/copilot.sh +13 -0
- package/adapters/cursor.sh +13 -0
- package/adapters/gemini.sh +13 -0
- package/adapters/lib/__pycache__/modules.cpython-312.pyc +0 -0
- package/adapters/lib/modules.py +592 -0
- package/adapters/lib/shared.sh +83 -0
- package/adapters/lib/sync_plugins.py +113 -0
- package/adapters/windsurf.sh +15 -0
- package/bin/100xprism.js +29 -0
- package/get.sh +24 -0
- package/install-project.sh +82 -0
- package/install.sh +281 -0
- package/lib/adapters/windows.js +429 -0
- package/lib/bootstrap.js +33 -0
- package/lib/init.js +19 -0
- package/lib/install.js +18 -0
- package/lib/migrate.js +52 -0
- package/lib/platform.js +22 -0
- package/lib/update.js +29 -0
- package/modules/_lib/reference.md +77 -0
- package/modules/a11y-auditor/SKILL.md +151 -0
- package/modules/ab-test-setup/SKILL.md +266 -0
- package/modules/ab-test-setup/evals/evals.json +105 -0
- package/modules/ab-test-setup/references/sample-size-guide.md +263 -0
- package/modules/ab-test-setup/references/test-templates.md +277 -0
- package/modules/ad-creative/SKILL.md +362 -0
- package/modules/ad-creative/evals/evals.json +90 -0
- package/modules/ad-creative/references/generative-tools.md +637 -0
- package/modules/ad-creative/references/platform-specs.md +213 -0
- package/modules/ai-seo/SKILL.md +398 -0
- package/modules/ai-seo/evals/evals.json +90 -0
- package/modules/ai-seo/references/content-patterns.md +285 -0
- package/modules/ai-seo/references/platform-ranking-factors.md +152 -0
- package/modules/analytics-tracking/SKILL.md +309 -0
- package/modules/analytics-tracking/evals/evals.json +90 -0
- package/modules/analytics-tracking/references/event-library.md +260 -0
- package/modules/analytics-tracking/references/ga4-implementation.md +300 -0
- package/modules/analytics-tracking/references/gtm-implementation.md +390 -0
- package/modules/architect/SKILL.md +282 -0
- package/modules/branch/SKILL.md +105 -0
- package/modules/churn-prevention/SKILL.md +424 -0
- package/modules/churn-prevention/evals/evals.json +93 -0
- package/modules/churn-prevention/references/cancel-flow-patterns.md +316 -0
- package/modules/churn-prevention/references/dunning-playbook.md +408 -0
- package/modules/cloud-security/SKILL.md +240 -0
- package/modules/cold-email/SKILL.md +178 -0
- package/modules/cold-email/evals/evals.json +94 -0
- package/modules/cold-email/references/benchmarks.md +83 -0
- package/modules/cold-email/references/follow-up-sequences.md +81 -0
- package/modules/cold-email/references/frameworks.md +90 -0
- package/modules/cold-email/references/personalization.md +79 -0
- package/modules/cold-email/references/subject-lines.md +53 -0
- package/modules/commit/SKILL.md +195 -0
- package/modules/competitor-alternatives/SKILL.md +256 -0
- package/modules/competitor-alternatives/evals/evals.json +93 -0
- package/modules/competitor-alternatives/references/content-architecture.md +271 -0
- package/modules/competitor-alternatives/references/templates.md +223 -0
- package/modules/connect/SKILL.md +894 -0
- package/modules/content-strategy/SKILL.md +359 -0
- package/modules/content-strategy/evals/evals.json +90 -0
- package/modules/context-dump/SKILL.md +67 -0
- package/modules/copy-editing/SKILL.md +447 -0
- package/modules/copy-editing/evals/evals.json +89 -0
- package/modules/copy-editing/references/plain-english-alternatives.md +394 -0
- package/modules/copywriting/SKILL.md +271 -0
- package/modules/copywriting/evals/evals.json +111 -0
- package/modules/copywriting/references/cold-email-benchmarks.md +83 -0
- package/modules/copywriting/references/cold-email-follow-ups.md +81 -0
- package/modules/copywriting/references/cold-email-frameworks.md +90 -0
- package/modules/copywriting/references/cold-email-personalization.md +79 -0
- package/modules/copywriting/references/cold-email-subject-lines.md +53 -0
- package/modules/copywriting/references/copy-frameworks.md +344 -0
- package/modules/copywriting/references/email-copy-guidelines.md +113 -0
- package/modules/copywriting/references/email-types.md +515 -0
- package/modules/copywriting/references/natural-transitions.md +272 -0
- package/modules/copywriting/references/sequence-templates.md +168 -0
- package/modules/data-query/SKILL.md +58 -0
- package/modules/data-viz/SKILL.md +225 -0
- package/modules/db/SKILL.md +205 -0
- package/modules/db/db-engines/_router.md +24 -0
- package/modules/db/db-engines/athena.md +16 -0
- package/modules/db/db-engines/cloud-sql.md +16 -0
- package/modules/db/db-engines/databricks.md +14 -0
- package/modules/db/db-engines/oracle.md +14 -0
- package/modules/db/db-engines/postgres.md +15 -0
- package/modules/db/db-engines/presto.md +14 -0
- package/modules/db/db-engines/snowflake.md +14 -0
- package/modules/docs/SKILL.md +100 -0
- package/modules/email-sequence/SKILL.md +309 -0
- package/modules/email-sequence/evals/evals.json +93 -0
- package/modules/email-sequence/references/copy-guidelines.md +113 -0
- package/modules/email-sequence/references/email-types.md +515 -0
- package/modules/email-sequence/references/sequence-templates.md +168 -0
- package/modules/enterprise-design/SKILL.md +75 -0
- package/modules/eval/SKILL.md +105 -0
- package/modules/figma-translator/SKILL.md +49 -0
- package/modules/fix-bugs/SKILL.md +104 -0
- package/modules/form-cro/SKILL.md +429 -0
- package/modules/form-cro/evals/evals.json +90 -0
- package/modules/free-tool-strategy/SKILL.md +178 -0
- package/modules/free-tool-strategy/evals/evals.json +90 -0
- package/modules/free-tool-strategy/references/tool-types.md +217 -0
- package/modules/gate/SKILL.md +232 -0
- package/modules/grill-me/SKILL.md +59 -0
- package/modules/interaction-engineer/SKILL.md +49 -0
- package/modules/issue/SKILL.md +272 -0
- package/modules/launch/SKILL.md +345 -0
- package/modules/launch-strategy/SKILL.md +353 -0
- package/modules/launch-strategy/evals/evals.json +91 -0
- package/modules/lint/SKILL.md +126 -0
- package/modules/marketing-ideas/SKILL.md +167 -0
- package/modules/marketing-ideas/evals/evals.json +90 -0
- package/modules/marketing-ideas/references/ideas-by-category.md +366 -0
- package/modules/marketing-psychology/SKILL.md +455 -0
- package/modules/marketing-psychology/evals/evals.json +88 -0
- package/modules/motion-designer/SKILL.md +214 -0
- package/modules/onboarding-cro/SKILL.md +220 -0
- package/modules/onboarding-cro/evals/evals.json +92 -0
- package/modules/onboarding-cro/references/experiments.md +258 -0
- package/modules/orchestrate/SKILL.md +77 -0
- package/modules/page-cro/SKILL.md +182 -0
- package/modules/page-cro/evals/evals.json +111 -0
- package/modules/page-cro/references/experiments.md +248 -0
- package/modules/page-cro/references/paywall-experiments.md +164 -0
- package/modules/paid-ads/SKILL.md +315 -0
- package/modules/paid-ads/evals/evals.json +90 -0
- package/modules/paid-ads/references/ad-copy-templates.md +207 -0
- package/modules/paid-ads/references/audience-targeting.md +243 -0
- package/modules/paid-ads/references/platform-setup-checklists.md +277 -0
- package/modules/paywall-upgrade-cro/SKILL.md +227 -0
- package/modules/paywall-upgrade-cro/evals/evals.json +93 -0
- package/modules/paywall-upgrade-cro/references/experiments.md +164 -0
- package/modules/popup-cro/SKILL.md +453 -0
- package/modules/popup-cro/evals/evals.json +94 -0
- package/modules/pr/SKILL.md +203 -0
- package/modules/pricing-strategy/SKILL.md +231 -0
- package/modules/pricing-strategy/evals/evals.json +90 -0
- package/modules/pricing-strategy/references/research-methods.md +152 -0
- package/modules/pricing-strategy/references/tier-structure.md +232 -0
- package/modules/product-marketing-context/SKILL.md +241 -0
- package/modules/product-marketing-context/evals/evals.json +85 -0
- package/modules/programmatic-seo/SKILL.md +238 -0
- package/modules/programmatic-seo/evals/evals.json +94 -0
- package/modules/programmatic-seo/references/playbooks.md +308 -0
- package/modules/push/SKILL.md +202 -0
- package/modules/referral-program/SKILL.md +255 -0
- package/modules/referral-program/evals/evals.json +89 -0
- package/modules/referral-program/references/affiliate-programs.md +164 -0
- package/modules/referral-program/references/program-examples.md +143 -0
- package/modules/release/SKILL.md +293 -0
- package/modules/revops/SKILL.md +343 -0
- package/modules/revops/evals/evals.json +91 -0
- package/modules/revops/references/automation-playbooks.md +290 -0
- package/modules/revops/references/lifecycle-definitions.md +278 -0
- package/modules/revops/references/routing-rules.md +203 -0
- package/modules/revops/references/scoring-models.md +247 -0
- package/modules/sales-enablement/SKILL.md +349 -0
- package/modules/sales-enablement/evals/evals.json +91 -0
- package/modules/sales-enablement/references/deck-frameworks.md +263 -0
- package/modules/sales-enablement/references/demo-scripts.md +355 -0
- package/modules/sales-enablement/references/objection-library.md +270 -0
- package/modules/sales-enablement/references/one-pager-templates.md +208 -0
- package/modules/schema-markup/SKILL.md +179 -0
- package/modules/schema-markup/evals/evals.json +87 -0
- package/modules/schema-markup/references/schema-examples.md +398 -0
- package/modules/security/SKILL.md +138 -0
- package/modules/seo-audit/SKILL.md +412 -0
- package/modules/seo-audit/evals/evals.json +136 -0
- package/modules/seo-audit/references/ai-writing-detection.md +200 -0
- package/modules/seo-audit/references/content-patterns.md +285 -0
- package/modules/seo-audit/references/platform-ranking-factors.md +152 -0
- package/modules/signup-flow-cro/SKILL.md +359 -0
- package/modules/signup-flow-cro/evals/evals.json +88 -0
- package/modules/site-architecture/SKILL.md +357 -0
- package/modules/site-architecture/evals/evals.json +88 -0
- package/modules/site-architecture/references/mermaid-templates.md +216 -0
- package/modules/site-architecture/references/navigation-patterns.md +305 -0
- package/modules/site-architecture/references/site-type-templates.md +293 -0
- package/modules/social-content/SKILL.md +278 -0
- package/modules/social-content/evals/evals.json +92 -0
- package/modules/social-content/references/platforms.md +170 -0
- package/modules/social-content/references/post-templates.md +177 -0
- package/modules/social-content/references/reverse-engineering.md +195 -0
- package/modules/spec/SKILL.md +81 -0
- package/modules/subagents/SKILL.md +123 -0
- package/modules/techdebt/SKILL.md +71 -0
- package/modules/terminal-setup/SKILL.md +49 -0
- package/modules/test/SKILL.md +493 -0
- package/modules/test/references/e2e-patterns.md +294 -0
- package/modules/update-claude-md/SKILL.md +52 -0
- package/modules/visual-system-architect/SKILL.md +53 -0
- package/package.json +44 -0
- package/plugins/plugins.json +43 -0
- package/shell/aliases.sh +24 -0
- package/shell/check-update.sh +212 -0
- package/templates/.env.example +199 -0
- package/templates/docker-compose.md +46 -0
- package/templates/node-frontend.md +56 -0
- package/templates/node-fullstack.md +59 -0
- package/templates/python-api.md +57 -0
- package/update.sh +231 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const { spawnSync } = require('child_process')
|
|
6
|
+
|
|
7
|
+
// ── v2 module emit (pure-JS port of adapters/lib/modules.py emit-claude-code) ──
|
|
8
|
+
// Windows has no bash, so the Node path can't call install.sh/update.sh. This
|
|
9
|
+
// mirrors the Python emitter: parse modules/<slug>/SKILL.md, write Claude Code
|
|
10
|
+
// skills + slash-command aliases, and prune anything we previously emitted that
|
|
11
|
+
// no longer exists — without touching the user's own skills/commands.
|
|
12
|
+
|
|
13
|
+
const GENERATED_MARKER = '.100xprism-generated'
|
|
14
|
+
const ALIAS_MARKER = '<!-- 100xprism generated alias — regenerate, do not edit -->'
|
|
15
|
+
const MANIFEST_NAME = '.100xprism-manifest.json'
|
|
16
|
+
// Modules removed/merged upstream — cleaned up even for installs that predate the
|
|
17
|
+
// manifest/marker. Safe to trim once installs have cycled through a v2 update.
|
|
18
|
+
const REMOVED_MODULES = new Set(['systems-architect', 'conversion-copy'])
|
|
19
|
+
|
|
20
|
+
const MODEL_TIER_HINT = {
|
|
21
|
+
haiku: 'fast / low-cost (mechanical task)',
|
|
22
|
+
sonnet: 'balanced (moderate reasoning)',
|
|
23
|
+
opus: 'most capable (deep reasoning)',
|
|
24
|
+
}
|
|
25
|
+
const CATEGORY_ORDER = [
|
|
26
|
+
'lifecycle', 'quality', 'engineering', 'data', 'design', 'docs', 'marketing', 'uncategorized',
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
function tierAnnotation(model) {
|
|
30
|
+
const hint = MODEL_TIER_HINT[(model || '').trim()]
|
|
31
|
+
return hint ? `_Suggested model tier: ${hint}_` : ''
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function shortDescription(desc) {
|
|
35
|
+
let d = (desc || '').split('. ')[0]
|
|
36
|
+
if (d.length > 140) d = d.slice(0, 137) + '...'
|
|
37
|
+
return d
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Mirror modules.py split_frontmatter: simple `key: value` block with indented
|
|
41
|
+
// continuation lines folded into the previous key.
|
|
42
|
+
function parseFrontmatter(text) {
|
|
43
|
+
if (!text.startsWith('---\n')) return { fm: {}, body: text }
|
|
44
|
+
const end = text.indexOf('\n---\n', 4)
|
|
45
|
+
if (end === -1) return { fm: {}, body: text }
|
|
46
|
+
const block = text.slice(4, end)
|
|
47
|
+
const body = text.slice(end + 5)
|
|
48
|
+
const fm = {}
|
|
49
|
+
let currentKey = null
|
|
50
|
+
for (const line of block.split('\n')) {
|
|
51
|
+
if (!line.trim()) continue
|
|
52
|
+
if (/^\s/.test(line) && currentKey) {
|
|
53
|
+
fm[currentKey] = `${fm[currentKey]} ${line.trim()}`.trim()
|
|
54
|
+
continue
|
|
55
|
+
}
|
|
56
|
+
const idx = line.indexOf(':')
|
|
57
|
+
if (idx !== -1) {
|
|
58
|
+
currentKey = line.slice(0, idx).trim()
|
|
59
|
+
fm[currentKey] = line.slice(idx + 1).trim()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return { fm, body }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function listModules(modulesDir) {
|
|
66
|
+
const out = []
|
|
67
|
+
let entries = []
|
|
68
|
+
try {
|
|
69
|
+
entries = fs.readdirSync(modulesDir, { withFileTypes: true })
|
|
70
|
+
} catch {
|
|
71
|
+
return out
|
|
72
|
+
}
|
|
73
|
+
for (const entry of entries) {
|
|
74
|
+
if (!entry.isDirectory()) continue
|
|
75
|
+
const skillMd = path.join(modulesDir, entry.name, 'SKILL.md')
|
|
76
|
+
if (!fs.existsSync(skillMd)) continue // skips shared-reference dirs like _lib/
|
|
77
|
+
const { fm, body } = parseFrontmatter(fs.readFileSync(skillMd, 'utf8'))
|
|
78
|
+
out.push({
|
|
79
|
+
slug: entry.name,
|
|
80
|
+
category: fm.category || 'uncategorized',
|
|
81
|
+
tier: fm.tier || 'on-demand',
|
|
82
|
+
slash: (fm.slash_command || '').replace(/^\//, ''),
|
|
83
|
+
description: fm.description || '',
|
|
84
|
+
model: fm.model || '',
|
|
85
|
+
fm,
|
|
86
|
+
body,
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
out.sort((a, b) =>
|
|
90
|
+
(a.tier !== 'core') - (b.tier !== 'core') ||
|
|
91
|
+
a.category.localeCompare(b.category) ||
|
|
92
|
+
a.slug.localeCompare(b.slug))
|
|
93
|
+
return out
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function renderCommandAlias(m) {
|
|
97
|
+
const lines = ['---', `description: ${shortDescription(m.description)}`]
|
|
98
|
+
if (m.model) lines.push(`model: ${m.model}`)
|
|
99
|
+
const allowed = (m.fm['allowed-tools'] || '').trim()
|
|
100
|
+
if (allowed) lines.push(`allowed-tools: ${allowed}`)
|
|
101
|
+
if (m.body.includes('$ARGUMENTS') || m.body.includes('$1')) lines.push('argument-hint: [arguments]')
|
|
102
|
+
lines.push('---', '', ALIAS_MARKER, '', `Use the \`${m.fm.name || m.slug}\` skill.`, '', '$ARGUMENTS', '')
|
|
103
|
+
return lines.join('\n')
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function readManifest(skillsDir) {
|
|
107
|
+
try {
|
|
108
|
+
return JSON.parse(fs.readFileSync(path.join(skillsDir, MANIFEST_NAME), 'utf8'))
|
|
109
|
+
} catch {
|
|
110
|
+
return {}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Emit all modules as Claude Code skills + slash aliases, then prune stale ones.
|
|
115
|
+
// Returns { skills, commands, prunedSkills, prunedCommands }.
|
|
116
|
+
function emitClaudeModules(modulesDir, skillsDir, commandsDir) {
|
|
117
|
+
fs.mkdirSync(skillsDir, { recursive: true })
|
|
118
|
+
fs.mkdirSync(commandsDir, { recursive: true })
|
|
119
|
+
|
|
120
|
+
const prev = readManifest(skillsDir)
|
|
121
|
+
const prevSkills = new Set(prev.skills || [])
|
|
122
|
+
const prevCmds = new Set(prev.commands || [])
|
|
123
|
+
|
|
124
|
+
const modules = listModules(modulesDir)
|
|
125
|
+
const currentSkills = []
|
|
126
|
+
const currentCmds = []
|
|
127
|
+
|
|
128
|
+
for (const m of modules) {
|
|
129
|
+
const target = path.join(skillsDir, m.slug)
|
|
130
|
+
fs.rmSync(target, { recursive: true, force: true })
|
|
131
|
+
fs.cpSync(path.join(modulesDir, m.slug), target, { recursive: true })
|
|
132
|
+
fs.writeFileSync(path.join(target, GENERATED_MARKER),
|
|
133
|
+
'Generated by 100xprism from modules/<slug>/SKILL.md. Regenerate instead of editing here.\n')
|
|
134
|
+
currentSkills.push(m.slug)
|
|
135
|
+
if (m.slash) {
|
|
136
|
+
fs.writeFileSync(path.join(commandsDir, `${m.slash}.md`), renderCommandAlias(m))
|
|
137
|
+
currentCmds.push(m.slash)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const curSkillSet = new Set(currentSkills)
|
|
142
|
+
const curCmdSet = new Set(currentCmds)
|
|
143
|
+
|
|
144
|
+
// Prune skills we previously emitted / marked / shipped-as-removed that are gone now.
|
|
145
|
+
const orphanSkills = new Set([...prevSkills, ...REMOVED_MODULES].filter(s => !curSkillSet.has(s)))
|
|
146
|
+
for (const child of fs.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
147
|
+
if (child.isDirectory() && !curSkillSet.has(child.name) &&
|
|
148
|
+
fs.existsSync(path.join(skillsDir, child.name, GENERATED_MARKER))) {
|
|
149
|
+
orphanSkills.add(child.name)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
let prunedSkills = 0
|
|
153
|
+
for (const slug of orphanSkills) {
|
|
154
|
+
const p = path.join(skillsDir, slug)
|
|
155
|
+
const ours = fs.existsSync(path.join(p, GENERATED_MARKER)) || prevSkills.has(slug) || REMOVED_MODULES.has(slug)
|
|
156
|
+
if (fs.existsSync(p) && ours) { fs.rmSync(p, { recursive: true, force: true }); prunedSkills++ }
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Prune slash-command aliases we previously wrote (marker-guarded).
|
|
160
|
+
let prunedCommands = 0
|
|
161
|
+
for (const name of prevCmds) {
|
|
162
|
+
if (curCmdSet.has(name)) continue
|
|
163
|
+
const f = path.join(commandsDir, `${name}.md`)
|
|
164
|
+
try {
|
|
165
|
+
if (fs.existsSync(f) && fs.readFileSync(f, 'utf8').includes(ALIAS_MARKER)) {
|
|
166
|
+
fs.unlinkSync(f); prunedCommands++
|
|
167
|
+
}
|
|
168
|
+
} catch { /* ignore */ }
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
fs.writeFileSync(path.join(skillsDir, MANIFEST_NAME),
|
|
172
|
+
JSON.stringify({ skills: currentSkills.slice().sort(), commands: currentCmds.slice().sort() }, null, 2) + '\n')
|
|
173
|
+
|
|
174
|
+
return { skills: currentSkills.length, commands: currentCmds.length, prunedSkills, prunedCommands }
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const HEADER =
|
|
178
|
+
'# 100x Dev — Modules\n' +
|
|
179
|
+
'# Generated by 100xprism (https://github.com/rajitsaha/100xprism)\n' +
|
|
180
|
+
'# Source of truth: modules/<slug>/SKILL.md. Edit there and regenerate.\n\n'
|
|
181
|
+
|
|
182
|
+
function categorySortKey(c) {
|
|
183
|
+
const i = CATEGORY_ORDER.indexOf(c)
|
|
184
|
+
return i === -1 ? CATEGORY_ORDER.length : i
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function emitIndex(modules) {
|
|
188
|
+
const byCat = {}
|
|
189
|
+
for (const m of modules) (byCat[m.category] = byCat[m.category] || []).push(m)
|
|
190
|
+
const lines = []
|
|
191
|
+
for (const cat of Object.keys(byCat).sort((a, b) => categorySortKey(a) - categorySortKey(b))) {
|
|
192
|
+
lines.push(`**${cat.charAt(0).toUpperCase() + cat.slice(1)}** (${byCat[cat].length}):`)
|
|
193
|
+
for (const m of byCat[cat]) {
|
|
194
|
+
const slash = m.slash ? ` \`/${m.slash}\`` : ''
|
|
195
|
+
const tier = tierAnnotation(m.model)
|
|
196
|
+
lines.push(`- \`${m.slug}\`${slash} — ${shortDescription(m.description)}${tier ? ' ' + tier : ''}`)
|
|
197
|
+
}
|
|
198
|
+
lines.push('')
|
|
199
|
+
}
|
|
200
|
+
return lines.join('\n').replace(/\s+$/, '') + '\n'
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Single-file combined config (port of modules.py render_concat): inline core
|
|
204
|
+
// module bodies + an index of on-demand modules. Used for .cursorrules/AGENTS.md/etc.
|
|
205
|
+
function generateCombinedConfig(modulesDir) {
|
|
206
|
+
const modules = listModules(modulesDir)
|
|
207
|
+
const core = modules.filter(m => m.tier === 'core')
|
|
208
|
+
const onDemand = modules.filter(m => m.tier !== 'core')
|
|
209
|
+
const out = [HEADER, '## Core modules (always-on)\n']
|
|
210
|
+
core.sort((a, b) => categorySortKey(a.category) - categorySortKey(b.category) || a.slug.localeCompare(b.slug))
|
|
211
|
+
for (const m of core) {
|
|
212
|
+
out.push(`---\n\n## ${m.slug}\n`)
|
|
213
|
+
if (m.slash) out.push(`_Slash command: \`/${m.slash}\`_\n`)
|
|
214
|
+
const tier = tierAnnotation(m.model)
|
|
215
|
+
if (tier) out.push(`${tier}\n`)
|
|
216
|
+
out.push(m.body.replace(/^\n+/, '').replace(/\s+$/, '') + '\n\n')
|
|
217
|
+
}
|
|
218
|
+
out.push('---\n\n## On-demand modules (invoke by name)\n\n')
|
|
219
|
+
out.push('These load only when triggered. Ask Claude to use the relevant module by name when the situation matches.\n\n')
|
|
220
|
+
out.push(emitIndex(onDemand))
|
|
221
|
+
return out.join('')
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function scaffoldClaudeMd(projectPath) {
|
|
225
|
+
const existing = [
|
|
226
|
+
'CLAUDE.md', 'AGENTS.md', '.cursorrules', '.windsurfrules', 'GEMINI.md',
|
|
227
|
+
path.join('.github', 'copilot-instructions.md'),
|
|
228
|
+
]
|
|
229
|
+
if (existing.some(f => fs.existsSync(path.join(projectPath, f)))) return
|
|
230
|
+
|
|
231
|
+
const projectName = path.basename(path.resolve(projectPath))
|
|
232
|
+
fs.writeFileSync(path.join(projectPath, 'CLAUDE.md'), `# ${projectName} — Project Instructions
|
|
233
|
+
|
|
234
|
+
<!-- Generated by 100xprism. Fill in the sections below so modules like /db, /gate, /launch have project context. -->
|
|
235
|
+
|
|
236
|
+
## Project
|
|
237
|
+
|
|
238
|
+
<!-- Describe what this project does in 2-3 sentences. Used by /architect and /enterprise-design. -->
|
|
239
|
+
description: TODO
|
|
240
|
+
|
|
241
|
+
## Database
|
|
242
|
+
|
|
243
|
+
<!-- Used by /db and /query. Remove if not applicable. -->
|
|
244
|
+
# engine: postgres
|
|
245
|
+
# connection: default
|
|
246
|
+
# connections:
|
|
247
|
+
# default:
|
|
248
|
+
# host: localhost
|
|
249
|
+
# port: 5432
|
|
250
|
+
# name: mydb
|
|
251
|
+
# user: myuser
|
|
252
|
+
# auth: env:DB_PASSWORD
|
|
253
|
+
|
|
254
|
+
## Cloud (GCP)
|
|
255
|
+
|
|
256
|
+
<!-- Used by /gate, /cloud-security, /launch. Remove if not on GCP. -->
|
|
257
|
+
# gcp_project: my-gcp-project
|
|
258
|
+
# cloud_run_service: my-service
|
|
259
|
+
# region: us-central1
|
|
260
|
+
|
|
261
|
+
## Production
|
|
262
|
+
|
|
263
|
+
<!-- Used by /launch and /push for health checks and smoke tests. -->
|
|
264
|
+
# production_url: https://example.com
|
|
265
|
+
# health_url: https://example.com/health
|
|
266
|
+
|
|
267
|
+
## Security Exceptions
|
|
268
|
+
|
|
269
|
+
# security_exceptions:
|
|
270
|
+
# - CVE-2023-XXXX: false positive in test dependency
|
|
271
|
+
|
|
272
|
+
## Rules
|
|
273
|
+
|
|
274
|
+
<!-- Add project-specific rules for Claude here. /update-claude appends to this section. -->
|
|
275
|
+
`)
|
|
276
|
+
console.log(` → Scaffolded CLAUDE.md in ${projectPath} ✓`)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Reconcile enabledPlugins with plugins.json: ADD newly-declared plugins and
|
|
280
|
+
// REMOVE ones 100xprism previously installed but has since dropped — without
|
|
281
|
+
// touching plugins the user enabled themselves. The "managed" set is tracked in
|
|
282
|
+
// a sidecar beside settings.json (mirrors adapters/lib/sync_plugins.py). On the
|
|
283
|
+
// first run (no state) the managed set is seeded from declared ∧ enabled, so
|
|
284
|
+
// nothing is removed until a later run observes an actual drop.
|
|
285
|
+
function mergePluginsJson(pluginsFile, settingsFile) {
|
|
286
|
+
if (!fs.existsSync(settingsFile)) {
|
|
287
|
+
fs.mkdirSync(path.dirname(settingsFile), { recursive: true })
|
|
288
|
+
fs.writeFileSync(settingsFile, '{}')
|
|
289
|
+
}
|
|
290
|
+
const pluginsData = JSON.parse(fs.readFileSync(pluginsFile, 'utf8'))
|
|
291
|
+
const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'))
|
|
292
|
+
const enabled = settings.enabledPlugins || {}
|
|
293
|
+
const desired = pluginsData.plugins || []
|
|
294
|
+
const desiredSet = new Set(desired)
|
|
295
|
+
|
|
296
|
+
const stateFile = path.join(path.dirname(settingsFile), '.100xprism-plugins.json')
|
|
297
|
+
let state = {}
|
|
298
|
+
try { state = JSON.parse(fs.readFileSync(stateFile, 'utf8')) } catch { state = {} }
|
|
299
|
+
const firstRun = !('managed' in state)
|
|
300
|
+
const managed = new Set(
|
|
301
|
+
firstRun ? desired.filter(p => p in enabled) : (state.managed || []),
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
for (const p of desired) {
|
|
305
|
+
if (!(p in enabled)) enabled[p] = true // never flip an existing value
|
|
306
|
+
}
|
|
307
|
+
for (const p of managed) {
|
|
308
|
+
if (!desiredSet.has(p)) delete enabled[p] // we installed it; it's gone now
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
settings.enabledPlugins = enabled
|
|
312
|
+
settings.extraKnownMarketplaces = {
|
|
313
|
+
...settings.extraKnownMarketplaces,
|
|
314
|
+
...(pluginsData.extraKnownMarketplaces || {}),
|
|
315
|
+
}
|
|
316
|
+
fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2))
|
|
317
|
+
fs.writeFileSync(stateFile, JSON.stringify({ managed: [...desiredSet].sort() }, null, 2))
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function addTrackedProject(projectPath, trackedFile) {
|
|
321
|
+
fs.mkdirSync(path.dirname(trackedFile), { recursive: true })
|
|
322
|
+
const existing = fs.existsSync(trackedFile)
|
|
323
|
+
? fs.readFileSync(trackedFile, 'utf8').split('\n').filter(Boolean)
|
|
324
|
+
: []
|
|
325
|
+
if (!existing.includes(projectPath)) {
|
|
326
|
+
fs.appendFileSync(trackedFile, projectPath + '\n')
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function writeAdapter(content, outputFile) {
|
|
331
|
+
fs.mkdirSync(path.dirname(outputFile), { recursive: true })
|
|
332
|
+
fs.writeFileSync(outputFile, content)
|
|
333
|
+
console.log(` → Generated ${outputFile} ✓`)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function installGlobalWindows(installDir) {
|
|
337
|
+
const { claudeDir, claudeCommandsDir, claudeSettingsFile } = require('../platform')
|
|
338
|
+
const skillsDir = path.join(claudeDir, 'skills')
|
|
339
|
+
const r = emitClaudeModules(path.join(installDir, 'modules'), skillsDir, claudeCommandsDir)
|
|
340
|
+
mergePluginsJson(path.join(installDir, 'plugins', 'plugins.json'), claudeSettingsFile)
|
|
341
|
+
const pruned = (r.prunedSkills || r.prunedCommands)
|
|
342
|
+
? ` (pruned ${r.prunedSkills} stale skill(s), ${r.prunedCommands} stale alias(es))`
|
|
343
|
+
: ''
|
|
344
|
+
console.log(`✓ ${r.skills} skills + ${r.commands} slash aliases installed to ~/.claude/${pruned}`)
|
|
345
|
+
console.log('✓ Plugins merged into ~/.claude/settings.json')
|
|
346
|
+
console.log('\nNext: cd into a project and run 100xprism init to set it up.')
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function initProjectWindows(installDir, projectPath) {
|
|
350
|
+
const { trackedProjectsFile } = require('../platform')
|
|
351
|
+
const modulesDir = path.join(installDir, 'modules')
|
|
352
|
+
const absProject = path.resolve(projectPath)
|
|
353
|
+
const readline = require('readline')
|
|
354
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
|
|
355
|
+
|
|
356
|
+
console.log('\n100x Dev — Project Setup')
|
|
357
|
+
console.log(` Project: ${absProject}\n`)
|
|
358
|
+
|
|
359
|
+
const tools = [
|
|
360
|
+
{ label: 'Claude Code (CLAUDE.md scaffold)', key: 'claude' },
|
|
361
|
+
{ label: 'Cursor (.cursorrules)', key: 'cursor' },
|
|
362
|
+
{ label: 'Codex (AGENTS.md)', key: 'codex' },
|
|
363
|
+
{ label: 'Windsurf (.windsurfrules)', key: 'windsurf' },
|
|
364
|
+
{ label: 'Copilot CLI (.github/copilot-instructions.md)', key: 'copilot' },
|
|
365
|
+
{ label: 'Gemini CLI (GEMINI.md)', key: 'gemini' },
|
|
366
|
+
{ label: 'Antigravity (ANTIGRAVITY.md)', key: 'antigravity' },
|
|
367
|
+
]
|
|
368
|
+
const selected = {}
|
|
369
|
+
|
|
370
|
+
function promptNext(idx) {
|
|
371
|
+
if (idx >= tools.length) {
|
|
372
|
+
rl.close()
|
|
373
|
+
applyAdapters()
|
|
374
|
+
return
|
|
375
|
+
}
|
|
376
|
+
rl.question(` Set up ${tools[idx].label}? [y/N] `, ans => {
|
|
377
|
+
selected[tools[idx].key] = /^y$/i.test(ans.trim())
|
|
378
|
+
promptNext(idx + 1)
|
|
379
|
+
})
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function applyAdapters() {
|
|
383
|
+
const combined = generateCombinedConfig(modulesDir)
|
|
384
|
+
if (selected.claude) scaffoldClaudeMd(absProject)
|
|
385
|
+
if (selected.cursor) writeAdapter(combined, path.join(absProject, '.cursorrules'))
|
|
386
|
+
if (selected.codex) writeAdapter(combined, path.join(absProject, 'AGENTS.md'))
|
|
387
|
+
if (selected.windsurf) writeAdapter(combined, path.join(absProject, '.windsurfrules'))
|
|
388
|
+
if (selected.copilot) writeAdapter(combined, path.join(absProject, '.github', 'copilot-instructions.md'))
|
|
389
|
+
if (selected.gemini) writeAdapter(combined, path.join(absProject, 'GEMINI.md'))
|
|
390
|
+
if (selected.antigravity) writeAdapter(combined, path.join(absProject, 'ANTIGRAVITY.md'))
|
|
391
|
+
addTrackedProject(absProject, trackedProjectsFile)
|
|
392
|
+
console.log('\n✓ Project set up!')
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
promptNext(0)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function updateWindows(installDir, checkOnly) {
|
|
399
|
+
const fetchResult = spawnSync('git', ['-C', installDir, 'fetch', 'origin', 'main', '--quiet'], { stdio: 'inherit' })
|
|
400
|
+
if (fetchResult.status !== 0) { console.error('git fetch failed'); process.exit(1) }
|
|
401
|
+
|
|
402
|
+
const local = spawnSync('git', ['-C', installDir, 'rev-parse', 'HEAD']).stdout.toString().trim()
|
|
403
|
+
const remote = spawnSync('git', ['-C', installDir, 'rev-parse', 'origin/main']).stdout.toString().trim()
|
|
404
|
+
|
|
405
|
+
if (local === remote) { console.log('✓ Already up to date.'); return }
|
|
406
|
+
|
|
407
|
+
if (checkOnly) { console.log('Update available. Run: 100xprism update'); return }
|
|
408
|
+
|
|
409
|
+
const pullResult = spawnSync('git', ['-C', installDir, 'pull', '--rebase', 'origin', 'main', '--quiet'], { stdio: 'inherit' })
|
|
410
|
+
if (pullResult.status !== 0) { console.error('git pull failed'); process.exit(1) }
|
|
411
|
+
|
|
412
|
+
installGlobalWindows(installDir)
|
|
413
|
+
console.log('✓ 100xprism updated!')
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
module.exports = {
|
|
417
|
+
parseFrontmatter,
|
|
418
|
+
shortDescription,
|
|
419
|
+
listModules,
|
|
420
|
+
renderCommandAlias,
|
|
421
|
+
emitClaudeModules,
|
|
422
|
+
generateCombinedConfig,
|
|
423
|
+
scaffoldClaudeMd,
|
|
424
|
+
mergePluginsJson,
|
|
425
|
+
addTrackedProject,
|
|
426
|
+
installGlobalWindows,
|
|
427
|
+
initProjectWindows,
|
|
428
|
+
updateWindows,
|
|
429
|
+
}
|
package/lib/bootstrap.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require('child_process')
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const { installDir } = require('./platform')
|
|
7
|
+
const { migrateLegacy } = require('./migrate')
|
|
8
|
+
|
|
9
|
+
const REPO_URL = 'https://github.com/rajitsaha/100xprism.git'
|
|
10
|
+
|
|
11
|
+
function bootstrap() {
|
|
12
|
+
migrateLegacy()
|
|
13
|
+
const gitDir = path.join(installDir, '.git')
|
|
14
|
+
|
|
15
|
+
if (fs.existsSync(gitDir)) {
|
|
16
|
+
console.log('100xprism already installed — pulling latest...')
|
|
17
|
+
const result = spawnSync('git', ['-C', installDir, 'pull', '--rebase', 'origin', 'main', '--quiet'], { stdio: 'inherit' })
|
|
18
|
+
if (result.status !== 0) {
|
|
19
|
+
console.error('Error: git pull failed. Check your network or resolve any conflicts in ' + installDir)
|
|
20
|
+
process.exit(result.status ?? 1)
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
console.log('Installing 100xprism...')
|
|
24
|
+
fs.mkdirSync(path.dirname(installDir), { recursive: true })
|
|
25
|
+
const result = spawnSync('git', ['clone', REPO_URL, installDir, '--quiet'], { stdio: 'inherit' })
|
|
26
|
+
if (result.status !== 0) {
|
|
27
|
+
console.error('Error: git clone failed. Check your network and try again.')
|
|
28
|
+
process.exit(result.status ?? 1)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = { bootstrap }
|
package/lib/init.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require('child_process')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const { isWindows, installDir } = require('./platform')
|
|
6
|
+
const { bootstrap } = require('./bootstrap')
|
|
7
|
+
|
|
8
|
+
function run(args) {
|
|
9
|
+
bootstrap()
|
|
10
|
+
const projectPath = args[0] || process.cwd()
|
|
11
|
+
if (isWindows) {
|
|
12
|
+
require('./adapters/windows').initProjectWindows(installDir, projectPath)
|
|
13
|
+
} else {
|
|
14
|
+
const result = spawnSync('bash', [path.join(installDir, 'install-project.sh'), projectPath], { stdio: 'inherit' })
|
|
15
|
+
if (result.status !== 0) process.exit(result.status ?? 1)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = { run }
|
package/lib/install.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require('child_process')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const { isWindows, installDir } = require('./platform')
|
|
6
|
+
const { bootstrap } = require('./bootstrap')
|
|
7
|
+
|
|
8
|
+
function run(_args) {
|
|
9
|
+
bootstrap()
|
|
10
|
+
if (isWindows) {
|
|
11
|
+
require('./adapters/windows').installGlobalWindows(installDir)
|
|
12
|
+
} else {
|
|
13
|
+
const result = spawnSync('bash', [path.join(installDir, 'install.sh')], { stdio: 'inherit' })
|
|
14
|
+
if (result.status !== 0) process.exit(result.status ?? 1)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = { run }
|
package/lib/migrate.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// One-time, idempotent migration for installs created under the old `100x-dev`
|
|
4
|
+
// name. Moves the clone dir (~/100x-dev → ~/100xprism) and the config/cache dir
|
|
5
|
+
// (~/.100x-dev → ~/.100xprism), and repoints the git remote at the renamed
|
|
6
|
+
// GitHub repo. Safe to call on every install/update: it no-ops once migrated,
|
|
7
|
+
// and never overwrites a new-name dir that already exists.
|
|
8
|
+
|
|
9
|
+
const { spawnSync } = require('child_process')
|
|
10
|
+
const fs = require('fs')
|
|
11
|
+
const path = require('path')
|
|
12
|
+
const os = require('os')
|
|
13
|
+
|
|
14
|
+
const NEW_REMOTE = 'https://github.com/rajitsaha/100xprism.git'
|
|
15
|
+
|
|
16
|
+
function migrateLegacy(opts = {}) {
|
|
17
|
+
const home = opts.home || os.homedir()
|
|
18
|
+
const legacyInstall = opts.legacyInstall || path.join(home, '100x-dev')
|
|
19
|
+
const newInstall = opts.newInstall || path.join(home, '100xprism')
|
|
20
|
+
const legacyConfig = opts.legacyConfig || path.join(home, '.100x-dev')
|
|
21
|
+
const newConfig = opts.newConfig || path.join(home, '.100xprism')
|
|
22
|
+
const remote = opts.remote || NEW_REMOTE
|
|
23
|
+
const runGit = opts.runGit !== false
|
|
24
|
+
const log = opts.log || console.log
|
|
25
|
+
|
|
26
|
+
const actions = []
|
|
27
|
+
|
|
28
|
+
// 1. Clone/install directory + git remote.
|
|
29
|
+
if (fs.existsSync(legacyInstall) && !fs.existsSync(newInstall)) {
|
|
30
|
+
fs.renameSync(legacyInstall, newInstall)
|
|
31
|
+
actions.push('moved ~/100x-dev → ~/100xprism')
|
|
32
|
+
if (runGit && fs.existsSync(path.join(newInstall, '.git'))) {
|
|
33
|
+
const r = spawnSync('git', ['-C', newInstall, 'remote', 'set-url', 'origin', remote], { stdio: 'ignore' })
|
|
34
|
+
if (!r.error && r.status === 0) actions.push('repointed git remote → 100xprism')
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 2. Config/cache directory (tracked-projects, gate-cache, update-cache).
|
|
39
|
+
if (fs.existsSync(legacyConfig) && !fs.existsSync(newConfig)) {
|
|
40
|
+
fs.renameSync(legacyConfig, newConfig)
|
|
41
|
+
actions.push('moved ~/.100x-dev → ~/.100xprism')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (actions.length) {
|
|
45
|
+
log('Migrating your install from 100x-dev to 100xPrism:')
|
|
46
|
+
for (const a of actions) log(' ✓ ' + a)
|
|
47
|
+
log(' (the `100x-dev` command still works as an alias)')
|
|
48
|
+
}
|
|
49
|
+
return actions
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = { migrateLegacy }
|
package/lib/platform.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const os = require('os')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
|
|
6
|
+
const isWindows = process.platform === 'win32'
|
|
7
|
+
const isMac = process.platform === 'darwin'
|
|
8
|
+
const isLinux = process.platform === 'linux'
|
|
9
|
+
const home = os.homedir()
|
|
10
|
+
|
|
11
|
+
const installDir = path.join(home, '100xprism')
|
|
12
|
+
const claudeDir = path.join(home, '.claude')
|
|
13
|
+
const claudeCommandsDir = path.join(claudeDir, 'commands')
|
|
14
|
+
const claudeSettingsFile = path.join(claudeDir, 'settings.json')
|
|
15
|
+
const trackedProjectsFile = path.join(home, '.100xprism', 'tracked-projects')
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
isWindows, isMac, isLinux,
|
|
19
|
+
home, installDir,
|
|
20
|
+
claudeDir, claudeCommandsDir, claudeSettingsFile,
|
|
21
|
+
trackedProjectsFile,
|
|
22
|
+
}
|
package/lib/update.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require('child_process')
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const { isWindows, installDir } = require('./platform')
|
|
7
|
+
const { migrateLegacy } = require('./migrate')
|
|
8
|
+
|
|
9
|
+
function run(args) {
|
|
10
|
+
const checkOnly = args.includes('--check-only')
|
|
11
|
+
|
|
12
|
+
migrateLegacy()
|
|
13
|
+
|
|
14
|
+
if (!fs.existsSync(installDir)) {
|
|
15
|
+
console.error('100xprism is not installed. Run: 100xprism install')
|
|
16
|
+
process.exit(1)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (isWindows) {
|
|
20
|
+
require('./adapters/windows').updateWindows(installDir, checkOnly)
|
|
21
|
+
} else {
|
|
22
|
+
const script = path.join(installDir, 'update.sh')
|
|
23
|
+
const scriptArgs = checkOnly ? ['--check-only'] : []
|
|
24
|
+
const result = spawnSync('bash', [script, ...scriptArgs], { stdio: 'inherit' })
|
|
25
|
+
if (result.status !== 0) process.exit(result.status ?? 1)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = { run }
|