create-clicksmith 0.1.3 → 0.1.5

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.
@@ -140,6 +140,7 @@ async function planInstall(info, options = {}) {
140
140
  }
141
141
  const configPath = join2(".clicksmith", "agents.config.json");
142
142
  const existingConfigRaw = await readText(join2(root, configPath));
143
+ const builtinIds = new Set(DEFAULT_AGENTS_CONFIG.agents.map((a) => a.id));
143
144
  if (existingConfigRaw == null) {
144
145
  changes.push({
145
146
  path: configPath,
@@ -153,19 +154,32 @@ async function planInstall(info, options = {}) {
153
154
  });
154
155
  } else {
155
156
  try {
156
- const parsed = parseAgentsConfig(JSON.parse(existingConfigRaw));
157
+ const doc = JSON.parse(existingConfigRaw);
158
+ const parsed = parseAgentsConfig(doc);
157
159
  if (!parsed.ok) {
158
160
  messages.push("Existing agents.config.json was invalid; left it untouched.");
161
+ changes.push({ path: configPath, action: "skip", contents: existingConfigRaw });
162
+ } else {
163
+ const customAgents = (parsed.config.agents ?? []).filter((a) => !builtinIds.has(a.id));
164
+ const hadBuiltins = customAgents.length < (parsed.config.agents ?? []).length;
165
+ const updated = { ...doc, agents: customAgents };
166
+ changes.push({
167
+ path: configPath,
168
+ action: hadBuiltins ? "merge" : "skip",
169
+ contents: `${JSON.stringify(updated, null, 2)}
170
+ `,
171
+ reason: hadBuiltins ? "stripped built-in agent entries so daemon defaults take over" : "no built-in entries found; preserved as-is"
172
+ });
173
+ if (hadBuiltins) {
174
+ messages.push(
175
+ "agents.config.json: removed built-in agent entries \u2014 daemon will use its latest defaults."
176
+ );
177
+ }
159
178
  }
160
179
  } catch {
161
180
  messages.push("Existing agents.config.json was invalid JSON; left it untouched.");
181
+ changes.push({ path: configPath, action: "skip", contents: existingConfigRaw });
162
182
  }
163
- changes.push({
164
- path: configPath,
165
- action: "skip",
166
- contents: existingConfigRaw,
167
- reason: "existing user agent overrides preserved"
168
- });
169
183
  }
170
184
  const mcp = mcpCommand(info.packageManager);
171
185
  changes.push(await mcpChange(root, ".mcp.json", mcp));
@@ -309,4 +323,4 @@ export {
309
323
  planInstall,
310
324
  applyPlan
311
325
  };
312
- //# sourceMappingURL=chunk-TE3BLNLM.js.map
326
+ //# sourceMappingURL=chunk-DAPCF44S.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/detect.ts","../src/install.ts"],"sourcesContent":["import { readFile, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport type PackageManager = 'pnpm' | 'yarn' | 'bun' | 'npm';\nexport type Bundler = 'vite' | 'webpack' | 'rspack' | 'rollup' | 'next' | 'unknown';\nexport type Framework = 'react' | 'vue' | 'svelte' | 'angular' | 'solid' | 'unknown';\n\nexport interface ProjectInfo {\n root: string;\n packageManager: PackageManager;\n bundler: Bundler;\n framework: Framework;\n /** Stable attributes already used in the codebase (e.g. data-testid). */\n stableAttrs: string[];\n /** Whether the dev-only data-loc unplugin can be wired up for this stack. */\n supportsUnplugin: boolean;\n /** Path to the detected Vite config, if any (relative to root). */\n viteConfig?: string;\n}\n\nconst KNOWN_STABLE_ATTRS = ['data-testid', 'data-test', 'data-cy', 'data-qa'];\n\n/** Inspect a project directory and infer its stack. Read-only. */\nexport async function detectProject(root: string): Promise<ProjectInfo> {\n const pkg = await readJson(join(root, 'package.json'));\n const deps = { ...(pkg?.dependencies ?? {}), ...(pkg?.devDependencies ?? {}) } as Record<string, string>;\n\n const packageManager = await detectPackageManager(root);\n const bundler = detectBundler(deps);\n const framework = detectFramework(deps);\n const viteConfig = await findFile(root, ['vite.config.ts', 'vite.config.js', 'vite.config.mjs']);\n const stableAttrs = await scanStableAttrs(root);\n\n const supportsUnplugin =\n framework === 'react' && ['vite', 'webpack', 'rspack', 'rollup'].includes(bundler);\n\n return {\n root,\n packageManager,\n bundler,\n framework,\n stableAttrs,\n supportsUnplugin,\n ...(viteConfig ? { viteConfig } : {}),\n };\n}\n\nasync function detectPackageManager(root: string): Promise<PackageManager> {\n if (await fileExists(join(root, 'pnpm-lock.yaml'))) return 'pnpm';\n if (await fileExists(join(root, 'yarn.lock'))) return 'yarn';\n if (await fileExists(join(root, 'bun.lockb'))) return 'bun';\n return 'npm';\n}\n\nfunction detectBundler(deps: Record<string, string>): Bundler {\n if (deps.next) return 'next';\n if (deps.vite) return 'vite';\n if (deps['@rspack/core']) return 'rspack';\n if (deps.webpack) return 'webpack';\n if (deps.rollup) return 'rollup';\n return 'unknown';\n}\n\nfunction detectFramework(deps: Record<string, string>): Framework {\n if (deps['@angular/core']) return 'angular';\n if (deps.svelte) return 'svelte';\n if (deps.vue) return 'vue';\n if (deps['solid-js']) return 'solid';\n if (deps.react) return 'react';\n return 'unknown';\n}\n\n/** Scan a handful of source files for stable test/id attributes. */\nasync function scanStableAttrs(root: string): Promise<string[]> {\n const found = new Set<string>();\n const dirs = ['src', 'app', 'components'];\n for (const dir of dirs) {\n const files = await listSourceFiles(join(root, dir), 40);\n for (const file of files) {\n const content = await safeRead(file);\n if (!content) continue;\n for (const attr of KNOWN_STABLE_ATTRS) {\n if (content.includes(attr)) found.add(attr);\n }\n if (found.size === KNOWN_STABLE_ATTRS.length) return [...found];\n }\n }\n return [...found];\n}\n\nasync function listSourceFiles(dir: string, limit: number): Promise<string[]> {\n const out: string[] = [];\n async function walk(d: string) {\n if (out.length >= limit) return;\n let entries;\n try {\n entries = await readdir(d, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n if (out.length >= limit) return;\n if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue;\n const full = join(d, entry.name);\n if (entry.isDirectory()) await walk(full);\n else if (/\\.(jsx?|tsx?|vue|svelte)$/.test(entry.name)) out.push(full);\n }\n }\n await walk(dir);\n return out;\n}\n\nasync function findFile(root: string, names: string[]): Promise<string | undefined> {\n for (const name of names) {\n if (await fileExists(join(root, name))) return name;\n }\n return undefined;\n}\n\nasync function readJson(file: string): Promise<Record<string, unknown> | undefined> {\n const raw = await safeRead(file);\n if (!raw) return undefined;\n try {\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n}\n\nasync function safeRead(file: string): Promise<string | undefined> {\n try {\n return await readFile(file, 'utf8');\n } catch {\n return undefined;\n }\n}\n\nasync function fileExists(file: string): Promise<boolean> {\n return (await safeRead(file)) !== undefined;\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport {\n applyManagedBlock,\n DEFAULT_AGENTS_CONFIG,\n parseAgentsConfig,\n renderInstructions,\n type InstructionTarget,\n} from '@clicksmith/agent-config';\nimport { DEFAULT_DAEMON_PORT } from '@clicksmith/core';\nimport type { PackageManager, ProjectInfo } from './detect.js';\n\nexport interface InstallOptions {\n /** Which agents to render instruction files for. */\n agents?: InstructionTarget[];\n /** Wire the dev-only data-loc unplugin. Defaults to detection. */\n useUnplugin?: boolean;\n daemonPort?: number;\n}\n\nexport interface FileChange {\n path: string;\n action: 'create' | 'merge' | 'skip';\n contents: string;\n reason?: string;\n}\n\nexport interface InstallPlan {\n changes: FileChange[];\n messages: string[];\n nextSteps: string[];\n}\n\nconst DEFAULT_AGENT_TARGETS: InstructionTarget[] = ['claude', 'cursor', 'codex', 'generic'];\n\n/**\n * Compute every file change needed to install ClickSmith into a project,\n * reading existing files so merges preserve user content. Nothing is written —\n * call {@link applyPlan} for that. This split keeps the installer fully testable.\n */\nexport async function planInstall(\n info: ProjectInfo,\n options: InstallOptions = {},\n): Promise<InstallPlan> {\n const root = info.root;\n const targets = options.agents ?? DEFAULT_AGENT_TARGETS;\n const useUnplugin = options.useUnplugin ?? info.supportsUnplugin;\n const port = options.daemonPort ?? DEFAULT_DAEMON_PORT;\n\n const changes: FileChange[] = [];\n const messages: string[] = [];\n const nextSteps: string[] = [];\n\n // 1. Agent instruction files (managed blocks preserve user content).\n for (const target of targets) {\n const rendered = renderInstructions(target, {\n stableAttrs: info.stableAttrs,\n daemonPort: port,\n });\n const existing = await readText(join(root, rendered.path));\n const contents = rendered.shared\n ? applyManagedBlock(existing, rendered.content)\n : rendered.content;\n changes.push({\n path: rendered.path,\n action: existing == null ? 'create' : 'merge',\n contents,\n });\n }\n\n // 2. agents.config.json — keep this as user overrides only. The daemon ships\n // built-in defaults, so copying them into projects would freeze old commands.\n const configPath = join('.clicksmith', 'agents.config.json');\n const existingConfigRaw = await readText(join(root, configPath));\n if (existingConfigRaw == null) {\n changes.push({\n path: configPath,\n action: 'create',\n contents: `${JSON.stringify(\n { version: 1, defaultAgent: DEFAULT_AGENTS_CONFIG.defaultAgent, agents: [] },\n null,\n 2,\n )}\\n`,\n });\n } else {\n try {\n const parsed = parseAgentsConfig(JSON.parse(existingConfigRaw));\n if (!parsed.ok) {\n messages.push('Existing agents.config.json was invalid; left it untouched.');\n }\n } catch {\n messages.push('Existing agents.config.json was invalid JSON; left it untouched.');\n }\n changes.push({\n path: configPath,\n action: 'skip',\n contents: existingConfigRaw,\n reason: 'existing user agent overrides preserved',\n });\n }\n\n // 3. MCP registration for the daemon's stdio server.\n const mcp = mcpCommand(info.packageManager);\n changes.push(await mcpChange(root, '.mcp.json', mcp));\n if (targets.includes('cursor')) {\n changes.push(await mcpChange(root, join('.cursor', 'mcp.json'), mcp));\n }\n\n // 4. Wire the data-loc unplugin (best effort) + record the dependency.\n if (useUnplugin && info.viteConfig) {\n const wired = await wireViteConfig(root, info.viteConfig);\n if (wired) changes.push(wired);\n else\n messages.push(\n `Could not automatically wire ${info.viteConfig}. Add: import clicksmith from '@clicksmith/unplugin/vite'; and put clicksmith() first in plugins.`,\n );\n } else if (useUnplugin) {\n messages.push(\n 'No Vite config found — add the @clicksmith/unplugin plugin to your bundler manually.',\n );\n } else {\n messages.push(\n `Stable source locators via unplugin aren't available for this stack; ClickSmith will use ${\n info.stableAttrs.length\n ? `your attributes (${info.stableAttrs.join(', ')})`\n : 'attribute/behavioral/DOM'\n } as the fallback locator (source → attr → behavioral → dom).`,\n );\n }\n\n // 5. package.json — add the dependencies needed for the bin + plugin.\n changes.push(await packageJsonChange(root, useUnplugin));\n\n // 6. Ensure .clicksmith/ is gitignored.\n changes.push(await gitignoreChange(root));\n\n nextSteps.push(\n `${installCmd(info.packageManager)} # install the new dependencies`,\n `${runCmd(info.packageManager, 'clicksmith daemon')} # start the localhost daemon`,\n `${runCmd(info.packageManager, 'clicksmith doctor')} # verify agent CLIs are visible to the daemon`,\n 'Install the ClickSmith browser extension, toggle AI Mode, and Alt+Click an element.',\n );\n\n return { changes, messages, nextSteps };\n}\n\n/** Apply a plan to disk, writing create/merge changes and skipping the rest. */\nexport async function applyPlan(root: string, plan: InstallPlan): Promise<FileChange[]> {\n const written: FileChange[] = [];\n for (const change of plan.changes) {\n if (change.action === 'skip') continue;\n const full = join(root, change.path);\n await mkdir(dirname(full), { recursive: true });\n await writeFile(full, change.contents, 'utf8');\n written.push(change);\n }\n return written;\n}\n\n/* ------------------------------- helpers ---------------------------------- */\n\ninterface McpServerSpec {\n command: string;\n args: string[];\n}\n\nfunction mcpCommand(pm: PackageManager): McpServerSpec {\n switch (pm) {\n case 'pnpm':\n return { command: 'pnpm', args: ['exec', 'clicksmith', 'mcp'] };\n case 'yarn':\n return { command: 'yarn', args: ['clicksmith', 'mcp'] };\n case 'bun':\n return { command: 'bunx', args: ['clicksmith', 'mcp'] };\n case 'npm':\n return { command: 'npx', args: ['clicksmith', 'mcp'] };\n }\n}\n\nasync function mcpChange(root: string, path: string, spec: McpServerSpec): Promise<FileChange> {\n const existingRaw = await readText(join(root, path));\n let doc: { mcpServers?: Record<string, unknown> } = {};\n if (existingRaw) {\n try {\n doc = JSON.parse(existingRaw);\n } catch {\n doc = {};\n }\n }\n doc.mcpServers = { ...(doc.mcpServers ?? {}), clicksmith: spec };\n return {\n path,\n action: existingRaw == null ? 'create' : 'merge',\n contents: `${JSON.stringify(doc, null, 2)}\\n`,\n };\n}\n\nasync function wireViteConfig(root: string, relPath: string): Promise<FileChange | null> {\n const file = join(root, relPath);\n const code = await readText(file);\n if (code == null) return null;\n if (code.includes('@clicksmith/unplugin')) {\n return { path: relPath, action: 'skip', contents: code, reason: 'already wired' };\n }\n const importLine = `import clicksmith from '@clicksmith/unplugin/vite';\\n`;\n // Insert plugin first in the array so it runs before the framework plugin.\n const pluginsMatch = code.match(/plugins\\s*:\\s*\\[/);\n if (!pluginsMatch) return null;\n const idx = pluginsMatch.index! + pluginsMatch[0].length;\n const next = importLine + code.slice(0, idx) + 'clicksmith(), ' + code.slice(idx);\n return { path: relPath, action: 'merge', contents: next };\n}\n\nasync function packageJsonChange(root: string, useUnplugin: boolean): Promise<FileChange> {\n const raw = await readText(join(root, 'package.json'));\n const pkg = raw ? JSON.parse(raw) : { name: 'project', version: '0.0.0' };\n pkg.devDependencies = { ...(pkg.devDependencies ?? {}) };\n pkg.devDependencies['@clicksmith/daemon'] = pkg.devDependencies['@clicksmith/daemon'] ?? 'latest';\n if (useUnplugin) {\n pkg.devDependencies['@clicksmith/unplugin'] =\n pkg.devDependencies['@clicksmith/unplugin'] ?? 'latest';\n }\n return {\n path: 'package.json',\n action: raw == null ? 'create' : 'merge',\n contents: `${JSON.stringify(pkg, null, 2)}\\n`,\n };\n}\n\nasync function gitignoreChange(root: string): Promise<FileChange> {\n const existing = await readText(join(root, '.gitignore'));\n if (\n existing?.split(/\\r?\\n/).some((l) => l.trim() === '.clicksmith/' || l.trim() === '.clicksmith')\n ) {\n return { path: '.gitignore', action: 'skip', contents: existing };\n }\n const contents = existing\n ? `${existing.trimEnd()}\\n\\n# ClickSmith runtime state\\n.clicksmith/\\n`\n : '# ClickSmith runtime state\\n.clicksmith/\\n';\n return { path: '.gitignore', action: existing == null ? 'create' : 'merge', contents };\n}\n\nfunction installCmd(pm: PackageManager): string {\n return pm === 'npm' ? 'npm install' : `${pm} install`;\n}\n\nfunction runCmd(pm: PackageManager, script: string): string {\n switch (pm) {\n case 'pnpm':\n return `pnpm exec ${script}`;\n case 'yarn':\n return `yarn ${script}`;\n case 'bun':\n return `bunx ${script}`;\n case 'npm':\n return `npx ${script}`;\n }\n}\n\nasync function readText(file: string): Promise<string | undefined> {\n try {\n return await readFile(file, 'utf8');\n } catch {\n return undefined;\n }\n}\n"],"mappings":";AAAA,SAAS,UAAU,eAAe;AAClC,SAAS,YAAY;AAmBrB,IAAM,qBAAqB,CAAC,eAAe,aAAa,WAAW,SAAS;AAG5E,eAAsB,cAAc,MAAoC;AACtE,QAAM,MAAM,MAAM,SAAS,KAAK,MAAM,cAAc,CAAC;AACrD,QAAM,OAAO,EAAE,GAAI,KAAK,gBAAgB,CAAC,GAAI,GAAI,KAAK,mBAAmB,CAAC,EAAG;AAE7E,QAAM,iBAAiB,MAAM,qBAAqB,IAAI;AACtD,QAAM,UAAU,cAAc,IAAI;AAClC,QAAM,YAAY,gBAAgB,IAAI;AACtC,QAAM,aAAa,MAAM,SAAS,MAAM,CAAC,kBAAkB,kBAAkB,iBAAiB,CAAC;AAC/F,QAAM,cAAc,MAAM,gBAAgB,IAAI;AAE9C,QAAM,mBACJ,cAAc,WAAW,CAAC,QAAQ,WAAW,UAAU,QAAQ,EAAE,SAAS,OAAO;AAEnF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,EACrC;AACF;AAEA,eAAe,qBAAqB,MAAuC;AACzE,MAAI,MAAM,WAAW,KAAK,MAAM,gBAAgB,CAAC,EAAG,QAAO;AAC3D,MAAI,MAAM,WAAW,KAAK,MAAM,WAAW,CAAC,EAAG,QAAO;AACtD,MAAI,MAAM,WAAW,KAAK,MAAM,WAAW,CAAC,EAAG,QAAO;AACtD,SAAO;AACT;AAEA,SAAS,cAAc,MAAuC;AAC5D,MAAI,KAAK,KAAM,QAAO;AACtB,MAAI,KAAK,KAAM,QAAO;AACtB,MAAI,KAAK,cAAc,EAAG,QAAO;AACjC,MAAI,KAAK,QAAS,QAAO;AACzB,MAAI,KAAK,OAAQ,QAAO;AACxB,SAAO;AACT;AAEA,SAAS,gBAAgB,MAAyC;AAChE,MAAI,KAAK,eAAe,EAAG,QAAO;AAClC,MAAI,KAAK,OAAQ,QAAO;AACxB,MAAI,KAAK,IAAK,QAAO;AACrB,MAAI,KAAK,UAAU,EAAG,QAAO;AAC7B,MAAI,KAAK,MAAO,QAAO;AACvB,SAAO;AACT;AAGA,eAAe,gBAAgB,MAAiC;AAC9D,QAAM,QAAQ,oBAAI,IAAY;AAC9B,QAAM,OAAO,CAAC,OAAO,OAAO,YAAY;AACxC,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,MAAM,gBAAgB,KAAK,MAAM,GAAG,GAAG,EAAE;AACvD,eAAW,QAAQ,OAAO;AACxB,YAAM,UAAU,MAAM,SAAS,IAAI;AACnC,UAAI,CAAC,QAAS;AACd,iBAAW,QAAQ,oBAAoB;AACrC,YAAI,QAAQ,SAAS,IAAI,EAAG,OAAM,IAAI,IAAI;AAAA,MAC5C;AACA,UAAI,MAAM,SAAS,mBAAmB,OAAQ,QAAO,CAAC,GAAG,KAAK;AAAA,IAChE;AAAA,EACF;AACA,SAAO,CAAC,GAAG,KAAK;AAClB;AAEA,eAAe,gBAAgB,KAAa,OAAkC;AAC5E,QAAM,MAAgB,CAAC;AACvB,iBAAe,KAAK,GAAW;AAC7B,QAAI,IAAI,UAAU,MAAO;AACzB,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,QAAQ,GAAG,EAAE,eAAe,KAAK,CAAC;AAAA,IACpD,QAAQ;AACN;AAAA,IACF;AACA,eAAW,SAAS,SAAS;AAC3B,UAAI,IAAI,UAAU,MAAO;AACzB,UAAI,MAAM,SAAS,kBAAkB,MAAM,KAAK,WAAW,GAAG,EAAG;AACjE,YAAM,OAAO,KAAK,GAAG,MAAM,IAAI;AAC/B,UAAI,MAAM,YAAY,EAAG,OAAM,KAAK,IAAI;AAAA,eAC/B,4BAA4B,KAAK,MAAM,IAAI,EAAG,KAAI,KAAK,IAAI;AAAA,IACtE;AAAA,EACF;AACA,QAAM,KAAK,GAAG;AACd,SAAO;AACT;AAEA,eAAe,SAAS,MAAc,OAA8C;AAClF,aAAW,QAAQ,OAAO;AACxB,QAAI,MAAM,WAAW,KAAK,MAAM,IAAI,CAAC,EAAG,QAAO;AAAA,EACjD;AACA,SAAO;AACT;AAEA,eAAe,SAAS,MAA4D;AAClF,QAAM,MAAM,MAAM,SAAS,IAAI;AAC/B,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI;AACF,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,SAAS,MAA2C;AACjE,MAAI;AACF,WAAO,MAAM,SAAS,MAAM,MAAM;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,WAAW,MAAgC;AACxD,SAAQ,MAAM,SAAS,IAAI,MAAO;AACpC;;;AC3IA,SAAS,OAAO,YAAAA,WAAU,iBAAiB;AAC3C,SAAS,SAAS,QAAAC,aAAY;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,2BAA2B;AAwBpC,IAAM,wBAA6C,CAAC,UAAU,UAAU,SAAS,SAAS;AAO1F,eAAsB,YACpB,MACA,UAA0B,CAAC,GACL;AACtB,QAAM,OAAO,KAAK;AAClB,QAAM,UAAU,QAAQ,UAAU;AAClC,QAAM,cAAc,QAAQ,eAAe,KAAK;AAChD,QAAM,OAAO,QAAQ,cAAc;AAEnC,QAAM,UAAwB,CAAC;AAC/B,QAAM,WAAqB,CAAC;AAC5B,QAAM,YAAsB,CAAC;AAG7B,aAAW,UAAU,SAAS;AAC5B,UAAM,WAAW,mBAAmB,QAAQ;AAAA,MAC1C,aAAa,KAAK;AAAA,MAClB,YAAY;AAAA,IACd,CAAC;AACD,UAAM,WAAW,MAAM,SAASA,MAAK,MAAM,SAAS,IAAI,CAAC;AACzD,UAAM,WAAW,SAAS,SACtB,kBAAkB,UAAU,SAAS,OAAO,IAC5C,SAAS;AACb,YAAQ,KAAK;AAAA,MACX,MAAM,SAAS;AAAA,MACf,QAAQ,YAAY,OAAO,WAAW;AAAA,MACtC;AAAA,IACF,CAAC;AAAA,EACH;AAIA,QAAM,aAAaA,MAAK,eAAe,oBAAoB;AAC3D,QAAM,oBAAoB,MAAM,SAASA,MAAK,MAAM,UAAU,CAAC;AAC/D,MAAI,qBAAqB,MAAM;AAC7B,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,UAAU,GAAG,KAAK;AAAA,QAChB,EAAE,SAAS,GAAG,cAAc,sBAAsB,cAAc,QAAQ,CAAC,EAAE;AAAA,QAC3E;AAAA,QACA;AAAA,MACF,CAAC;AAAA;AAAA,IACH,CAAC;AAAA,EACH,OAAO;AACL,QAAI;AACF,YAAM,SAAS,kBAAkB,KAAK,MAAM,iBAAiB,CAAC;AAC9D,UAAI,CAAC,OAAO,IAAI;AACd,iBAAS,KAAK,6DAA6D;AAAA,MAC7E;AAAA,IACF,QAAQ;AACN,eAAS,KAAK,kEAAkE;AAAA,IAClF;AACA,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAGA,QAAM,MAAM,WAAW,KAAK,cAAc;AAC1C,UAAQ,KAAK,MAAM,UAAU,MAAM,aAAa,GAAG,CAAC;AACpD,MAAI,QAAQ,SAAS,QAAQ,GAAG;AAC9B,YAAQ,KAAK,MAAM,UAAU,MAAMA,MAAK,WAAW,UAAU,GAAG,GAAG,CAAC;AAAA,EACtE;AAGA,MAAI,eAAe,KAAK,YAAY;AAClC,UAAM,QAAQ,MAAM,eAAe,MAAM,KAAK,UAAU;AACxD,QAAI,MAAO,SAAQ,KAAK,KAAK;AAAA;AAE3B,eAAS;AAAA,QACP,gCAAgC,KAAK,UAAU;AAAA,MACjD;AAAA,EACJ,WAAW,aAAa;AACtB,aAAS;AAAA,MACP;AAAA,IACF;AAAA,EACF,OAAO;AACL,aAAS;AAAA,MACP,4FACE,KAAK,YAAY,SACb,oBAAoB,KAAK,YAAY,KAAK,IAAI,CAAC,MAC/C,0BACN;AAAA,IACF;AAAA,EACF;AAGA,UAAQ,KAAK,MAAM,kBAAkB,MAAM,WAAW,CAAC;AAGvD,UAAQ,KAAK,MAAM,gBAAgB,IAAI,CAAC;AAExC,YAAU;AAAA,IACR,GAAG,WAAW,KAAK,cAAc,CAAC;AAAA,IAClC,GAAG,OAAO,KAAK,gBAAgB,mBAAmB,CAAC;AAAA,IACnD,GAAG,OAAO,KAAK,gBAAgB,mBAAmB,CAAC;AAAA,IACnD;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,UAAU,UAAU;AACxC;AAGA,eAAsB,UAAU,MAAc,MAA0C;AACtF,QAAM,UAAwB,CAAC;AAC/B,aAAW,UAAU,KAAK,SAAS;AACjC,QAAI,OAAO,WAAW,OAAQ;AAC9B,UAAM,OAAOA,MAAK,MAAM,OAAO,IAAI;AACnC,UAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,UAAM,UAAU,MAAM,OAAO,UAAU,MAAM;AAC7C,YAAQ,KAAK,MAAM;AAAA,EACrB;AACA,SAAO;AACT;AASA,SAAS,WAAW,IAAmC;AACrD,UAAQ,IAAI;AAAA,IACV,KAAK;AACH,aAAO,EAAE,SAAS,QAAQ,MAAM,CAAC,QAAQ,cAAc,KAAK,EAAE;AAAA,IAChE,KAAK;AACH,aAAO,EAAE,SAAS,QAAQ,MAAM,CAAC,cAAc,KAAK,EAAE;AAAA,IACxD,KAAK;AACH,aAAO,EAAE,SAAS,QAAQ,MAAM,CAAC,cAAc,KAAK,EAAE;AAAA,IACxD,KAAK;AACH,aAAO,EAAE,SAAS,OAAO,MAAM,CAAC,cAAc,KAAK,EAAE;AAAA,EACzD;AACF;AAEA,eAAe,UAAU,MAAc,MAAc,MAA0C;AAC7F,QAAM,cAAc,MAAM,SAASA,MAAK,MAAM,IAAI,CAAC;AACnD,MAAI,MAAgD,CAAC;AACrD,MAAI,aAAa;AACf,QAAI;AACF,YAAM,KAAK,MAAM,WAAW;AAAA,IAC9B,QAAQ;AACN,YAAM,CAAC;AAAA,IACT;AAAA,EACF;AACA,MAAI,aAAa,EAAE,GAAI,IAAI,cAAc,CAAC,GAAI,YAAY,KAAK;AAC/D,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,eAAe,OAAO,WAAW;AAAA,IACzC,UAAU,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,CAAC;AAAA;AAAA,EAC3C;AACF;AAEA,eAAe,eAAe,MAAc,SAA6C;AACvF,QAAM,OAAOA,MAAK,MAAM,OAAO;AAC/B,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,QAAQ,KAAM,QAAO;AACzB,MAAI,KAAK,SAAS,sBAAsB,GAAG;AACzC,WAAO,EAAE,MAAM,SAAS,QAAQ,QAAQ,UAAU,MAAM,QAAQ,gBAAgB;AAAA,EAClF;AACA,QAAM,aAAa;AAAA;AAEnB,QAAM,eAAe,KAAK,MAAM,kBAAkB;AAClD,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,MAAM,aAAa,QAAS,aAAa,CAAC,EAAE;AAClD,QAAM,OAAO,aAAa,KAAK,MAAM,GAAG,GAAG,IAAI,mBAAmB,KAAK,MAAM,GAAG;AAChF,SAAO,EAAE,MAAM,SAAS,QAAQ,SAAS,UAAU,KAAK;AAC1D;AAEA,eAAe,kBAAkB,MAAc,aAA2C;AACxF,QAAM,MAAM,MAAM,SAASA,MAAK,MAAM,cAAc,CAAC;AACrD,QAAM,MAAM,MAAM,KAAK,MAAM,GAAG,IAAI,EAAE,MAAM,WAAW,SAAS,QAAQ;AACxE,MAAI,kBAAkB,EAAE,GAAI,IAAI,mBAAmB,CAAC,EAAG;AACvD,MAAI,gBAAgB,oBAAoB,IAAI,IAAI,gBAAgB,oBAAoB,KAAK;AACzF,MAAI,aAAa;AACf,QAAI,gBAAgB,sBAAsB,IACxC,IAAI,gBAAgB,sBAAsB,KAAK;AAAA,EACnD;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,OAAO,OAAO,WAAW;AAAA,IACjC,UAAU,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,CAAC;AAAA;AAAA,EAC3C;AACF;AAEA,eAAe,gBAAgB,MAAmC;AAChE,QAAM,WAAW,MAAM,SAASA,MAAK,MAAM,YAAY,CAAC;AACxD,MACE,UAAU,MAAM,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,MAAM,kBAAkB,EAAE,KAAK,MAAM,aAAa,GAC9F;AACA,WAAO,EAAE,MAAM,cAAc,QAAQ,QAAQ,UAAU,SAAS;AAAA,EAClE;AACA,QAAM,WAAW,WACb,GAAG,SAAS,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA,IACrB;AACJ,SAAO,EAAE,MAAM,cAAc,QAAQ,YAAY,OAAO,WAAW,SAAS,SAAS;AACvF;AAEA,SAAS,WAAW,IAA4B;AAC9C,SAAO,OAAO,QAAQ,gBAAgB,GAAG,EAAE;AAC7C;AAEA,SAAS,OAAO,IAAoB,QAAwB;AAC1D,UAAQ,IAAI;AAAA,IACV,KAAK;AACH,aAAO,aAAa,MAAM;AAAA,IAC5B,KAAK;AACH,aAAO,QAAQ,MAAM;AAAA,IACvB,KAAK;AACH,aAAO,QAAQ,MAAM;AAAA,IACvB,KAAK;AACH,aAAO,OAAO,MAAM;AAAA,EACxB;AACF;AAEA,eAAe,SAAS,MAA2C;AACjE,MAAI;AACF,WAAO,MAAMD,UAAS,MAAM,MAAM;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":["readFile","join"]}
1
+ {"version":3,"sources":["../src/detect.ts","../src/install.ts"],"sourcesContent":["import { readFile, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport type PackageManager = 'pnpm' | 'yarn' | 'bun' | 'npm';\nexport type Bundler = 'vite' | 'webpack' | 'rspack' | 'rollup' | 'next' | 'unknown';\nexport type Framework = 'react' | 'vue' | 'svelte' | 'angular' | 'solid' | 'unknown';\n\nexport interface ProjectInfo {\n root: string;\n packageManager: PackageManager;\n bundler: Bundler;\n framework: Framework;\n /** Stable attributes already used in the codebase (e.g. data-testid). */\n stableAttrs: string[];\n /** Whether the dev-only data-loc unplugin can be wired up for this stack. */\n supportsUnplugin: boolean;\n /** Path to the detected Vite config, if any (relative to root). */\n viteConfig?: string;\n}\n\nconst KNOWN_STABLE_ATTRS = ['data-testid', 'data-test', 'data-cy', 'data-qa'];\n\n/** Inspect a project directory and infer its stack. Read-only. */\nexport async function detectProject(root: string): Promise<ProjectInfo> {\n const pkg = await readJson(join(root, 'package.json'));\n const deps = { ...(pkg?.dependencies ?? {}), ...(pkg?.devDependencies ?? {}) } as Record<string, string>;\n\n const packageManager = await detectPackageManager(root);\n const bundler = detectBundler(deps);\n const framework = detectFramework(deps);\n const viteConfig = await findFile(root, ['vite.config.ts', 'vite.config.js', 'vite.config.mjs']);\n const stableAttrs = await scanStableAttrs(root);\n\n const supportsUnplugin =\n framework === 'react' && ['vite', 'webpack', 'rspack', 'rollup'].includes(bundler);\n\n return {\n root,\n packageManager,\n bundler,\n framework,\n stableAttrs,\n supportsUnplugin,\n ...(viteConfig ? { viteConfig } : {}),\n };\n}\n\nasync function detectPackageManager(root: string): Promise<PackageManager> {\n if (await fileExists(join(root, 'pnpm-lock.yaml'))) return 'pnpm';\n if (await fileExists(join(root, 'yarn.lock'))) return 'yarn';\n if (await fileExists(join(root, 'bun.lockb'))) return 'bun';\n return 'npm';\n}\n\nfunction detectBundler(deps: Record<string, string>): Bundler {\n if (deps.next) return 'next';\n if (deps.vite) return 'vite';\n if (deps['@rspack/core']) return 'rspack';\n if (deps.webpack) return 'webpack';\n if (deps.rollup) return 'rollup';\n return 'unknown';\n}\n\nfunction detectFramework(deps: Record<string, string>): Framework {\n if (deps['@angular/core']) return 'angular';\n if (deps.svelte) return 'svelte';\n if (deps.vue) return 'vue';\n if (deps['solid-js']) return 'solid';\n if (deps.react) return 'react';\n return 'unknown';\n}\n\n/** Scan a handful of source files for stable test/id attributes. */\nasync function scanStableAttrs(root: string): Promise<string[]> {\n const found = new Set<string>();\n const dirs = ['src', 'app', 'components'];\n for (const dir of dirs) {\n const files = await listSourceFiles(join(root, dir), 40);\n for (const file of files) {\n const content = await safeRead(file);\n if (!content) continue;\n for (const attr of KNOWN_STABLE_ATTRS) {\n if (content.includes(attr)) found.add(attr);\n }\n if (found.size === KNOWN_STABLE_ATTRS.length) return [...found];\n }\n }\n return [...found];\n}\n\nasync function listSourceFiles(dir: string, limit: number): Promise<string[]> {\n const out: string[] = [];\n async function walk(d: string) {\n if (out.length >= limit) return;\n let entries;\n try {\n entries = await readdir(d, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n if (out.length >= limit) return;\n if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue;\n const full = join(d, entry.name);\n if (entry.isDirectory()) await walk(full);\n else if (/\\.(jsx?|tsx?|vue|svelte)$/.test(entry.name)) out.push(full);\n }\n }\n await walk(dir);\n return out;\n}\n\nasync function findFile(root: string, names: string[]): Promise<string | undefined> {\n for (const name of names) {\n if (await fileExists(join(root, name))) return name;\n }\n return undefined;\n}\n\nasync function readJson(file: string): Promise<Record<string, unknown> | undefined> {\n const raw = await safeRead(file);\n if (!raw) return undefined;\n try {\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n}\n\nasync function safeRead(file: string): Promise<string | undefined> {\n try {\n return await readFile(file, 'utf8');\n } catch {\n return undefined;\n }\n}\n\nasync function fileExists(file: string): Promise<boolean> {\n return (await safeRead(file)) !== undefined;\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport {\n applyManagedBlock,\n DEFAULT_AGENTS_CONFIG,\n parseAgentsConfig,\n renderInstructions,\n type InstructionTarget,\n} from '@clicksmith/agent-config';\nimport { DEFAULT_DAEMON_PORT } from '@clicksmith/core';\nimport type { PackageManager, ProjectInfo } from './detect.js';\n\nexport interface InstallOptions {\n /** Which agents to render instruction files for. */\n agents?: InstructionTarget[];\n /** Wire the dev-only data-loc unplugin. Defaults to detection. */\n useUnplugin?: boolean;\n daemonPort?: number;\n}\n\nexport interface FileChange {\n path: string;\n action: 'create' | 'merge' | 'skip';\n contents: string;\n reason?: string;\n}\n\nexport interface InstallPlan {\n changes: FileChange[];\n messages: string[];\n nextSteps: string[];\n}\n\nconst DEFAULT_AGENT_TARGETS: InstructionTarget[] = ['claude', 'cursor', 'codex', 'generic'];\n\n/**\n * Compute every file change needed to install ClickSmith into a project,\n * reading existing files so merges preserve user content. Nothing is written —\n * call {@link applyPlan} for that. This split keeps the installer fully testable.\n */\nexport async function planInstall(\n info: ProjectInfo,\n options: InstallOptions = {},\n): Promise<InstallPlan> {\n const root = info.root;\n const targets = options.agents ?? DEFAULT_AGENT_TARGETS;\n const useUnplugin = options.useUnplugin ?? info.supportsUnplugin;\n const port = options.daemonPort ?? DEFAULT_DAEMON_PORT;\n\n const changes: FileChange[] = [];\n const messages: string[] = [];\n const nextSteps: string[] = [];\n\n // 1. Agent instruction files (managed blocks preserve user content).\n for (const target of targets) {\n const rendered = renderInstructions(target, {\n stableAttrs: info.stableAttrs,\n daemonPort: port,\n });\n const existing = await readText(join(root, rendered.path));\n const contents = rendered.shared\n ? applyManagedBlock(existing, rendered.content)\n : rendered.content;\n changes.push({\n path: rendered.path,\n action: existing == null ? 'create' : 'merge',\n contents,\n });\n }\n\n // 2. agents.config.json — keep user-defined agents only. The daemon ships\n // built-in defaults, so any entry whose ID matches a built-in is stripped so\n // the daemon's latest default (with correct placeholders) takes over. Custom\n // agent IDs are preserved untouched.\n const configPath = join('.clicksmith', 'agents.config.json');\n const existingConfigRaw = await readText(join(root, configPath));\n const builtinIds = new Set(DEFAULT_AGENTS_CONFIG.agents.map((a) => a.id));\n if (existingConfigRaw == null) {\n changes.push({\n path: configPath,\n action: 'create',\n contents: `${JSON.stringify(\n { version: 1, defaultAgent: DEFAULT_AGENTS_CONFIG.defaultAgent, agents: [] },\n null,\n 2,\n )}\\n`,\n });\n } else {\n try {\n const doc = JSON.parse(existingConfigRaw);\n const parsed = parseAgentsConfig(doc);\n if (!parsed.ok) {\n messages.push('Existing agents.config.json was invalid; left it untouched.');\n changes.push({ path: configPath, action: 'skip', contents: existingConfigRaw });\n } else {\n const customAgents = (parsed.config.agents ?? []).filter((a) => !builtinIds.has(a.id));\n const hadBuiltins = customAgents.length < (parsed.config.agents ?? []).length;\n const updated = { ...doc, agents: customAgents };\n changes.push({\n path: configPath,\n action: hadBuiltins ? 'merge' : 'skip',\n contents: `${JSON.stringify(updated, null, 2)}\\n`,\n reason: hadBuiltins\n ? 'stripped built-in agent entries so daemon defaults take over'\n : 'no built-in entries found; preserved as-is',\n });\n if (hadBuiltins) {\n messages.push(\n 'agents.config.json: removed built-in agent entries — daemon will use its latest defaults.',\n );\n }\n }\n } catch {\n messages.push('Existing agents.config.json was invalid JSON; left it untouched.');\n changes.push({ path: configPath, action: 'skip', contents: existingConfigRaw });\n }\n }\n\n // 3. MCP registration for the daemon's stdio server.\n const mcp = mcpCommand(info.packageManager);\n changes.push(await mcpChange(root, '.mcp.json', mcp));\n if (targets.includes('cursor')) {\n changes.push(await mcpChange(root, join('.cursor', 'mcp.json'), mcp));\n }\n\n // 4. Wire the data-loc unplugin (best effort) + record the dependency.\n if (useUnplugin && info.viteConfig) {\n const wired = await wireViteConfig(root, info.viteConfig);\n if (wired) changes.push(wired);\n else\n messages.push(\n `Could not automatically wire ${info.viteConfig}. Add: import clicksmith from '@clicksmith/unplugin/vite'; and put clicksmith() first in plugins.`,\n );\n } else if (useUnplugin) {\n messages.push(\n 'No Vite config found — add the @clicksmith/unplugin plugin to your bundler manually.',\n );\n } else {\n messages.push(\n `Stable source locators via unplugin aren't available for this stack; ClickSmith will use ${\n info.stableAttrs.length\n ? `your attributes (${info.stableAttrs.join(', ')})`\n : 'attribute/behavioral/DOM'\n } as the fallback locator (source → attr → behavioral → dom).`,\n );\n }\n\n // 5. package.json — add the dependencies needed for the bin + plugin.\n changes.push(await packageJsonChange(root, useUnplugin));\n\n // 6. Ensure .clicksmith/ is gitignored.\n changes.push(await gitignoreChange(root));\n\n nextSteps.push(\n `${installCmd(info.packageManager)} # install the new dependencies`,\n `${runCmd(info.packageManager, 'clicksmith daemon')} # start the localhost daemon`,\n `${runCmd(info.packageManager, 'clicksmith doctor')} # verify agent CLIs are visible to the daemon`,\n 'Install the ClickSmith browser extension, toggle AI Mode, and Alt+Click an element.',\n );\n\n return { changes, messages, nextSteps };\n}\n\n/** Apply a plan to disk, writing create/merge changes and skipping the rest. */\nexport async function applyPlan(root: string, plan: InstallPlan): Promise<FileChange[]> {\n const written: FileChange[] = [];\n for (const change of plan.changes) {\n if (change.action === 'skip') continue;\n const full = join(root, change.path);\n await mkdir(dirname(full), { recursive: true });\n await writeFile(full, change.contents, 'utf8');\n written.push(change);\n }\n return written;\n}\n\n/* ------------------------------- helpers ---------------------------------- */\n\ninterface McpServerSpec {\n command: string;\n args: string[];\n}\n\nfunction mcpCommand(pm: PackageManager): McpServerSpec {\n switch (pm) {\n case 'pnpm':\n return { command: 'pnpm', args: ['exec', 'clicksmith', 'mcp'] };\n case 'yarn':\n return { command: 'yarn', args: ['clicksmith', 'mcp'] };\n case 'bun':\n return { command: 'bunx', args: ['clicksmith', 'mcp'] };\n case 'npm':\n return { command: 'npx', args: ['clicksmith', 'mcp'] };\n }\n}\n\nasync function mcpChange(root: string, path: string, spec: McpServerSpec): Promise<FileChange> {\n const existingRaw = await readText(join(root, path));\n let doc: { mcpServers?: Record<string, unknown> } = {};\n if (existingRaw) {\n try {\n doc = JSON.parse(existingRaw);\n } catch {\n doc = {};\n }\n }\n doc.mcpServers = { ...(doc.mcpServers ?? {}), clicksmith: spec };\n return {\n path,\n action: existingRaw == null ? 'create' : 'merge',\n contents: `${JSON.stringify(doc, null, 2)}\\n`,\n };\n}\n\nasync function wireViteConfig(root: string, relPath: string): Promise<FileChange | null> {\n const file = join(root, relPath);\n const code = await readText(file);\n if (code == null) return null;\n if (code.includes('@clicksmith/unplugin')) {\n return { path: relPath, action: 'skip', contents: code, reason: 'already wired' };\n }\n const importLine = `import clicksmith from '@clicksmith/unplugin/vite';\\n`;\n // Insert plugin first in the array so it runs before the framework plugin.\n const pluginsMatch = code.match(/plugins\\s*:\\s*\\[/);\n if (!pluginsMatch) return null;\n const idx = pluginsMatch.index! + pluginsMatch[0].length;\n const next = importLine + code.slice(0, idx) + 'clicksmith(), ' + code.slice(idx);\n return { path: relPath, action: 'merge', contents: next };\n}\n\nasync function packageJsonChange(root: string, useUnplugin: boolean): Promise<FileChange> {\n const raw = await readText(join(root, 'package.json'));\n const pkg = raw ? JSON.parse(raw) : { name: 'project', version: '0.0.0' };\n pkg.devDependencies = { ...(pkg.devDependencies ?? {}) };\n pkg.devDependencies['@clicksmith/daemon'] = pkg.devDependencies['@clicksmith/daemon'] ?? 'latest';\n if (useUnplugin) {\n pkg.devDependencies['@clicksmith/unplugin'] =\n pkg.devDependencies['@clicksmith/unplugin'] ?? 'latest';\n }\n return {\n path: 'package.json',\n action: raw == null ? 'create' : 'merge',\n contents: `${JSON.stringify(pkg, null, 2)}\\n`,\n };\n}\n\nasync function gitignoreChange(root: string): Promise<FileChange> {\n const existing = await readText(join(root, '.gitignore'));\n if (\n existing?.split(/\\r?\\n/).some((l) => l.trim() === '.clicksmith/' || l.trim() === '.clicksmith')\n ) {\n return { path: '.gitignore', action: 'skip', contents: existing };\n }\n const contents = existing\n ? `${existing.trimEnd()}\\n\\n# ClickSmith runtime state\\n.clicksmith/\\n`\n : '# ClickSmith runtime state\\n.clicksmith/\\n';\n return { path: '.gitignore', action: existing == null ? 'create' : 'merge', contents };\n}\n\nfunction installCmd(pm: PackageManager): string {\n return pm === 'npm' ? 'npm install' : `${pm} install`;\n}\n\nfunction runCmd(pm: PackageManager, script: string): string {\n switch (pm) {\n case 'pnpm':\n return `pnpm exec ${script}`;\n case 'yarn':\n return `yarn ${script}`;\n case 'bun':\n return `bunx ${script}`;\n case 'npm':\n return `npx ${script}`;\n }\n}\n\nasync function readText(file: string): Promise<string | undefined> {\n try {\n return await readFile(file, 'utf8');\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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","names":["readFile","join"]}
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  applyPlan,
4
4
  detectProject,
5
5
  planInstall
6
- } from "./chunk-TE3BLNLM.js";
6
+ } from "./chunk-DAPCF44S.js";
7
7
 
