create-clicksmith 0.1.3 → 0.1.4
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
|
|
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-
|
|
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":"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","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
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-clicksmith",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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",
|