8
8
  // src/cli.ts
9
9
  var HELP = `create-clicksmith
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  applyPlan,
3
3
  detectProject,
4
4
  planInstall
5
- } from "./chunk-TE3BLNLM.js";
5
+ } from "./chunk-DAPCF44S.js";
6
6
  export {
7
7
  applyPlan,
8
8
  detectProject,
package/package.json CHANGED
@@ -1,27 +1,18 @@
1
1
  {
2
2
  "name": "create-clicksmith",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Project installer for ClickSmith: detect your stack, wire stable locators, write agent instructions, merge agents.config.json, and register the MCP server.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "exports": {
8
- ".": {
9
- "types": "./dist/index.d.ts",
10
- "import": "./dist/index.js"
11
- }
8
+ ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" }
12
9
  },
13
10
  "main": "./dist/index.js",
14
11
  "types": "./dist/index.d.ts",
15
12
  "bin": {
16
13
  "create-clicksmith": "./dist/cli.js"
17
14
  },
18
- "files": [
19
- "dist"
20
- ],
21
- "dependencies": {
22
- "@clicksmith/agent-config": "0.1.4",
23
- "@clicksmith/core": "0.1.1"
24
- },
15
+ "files": ["dist"],
25
16
  "scripts": {
26
17
  "build": "tsup",
27
18
  "dev": "tsup --watch",
@@ -30,5 +21,9 @@
30
21
  "typecheck": "tsc --noEmit",
31
22
  "lint": "eslint src",
32
23
  "clean": "rimraf dist .turbo"
24
+ },
25
+ "dependencies": {
26
+ "@clicksmith/agent-config": "workspace:*",
27
+ "@clicksmith/core": "workspace:*"
33
28
  }
34
- }
29
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 ClickSmith contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.