@clicksmith/daemon 0.1.5 → 0.1.6
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/dist/{chunk-SNJE2UQR.js → chunk-273SGCZB.js} +2 -2
- package/dist/{chunk-SNJE2UQR.js.map → chunk-273SGCZB.js.map} +1 -1
- package/dist/{chunk-X2P7S4L4.js → chunk-D5ABXTTD.js} +168 -50
- package/dist/chunk-D5ABXTTD.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/mcp.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-X2P7S4L4.js.map +0 -1
|
@@ -479,7 +479,7 @@ function json(value) {
|
|
|
479
479
|
}
|
|
480
480
|
|
|
481
481
|
// src/version.ts
|
|
482
|
-
var version = "0.1.
|
|
482
|
+
var version = "0.1.6";
|
|
483
483
|
|
|
484
484
|
// src/mcp.ts
|
|
485
485
|
function createMcpServer(reader) {
|
|
@@ -538,4 +538,4 @@ export {
|
|
|
538
538
|
createMcpServer,
|
|
539
539
|
startMcp
|
|
540
540
|
};
|
|
541
|
-
//# sourceMappingURL=chunk-
|
|
541
|
+
//# sourceMappingURL=chunk-273SGCZB.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcp.ts","../src/config.ts","../src/git.ts","../src/paths.ts","../src/logger.ts","../src/store.ts","../src/mcp-tools.ts","../src/version.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { resolveDaemonConfig } from './config.js';\nimport { FileStore } from './store.js';\nimport { callTool, readerFromStore, TOOL_DEFINITIONS, type McpReader } from './mcp-tools.js';\nimport { version } from './version.js';\n\n/**\n * Create an MCP {@link Server} exposing ClickSmith's read-only tools over the\n * given reader. The daemon and a standalone `clicksmith mcp` process both use\n * this; state is shared via the on-disk store, so the MCP process never needs\n * to talk to the HTTP daemon directly.\n */\nexport function createMcpServer(reader: McpReader): Server {\n const server = new Server(\n { name: 'clicksmith', version },\n { capabilities: { tools: {} } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: TOOL_DEFINITIONS.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n const result = await callTool(name, args ?? {}, reader);\n if (!result.ok) {\n return { content: [{ type: 'text', text: result.error }], isError: true };\n }\n return { content: [{ type: 'text', text: result.text }] };\n });\n\n return server;\n}\n\n/** Entry point for `clicksmith mcp`: connect the stdio transport. */\nexport async function startMcp(): Promise<void> {\n const config = await resolveDaemonConfig({ logLevel: 'silent' });\n const store = new FileStore(config.storageRoot);\n await store.init();\n const server = createMcpServer(readerFromStore(store));\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\n// Allow running this file directly as the MCP server.\nif (import.meta.url === `file://${process.argv[1]}`) {\n startMcp().catch((err) => {\n process.stderr.write(`clicksmith mcp failed: ${err instanceof Error ? err.stack : String(err)}\\n`);\n process.exit(1);\n });\n}\n","import { readFile } from 'node:fs/promises';\nimport {\n DEFAULT_AGENTS_CONFIG,\n mergeAgentsConfig,\n parseAgentsConfig,\n type AgentsConfig,\n} from '@clicksmith/agent-config';\nimport { DEFAULT_DAEMON_HOST, DEFAULT_DAEMON_PORT, DEFAULT_SESSION_TTL_MS } from '@clicksmith/core';\nimport { Git } from './git.js';\nimport { resolveStorageRoot, storagePaths } from './paths.js';\nimport { createLogger, type LogLevel, type Logger } from './logger.js';\n\nexport interface DaemonConfigInput {\n cwd?: string;\n host?: string;\n port?: number;\n ttlMs?: number;\n logLevel?: LogLevel;\n /** Override the storage root (mainly for tests). */\n storageRoot?: string;\n /** Override repo detection (mainly for tests). */\n repoRoot?: string | null;\n}\n\nexport interface DaemonConfig {\n host: string;\n port: number;\n ttlMs: number;\n cwd: string;\n repoRoot: string | null;\n storageRoot: string;\n agents: AgentsConfig;\n logger: Logger;\n}\n\n/**\n * Resolve the full daemon configuration: detect the repo, choose a storage\n * root, and layer agent configs (shipped defaults → project `agents.config.json`).\n */\nexport async function resolveDaemonConfig(input: DaemonConfigInput = {}): Promise<DaemonConfig> {\n const cwd = input.cwd ?? process.cwd();\n const repoRoot = input.repoRoot !== undefined ? input.repoRoot : await Git.findRepoRoot(cwd);\n const storageRoot = input.storageRoot ?? resolveStorageRoot(repoRoot);\n const agents = await loadAgentsConfig(storageRoot);\n\n return {\n host: input.host ?? DEFAULT_DAEMON_HOST,\n port: input.port ?? DEFAULT_DAEMON_PORT,\n ttlMs: input.ttlMs ?? DEFAULT_SESSION_TTL_MS,\n cwd,\n repoRoot,\n storageRoot,\n agents,\n logger: createLogger(input.logLevel ?? 'info'),\n };\n}\n\n/** Load `agents.config.json` from the storage root and merge over defaults. */\nexport async function loadAgentsConfig(storageRoot: string): Promise<AgentsConfig> {\n const file = storagePaths(storageRoot).config;\n let raw: string | undefined;\n try {\n raw = await readFile(file, 'utf8');\n } catch {\n return DEFAULT_AGENTS_CONFIG;\n }\n let json: unknown;\n try {\n json = JSON.parse(raw);\n } catch {\n return DEFAULT_AGENTS_CONFIG;\n }\n const parsed = parseAgentsConfig(json);\n if (!parsed.ok) return DEFAULT_AGENTS_CONFIG;\n return mergeAgentsConfig(DEFAULT_AGENTS_CONFIG, parsed.config);\n}\n","import { execa } from 'execa';\nimport { mkdtemp, rm, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport type { Isolation, SandboxInfo } from '@clicksmith/core';\n\nexport class GitError extends Error {}\n\n/** Thin wrapper around the `git` CLI, scoped to a working directory. */\nexport class Git {\n constructor(private readonly cwd: string) {}\n\n private async run(args: string[], opts: { reject?: boolean } = {}) {\n const result = await execa('git', args, { cwd: this.cwd, reject: false });\n if (opts.reject !== false && result.exitCode !== 0) {\n throw new GitError(`git ${args.join(' ')} failed: ${result.stderr || result.stdout}`);\n }\n return result;\n }\n\n /** Absolute repo root, or `null` if `cwd` is not inside a git repo. */\n static async findRepoRoot(cwd: string): Promise<string | null> {\n const result = await execa('git', ['rev-parse', '--show-toplevel'], { cwd, reject: false });\n return result.exitCode === 0 ? result.stdout.trim() : null;\n }\n\n async headCommit(): Promise<string> {\n return (await this.run(['rev-parse', 'HEAD'])).stdout.trim();\n }\n\n async currentBranch(): Promise<string> {\n return (await this.run(['rev-parse', '--abbrev-ref', 'HEAD'])).stdout.trim();\n }\n\n /**\n * Whether the working tree has uncommitted (tracked or untracked) changes,\n * ignoring any paths under `exclude` prefixes (e.g. ClickSmith's own\n * `.clicksmith/` state directory).\n */\n async isDirty(opts: { exclude?: string[] } = {}): Promise<boolean> {\n const result = await this.run(['status', '--porcelain']);\n const exclude = opts.exclude ?? [];\n return result.stdout\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean)\n .some((line) => {\n const path = line.slice(2).trim();\n const actual = path.includes(' -> ') ? path.split(' -> ')[1]! : path;\n return !exclude.some((prefix) => actual.startsWith(prefix));\n });\n }\n\n /** Whether this git supports worktrees (>= 2.5). */\n async supportsWorktree(): Promise<boolean> {\n const result = await execa('git', ['worktree', 'list'], { cwd: this.cwd, reject: false });\n return result.exitCode === 0;\n }\n\n /**\n * Create a throwaway worktree on a fresh branch for a run. The worktree lives\n * outside the main tree so the agent's edits never touch it.\n */\n async createWorktree(path: string, branch: string, baseRef: string): Promise<void> {\n await this.run(['worktree', 'add', '-b', branch, path, baseRef]);\n }\n\n async removeWorktree(path: string, branch?: string): Promise<void> {\n await this.run(['worktree', 'remove', '--force', path], { reject: false });\n if (branch) await this.run(['branch', '-D', branch], { reject: false });\n }\n\n /** Create + checkout a dedicated branch (the worktree fallback). */\n async createBranch(branch: string, baseRef: string): Promise<void> {\n await this.run(['switch', '-c', branch, baseRef]);\n }\n\n async switchTo(ref: string): Promise<void> {\n await this.run(['switch', ref]);\n }\n\n async deleteBranch(branch: string): Promise<void> {\n await this.run(['branch', '-D', branch], { reject: false });\n }\n\n /**\n * Capture every change in a sandbox (relative to its HEAD) as a single\n * binary-safe patch, including new and deleted files. Returns '' if clean.\n */\n static async captureDiff(sandboxPath: string): Promise<string> {\n await execa('git', ['add', '-A'], { cwd: sandboxPath, reject: false });\n const result = await execa('git', ['diff', '--cached', '--binary'], {\n cwd: sandboxPath,\n reject: false,\n });\n return result.exitCode === 0 ? result.stdout : '';\n }\n\n /**\n * Apply a captured patch onto the main working tree using a 3-way merge,\n * staging the result. Returns the list of conflicted files (empty on success).\n */\n async applyPatch(patch: string): Promise<{ ok: boolean; conflicts: string[] }> {\n if (!patch.trim()) return { ok: true, conflicts: [] };\n const tmp = await mkdtemp(join(tmpdir(), 'clicksmith-patch-'));\n const patchFile = join(tmp, 'run.patch');\n try {\n await writeFile(patchFile, patch.endsWith('\\n') ? patch : `${patch}\\n`, 'utf8');\n const result = await this.run(['apply', '--index', '--3way', patchFile], { reject: false });\n if (result.exitCode === 0) return { ok: true, conflicts: [] };\n const unmerged = (await this.run(['diff', '--name-only', '--diff-filter=U'], { reject: false }))\n .stdout.split(/\\r?\\n/)\n .map((s) => s.trim())\n .filter(Boolean);\n const conflicts = [...new Set([...unmerged, ...parseConflicts(result.stderr)])];\n return { ok: false, conflicts: conflicts.length ? conflicts : ['(unresolved — see git status)'] };\n } finally {\n await rm(tmp, { recursive: true, force: true });\n }\n }\n\n /** Commit the currently staged changes; returns the new commit sha. */\n async commit(message: string): Promise<string> {\n await this.run(['commit', '-m', message, '--no-verify']);\n return this.headCommit();\n }\n\n /** Whether there is anything staged or unstaged to commit. */\n async hasChanges(): Promise<boolean> {\n return this.isDirty();\n }\n\n /**\n * Merge a branch into the current branch with `--no-ff`. On conflict, the\n * merge is aborted and the conflicted files are returned.\n */\n async merge(branch: string, message: string): Promise<{ ok: boolean; conflicts: string[] }> {\n const result = await this.run(['merge', '--no-ff', '-m', message, branch], { reject: false });\n if (result.exitCode === 0) return { ok: true, conflicts: [] };\n const conflicts = (await this.run(['diff', '--name-only', '--diff-filter=U'], { reject: false }))\n .stdout.split(/\\r?\\n/)\n .map((s) => s.trim())\n .filter(Boolean);\n await this.run(['merge', '--abort'], { reject: false });\n return { ok: false, conflicts };\n }\n\n async resetHard(ref: string): Promise<void> {\n await this.run(['reset', '--hard', ref]);\n }\n}\n\nfunction parseConflicts(stderr: string): string[] {\n const files = new Set<string>();\n for (const line of stderr.split(/\\r?\\n/)) {\n // `error: <path>: already exists in working directory`\n const errColon = line.match(/^error:\\s*(.+?):\\s/);\n if (errColon?.[1]) files.add(errColon[1].trim());\n // Quoted paths, e.g. `Applied patch to 'foo.ts' with conflicts.`\n for (const q of line.matchAll(/'([^']+)'/g)) files.add(q[1]!.trim());\n // Porcelain unmerged lines.\n const u = line.match(/^U\\s+(.+)$/);\n if (u?.[1]) files.add(u[1].trim());\n }\n return [...files];\n}\n\n/** Build the sandbox descriptor for a prepared run. */\nexport function describeSandbox(\n isolation: Isolation,\n path: string,\n branch: string | null,\n baseCommit: string,\n): SandboxInfo {\n return { isolation, path, branch, baseCommit };\n}\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/** OS-appropriate cache root used when not inside a git repo. */\nexport function osCacheRoot(): string {\n if (process.platform === 'win32') {\n return join(process.env.LOCALAPPDATA ?? join(homedir(), 'AppData', 'Local'), 'clicksmith', 'Cache');\n }\n if (process.platform === 'darwin') {\n return join(homedir(), 'Library', 'Caches', 'clicksmith');\n }\n return join(process.env.XDG_CACHE_HOME ?? join(homedir(), '.cache'), 'clicksmith');\n}\n\n/**\n * The storage root for sessions/runs: the project's `.clicksmith/` when inside\n * a repo, otherwise the OS cache directory.\n */\nexport function resolveStorageRoot(repoRoot: string | null): string {\n return repoRoot ? join(repoRoot, '.clicksmith') : osCacheRoot();\n}\n\n/** Well-known sub-paths within a storage root. */\nexport function storagePaths(root: string) {\n return {\n root,\n sessions: join(root, 'sessions'),\n runs: join(root, 'runs'),\n screenshots: join(root, 'screenshots'),\n config: join(root, 'agents.config.json'),\n runDir: (runId: string) => join(root, 'runs', runId),\n sandboxDir: (runId: string) => join(root, 'worktrees', runId),\n };\n}\n\nexport type StoragePaths = ReturnType<typeof storagePaths>;\n","/* Minimal leveled logger. Writes to stderr so it never corrupts MCP stdio. */\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';\n\nconst ORDER: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3, silent: 4 };\n\nexport interface Logger {\n debug(msg: string, ...rest: unknown[]): void;\n info(msg: string, ...rest: unknown[]): void;\n warn(msg: string, ...rest: unknown[]): void;\n error(msg: string, ...rest: unknown[]): void;\n}\n\nexport function createLogger(level: LogLevel = 'info', prefix = 'clicksmith'): Logger {\n const min = ORDER[level];\n const emit = (lvl: LogLevel, msg: string, rest: unknown[]) => {\n if (ORDER[lvl] < min) return;\n const line = `[${prefix}] ${lvl.toUpperCase()} ${msg}`;\n // Always stderr — stdout is reserved for MCP's JSON-RPC transport.\n process.stderr.write(rest.length ? `${line} ${rest.map(fmt).join(' ')}\\n` : `${line}\\n`);\n };\n return {\n debug: (m, ...r) => emit('debug', m, r),\n info: (m, ...r) => emit('info', m, r),\n warn: (m, ...r) => emit('warn', m, r),\n error: (m, ...r) => emit('error', m, r),\n };\n}\n\nfunction fmt(v: unknown): string {\n if (typeof v === 'string') return v;\n if (v instanceof Error) return v.stack ?? v.message;\n try {\n return JSON.stringify(v);\n } catch {\n return String(v);\n }\n}\n","import { appendFile, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n deserializeBundle,\n isExpired,\n serializeBundle,\n type CaptureBundle,\n type Session,\n} from '@clicksmith/core';\nimport { storagePaths, type StoragePaths } from './paths.js';\nimport type { RunRecord } from './types.js';\n\n/**\n * File-backed persistence for sessions, runs, and run artifacts. Everything is\n * stored as JSON/markdown/patch files under the storage root — no database, no\n * network. Writes are atomic (temp file + rename) to survive crashes.\n */\nexport class FileStore {\n readonly paths: StoragePaths;\n\n constructor(root: string) {\n this.paths = storagePaths(root);\n }\n\n async init(): Promise<void> {\n await Promise.all([\n mkdir(this.paths.sessions, { recursive: true }),\n mkdir(this.paths.runs, { recursive: true }),\n mkdir(this.paths.screenshots, { recursive: true }),\n ]);\n }\n\n /* ----------------------------- sessions ------------------------------ */\n\n private sessionFile(id: string): string {\n return join(this.paths.sessions, `${sanitize(id)}.json`);\n }\n\n async saveSession(session: Session): Promise<void> {\n await atomicWrite(this.sessionFile(session.id), JSON.stringify(session, null, 2));\n }\n\n async getSession(id: string): Promise<Session | undefined> {\n return readJson<Session>(this.sessionFile(id));\n }\n\n async listSessions(): Promise<Session[]> {\n const out: Session[] = [];\n for (const file of await listJson(this.paths.sessions)) {\n const s = await readJson<Session>(join(this.paths.sessions, file));\n if (s) out.push(s);\n }\n return out;\n }\n\n async deleteSession(id: string): Promise<void> {\n await rm(this.sessionFile(id), { force: true });\n }\n\n /* -------------------------------- runs -------------------------------- */\n\n private runDir(runId: string): string {\n return this.paths.runDir(runId);\n }\n\n private runFile(runId: string): string {\n return join(this.runDir(runId), 'run.json');\n }\n\n async saveRun(run: RunRecord): Promise<void> {\n await mkdir(this.runDir(run.runId), { recursive: true });\n await atomicWrite(this.runFile(run.runId), JSON.stringify(run, null, 2));\n }\n\n async getRun(runId: string): Promise<RunRecord | undefined> {\n return readJson<RunRecord>(this.runFile(runId));\n }\n\n async listRuns(): Promise<RunRecord[]> {\n let dirs: string[];\n try {\n dirs = await readdir(this.paths.runs);\n } catch {\n return [];\n }\n const out: RunRecord[] = [];\n for (const dir of dirs) {\n const run = await readJson<RunRecord>(join(this.paths.runs, dir, 'run.json'));\n if (run) out.push(run);\n }\n return out.sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n }\n\n /** The most recent run that has a persisted bundle (for `get_latest_request`). */\n async latestRun(): Promise<RunRecord | undefined> {\n const runs = await this.listRuns();\n return runs.at(-1);\n }\n\n /* ----------------------------- artifacts ------------------------------ */\n\n async saveBundle(runId: string, bundle: CaptureBundle): Promise<string> {\n await mkdir(this.runDir(runId), { recursive: true });\n const file = join(this.runDir(runId), 'bundle.json');\n await atomicWrite(file, serializeBundle(bundle));\n return file;\n }\n\n async getBundle(runId: string): Promise<CaptureBundle | undefined> {\n const raw = await readText(join(this.runDir(runId), 'bundle.json'));\n return raw ? deserializeBundle(raw) : undefined;\n }\n\n bundlePath(runId: string): string {\n return join(this.runDir(runId), 'bundle.json');\n }\n\n async writeArtifact(runId: string, name: string, content: string): Promise<string> {\n await mkdir(this.runDir(runId), { recursive: true });\n const file = join(this.runDir(runId), name);\n await atomicWrite(file, content);\n return file;\n }\n\n async readArtifact(runId: string, name: string): Promise<string | undefined> {\n return readText(join(this.runDir(runId), name));\n }\n\n async appendLog(runId: string, chunk: string): Promise<void> {\n const file = join(this.runDir(runId), 'agent.log');\n await mkdir(this.runDir(runId), { recursive: true });\n await appendFile(file, chunk, 'utf8');\n }\n\n /* ----------------------------- maintenance ----------------------------- */\n\n /** Delete expired, unsubmitted sessions. Returns the ids removed. */\n async cleanupExpired(now: Date = new Date()): Promise<string[]> {\n const removed: string[] = [];\n for (const session of await this.listSessions()) {\n if (isExpired(session, now)) {\n await this.deleteSession(session.id);\n removed.push(session.id);\n }\n }\n return removed;\n }\n}\n\n/* -------------------------------- helpers --------------------------------- */\n\nfunction sanitize(id: string): string {\n return id.replace(/[^a-zA-Z0-9._-]/g, '_');\n}\n\nasync function atomicWrite(file: string, content: string): Promise<void> {\n const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;\n await writeFile(tmp, content, 'utf8');\n const { rename } = await import('node:fs/promises');\n await rename(tmp, file);\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\nasync function readJson<T>(file: string): Promise<T | undefined> {\n const raw = await readText(file);\n if (raw == null) return undefined;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n}\n\nasync function listJson(dir: string): Promise<string[]> {\n try {\n return (await readdir(dir)).filter((f) => f.endsWith('.json'));\n } catch {\n return [];\n }\n}\n","import type { CaptureBundle, CapturedElement, Session } from '@clicksmith/core';\nimport type { FileStore } from './store.js';\n\n/** Read-only view the MCP tools operate over, backed by daemon persistence. */\nexport interface McpReader {\n getSession(id: string): Promise<Session | undefined>;\n /** The bundle from the most recent submission, if any. */\n latestBundle(): Promise<CaptureBundle | undefined>;\n}\n\n/** Build an {@link McpReader} from a {@link FileStore}. */\nexport function readerFromStore(store: FileStore): McpReader {\n return {\n getSession: (id) => store.getSession(id),\n latestBundle: async () => {\n const run = await store.latestRun();\n return run ? store.getBundle(run.runId) : undefined;\n },\n };\n}\n\n/**\n * Tool definitions exposed over MCP. The **descriptions** deliberately teach the\n * agent the three ClickSmith conventions: `#N` references, locator priority, and\n * the plan/worktree safety contract.\n */\nexport const TOOL_DEFINITIONS = [\n {\n name: 'get_latest_request',\n description:\n 'Get the most recently submitted ClickSmith capture bundle (the latest UI change request). ' +\n 'Returns the prompt, the app route, and the captured elements numbered #1, #2, … . ' +\n 'The user prompt refers to elements by these numbers. Trust each element’s locator in the ' +\n 'order source → attr → behavioral → dom (source = exact file:line). You are running in an ' +\n 'isolated git worktree; in plan mode propose changes — the human clicks Apply to ship them.',\n inputSchema: { type: 'object', properties: {}, additionalProperties: false },\n },\n {\n name: 'get_session',\n description:\n 'Get a ClickSmith capture session by id, including all captured elements (#1, #2, …) and the ' +\n 'app/route context. Use this to resolve which concrete elements the user’s #N references mean.',\n inputSchema: {\n type: 'object',\n properties: { sessionId: { type: 'string', description: 'The session id.' } },\n required: ['sessionId'],\n additionalProperties: false,\n },\n },\n {\n name: 'list_elements',\n description:\n 'List the captured elements for a session (or the latest request if no sessionId is given). ' +\n 'Each element has an id (its #N), a ranked locator (source → attr → behavioral → dom), the ' +\n 'tag/text/role/label, and nearby context for disambiguation.',\n inputSchema: {\n type: 'object',\n properties: { sessionId: { type: 'string', description: 'Optional session id.' } },\n additionalProperties: false,\n },\n },\n {\n name: 'get_element_by_id',\n description:\n 'Resolve a single captured element by its #N id (e.g. 1 for #1), within a session or the ' +\n 'latest request. Returns its locator (prefer source over attr over behavioral over dom), the ' +\n 'element descriptor, and near context.',\n inputSchema: {\n type: 'object',\n properties: {\n id: { type: 'number', description: 'The element’s #N number.' },\n sessionId: { type: 'string', description: 'Optional session id; defaults to latest.' },\n },\n required: ['id'],\n additionalProperties: false,\n },\n },\n] as const;\n\nexport type ToolResult = { ok: true; text: string } | { ok: false; error: string };\n\n/** Execute a read-only tool by name. Pure with respect to the reader. */\nexport async function callTool(\n name: string,\n args: Record<string, unknown>,\n reader: McpReader,\n): Promise<ToolResult> {\n switch (name) {\n case 'get_latest_request': {\n const bundle = await reader.latestBundle();\n if (!bundle) return { ok: false, error: 'No request has been submitted yet.' };\n return { ok: true, text: json(bundle) };\n }\n case 'get_session': {\n const session = await reader.getSession(String(args.sessionId));\n if (!session) return { ok: false, error: `Unknown session: ${String(args.sessionId)}` };\n return { ok: true, text: json(session) };\n }\n case 'list_elements': {\n const elements = await resolveElements(reader, args.sessionId as string | undefined);\n if (!elements) return { ok: false, error: 'No session or latest request found.' };\n return { ok: true, text: json(elements) };\n }\n case 'get_element_by_id': {\n const id = Number(args.id);\n const elements = await resolveElements(reader, args.sessionId as string | undefined);\n if (!elements) return { ok: false, error: 'No session or latest request found.' };\n const element = elements.find((e) => e.id === id);\n if (!element) return { ok: false, error: `No element #${id} in this session.` };\n return { ok: true, text: json(element) };\n }\n default:\n return { ok: false, error: `Unknown tool: ${name}` };\n }\n}\n\nasync function resolveElements(\n reader: McpReader,\n sessionId?: string,\n): Promise<CapturedElement[] | undefined> {\n if (sessionId) return (await reader.getSession(sessionId))?.elements;\n return (await reader.latestBundle())?.elements;\n}\n\nfunction json(value: unknown): string {\n return JSON.stringify(value, null, 2);\n}\n","/** The daemon's semantic version, surfaced via /health and MCP. */\nexport const version = '0.1.5';\n"],"mappings":"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","names":["join","json","readFile","rm","writeFile","join","join","rm","writeFile","readFile"]}
|
|
1
|
+
{"version":3,"sources":["../src/mcp.ts","../src/config.ts","../src/git.ts","../src/paths.ts","../src/logger.ts","../src/store.ts","../src/mcp-tools.ts","../src/version.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { resolveDaemonConfig } from './config.js';\nimport { FileStore } from './store.js';\nimport { callTool, readerFromStore, TOOL_DEFINITIONS, type McpReader } from './mcp-tools.js';\nimport { version } from './version.js';\n\n/**\n * Create an MCP {@link Server} exposing ClickSmith's read-only tools over the\n * given reader. The daemon and a standalone `clicksmith mcp` process both use\n * this; state is shared via the on-disk store, so the MCP process never needs\n * to talk to the HTTP daemon directly.\n */\nexport function createMcpServer(reader: McpReader): Server {\n const server = new Server(\n { name: 'clicksmith', version },\n { capabilities: { tools: {} } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: TOOL_DEFINITIONS.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n const result = await callTool(name, args ?? {}, reader);\n if (!result.ok) {\n return { content: [{ type: 'text', text: result.error }], isError: true };\n }\n return { content: [{ type: 'text', text: result.text }] };\n });\n\n return server;\n}\n\n/** Entry point for `clicksmith mcp`: connect the stdio transport. */\nexport async function startMcp(): Promise<void> {\n const config = await resolveDaemonConfig({ logLevel: 'silent' });\n const store = new FileStore(config.storageRoot);\n await store.init();\n const server = createMcpServer(readerFromStore(store));\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\n// Allow running this file directly as the MCP server.\nif (import.meta.url === `file://${process.argv[1]}`) {\n startMcp().catch((err) => {\n process.stderr.write(`clicksmith mcp failed: ${err instanceof Error ? err.stack : String(err)}\\n`);\n process.exit(1);\n });\n}\n","import { readFile } from 'node:fs/promises';\nimport {\n DEFAULT_AGENTS_CONFIG,\n mergeAgentsConfig,\n parseAgentsConfig,\n type AgentsConfig,\n} from '@clicksmith/agent-config';\nimport { DEFAULT_DAEMON_HOST, DEFAULT_DAEMON_PORT, DEFAULT_SESSION_TTL_MS } from '@clicksmith/core';\nimport { Git } from './git.js';\nimport { resolveStorageRoot, storagePaths } from './paths.js';\nimport { createLogger, type LogLevel, type Logger } from './logger.js';\n\nexport interface DaemonConfigInput {\n cwd?: string;\n host?: string;\n port?: number;\n ttlMs?: number;\n logLevel?: LogLevel;\n /** Override the storage root (mainly for tests). */\n storageRoot?: string;\n /** Override repo detection (mainly for tests). */\n repoRoot?: string | null;\n}\n\nexport interface DaemonConfig {\n host: string;\n port: number;\n ttlMs: number;\n cwd: string;\n repoRoot: string | null;\n storageRoot: string;\n agents: AgentsConfig;\n logger: Logger;\n}\n\n/**\n * Resolve the full daemon configuration: detect the repo, choose a storage\n * root, and layer agent configs (shipped defaults → project `agents.config.json`).\n */\nexport async function resolveDaemonConfig(input: DaemonConfigInput = {}): Promise<DaemonConfig> {\n const cwd = input.cwd ?? process.cwd();\n const repoRoot = input.repoRoot !== undefined ? input.repoRoot : await Git.findRepoRoot(cwd);\n const storageRoot = input.storageRoot ?? resolveStorageRoot(repoRoot);\n const agents = await loadAgentsConfig(storageRoot);\n\n return {\n host: input.host ?? DEFAULT_DAEMON_HOST,\n port: input.port ?? DEFAULT_DAEMON_PORT,\n ttlMs: input.ttlMs ?? DEFAULT_SESSION_TTL_MS,\n cwd,\n repoRoot,\n storageRoot,\n agents,\n logger: createLogger(input.logLevel ?? 'info'),\n };\n}\n\n/** Load `agents.config.json` from the storage root and merge over defaults. */\nexport async function loadAgentsConfig(storageRoot: string): Promise<AgentsConfig> {\n const file = storagePaths(storageRoot).config;\n let raw: string | undefined;\n try {\n raw = await readFile(file, 'utf8');\n } catch {\n return DEFAULT_AGENTS_CONFIG;\n }\n let json: unknown;\n try {\n json = JSON.parse(raw);\n } catch {\n return DEFAULT_AGENTS_CONFIG;\n }\n const parsed = parseAgentsConfig(json);\n if (!parsed.ok) return DEFAULT_AGENTS_CONFIG;\n return mergeAgentsConfig(DEFAULT_AGENTS_CONFIG, parsed.config);\n}\n","import { execa } from 'execa';\nimport { mkdtemp, rm, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport type { Isolation, SandboxInfo } from '@clicksmith/core';\n\nexport class GitError extends Error {}\n\n/** Thin wrapper around the `git` CLI, scoped to a working directory. */\nexport class Git {\n constructor(private readonly cwd: string) {}\n\n private async run(args: string[], opts: { reject?: boolean } = {}) {\n const result = await execa('git', args, { cwd: this.cwd, reject: false });\n if (opts.reject !== false && result.exitCode !== 0) {\n throw new GitError(`git ${args.join(' ')} failed: ${result.stderr || result.stdout}`);\n }\n return result;\n }\n\n /** Absolute repo root, or `null` if `cwd` is not inside a git repo. */\n static async findRepoRoot(cwd: string): Promise<string | null> {\n const result = await execa('git', ['rev-parse', '--show-toplevel'], { cwd, reject: false });\n return result.exitCode === 0 ? result.stdout.trim() : null;\n }\n\n async headCommit(): Promise<string> {\n return (await this.run(['rev-parse', 'HEAD'])).stdout.trim();\n }\n\n async currentBranch(): Promise<string> {\n return (await this.run(['rev-parse', '--abbrev-ref', 'HEAD'])).stdout.trim();\n }\n\n /**\n * Whether the working tree has uncommitted (tracked or untracked) changes,\n * ignoring any paths under `exclude` prefixes (e.g. ClickSmith's own\n * `.clicksmith/` state directory).\n */\n async isDirty(opts: { exclude?: string[] } = {}): Promise<boolean> {\n const result = await this.run(['status', '--porcelain']);\n const exclude = opts.exclude ?? [];\n return result.stdout\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean)\n .some((line) => {\n const path = line.slice(2).trim();\n const actual = path.includes(' -> ') ? path.split(' -> ')[1]! : path;\n return !exclude.some((prefix) => actual.startsWith(prefix));\n });\n }\n\n /** Whether this git supports worktrees (>= 2.5). */\n async supportsWorktree(): Promise<boolean> {\n const result = await execa('git', ['worktree', 'list'], { cwd: this.cwd, reject: false });\n return result.exitCode === 0;\n }\n\n /**\n * Create a throwaway worktree on a fresh branch for a run. The worktree lives\n * outside the main tree so the agent's edits never touch it.\n */\n async createWorktree(path: string, branch: string, baseRef: string): Promise<void> {\n await this.run(['worktree', 'add', '-b', branch, path, baseRef]);\n }\n\n async removeWorktree(path: string, branch?: string): Promise<void> {\n await this.run(['worktree', 'remove', '--force', path], { reject: false });\n if (branch) await this.run(['branch', '-D', branch], { reject: false });\n }\n\n /** Create + checkout a dedicated branch (the worktree fallback). */\n async createBranch(branch: string, baseRef: string): Promise<void> {\n await this.run(['switch', '-c', branch, baseRef]);\n }\n\n async switchTo(ref: string): Promise<void> {\n await this.run(['switch', ref]);\n }\n\n async deleteBranch(branch: string): Promise<void> {\n await this.run(['branch', '-D', branch], { reject: false });\n }\n\n /**\n * Capture every change in a sandbox (relative to its HEAD) as a single\n * binary-safe patch, including new and deleted files. Returns '' if clean.\n */\n static async captureDiff(sandboxPath: string): Promise<string> {\n await execa('git', ['add', '-A'], { cwd: sandboxPath, reject: false });\n const result = await execa('git', ['diff', '--cached', '--binary'], {\n cwd: sandboxPath,\n reject: false,\n });\n return result.exitCode === 0 ? result.stdout : '';\n }\n\n /**\n * Apply a captured patch onto the main working tree using a 3-way merge,\n * staging the result. Returns the list of conflicted files (empty on success).\n */\n async applyPatch(patch: string): Promise<{ ok: boolean; conflicts: string[] }> {\n if (!patch.trim()) return { ok: true, conflicts: [] };\n const tmp = await mkdtemp(join(tmpdir(), 'clicksmith-patch-'));\n const patchFile = join(tmp, 'run.patch');\n try {\n await writeFile(patchFile, patch.endsWith('\\n') ? patch : `${patch}\\n`, 'utf8');\n const result = await this.run(['apply', '--index', '--3way', patchFile], { reject: false });\n if (result.exitCode === 0) return { ok: true, conflicts: [] };\n const unmerged = (await this.run(['diff', '--name-only', '--diff-filter=U'], { reject: false }))\n .stdout.split(/\\r?\\n/)\n .map((s) => s.trim())\n .filter(Boolean);\n const conflicts = [...new Set([...unmerged, ...parseConflicts(result.stderr)])];\n return { ok: false, conflicts: conflicts.length ? conflicts : ['(unresolved — see git status)'] };\n } finally {\n await rm(tmp, { recursive: true, force: true });\n }\n }\n\n /** Commit the currently staged changes; returns the new commit sha. */\n async commit(message: string): Promise<string> {\n await this.run(['commit', '-m', message, '--no-verify']);\n return this.headCommit();\n }\n\n /** Whether there is anything staged or unstaged to commit. */\n async hasChanges(): Promise<boolean> {\n return this.isDirty();\n }\n\n /**\n * Merge a branch into the current branch with `--no-ff`. On conflict, the\n * merge is aborted and the conflicted files are returned.\n */\n async merge(branch: string, message: string): Promise<{ ok: boolean; conflicts: string[] }> {\n const result = await this.run(['merge', '--no-ff', '-m', message, branch], { reject: false });\n if (result.exitCode === 0) return { ok: true, conflicts: [] };\n const conflicts = (await this.run(['diff', '--name-only', '--diff-filter=U'], { reject: false }))\n .stdout.split(/\\r?\\n/)\n .map((s) => s.trim())\n .filter(Boolean);\n await this.run(['merge', '--abort'], { reject: false });\n return { ok: false, conflicts };\n }\n\n async resetHard(ref: string): Promise<void> {\n await this.run(['reset', '--hard', ref]);\n }\n}\n\nfunction parseConflicts(stderr: string): string[] {\n const files = new Set<string>();\n for (const line of stderr.split(/\\r?\\n/)) {\n // `error: <path>: already exists in working directory`\n const errColon = line.match(/^error:\\s*(.+?):\\s/);\n if (errColon?.[1]) files.add(errColon[1].trim());\n // Quoted paths, e.g. `Applied patch to 'foo.ts' with conflicts.`\n for (const q of line.matchAll(/'([^']+)'/g)) files.add(q[1]!.trim());\n // Porcelain unmerged lines.\n const u = line.match(/^U\\s+(.+)$/);\n if (u?.[1]) files.add(u[1].trim());\n }\n return [...files];\n}\n\n/** Build the sandbox descriptor for a prepared run. */\nexport function describeSandbox(\n isolation: Isolation,\n path: string,\n branch: string | null,\n baseCommit: string,\n): SandboxInfo {\n return { isolation, path, branch, baseCommit };\n}\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/** OS-appropriate cache root used when not inside a git repo. */\nexport function osCacheRoot(): string {\n if (process.platform === 'win32') {\n return join(process.env.LOCALAPPDATA ?? join(homedir(), 'AppData', 'Local'), 'clicksmith', 'Cache');\n }\n if (process.platform === 'darwin') {\n return join(homedir(), 'Library', 'Caches', 'clicksmith');\n }\n return join(process.env.XDG_CACHE_HOME ?? join(homedir(), '.cache'), 'clicksmith');\n}\n\n/**\n * The storage root for sessions/runs: the project's `.clicksmith/` when inside\n * a repo, otherwise the OS cache directory.\n */\nexport function resolveStorageRoot(repoRoot: string | null): string {\n return repoRoot ? join(repoRoot, '.clicksmith') : osCacheRoot();\n}\n\n/** Well-known sub-paths within a storage root. */\nexport function storagePaths(root: string) {\n return {\n root,\n sessions: join(root, 'sessions'),\n runs: join(root, 'runs'),\n screenshots: join(root, 'screenshots'),\n config: join(root, 'agents.config.json'),\n runDir: (runId: string) => join(root, 'runs', runId),\n sandboxDir: (runId: string) => join(root, 'worktrees', runId),\n };\n}\n\nexport type StoragePaths = ReturnType<typeof storagePaths>;\n","/* Minimal leveled logger. Writes to stderr so it never corrupts MCP stdio. */\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';\n\nconst ORDER: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3, silent: 4 };\n\nexport interface Logger {\n debug(msg: string, ...rest: unknown[]): void;\n info(msg: string, ...rest: unknown[]): void;\n warn(msg: string, ...rest: unknown[]): void;\n error(msg: string, ...rest: unknown[]): void;\n}\n\nexport function createLogger(level: LogLevel = 'info', prefix = 'clicksmith'): Logger {\n const min = ORDER[level];\n const emit = (lvl: LogLevel, msg: string, rest: unknown[]) => {\n if (ORDER[lvl] < min) return;\n const line = `[${prefix}] ${lvl.toUpperCase()} ${msg}`;\n // Always stderr — stdout is reserved for MCP's JSON-RPC transport.\n process.stderr.write(rest.length ? `${line} ${rest.map(fmt).join(' ')}\\n` : `${line}\\n`);\n };\n return {\n debug: (m, ...r) => emit('debug', m, r),\n info: (m, ...r) => emit('info', m, r),\n warn: (m, ...r) => emit('warn', m, r),\n error: (m, ...r) => emit('error', m, r),\n };\n}\n\nfunction fmt(v: unknown): string {\n if (typeof v === 'string') return v;\n if (v instanceof Error) return v.stack ?? v.message;\n try {\n return JSON.stringify(v);\n } catch {\n return String(v);\n }\n}\n","import { appendFile, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n deserializeBundle,\n isExpired,\n serializeBundle,\n type CaptureBundle,\n type Session,\n} from '@clicksmith/core';\nimport { storagePaths, type StoragePaths } from './paths.js';\nimport type { RunRecord } from './types.js';\n\n/**\n * File-backed persistence for sessions, runs, and run artifacts. Everything is\n * stored as JSON/markdown/patch files under the storage root — no database, no\n * network. Writes are atomic (temp file + rename) to survive crashes.\n */\nexport class FileStore {\n readonly paths: StoragePaths;\n\n constructor(root: string) {\n this.paths = storagePaths(root);\n }\n\n async init(): Promise<void> {\n await Promise.all([\n mkdir(this.paths.sessions, { recursive: true }),\n mkdir(this.paths.runs, { recursive: true }),\n mkdir(this.paths.screenshots, { recursive: true }),\n ]);\n }\n\n /* ----------------------------- sessions ------------------------------ */\n\n private sessionFile(id: string): string {\n return join(this.paths.sessions, `${sanitize(id)}.json`);\n }\n\n async saveSession(session: Session): Promise<void> {\n await atomicWrite(this.sessionFile(session.id), JSON.stringify(session, null, 2));\n }\n\n async getSession(id: string): Promise<Session | undefined> {\n return readJson<Session>(this.sessionFile(id));\n }\n\n async listSessions(): Promise<Session[]> {\n const out: Session[] = [];\n for (const file of await listJson(this.paths.sessions)) {\n const s = await readJson<Session>(join(this.paths.sessions, file));\n if (s) out.push(s);\n }\n return out;\n }\n\n async deleteSession(id: string): Promise<void> {\n await rm(this.sessionFile(id), { force: true });\n }\n\n /* -------------------------------- runs -------------------------------- */\n\n private runDir(runId: string): string {\n return this.paths.runDir(runId);\n }\n\n private runFile(runId: string): string {\n return join(this.runDir(runId), 'run.json');\n }\n\n async saveRun(run: RunRecord): Promise<void> {\n await mkdir(this.runDir(run.runId), { recursive: true });\n await atomicWrite(this.runFile(run.runId), JSON.stringify(run, null, 2));\n }\n\n async getRun(runId: string): Promise<RunRecord | undefined> {\n return readJson<RunRecord>(this.runFile(runId));\n }\n\n async listRuns(): Promise<RunRecord[]> {\n let dirs: string[];\n try {\n dirs = await readdir(this.paths.runs);\n } catch {\n return [];\n }\n const out: RunRecord[] = [];\n for (const dir of dirs) {\n const run = await readJson<RunRecord>(join(this.paths.runs, dir, 'run.json'));\n if (run) out.push(run);\n }\n return out.sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n }\n\n /** The most recent run that has a persisted bundle (for `get_latest_request`). */\n async latestRun(): Promise<RunRecord | undefined> {\n const runs = await this.listRuns();\n return runs.at(-1);\n }\n\n /* ----------------------------- artifacts ------------------------------ */\n\n async saveBundle(runId: string, bundle: CaptureBundle): Promise<string> {\n await mkdir(this.runDir(runId), { recursive: true });\n const file = join(this.runDir(runId), 'bundle.json');\n await atomicWrite(file, serializeBundle(bundle));\n return file;\n }\n\n async getBundle(runId: string): Promise<CaptureBundle | undefined> {\n const raw = await readText(join(this.runDir(runId), 'bundle.json'));\n return raw ? deserializeBundle(raw) : undefined;\n }\n\n bundlePath(runId: string): string {\n return join(this.runDir(runId), 'bundle.json');\n }\n\n async writeArtifact(runId: string, name: string, content: string): Promise<string> {\n await mkdir(this.runDir(runId), { recursive: true });\n const file = join(this.runDir(runId), name);\n await atomicWrite(file, content);\n return file;\n }\n\n async readArtifact(runId: string, name: string): Promise<string | undefined> {\n return readText(join(this.runDir(runId), name));\n }\n\n async appendLog(runId: string, chunk: string): Promise<void> {\n const file = join(this.runDir(runId), 'agent.log');\n await mkdir(this.runDir(runId), { recursive: true });\n await appendFile(file, chunk, 'utf8');\n }\n\n /* ----------------------------- maintenance ----------------------------- */\n\n /** Delete expired, unsubmitted sessions. Returns the ids removed. */\n async cleanupExpired(now: Date = new Date()): Promise<string[]> {\n const removed: string[] = [];\n for (const session of await this.listSessions()) {\n if (isExpired(session, now)) {\n await this.deleteSession(session.id);\n removed.push(session.id);\n }\n }\n return removed;\n }\n}\n\n/* -------------------------------- helpers --------------------------------- */\n\nfunction sanitize(id: string): string {\n return id.replace(/[^a-zA-Z0-9._-]/g, '_');\n}\n\nasync function atomicWrite(file: string, content: string): Promise<void> {\n const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;\n await writeFile(tmp, content, 'utf8');\n const { rename } = await import('node:fs/promises');\n await rename(tmp, file);\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\nasync function readJson<T>(file: string): Promise<T | undefined> {\n const raw = await readText(file);\n if (raw == null) return undefined;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n}\n\nasync function listJson(dir: string): Promise<string[]> {\n try {\n return (await readdir(dir)).filter((f) => f.endsWith('.json'));\n } catch {\n return [];\n }\n}\n","import type { CaptureBundle, CapturedElement, Session } from '@clicksmith/core';\nimport type { FileStore } from './store.js';\n\n/** Read-only view the MCP tools operate over, backed by daemon persistence. */\nexport interface McpReader {\n getSession(id: string): Promise<Session | undefined>;\n /** The bundle from the most recent submission, if any. */\n latestBundle(): Promise<CaptureBundle | undefined>;\n}\n\n/** Build an {@link McpReader} from a {@link FileStore}. */\nexport function readerFromStore(store: FileStore): McpReader {\n return {\n getSession: (id) => store.getSession(id),\n latestBundle: async () => {\n const run = await store.latestRun();\n return run ? store.getBundle(run.runId) : undefined;\n },\n };\n}\n\n/**\n * Tool definitions exposed over MCP. The **descriptions** deliberately teach the\n * agent the three ClickSmith conventions: `#N` references, locator priority, and\n * the plan/worktree safety contract.\n */\nexport const TOOL_DEFINITIONS = [\n {\n name: 'get_latest_request',\n description:\n 'Get the most recently submitted ClickSmith capture bundle (the latest UI change request). ' +\n 'Returns the prompt, the app route, and the captured elements numbered #1, #2, … . ' +\n 'The user prompt refers to elements by these numbers. Trust each element’s locator in the ' +\n 'order source → attr → behavioral → dom (source = exact file:line). You are running in an ' +\n 'isolated git worktree; in plan mode propose changes — the human clicks Apply to ship them.',\n inputSchema: { type: 'object', properties: {}, additionalProperties: false },\n },\n {\n name: 'get_session',\n description:\n 'Get a ClickSmith capture session by id, including all captured elements (#1, #2, …) and the ' +\n 'app/route context. Use this to resolve which concrete elements the user’s #N references mean.',\n inputSchema: {\n type: 'object',\n properties: { sessionId: { type: 'string', description: 'The session id.' } },\n required: ['sessionId'],\n additionalProperties: false,\n },\n },\n {\n name: 'list_elements',\n description:\n 'List the captured elements for a session (or the latest request if no sessionId is given). ' +\n 'Each element has an id (its #N), a ranked locator (source → attr → behavioral → dom), the ' +\n 'tag/text/role/label, and nearby context for disambiguation.',\n inputSchema: {\n type: 'object',\n properties: { sessionId: { type: 'string', description: 'Optional session id.' } },\n additionalProperties: false,\n },\n },\n {\n name: 'get_element_by_id',\n description:\n 'Resolve a single captured element by its #N id (e.g. 1 for #1), within a session or the ' +\n 'latest request. Returns its locator (prefer source over attr over behavioral over dom), the ' +\n 'element descriptor, and near context.',\n inputSchema: {\n type: 'object',\n properties: {\n id: { type: 'number', description: 'The element’s #N number.' },\n sessionId: { type: 'string', description: 'Optional session id; defaults to latest.' },\n },\n required: ['id'],\n additionalProperties: false,\n },\n },\n] as const;\n\nexport type ToolResult = { ok: true; text: string } | { ok: false; error: string };\n\n/** Execute a read-only tool by name. Pure with respect to the reader. */\nexport async function callTool(\n name: string,\n args: Record<string, unknown>,\n reader: McpReader,\n): Promise<ToolResult> {\n switch (name) {\n case 'get_latest_request': {\n const bundle = await reader.latestBundle();\n if (!bundle) return { ok: false, error: 'No request has been submitted yet.' };\n return { ok: true, text: json(bundle) };\n }\n case 'get_session': {\n const session = await reader.getSession(String(args.sessionId));\n if (!session) return { ok: false, error: `Unknown session: ${String(args.sessionId)}` };\n return { ok: true, text: json(session) };\n }\n case 'list_elements': {\n const elements = await resolveElements(reader, args.sessionId as string | undefined);\n if (!elements) return { ok: false, error: 'No session or latest request found.' };\n return { ok: true, text: json(elements) };\n }\n case 'get_element_by_id': {\n const id = Number(args.id);\n const elements = await resolveElements(reader, args.sessionId as string | undefined);\n if (!elements) return { ok: false, error: 'No session or latest request found.' };\n const element = elements.find((e) => e.id === id);\n if (!element) return { ok: false, error: `No element #${id} in this session.` };\n return { ok: true, text: json(element) };\n }\n default:\n return { ok: false, error: `Unknown tool: ${name}` };\n }\n}\n\nasync function resolveElements(\n reader: McpReader,\n sessionId?: string,\n): Promise<CapturedElement[] | undefined> {\n if (sessionId) return (await reader.getSession(sessionId))?.elements;\n return (await reader.latestBundle())?.elements;\n}\n\nfunction json(value: unknown): string {\n return JSON.stringify(value, null, 2);\n}\n","/** The daemon's semantic version, surfaced via /health and MCP. */\nexport const version = '0.1.6';\n"],"mappings":"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","names":["join","json","readFile","rm","writeFile","join","join","rm","writeFile","readFile"]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
Git,
|
|
4
4
|
describeSandbox,
|
|
5
5
|
version
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-273SGCZB.js";
|
|
7
7
|
|
|
8
8
|
// src/events.ts
|
|
9
9
|
var EventBus = class {
|
|
@@ -207,11 +207,10 @@ var RunManager = class {
|
|
|
207
207
|
async execute(run, bundle, agentConfig) {
|
|
208
208
|
const { store, config, bus, logger } = this.deps;
|
|
209
209
|
const sandboxPath = run.sandbox?.path ?? config.cwd;
|
|
210
|
-
const instructionFile = await this.resolveInstructionFile(run
|
|
210
|
+
const instructionFile = await this.resolveInstructionFile(run);
|
|
211
211
|
const agentPrompt = buildAgentPrompt({
|
|
212
212
|
bundle,
|
|
213
213
|
bundlePath: store.bundlePath(run.runId),
|
|
214
|
-
instructionFile,
|
|
215
214
|
run
|
|
216
215
|
});
|
|
217
216
|
const ctx = {
|
|
@@ -386,29 +385,16 @@ var RunManager = class {
|
|
|
386
385
|
}
|
|
387
386
|
}
|
|
388
387
|
/**
|
|
389
|
-
* Resolve the instruction file passed to the agent.
|
|
390
|
-
*
|
|
391
|
-
*
|
|
388
|
+
* Resolve the instruction file passed to the agent. Use a run-local compact
|
|
389
|
+
* file so ClickSmith runs do not ingest a project's full AGENTS/CLAUDE/rules
|
|
390
|
+
* corpus before the targeted UI lookup has even started.
|
|
392
391
|
*/
|
|
393
|
-
async resolveInstructionFile(run
|
|
392
|
+
async resolveInstructionFile(run) {
|
|
394
393
|
const { config, store } = this.deps;
|
|
395
|
-
if (config.repoRoot && agentConfig.instructions) {
|
|
396
|
-
const projectFile = join(config.repoRoot, agentConfig.instructions.file);
|
|
397
|
-
if (await fileExists(projectFile)) return projectFile;
|
|
398
|
-
}
|
|
399
394
|
const body = renderInstructionBody({ daemonPort: config.port });
|
|
400
395
|
return store.writeArtifact(run.runId, "AGENT_INSTRUCTIONS.md", body);
|
|
401
396
|
}
|
|
402
397
|
};
|
|
403
|
-
async function fileExists(path) {
|
|
404
|
-
const { access } = await import("fs/promises");
|
|
405
|
-
try {
|
|
406
|
-
await access(path);
|
|
407
|
-
return true;
|
|
408
|
-
} catch {
|
|
409
|
-
return false;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
398
|
async function safe(fn) {
|
|
413
399
|
try {
|
|
414
400
|
return await fn();
|
|
@@ -458,41 +444,173 @@ function createLogBuffer(write, onError) {
|
|
|
458
444
|
};
|
|
459
445
|
}
|
|
460
446
|
function buildAgentPrompt(input) {
|
|
461
|
-
const { bundle, bundlePath,
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
const sourceHints = bundle.elements.flatMap((element) => {
|
|
468
|
-
if (element.locator.kind === "source") {
|
|
469
|
-
return [`#${element.id} source ${element.locator.file}:${element.locator.line}`];
|
|
470
|
-
}
|
|
471
|
-
if (element.locator.kind === "attr") {
|
|
472
|
-
return [
|
|
473
|
-
`#${element.id} search ${element.locator.attr}=${JSON.stringify(element.locator.value)}`
|
|
474
|
-
];
|
|
475
|
-
}
|
|
476
|
-
return [];
|
|
477
|
-
}).join("\n");
|
|
478
|
-
const modeLine = bundle.execution.mode === "edit" ? "Edit mode: modify the smallest set of files in the working directory. Do not ask for confirmation." : "Plan mode: inspect files and produce a concise plan without modifying files.";
|
|
447
|
+
const { bundle, bundlePath, run } = input;
|
|
448
|
+
const targets = bundle.elements.map(formatTargetSummary).join("\n");
|
|
449
|
+
const firstActions = buildFirstActions(bundle, bundlePath).map((action, index) => {
|
|
450
|
+
return `${index + 1}. ${action}`;
|
|
451
|
+
});
|
|
452
|
+
const modeLine = bundle.execution.mode === "edit" ? "Mode: edit. Make the smallest working-tree change; do not ask for confirmation." : "Mode: plan. Inspect only and return a concise plan.";
|
|
479
453
|
return [
|
|
480
|
-
|
|
481
|
-
`
|
|
482
|
-
`
|
|
483
|
-
`
|
|
484
|
-
`Project instructions: ${instructionFile}`,
|
|
485
|
-
`Working directory: ${run.sandbox?.path ?? run.repoRoot ?? "(none)"}`,
|
|
454
|
+
"ClickSmith fast UI edit.",
|
|
455
|
+
`Request: ${truncateLine(bundle.prompt, 300)}`,
|
|
456
|
+
`Route: ${truncateLine(bundle.app.route, 160)}`,
|
|
457
|
+
`Cwd: ${run.sandbox?.path ?? run.repoRoot ?? "(none)"}`,
|
|
486
458
|
`Isolation: ${run.isolation}`,
|
|
487
459
|
"",
|
|
488
|
-
"
|
|
489
|
-
|
|
490
|
-
|
|
460
|
+
"Targets:",
|
|
461
|
+
targets,
|
|
462
|
+
"",
|
|
463
|
+
"First actions (do these before reading broad project docs):",
|
|
464
|
+
...firstActions,
|
|
491
465
|
"",
|
|
492
|
-
|
|
493
|
-
"
|
|
466
|
+
"Fast-path rules:",
|
|
467
|
+
"- Do not read AGENTS.md, CLAUDE.md, .cursor/rules, skills, guidelines, or bundle.json first.",
|
|
468
|
+
"- If source file:line exists, open only that file around the line; no grep needed.",
|
|
469
|
+
"- Otherwise run at most two targeted grep searches from First actions, then edit the smallest file.",
|
|
470
|
+
"- If a grep result is only a shared sprite/icon definition, use it to find the component usage.",
|
|
471
|
+
`- Fallback only if the target is still ambiguous: ${bundlePath}`,
|
|
472
|
+
"- Keep final output brief: changed files and checks run.",
|
|
473
|
+
"",
|
|
474
|
+
modeLine
|
|
494
475
|
].join("\n");
|
|
495
476
|
}
|
|
477
|
+
function formatTargetSummary(element) {
|
|
478
|
+
const label = element.el.text || element.el.label || element.el.role || element.el.tag;
|
|
479
|
+
const attrs = formatAttrs(element.el.attrs);
|
|
480
|
+
const locator = formatLocator(element);
|
|
481
|
+
const near = formatNear(element);
|
|
482
|
+
const tokens = collectElementSearchTokens(element).slice(0, 5);
|
|
483
|
+
return [
|
|
484
|
+
`#${element.id} <${element.el.tag}> ${quoteText(label)}`,
|
|
485
|
+
`locator=${locator}`,
|
|
486
|
+
attrs ? `attrs=${attrs}` : "",
|
|
487
|
+
near ? `near=${near}` : "",
|
|
488
|
+
tokens.length ? `tokens=${tokens.map(quoteText).join(", ")}` : ""
|
|
489
|
+
].filter(Boolean).join(" | ");
|
|
490
|
+
}
|
|
491
|
+
function formatLocator(element) {
|
|
492
|
+
const { locator } = element;
|
|
493
|
+
if (locator.kind === "source") {
|
|
494
|
+
return `source:${locator.file}:${locator.line}${locator.column != null ? `:${locator.column}` : ""}`;
|
|
495
|
+
}
|
|
496
|
+
if (locator.kind === "attr") {
|
|
497
|
+
return `attr:${locator.attr}=${quoteText(locator.value)}`;
|
|
498
|
+
}
|
|
499
|
+
if (locator.kind === "behavioral") {
|
|
500
|
+
return `behavioral:${locator.role} ${quoteText(locator.name)}`;
|
|
501
|
+
}
|
|
502
|
+
return `dom:${truncateLine(locator.selector, 120)}`;
|
|
503
|
+
}
|
|
504
|
+
function formatAttrs(attrs) {
|
|
505
|
+
return Object.entries(attrs).slice(0, 5).map(([key, value]) => `${key}=${quoteText(value)}`).join(" ");
|
|
506
|
+
}
|
|
507
|
+
function formatNear(element) {
|
|
508
|
+
return [
|
|
509
|
+
...(element.near.labels ?? []).slice(0, 1).map((label) => `label=${quoteText(label)}`),
|
|
510
|
+
...(element.near.headings ?? []).slice(0, 1).map((heading) => `heading=${quoteText(heading)}`),
|
|
511
|
+
...(element.near.landmarks ?? []).slice(0, 1).map((landmark) => `landmark=${quoteText(landmark)}`)
|
|
512
|
+
].join(" ");
|
|
513
|
+
}
|
|
514
|
+
function buildFirstActions(bundle, bundlePath) {
|
|
515
|
+
const sourceActions = bundle.elements.filter((element) => element.locator.kind === "source").map((element) => {
|
|
516
|
+
if (element.locator.kind !== "source") return "";
|
|
517
|
+
const start = Math.max(1, element.locator.line - 30);
|
|
518
|
+
const end = element.locator.line + 30;
|
|
519
|
+
return `Open #${element.id} source ${element.locator.file}:${element.locator.line} (for example: sed -n '${start},${end}p' ${shellQuote(element.locator.file)}).`;
|
|
520
|
+
}).filter(Boolean);
|
|
521
|
+
if (sourceActions.length) return sourceActions.slice(0, 2);
|
|
522
|
+
const tokens = collectBundleSearchTokens(bundle);
|
|
523
|
+
const actions = [];
|
|
524
|
+
if (tokens.length) {
|
|
525
|
+
actions.push(`git grep -n ${tokens.slice(0, 2).map((token) => `-e ${shellQuote(token)}`).join(" ")} -- . 2>/dev/null || true`);
|
|
526
|
+
}
|
|
527
|
+
if (tokens.length > 2) {
|
|
528
|
+
actions.push(`git grep -n ${tokens.slice(2, 4).map((token) => `-e ${shellQuote(token)}`).join(" ")} -- . 2>/dev/null || true`);
|
|
529
|
+
}
|
|
530
|
+
if (actions.length) return actions;
|
|
531
|
+
return [`No exact token captured. Read the fallback bundle once: ${bundlePath}`];
|
|
532
|
+
}
|
|
533
|
+
function collectBundleSearchTokens(bundle) {
|
|
534
|
+
const out = [];
|
|
535
|
+
const seen = /* @__PURE__ */ new Set();
|
|
536
|
+
const add = (token) => {
|
|
537
|
+
const key = token.toLowerCase();
|
|
538
|
+
if (seen.has(key)) return;
|
|
539
|
+
seen.add(key);
|
|
540
|
+
out.push(token);
|
|
541
|
+
};
|
|
542
|
+
for (const element of bundle.elements) {
|
|
543
|
+
for (const token of collectElementSearchTokens(element)) add(token);
|
|
544
|
+
}
|
|
545
|
+
return out.slice(0, 8);
|
|
546
|
+
}
|
|
547
|
+
function collectElementSearchTokens(element) {
|
|
548
|
+
const tokens = [];
|
|
549
|
+
const add = (value) => {
|
|
550
|
+
for (const token of searchTokenVariants(value)) {
|
|
551
|
+
if (!tokens.some((existing) => existing.toLowerCase() === token.toLowerCase())) {
|
|
552
|
+
tokens.push(token);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
if (element.locator.kind === "attr") add(element.locator.value);
|
|
557
|
+
if (element.locator.kind === "behavioral") add(element.locator.name);
|
|
558
|
+
for (const token of clicksmithHintTokens(element)) add(token);
|
|
559
|
+
for (const value of Object.values(element.el.attrs)) add(value);
|
|
560
|
+
for (const token of element.el.iconHints ?? []) add(token);
|
|
561
|
+
add(element.el.label);
|
|
562
|
+
add(element.el.text);
|
|
563
|
+
for (const label of element.near.labels ?? []) add(label);
|
|
564
|
+
for (const heading of element.near.headings ?? []) add(heading);
|
|
565
|
+
add(element.near.parentText);
|
|
566
|
+
return tokens.slice(0, 10);
|
|
567
|
+
}
|
|
568
|
+
function clicksmithHintTokens(element) {
|
|
569
|
+
const raw = element.frameworkHints?.clicksmith;
|
|
570
|
+
if (!isRecord(raw)) return [];
|
|
571
|
+
const tokens = raw.searchTokens;
|
|
572
|
+
return Array.isArray(tokens) ? tokens.filter((token) => typeof token === "string") : [];
|
|
573
|
+
}
|
|
574
|
+
function searchTokenVariants(value) {
|
|
575
|
+
if (!value) return [];
|
|
576
|
+
const normalized = value.trim().replace(/\s+/g, " ");
|
|
577
|
+
if (!normalized || normalized.length > 120) return [];
|
|
578
|
+
const variants = [normalized];
|
|
579
|
+
if (normalized.startsWith("#") && normalized.length > 1) variants.unshift(normalized.slice(1));
|
|
580
|
+
if (normalized.includes("/") && !normalized.includes(" ")) {
|
|
581
|
+
variants.push(normalized.split("/").filter(Boolean).at(-1) ?? "");
|
|
582
|
+
}
|
|
583
|
+
return variants.map((token) => token.trim().replace(/^["'`#]+|["'`]+$/g, "")).filter((token) => token.length >= 3 && token.length <= 80).filter((token) => !COMMON_SEARCH_TOKENS.has(token.toLowerCase()));
|
|
584
|
+
}
|
|
585
|
+
function quoteText(value) {
|
|
586
|
+
return JSON.stringify(truncateLine(value, 120));
|
|
587
|
+
}
|
|
588
|
+
function truncateLine(value, max) {
|
|
589
|
+
const oneLine = value.replace(/\s+/g, " ").trim();
|
|
590
|
+
return oneLine.length <= max ? oneLine : `${oneLine.slice(0, max - 3)}...`;
|
|
591
|
+
}
|
|
592
|
+
function shellQuote(value) {
|
|
593
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
594
|
+
}
|
|
595
|
+
function isRecord(value) {
|
|
596
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
597
|
+
}
|
|
598
|
+
var COMMON_SEARCH_TOKENS = /* @__PURE__ */ new Set([
|
|
599
|
+
"button",
|
|
600
|
+
"click",
|
|
601
|
+
"div",
|
|
602
|
+
"false",
|
|
603
|
+
"icon",
|
|
604
|
+
"input",
|
|
605
|
+
"label",
|
|
606
|
+
"link",
|
|
607
|
+
"main",
|
|
608
|
+
"section",
|
|
609
|
+
"span",
|
|
610
|
+
"svg",
|
|
611
|
+
"true",
|
|
612
|
+
"use"
|
|
613
|
+
]);
|
|
496
614
|
|
|
497
615
|
// src/daemon-service.ts
|
|
498
616
|
import {
|
|
@@ -681,4 +799,4 @@ export {
|
|
|
681
799
|
NotFoundError,
|
|
682
800
|
buildServer
|
|
683
801
|
};
|
|
684
|
-
//# sourceMappingURL=chunk-
|
|
802
|
+
//# sourceMappingURL=chunk-D5ABXTTD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/events.ts","../src/launcher.ts","../src/enrichment.ts","../src/run-manager.ts","../src/daemon-service.ts","../src/server.ts"],"sourcesContent":["import type { ServerEvent } from '@clicksmith/core';\n\ntype Listener = (event: ServerEvent) => void;\ninterface ReplayFilter {\n runId?: string;\n sessionId?: string;\n}\n\n/**\n * A tiny synchronous pub/sub for {@link ServerEvent}s. The Fastify WebSocket\n * layer subscribes to fan events out to connected extension clients; the run\n * manager publishes. Kept dependency-free and ordered (listeners fire in\n * registration order, events in emit order).\n */\nexport class EventBus {\n private readonly listeners = new Set<Listener>();\n private readonly history: ServerEvent[] = [];\n private readonly maxHistory: number;\n\n constructor(maxHistory = 500) {\n this.maxHistory = maxHistory;\n }\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n emit(event: ServerEvent): void {\n this.history.push(event);\n if (this.history.length > this.maxHistory) this.history.shift();\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch {\n // A misbehaving listener must never break the emit loop.\n }\n }\n }\n\n /** Replay buffered events, scoped to a run or session to avoid noisy reconnects. */\n replay(filter: ReplayFilter = {}): ServerEvent[] {\n if (!filter.runId && !filter.sessionId) return [];\n return this.history.filter((event) => {\n if (filter.runId && 'runId' in event && event.runId === filter.runId) return true;\n if (filter.sessionId && 'sessionId' in event && event.sessionId === filter.sessionId) {\n return true;\n }\n return false;\n });\n }\n}\n","import { execa } from 'execa';\nimport type { CommandSpec } from '@clicksmith/agent-config';\n\nexport interface LaunchHandlers {\n onLog: (stream: 'stdout' | 'stderr', chunk: string) => void;\n signal?: AbortSignal;\n}\n\nexport interface LaunchResult {\n exitCode: number;\n stdout: string;\n canceled: boolean;\n}\n\n/**\n * Spawn a resolved {@link CommandSpec} with execa, streaming stdout/stderr to\n * the caller as they arrive. Never rejects on non-zero exit — the run manager\n * decides what a non-zero code means.\n */\nexport async function launchAgent(\n spec: CommandSpec,\n handlers: LaunchHandlers,\n): Promise<LaunchResult> {\n const subprocess = execa(spec.command, spec.args, {\n cwd: spec.cwd,\n env: { ...process.env, ...spec.env },\n stdin: 'ignore',\n reject: false,\n all: false,\n cancelSignal: handlers.signal,\n });\n\n let stdout = '';\n subprocess.stdout?.on('data', (data: Buffer) => {\n const chunk = data.toString();\n stdout += chunk;\n handlers.onLog('stdout', chunk);\n });\n subprocess.stderr?.on('data', (data: Buffer) => {\n handlers.onLog('stderr', data.toString());\n });\n\n const result = await subprocess;\n return {\n exitCode: result.exitCode ?? (result.isCanceled ? 130 : 0),\n stdout,\n canceled: Boolean(result.isCanceled),\n };\n}\n","import type { CaptureBundle, Enrichment } from '@clicksmith/core';\n\n/**\n * Pluggable, best-effort enrichment. When configured (e.g. backed by the\n * code-review-graph MCP), it resolves source locators to attach review context\n * and impact radius per element. Failures must be **non-blocking**: the run\n * manager catches errors and records them as warnings on the bundle.\n */\nexport interface EnrichmentProvider {\n id: string;\n enrich(bundle: CaptureBundle): Promise<Enrichment | null>;\n}\n\n/**\n * Apply an enrichment provider to a bundle, swallowing failures into warnings.\n * Returns a (possibly) new bundle; never throws.\n */\nexport async function enrichBundle(\n bundle: CaptureBundle,\n provider: EnrichmentProvider | undefined,\n): Promise<CaptureBundle> {\n if (!provider) return bundle;\n try {\n const enrichment = await provider.enrich(bundle);\n if (!enrichment) return bundle;\n return { ...bundle, enrichment };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n ...bundle,\n enrichment: {\n source: 'code-review-graph',\n perElement: bundle.enrichment?.perElement ?? [],\n warnings: [...(bundle.enrichment?.warnings ?? []), `enrichment failed: ${message}`],\n },\n };\n }\n}\n","import { mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n configToAdapter,\n defaultBinExists,\n renderInstructionBody,\n resolveAgent,\n type AgentAdapter,\n type AgentConfig,\n type AgentLaunchContext,\n} from '@clicksmith/agent-config';\nimport {\n newRunId,\n type ApplyResponse,\n type CaptureBundle,\n type CapturedElement,\n type SandboxInfo,\n} from '@clicksmith/core';\nimport { describeSandbox, Git } from './git.js';\nimport { launchAgent } from './launcher.js';\nimport { enrichBundle, type EnrichmentProvider } from './enrichment.js';\nimport type { EventBus } from './events.js';\nimport type { FileStore } from './store.js';\nimport type { Logger } from './logger.js';\nimport type { DaemonConfig } from './config.js';\nimport type { RunRecord } from './types.js';\n\n/** Raised when a non-inplace run is requested against a dirty working tree. */\nexport class RefusalError extends Error {\n readonly code = 'DIRTY_TREE';\n}\n\nexport interface RunManagerDeps {\n store: FileStore;\n config: DaemonConfig;\n bus: EventBus;\n logger: Logger;\n enrichment?: EnrichmentProvider;\n /** Override PATH probing (tests inject a fake). */\n binExists?: (bin: string) => Promise<boolean>;\n}\n\nconst COMMIT_PREFIX = 'ClickSmith';\nconst POSITIVE_AVAILABILITY_CACHE_MS = 60_000;\nconst NEGATIVE_AVAILABILITY_CACHE_MS = 5_000;\nconst LOG_FLUSH_MS = 75;\nconst LOG_FLUSH_BYTES = 16 * 1024;\n\ninterface AvailabilityCacheEntry {\n ok: boolean;\n expiresAt: number;\n}\n\nexport class RunManager {\n private readonly availabilityCache = new Map<string, AvailabilityCacheEntry>();\n\n constructor(private readonly deps: RunManagerDeps) {}\n\n /**\n * Prepare a sandbox and start an agent run. The sandbox is prepared\n * synchronously so a dirty-tree refusal surfaces as an error to the caller;\n * the agent itself runs in the background, emitting WebSocket events.\n */\n async createRun(input: CaptureBundle): Promise<{ run: RunRecord }> {\n const { store, config, bus, logger } = this.deps;\n const agentConfig = resolveAgent(config.agents, input.execution.agentId);\n if (!agentConfig) {\n throw new RefusalError(\n `No agent configured (requested: ${input.execution.agentId ?? 'default'}).`,\n );\n }\n\n const runId = newRunId();\n const now = new Date();\n const repoRoot = config.repoRoot;\n const isolation = repoRoot ? input.execution.isolation : 'inplace';\n\n let sandbox: SandboxInfo | null = null;\n let baseCommit: string | null = null;\n let baseBranch: string | null = null;\n\n if (repoRoot) {\n const git = new Git(repoRoot);\n [baseCommit, baseBranch] = await Promise.all([\n git.headCommit(),\n safe(() => git.currentBranch()),\n ]);\n const baseRef = input.execution.baseRef ?? baseCommit;\n\n if (\n isolation !== 'inplace' &&\n (await git.isDirty({ exclude: ['.clicksmith/', '.clicksmith'] }))\n ) {\n throw new RefusalError(\n `Refusing to run in ${isolation} isolation: the working tree has uncommitted changes. ` +\n `Commit or stash them, or use inplace isolation explicitly.`,\n );\n }\n sandbox = await this.prepareSandbox(\n git,\n runId,\n isolation,\n baseRef,\n repoRoot,\n baseCommit,\n logger,\n );\n }\n\n const enriched = await enrichBundle(input, this.deps.enrichment);\n await store.saveBundle(runId, enriched);\n\n const run: RunRecord = {\n runId,\n sessionId: enriched.sessionId,\n agentId: agentConfig.id,\n status: 'running',\n createdAt: now.toISOString(),\n updatedAt: now.toISOString(),\n mode: enriched.execution.mode,\n isolation,\n prompt: enriched.prompt,\n repoRoot,\n baseCommit,\n baseBranch,\n sandbox,\n revert: null,\n };\n await store.saveRun(run);\n\n bus.emit({\n type: 'agent-started',\n runId,\n sessionId: run.sessionId,\n agentId: run.agentId,\n sandbox,\n });\n\n // Fire-and-forget the actual agent execution.\n void this.execute(run, enriched, agentConfig).catch((err) => {\n logger.error(`run ${runId} crashed`, err);\n });\n\n return { run };\n }\n\n private async prepareSandbox(\n git: Git,\n runId: string,\n isolation: SandboxInfo['isolation'],\n baseRef: string,\n repoRoot: string,\n baseCommit: string,\n logger: Logger,\n ): Promise<SandboxInfo> {\n const branch = `clicksmith/${runId}`;\n if (isolation === 'inplace') {\n return describeSandbox('inplace', repoRoot, null, baseCommit);\n }\n if (isolation === 'worktree') {\n if (await git.supportsWorktree()) {\n const path = this.deps.store.paths.sandboxDir(runId);\n await mkdir(join(path, '..'), { recursive: true });\n await git.createWorktree(path, branch, baseRef);\n return describeSandbox('worktree', path, branch, baseCommit);\n }\n logger.warn('git worktrees unavailable; falling back to a dedicated branch');\n }\n // branch isolation (explicit or worktree fallback)\n await git.createBranch(branch, baseRef);\n return describeSandbox('branch', repoRoot, branch, baseCommit);\n }\n\n private async execute(\n run: RunRecord,\n bundle: CaptureBundle,\n agentConfig: AgentConfig,\n ): Promise<void> {\n const { store, config, bus, logger } = this.deps;\n const sandboxPath = run.sandbox?.path ?? config.cwd;\n\n const instructionFile = await this.resolveInstructionFile(run);\n const agentPrompt = buildAgentPrompt({\n bundle,\n bundlePath: store.bundlePath(run.runId),\n run,\n });\n const ctx: AgentLaunchContext = {\n bundlePath: store.bundlePath(run.runId),\n prompt: bundle.prompt,\n agentPrompt,\n instructionFile,\n mode: bundle.execution.mode,\n mcpServer: 'clicksmith',\n cwd: sandboxPath,\n isolation: run.isolation,\n agentId: agentConfig.id,\n binExists: this.deps.binExists ?? defaultBinExists,\n };\n\n const adapter = configToAdapter(agentConfig);\n if (!(await this.isAgentAvailable(agentConfig, adapter, ctx))) {\n await this.fail(run, unavailableMessage(agentConfig));\n return;\n }\n\n const rawSpec = adapter.buildCommand(ctx);\n const spec = {\n ...rawSpec,\n env: {\n CLICKSMITH_BUNDLE_PATH: ctx.bundlePath,\n CLICKSMITH_INSTRUCTION_FILE: ctx.instructionFile,\n CLICKSMITH_MODE: ctx.mode,\n CLICKSMITH_ISOLATION: ctx.isolation,\n CLICKSMITH_RUN_ID: run.runId,\n ...(rawSpec.env ?? {}),\n },\n };\n logger.info(`run ${run.runId}: ${spec.command} ${spec.args.join(' ')}`);\n\n const logBuffer = createLogBuffer(\n (chunk) => store.appendLog(run.runId, chunk),\n (err) => logger.warn(`run ${run.runId}: failed to persist agent log`, err),\n );\n\n let result;\n try {\n result = await launchAgent(spec, {\n onLog: (stream, chunk) => {\n logBuffer.append(chunk);\n bus.emit({ type: 'agent-log', runId: run.runId, stream, chunk });\n },\n });\n } catch (err) {\n await logBuffer.flush();\n await this.fail(run, err instanceof Error ? err.message : String(err));\n return;\n }\n await logBuffer.flush();\n\n // Capture artifacts from isolated sandboxes. Inplace runs edit the current\n // tree directly, so the extension does not need a daemon-generated patch.\n const plan = result.stdout.trim();\n let diff = '';\n if (run.sandbox && run.repoRoot && run.sandbox.isolation !== 'inplace') {\n diff = await Git.captureDiff(run.sandbox.path);\n }\n if (plan) await store.writeArtifact(run.runId, 'plan.md', plan);\n if (diff) await store.writeArtifact(run.runId, 'diff.patch', diff);\n\n run.exitCode = result.exitCode;\n run.hasPlan = plan.length > 0;\n run.hasDiff = diff.length > 0;\n\n if (result.exitCode !== 0) {\n await this.fail(run, `Agent exited with code ${result.exitCode}.`);\n return;\n }\n\n run.status = 'plan-ready';\n run.updatedAt = new Date().toISOString();\n await store.saveRun(run);\n bus.emit({\n type: 'plan-ready',\n runId: run.runId,\n ...(plan ? { plan } : {}),\n ...(diff ? { diff } : {}),\n });\n bus.emit({ type: 'agent-done', runId: run.runId, exitCode: result.exitCode });\n\n if (bundle.execution.autoApply) {\n logger.info(`run ${run.runId}: autoApply enabled, applying`);\n await this.apply(run.runId);\n }\n }\n\n private async fail(run: RunRecord, message: string): Promise<void> {\n run.status = 'error';\n run.error = message;\n run.updatedAt = new Date().toISOString();\n await this.deps.store.saveRun(run);\n this.deps.bus.emit({ type: 'agent-error', runId: run.runId, message });\n }\n\n private async isAgentAvailable(\n agentConfig: AgentConfig,\n adapter: AgentAdapter,\n ctx: AgentLaunchContext,\n ): Promise<boolean> {\n const key = availabilityCacheKey(agentConfig);\n const now = Date.now();\n const cached = this.availabilityCache.get(key);\n if (cached && cached.expiresAt > now) return cached.ok;\n\n const ok = await adapter.isAvailable(ctx);\n this.availabilityCache.set(key, {\n ok,\n expiresAt: now + (ok ? POSITIVE_AVAILABILITY_CACHE_MS : NEGATIVE_AVAILABILITY_CACHE_MS),\n });\n return ok;\n }\n\n /**\n * Merge a finished run's sandbox changes back into the working tree. Reports\n * conflicts, commits on success, records revert metadata, and cleans up the\n * sandbox.\n */\n async apply(runId: string): Promise<ApplyResponse> {\n const { store, bus, logger } = this.deps;\n const run = await store.getRun(runId);\n if (!run) throw new Error(`Unknown run: ${runId}`);\n if (!run.repoRoot || !run.sandbox) {\n throw new Error(`Run ${runId} has no git sandbox to apply.`);\n }\n\n bus.emit({ type: 'apply-started', runId });\n const git = new Git(run.repoRoot);\n const previousHead = await git.headCommit();\n const message = `${COMMIT_PREFIX} run ${runId}: ${truncate(run.prompt, 72)}`;\n\n try {\n let commit: string | undefined;\n\n if (run.sandbox.isolation === 'worktree') {\n const diff = (await store.readArtifact(runId, 'diff.patch')) ?? '';\n const applied = await git.applyPatch(diff);\n if (!applied.ok) return await this.applyConflict(run, applied.conflicts);\n commit = diff.trim() ? await git.commit(message) : previousHead;\n await this.cleanupSandbox(run);\n } else if (run.sandbox.isolation === 'branch') {\n // Changes are already staged in the repo on the clicksmith branch.\n if (await git.hasChanges()) await git.commit(message);\n if (run.baseBranch) await git.switchTo(run.baseBranch);\n const merged = await git.merge(run.sandbox.branch!, message);\n if (!merged.ok) return await this.applyConflict(run, merged.conflicts);\n commit = await git.headCommit();\n await git.deleteBranch(run.sandbox.branch!);\n } else {\n // inplace: commit whatever the agent changed in the working tree.\n commit = (await git.hasChanges()) ? await git.commit(message) : previousHead;\n }\n\n run.status = 'applied';\n run.applied = { ...(commit ? { commit } : {}), at: new Date().toISOString() };\n run.revert = {\n previousHead,\n ...(commit && commit !== previousHead ? { appliedCommit: commit } : {}),\n instructions:\n commit && commit !== previousHead\n ? `git revert ${commit} # or: git reset --hard ${previousHead}`\n : 'No commit was created; nothing to revert.',\n };\n run.updatedAt = new Date().toISOString();\n await store.saveRun(run);\n\n bus.emit({ type: 'apply-done', runId, ...(commit ? { commit } : {}) });\n return { applied: true, ...(commit ? { commit } : {}) };\n } catch (err) {\n const messageText = err instanceof Error ? err.message : String(err);\n logger.error(`apply ${runId} failed`, messageText);\n run.status = 'apply-error';\n run.error = messageText;\n await store.saveRun(run);\n bus.emit({ type: 'apply-error', runId, message: messageText });\n return { applied: false };\n }\n }\n\n private async applyConflict(run: RunRecord, conflicts: string[]): Promise<ApplyResponse> {\n run.status = 'apply-error';\n run.error = `Apply conflicts in: ${conflicts.join(', ') || 'unknown files'}`;\n run.updatedAt = new Date().toISOString();\n await this.deps.store.saveRun(run);\n this.deps.bus.emit({\n type: 'apply-error',\n runId: run.runId,\n message: run.error,\n conflicts,\n });\n return { applied: false, conflicts };\n }\n\n private async cleanupSandbox(run: RunRecord): Promise<void> {\n if (!run.repoRoot || !run.sandbox) return;\n if (run.sandbox.isolation === 'worktree') {\n const git = new Git(run.repoRoot);\n await git.removeWorktree(run.sandbox.path, run.sandbox.branch ?? undefined);\n }\n }\n\n /**\n * Resolve the instruction file passed to the agent. Use a run-local compact\n * file so ClickSmith runs do not ingest a project's full AGENTS/CLAUDE/rules\n * corpus before the targeted UI lookup has even started.\n */\n private async resolveInstructionFile(run: RunRecord): Promise<string> {\n const { config, store } = this.deps;\n const body = renderInstructionBody({ daemonPort: config.port });\n return store.writeArtifact(run.runId, 'AGENT_INSTRUCTIONS.md', body);\n }\n}\n\nasync function safe<T>(fn: () => Promise<T>): Promise<T | null> {\n try {\n return await fn();\n } catch {\n return null;\n }\n}\n\nfunction truncate(s: string, n: number): string {\n return s.length <= n ? s : `${s.slice(0, n - 1)}…`;\n}\n\nfunction unavailableMessage(agentConfig: AgentConfig): string {\n const checked = agentConfig.detect?.anyOf ?? [agentConfig.command];\n return (\n `Agent \"${agentConfig.id}\" is not available to the ClickSmith daemon. ` +\n `Checked: ${checked.join(', ')}. ` +\n `Install the agent CLI so it is on the daemon PATH, or set command/detect to an absolute path in .clicksmith/agents.config.json.`\n );\n}\n\nfunction availabilityCacheKey(agentConfig: AgentConfig): string {\n return [\n agentConfig.id,\n agentConfig.command,\n ...(agentConfig.detect?.anyOf ?? [agentConfig.command]),\n ].join('\\0');\n}\n\nfunction createLogBuffer(\n write: (chunk: string) => Promise<void>,\n onError: (err: unknown) => void,\n): { append: (chunk: string) => void; flush: () => Promise<void> } {\n let buffer = '';\n let timer: ReturnType<typeof setTimeout> | null = null;\n let flushChain = Promise.resolve();\n\n async function flush(): Promise<void> {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n if (!buffer) return flushChain;\n\n const chunk = buffer;\n buffer = '';\n flushChain = flushChain.then(() => write(chunk)).catch(onError);\n await flushChain;\n }\n\n return {\n append(chunk) {\n buffer += chunk;\n if (buffer.length >= LOG_FLUSH_BYTES) {\n void flush();\n } else if (!timer) {\n timer = setTimeout(() => void flush(), LOG_FLUSH_MS);\n }\n },\n flush,\n };\n}\n\nfunction buildAgentPrompt(input: {\n bundle: CaptureBundle;\n bundlePath: string;\n run: RunRecord;\n}): string {\n const { bundle, bundlePath, run } = input;\n const targets = bundle.elements.map(formatTargetSummary).join('\\n');\n const firstActions = buildFirstActions(bundle, bundlePath).map((action, index) => {\n return `${index + 1}. ${action}`;\n });\n const modeLine =\n bundle.execution.mode === 'edit'\n ? 'Mode: edit. Make the smallest working-tree change; do not ask for confirmation.'\n : 'Mode: plan. Inspect only and return a concise plan.';\n\n return [\n 'ClickSmith fast UI edit.',\n `Request: ${truncateLine(bundle.prompt, 300)}`,\n `Route: ${truncateLine(bundle.app.route, 160)}`,\n `Cwd: ${run.sandbox?.path ?? run.repoRoot ?? '(none)'}`,\n `Isolation: ${run.isolation}`,\n '',\n 'Targets:',\n targets,\n '',\n 'First actions (do these before reading broad project docs):',\n ...firstActions,\n '',\n 'Fast-path rules:',\n '- Do not read AGENTS.md, CLAUDE.md, .cursor/rules, skills, guidelines, or bundle.json first.',\n '- If source file:line exists, open only that file around the line; no grep needed.',\n '- Otherwise run at most two targeted grep searches from First actions, then edit the smallest file.',\n '- If a grep result is only a shared sprite/icon definition, use it to find the component usage.',\n `- Fallback only if the target is still ambiguous: ${bundlePath}`,\n '- Keep final output brief: changed files and checks run.',\n '',\n modeLine,\n ].join('\\n');\n}\n\nfunction formatTargetSummary(element: CapturedElement): string {\n const label = element.el.text || element.el.label || element.el.role || element.el.tag;\n const attrs = formatAttrs(element.el.attrs);\n const locator = formatLocator(element);\n const near = formatNear(element);\n const tokens = collectElementSearchTokens(element).slice(0, 5);\n return [\n `#${element.id} <${element.el.tag}> ${quoteText(label)}`,\n `locator=${locator}`,\n attrs ? `attrs=${attrs}` : '',\n near ? `near=${near}` : '',\n tokens.length ? `tokens=${tokens.map(quoteText).join(', ')}` : '',\n ]\n .filter(Boolean)\n .join(' | ');\n}\n\nfunction formatLocator(element: CapturedElement): string {\n const { locator } = element;\n if (locator.kind === 'source') {\n return `source:${locator.file}:${locator.line}${locator.column != null ? `:${locator.column}` : ''}`;\n }\n if (locator.kind === 'attr') {\n return `attr:${locator.attr}=${quoteText(locator.value)}`;\n }\n if (locator.kind === 'behavioral') {\n return `behavioral:${locator.role} ${quoteText(locator.name)}`;\n }\n return `dom:${truncateLine(locator.selector, 120)}`;\n}\n\nfunction formatAttrs(attrs: Record<string, string>): string {\n return Object.entries(attrs)\n .slice(0, 5)\n .map(([key, value]) => `${key}=${quoteText(value)}`)\n .join(' ');\n}\n\nfunction formatNear(element: CapturedElement): string {\n return [\n ...(element.near.labels ?? []).slice(0, 1).map((label) => `label=${quoteText(label)}`),\n ...(element.near.headings ?? []).slice(0, 1).map((heading) => `heading=${quoteText(heading)}`),\n ...(element.near.landmarks ?? []).slice(0, 1).map((landmark) => `landmark=${quoteText(landmark)}`),\n ].join(' ');\n}\n\nfunction buildFirstActions(bundle: CaptureBundle, bundlePath: string): string[] {\n const sourceActions = bundle.elements\n .filter((element) => element.locator.kind === 'source')\n .map((element) => {\n if (element.locator.kind !== 'source') return '';\n const start = Math.max(1, element.locator.line - 30);\n const end = element.locator.line + 30;\n return `Open #${element.id} source ${element.locator.file}:${element.locator.line} (for example: sed -n '${start},${end}p' ${shellQuote(element.locator.file)}).`;\n })\n .filter(Boolean);\n if (sourceActions.length) return sourceActions.slice(0, 2);\n\n const tokens = collectBundleSearchTokens(bundle);\n const actions: string[] = [];\n if (tokens.length) {\n actions.push(`git grep -n ${tokens.slice(0, 2).map((token) => `-e ${shellQuote(token)}`).join(' ')} -- . 2>/dev/null || true`);\n }\n if (tokens.length > 2) {\n actions.push(`git grep -n ${tokens.slice(2, 4).map((token) => `-e ${shellQuote(token)}`).join(' ')} -- . 2>/dev/null || true`);\n }\n if (actions.length) return actions;\n\n return [`No exact token captured. Read the fallback bundle once: ${bundlePath}`];\n}\n\nfunction collectBundleSearchTokens(bundle: CaptureBundle): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n const add = (token: string) => {\n const key = token.toLowerCase();\n if (seen.has(key)) return;\n seen.add(key);\n out.push(token);\n };\n\n for (const element of bundle.elements) {\n for (const token of collectElementSearchTokens(element)) add(token);\n }\n return out.slice(0, 8);\n}\n\nfunction collectElementSearchTokens(element: CapturedElement): string[] {\n const tokens: string[] = [];\n const add = (value: string | undefined) => {\n for (const token of searchTokenVariants(value)) {\n if (!tokens.some((existing) => existing.toLowerCase() === token.toLowerCase())) {\n tokens.push(token);\n }\n }\n };\n\n if (element.locator.kind === 'attr') add(element.locator.value);\n if (element.locator.kind === 'behavioral') add(element.locator.name);\n for (const token of clicksmithHintTokens(element)) add(token);\n for (const value of Object.values(element.el.attrs)) add(value);\n for (const token of element.el.iconHints ?? []) add(token);\n add(element.el.label);\n add(element.el.text);\n for (const label of element.near.labels ?? []) add(label);\n for (const heading of element.near.headings ?? []) add(heading);\n add(element.near.parentText);\n\n return tokens.slice(0, 10);\n}\n\nfunction clicksmithHintTokens(element: CapturedElement): string[] {\n const raw = element.frameworkHints?.clicksmith;\n if (!isRecord(raw)) return [];\n const tokens = raw.searchTokens;\n return Array.isArray(tokens) ? tokens.filter((token): token is string => typeof token === 'string') : [];\n}\n\nfunction searchTokenVariants(value: string | undefined): string[] {\n if (!value) return [];\n const normalized = value.trim().replace(/\\s+/g, ' ');\n if (!normalized || normalized.length > 120) return [];\n const variants = [normalized];\n if (normalized.startsWith('#') && normalized.length > 1) variants.unshift(normalized.slice(1));\n if (normalized.includes('/') && !normalized.includes(' ')) {\n variants.push(normalized.split('/').filter(Boolean).at(-1) ?? '');\n }\n return variants\n .map((token) => token.trim().replace(/^[\"'`#]+|[\"'`]+$/g, ''))\n .filter((token) => token.length >= 3 && token.length <= 80)\n .filter((token) => !COMMON_SEARCH_TOKENS.has(token.toLowerCase()));\n}\n\nfunction quoteText(value: string): string {\n return JSON.stringify(truncateLine(value, 120));\n}\n\nfunction truncateLine(value: string, max: number): string {\n const oneLine = value.replace(/\\s+/g, ' ').trim();\n return oneLine.length <= max ? oneLine : `${oneLine.slice(0, max - 3)}...`;\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nconst COMMON_SEARCH_TOKENS = new Set([\n 'button',\n 'click',\n 'div',\n 'false',\n 'icon',\n 'input',\n 'label',\n 'link',\n 'main',\n 'section',\n 'span',\n 'svg',\n 'true',\n 'use',\n]);\n","import {\n appendElement,\n createSession,\n ExecutionOptionsSchema,\n finalizeSession,\n removeElement,\n touchSession,\n type ApplyResponse,\n type CaptureRequest,\n type CaptureResponse,\n type HealthResponse,\n type RemoveElementResponse,\n type Session,\n type SubmitRequest,\n type SubmitResponse,\n} from '@clicksmith/core';\nimport { EventBus } from './events.js';\nimport { FileStore } from './store.js';\nimport { RunManager, type RunManagerDeps } from './run-manager.js';\nimport type { DaemonConfig } from './config.js';\nimport type { EnrichmentProvider } from './enrichment.js';\nimport { version as DAEMON_VERSION } from './version.js';\n\nexport interface DaemonServiceOptions {\n config: DaemonConfig;\n enrichment?: EnrichmentProvider;\n binExists?: RunManagerDeps['binExists'];\n}\n\n/**\n * The framework-agnostic core of the daemon. The HTTP/WS server and tests both\n * drive this; it owns sessions, runs, the event bus, and persistence.\n */\nexport class DaemonService {\n readonly config: DaemonConfig;\n readonly store: FileStore;\n readonly bus: EventBus;\n readonly runs: RunManager;\n\n constructor(opts: DaemonServiceOptions) {\n this.config = opts.config;\n this.store = new FileStore(opts.config.storageRoot);\n this.bus = new EventBus();\n this.runs = new RunManager({\n store: this.store,\n config: opts.config,\n bus: this.bus,\n logger: opts.config.logger,\n ...(opts.enrichment ? { enrichment: opts.enrichment } : {}),\n ...(opts.binExists ? { binExists: opts.binExists } : {}),\n });\n }\n\n async init(): Promise<void> {\n await this.store.init();\n await this.store.cleanupExpired();\n }\n\n /* ------------------------------- capture ------------------------------ */\n\n /** Create or append to the active session for an app/route. */\n async capture(req: CaptureRequest): Promise<CaptureResponse> {\n const now = new Date();\n let session = req.sessionId ? await this.store.getSession(req.sessionId) : undefined;\n if (!session) {\n session = createSession({ app: req.app, now, ttlMs: this.config.ttlMs });\n } else {\n session = touchSession(session, now, this.config.ttlMs);\n }\n\n const { session: next, element } = appendElement(session, req.element, now);\n await this.store.saveSession(next);\n\n this.bus.emit({ type: 'capture-ack', sessionId: next.id, element });\n return { sessionId: next.id, element };\n }\n\n async removeElement(sessionId: string, elementId: number): Promise<RemoveElementResponse> {\n const session = await this.store.getSession(sessionId);\n if (!session) return { removed: false };\n const { session: next, removed } = removeElement(session, elementId);\n if (removed) {\n await this.store.saveSession(next);\n this.bus.emit({ type: 'element-removed', sessionId, elementId });\n }\n return { removed };\n }\n\n async getSession(id: string): Promise<Session | undefined> {\n return this.store.getSession(id);\n }\n\n /* ------------------------------- submit ------------------------------- */\n\n /** Finalize a session into a bundle and start a run. */\n async submit(req: SubmitRequest): Promise<SubmitResponse> {\n const session = await this.store.getSession(req.sessionId);\n if (!session) throw new NotFoundError(`Unknown session: ${req.sessionId}`);\n\n const execution = ExecutionOptionsSchema.parse(req.execution ?? {});\n const bundle = finalizeSession(session, {\n prompt: req.prompt,\n execution,\n ...(req.enrichment ? { enrichment: req.enrichment } : {}),\n });\n\n const submitted: Session = { ...session, status: 'submitted', prompt: req.prompt };\n await this.store.saveSession(submitted);\n\n const { run } = await this.runs.createRun(bundle);\n return { runId: run.runId, bundle };\n }\n\n async apply(runId: string): Promise<ApplyResponse> {\n return this.runs.apply(runId);\n }\n\n /* ------------------------------- health ------------------------------- */\n\n async health(): Promise<HealthResponse> {\n const sessions = await this.store.listSessions();\n return {\n ok: true,\n name: 'clicksmith-daemon',\n version: DAEMON_VERSION,\n host: this.config.host,\n port: this.config.port,\n repoRoot: this.config.repoRoot,\n activeSessions: sessions.filter((s) => s.status === 'active').length,\n };\n }\n}\n\nexport class NotFoundError extends Error {\n readonly code = 'NOT_FOUND';\n}\n","import Fastify, { type FastifyInstance } from 'fastify';\nimport websocket from '@fastify/websocket';\nimport {\n CaptureRequestSchema,\n SubmitRequestSchema,\n type ClientMessage,\n type ServerEvent,\n} from '@clicksmith/core';\nimport { type DaemonService, NotFoundError } from './daemon-service.js';\nimport { RefusalError } from './run-manager.js';\n\ntype SubscribeMessage = Extract<ClientMessage, { type: 'subscribe' }> & { runId?: string };\n\n/**\n * Build the Fastify app exposing ClickSmith's HTTP + WebSocket surface on top\n * of a {@link DaemonService}. Binds loopback only; CORS is opened for localhost\n * so the browser extension can talk to it.\n */\nexport async function buildServer(service: DaemonService): Promise<FastifyInstance> {\n const app = Fastify({ logger: false });\n\n // Minimal CORS for the extension (loopback only).\n app.addHook('onRequest', async (req, reply) => {\n reply.header('Access-Control-Allow-Origin', '*');\n reply.header('Access-Control-Allow-Methods', 'GET,POST,DELETE,OPTIONS');\n reply.header('Access-Control-Allow-Headers', 'content-type');\n if (req.method === 'OPTIONS') {\n reply.code(204).send();\n }\n });\n\n await app.register(websocket);\n\n /* ------------------------------- HTTP -------------------------------- */\n\n app.get('/health', async () => service.health());\n\n app.post('/capture', async (req, reply) => {\n const parsed = CaptureRequestSchema.safeParse(req.body);\n if (!parsed.success) return reply.code(400).send({ error: parsed.error.message });\n return service.capture(parsed.data);\n });\n\n app.post('/submit', async (req, reply) => {\n const parsed = SubmitRequestSchema.safeParse(req.body);\n if (!parsed.success) return reply.code(400).send({ error: parsed.error.message });\n try {\n return await service.submit(parsed.data);\n } catch (err) {\n if (err instanceof NotFoundError) return reply.code(404).send({ error: err.message });\n if (err instanceof RefusalError)\n return reply.code(409).send({ error: err.message, code: err.code });\n throw err;\n }\n });\n\n app.post<{ Params: { runId: string } }>('/apply/:runId', async (req, reply) => {\n try {\n return await service.apply(req.params.runId);\n } catch (err) {\n return reply.code(404).send({ error: err instanceof Error ? err.message : String(err) });\n }\n });\n\n app.get<{ Params: { id: string } }>('/session/:id', async (req, reply) => {\n const session = await service.getSession(req.params.id);\n if (!session) return reply.code(404).send({ error: `Unknown session: ${req.params.id}` });\n return session;\n });\n\n app.delete<{ Params: { sessionId: string; elementId: string } }>(\n '/element/:sessionId/:elementId',\n async (req) => {\n const elementId = Number.parseInt(req.params.elementId, 10);\n return service.removeElement(req.params.sessionId, elementId);\n },\n );\n\n /* ----------------------------- WebSocket ----------------------------- */\n\n app.get('/ws', { websocket: true }, (socket) => {\n const send = (event: ServerEvent) => {\n if (socket.readyState === socket.OPEN) socket.send(JSON.stringify(event));\n };\n const unsubscribe = service.bus.subscribe(send);\n\n socket.on('message', (raw: Buffer) => {\n let msg: ClientMessage;\n try {\n msg = JSON.parse(raw.toString()) as ClientMessage;\n } catch {\n return;\n }\n if (msg.type === 'ping') socket.send(JSON.stringify({ type: 'pong' }));\n else if (msg.type === 'subscribe') {\n // Replay only the run/session the client asks for. Unscoped replay can\n // flood reconnecting extension workers with unrelated historical logs.\n const sub = msg as SubscribeMessage;\n for (const event of service.bus.replay({ runId: sub.runId, sessionId: sub.sessionId })) {\n send(event);\n }\n }\n });\n\n socket.on('close', unsubscribe);\n socket.on('error', unsubscribe);\n });\n\n return app;\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
import {
|
|
3
3
|
DaemonService,
|
|
4
4
|
buildServer
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-D5ABXTTD.js";
|
|
6
6
|
import {
|
|
7
7
|
resolveDaemonConfig,
|
|
8
8
|
startMcp,
|
|
9
9
|
version
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-273SGCZB.js";
|
|
11
11
|
|
|
12
12
|
// src/cli.ts
|
|
13
13
|
import { defaultBinExists } from "@clicksmith/agent-config";
|
package/dist/index.d.ts
CHANGED
|
@@ -209,9 +209,9 @@ declare class RunManager {
|
|
|
209
209
|
private applyConflict;
|
|
210
210
|
private cleanupSandbox;
|
|
211
211
|
/**
|
|
212
|
-
* Resolve the instruction file passed to the agent.
|
|
213
|
-
*
|
|
214
|
-
*
|
|
212
|
+
* Resolve the instruction file passed to the agent. Use a run-local compact
|
|
213
|
+
* file so ClickSmith runs do not ingest a project's full AGENTS/CLAUDE/rules
|
|
214
|
+
* corpus before the targeted UI lookup has even started.
|
|
215
215
|
*/
|
|
216
216
|
private resolveInstructionFile;
|
|
217
217
|
}
|
|
@@ -417,6 +417,6 @@ declare function createMcpServer(reader: McpReader): Server;
|
|
|
417
417
|
declare function startMcp(): Promise<void>;
|
|
418
418
|
|
|
419
419
|
/** The daemon's semantic version, surfaced via /health and MCP. */
|
|
420
|
-
declare const version = "0.1.
|
|
420
|
+
declare const version = "0.1.6";
|
|
421
421
|
|
|
422
422
|
export { type DaemonConfig, type DaemonConfigInput, DaemonService, type DaemonServiceOptions, type EnrichmentProvider, EventBus, FileStore, Git, GitError, type LaunchHandlers, type LaunchResult, type LogLevel, type Logger, type McpReader, NotFoundError, RefusalError, type RevertMeta, type RunArtifacts, RunManager, type RunManagerDeps, type RunRecord, TOOL_DEFINITIONS, type ToolResult, buildServer, callTool, createLogger, createMcpServer, describeSandbox, enrichBundle, launchAgent, loadAgentsConfig, osCacheRoot, readerFromStore, resolveDaemonConfig, resolveStorageRoot, startMcp, storagePaths, version };
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
buildServer,
|
|
8
8
|
enrichBundle,
|
|
9
9
|
launchAgent
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-D5ABXTTD.js";
|
|
11
11
|
import {
|
|
12
12
|
FileStore,
|
|
13
13
|
Git,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
startMcp,
|
|
26
26
|
storagePaths,
|
|
27
27
|
version
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-273SGCZB.js";
|
|
29
29
|
export {
|
|
30
30
|
DaemonService,
|
|
31
31
|
EventBus,
|
package/dist/mcp.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clicksmith/daemon",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "The ClickSmith localhost daemon: Fastify HTTP + WebSocket, MCP stdio server, persistence, git sandbox orchestration, and config-driven agent launching.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"fastify": "^5.1.0",
|
|
26
26
|
"zod": "^3.23.8",
|
|
27
27
|
"@clicksmith/core": "0.1.0",
|
|
28
|
-
"@clicksmith/agent-config": "0.1.
|
|
28
|
+
"@clicksmith/agent-config": "0.1.2"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/ws": "^8.5.13",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/events.ts","../src/launcher.ts","../src/enrichment.ts","../src/run-manager.ts","../src/daemon-service.ts","../src/server.ts"],"sourcesContent":["import type { ServerEvent } from '@clicksmith/core';\n\ntype Listener = (event: ServerEvent) => void;\ninterface ReplayFilter {\n runId?: string;\n sessionId?: string;\n}\n\n/**\n * A tiny synchronous pub/sub for {@link ServerEvent}s. The Fastify WebSocket\n * layer subscribes to fan events out to connected extension clients; the run\n * manager publishes. Kept dependency-free and ordered (listeners fire in\n * registration order, events in emit order).\n */\nexport class EventBus {\n private readonly listeners = new Set<Listener>();\n private readonly history: ServerEvent[] = [];\n private readonly maxHistory: number;\n\n constructor(maxHistory = 500) {\n this.maxHistory = maxHistory;\n }\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n emit(event: ServerEvent): void {\n this.history.push(event);\n if (this.history.length > this.maxHistory) this.history.shift();\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch {\n // A misbehaving listener must never break the emit loop.\n }\n }\n }\n\n /** Replay buffered events, scoped to a run or session to avoid noisy reconnects. */\n replay(filter: ReplayFilter = {}): ServerEvent[] {\n if (!filter.runId && !filter.sessionId) return [];\n return this.history.filter((event) => {\n if (filter.runId && 'runId' in event && event.runId === filter.runId) return true;\n if (filter.sessionId && 'sessionId' in event && event.sessionId === filter.sessionId) {\n return true;\n }\n return false;\n });\n }\n}\n","import { execa } from 'execa';\nimport type { CommandSpec } from '@clicksmith/agent-config';\n\nexport interface LaunchHandlers {\n onLog: (stream: 'stdout' | 'stderr', chunk: string) => void;\n signal?: AbortSignal;\n}\n\nexport interface LaunchResult {\n exitCode: number;\n stdout: string;\n canceled: boolean;\n}\n\n/**\n * Spawn a resolved {@link CommandSpec} with execa, streaming stdout/stderr to\n * the caller as they arrive. Never rejects on non-zero exit — the run manager\n * decides what a non-zero code means.\n */\nexport async function launchAgent(\n spec: CommandSpec,\n handlers: LaunchHandlers,\n): Promise<LaunchResult> {\n const subprocess = execa(spec.command, spec.args, {\n cwd: spec.cwd,\n env: { ...process.env, ...spec.env },\n stdin: 'ignore',\n reject: false,\n all: false,\n cancelSignal: handlers.signal,\n });\n\n let stdout = '';\n subprocess.stdout?.on('data', (data: Buffer) => {\n const chunk = data.toString();\n stdout += chunk;\n handlers.onLog('stdout', chunk);\n });\n subprocess.stderr?.on('data', (data: Buffer) => {\n handlers.onLog('stderr', data.toString());\n });\n\n const result = await subprocess;\n return {\n exitCode: result.exitCode ?? (result.isCanceled ? 130 : 0),\n stdout,\n canceled: Boolean(result.isCanceled),\n };\n}\n","import type { CaptureBundle, Enrichment } from '@clicksmith/core';\n\n/**\n * Pluggable, best-effort enrichment. When configured (e.g. backed by the\n * code-review-graph MCP), it resolves source locators to attach review context\n * and impact radius per element. Failures must be **non-blocking**: the run\n * manager catches errors and records them as warnings on the bundle.\n */\nexport interface EnrichmentProvider {\n id: string;\n enrich(bundle: CaptureBundle): Promise<Enrichment | null>;\n}\n\n/**\n * Apply an enrichment provider to a bundle, swallowing failures into warnings.\n * Returns a (possibly) new bundle; never throws.\n */\nexport async function enrichBundle(\n bundle: CaptureBundle,\n provider: EnrichmentProvider | undefined,\n): Promise<CaptureBundle> {\n if (!provider) return bundle;\n try {\n const enrichment = await provider.enrich(bundle);\n if (!enrichment) return bundle;\n return { ...bundle, enrichment };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n ...bundle,\n enrichment: {\n source: 'code-review-graph',\n perElement: bundle.enrichment?.perElement ?? [],\n warnings: [...(bundle.enrichment?.warnings ?? []), `enrichment failed: ${message}`],\n },\n };\n }\n}\n","import { mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n configToAdapter,\n defaultBinExists,\n renderInstructionBody,\n resolveAgent,\n type AgentAdapter,\n type AgentConfig,\n type AgentLaunchContext,\n} from '@clicksmith/agent-config';\nimport {\n newRunId,\n type ApplyResponse,\n type CaptureBundle,\n type SandboxInfo,\n} from '@clicksmith/core';\nimport { describeSandbox, Git } from './git.js';\nimport { launchAgent } from './launcher.js';\nimport { enrichBundle, type EnrichmentProvider } from './enrichment.js';\nimport type { EventBus } from './events.js';\nimport type { FileStore } from './store.js';\nimport type { Logger } from './logger.js';\nimport type { DaemonConfig } from './config.js';\nimport type { RunRecord } from './types.js';\n\n/** Raised when a non-inplace run is requested against a dirty working tree. */\nexport class RefusalError extends Error {\n readonly code = 'DIRTY_TREE';\n}\n\nexport interface RunManagerDeps {\n store: FileStore;\n config: DaemonConfig;\n bus: EventBus;\n logger: Logger;\n enrichment?: EnrichmentProvider;\n /** Override PATH probing (tests inject a fake). */\n binExists?: (bin: string) => Promise<boolean>;\n}\n\nconst COMMIT_PREFIX = 'ClickSmith';\nconst POSITIVE_AVAILABILITY_CACHE_MS = 60_000;\nconst NEGATIVE_AVAILABILITY_CACHE_MS = 5_000;\nconst LOG_FLUSH_MS = 75;\nconst LOG_FLUSH_BYTES = 16 * 1024;\n\ninterface AvailabilityCacheEntry {\n ok: boolean;\n expiresAt: number;\n}\n\nexport class RunManager {\n private readonly availabilityCache = new Map<string, AvailabilityCacheEntry>();\n\n constructor(private readonly deps: RunManagerDeps) {}\n\n /**\n * Prepare a sandbox and start an agent run. The sandbox is prepared\n * synchronously so a dirty-tree refusal surfaces as an error to the caller;\n * the agent itself runs in the background, emitting WebSocket events.\n */\n async createRun(input: CaptureBundle): Promise<{ run: RunRecord }> {\n const { store, config, bus, logger } = this.deps;\n const agentConfig = resolveAgent(config.agents, input.execution.agentId);\n if (!agentConfig) {\n throw new RefusalError(\n `No agent configured (requested: ${input.execution.agentId ?? 'default'}).`,\n );\n }\n\n const runId = newRunId();\n const now = new Date();\n const repoRoot = config.repoRoot;\n const isolation = repoRoot ? input.execution.isolation : 'inplace';\n\n let sandbox: SandboxInfo | null = null;\n let baseCommit: string | null = null;\n let baseBranch: string | null = null;\n\n if (repoRoot) {\n const git = new Git(repoRoot);\n [baseCommit, baseBranch] = await Promise.all([\n git.headCommit(),\n safe(() => git.currentBranch()),\n ]);\n const baseRef = input.execution.baseRef ?? baseCommit;\n\n if (\n isolation !== 'inplace' &&\n (await git.isDirty({ exclude: ['.clicksmith/', '.clicksmith'] }))\n ) {\n throw new RefusalError(\n `Refusing to run in ${isolation} isolation: the working tree has uncommitted changes. ` +\n `Commit or stash them, or use inplace isolation explicitly.`,\n );\n }\n sandbox = await this.prepareSandbox(\n git,\n runId,\n isolation,\n baseRef,\n repoRoot,\n baseCommit,\n logger,\n );\n }\n\n const enriched = await enrichBundle(input, this.deps.enrichment);\n await store.saveBundle(runId, enriched);\n\n const run: RunRecord = {\n runId,\n sessionId: enriched.sessionId,\n agentId: agentConfig.id,\n status: 'running',\n createdAt: now.toISOString(),\n updatedAt: now.toISOString(),\n mode: enriched.execution.mode,\n isolation,\n prompt: enriched.prompt,\n repoRoot,\n baseCommit,\n baseBranch,\n sandbox,\n revert: null,\n };\n await store.saveRun(run);\n\n bus.emit({\n type: 'agent-started',\n runId,\n sessionId: run.sessionId,\n agentId: run.agentId,\n sandbox,\n });\n\n // Fire-and-forget the actual agent execution.\n void this.execute(run, enriched, agentConfig).catch((err) => {\n logger.error(`run ${runId} crashed`, err);\n });\n\n return { run };\n }\n\n private async prepareSandbox(\n git: Git,\n runId: string,\n isolation: SandboxInfo['isolation'],\n baseRef: string,\n repoRoot: string,\n baseCommit: string,\n logger: Logger,\n ): Promise<SandboxInfo> {\n const branch = `clicksmith/${runId}`;\n if (isolation === 'inplace') {\n return describeSandbox('inplace', repoRoot, null, baseCommit);\n }\n if (isolation === 'worktree') {\n if (await git.supportsWorktree()) {\n const path = this.deps.store.paths.sandboxDir(runId);\n await mkdir(join(path, '..'), { recursive: true });\n await git.createWorktree(path, branch, baseRef);\n return describeSandbox('worktree', path, branch, baseCommit);\n }\n logger.warn('git worktrees unavailable; falling back to a dedicated branch');\n }\n // branch isolation (explicit or worktree fallback)\n await git.createBranch(branch, baseRef);\n return describeSandbox('branch', repoRoot, branch, baseCommit);\n }\n\n private async execute(\n run: RunRecord,\n bundle: CaptureBundle,\n agentConfig: AgentConfig,\n ): Promise<void> {\n const { store, config, bus, logger } = this.deps;\n const sandboxPath = run.sandbox?.path ?? config.cwd;\n\n const instructionFile = await this.resolveInstructionFile(run, agentConfig);\n const agentPrompt = buildAgentPrompt({\n bundle,\n bundlePath: store.bundlePath(run.runId),\n instructionFile,\n run,\n });\n const ctx: AgentLaunchContext = {\n bundlePath: store.bundlePath(run.runId),\n prompt: bundle.prompt,\n agentPrompt,\n instructionFile,\n mode: bundle.execution.mode,\n mcpServer: 'clicksmith',\n cwd: sandboxPath,\n isolation: run.isolation,\n agentId: agentConfig.id,\n binExists: this.deps.binExists ?? defaultBinExists,\n };\n\n const adapter = configToAdapter(agentConfig);\n if (!(await this.isAgentAvailable(agentConfig, adapter, ctx))) {\n await this.fail(run, unavailableMessage(agentConfig));\n return;\n }\n\n const rawSpec = adapter.buildCommand(ctx);\n const spec = {\n ...rawSpec,\n env: {\n CLICKSMITH_BUNDLE_PATH: ctx.bundlePath,\n CLICKSMITH_INSTRUCTION_FILE: ctx.instructionFile,\n CLICKSMITH_MODE: ctx.mode,\n CLICKSMITH_ISOLATION: ctx.isolation,\n CLICKSMITH_RUN_ID: run.runId,\n ...(rawSpec.env ?? {}),\n },\n };\n logger.info(`run ${run.runId}: ${spec.command} ${spec.args.join(' ')}`);\n\n const logBuffer = createLogBuffer(\n (chunk) => store.appendLog(run.runId, chunk),\n (err) => logger.warn(`run ${run.runId}: failed to persist agent log`, err),\n );\n\n let result;\n try {\n result = await launchAgent(spec, {\n onLog: (stream, chunk) => {\n logBuffer.append(chunk);\n bus.emit({ type: 'agent-log', runId: run.runId, stream, chunk });\n },\n });\n } catch (err) {\n await logBuffer.flush();\n await this.fail(run, err instanceof Error ? err.message : String(err));\n return;\n }\n await logBuffer.flush();\n\n // Capture artifacts from isolated sandboxes. Inplace runs edit the current\n // tree directly, so the extension does not need a daemon-generated patch.\n const plan = result.stdout.trim();\n let diff = '';\n if (run.sandbox && run.repoRoot && run.sandbox.isolation !== 'inplace') {\n diff = await Git.captureDiff(run.sandbox.path);\n }\n if (plan) await store.writeArtifact(run.runId, 'plan.md', plan);\n if (diff) await store.writeArtifact(run.runId, 'diff.patch', diff);\n\n run.exitCode = result.exitCode;\n run.hasPlan = plan.length > 0;\n run.hasDiff = diff.length > 0;\n\n if (result.exitCode !== 0) {\n await this.fail(run, `Agent exited with code ${result.exitCode}.`);\n return;\n }\n\n run.status = 'plan-ready';\n run.updatedAt = new Date().toISOString();\n await store.saveRun(run);\n bus.emit({\n type: 'plan-ready',\n runId: run.runId,\n ...(plan ? { plan } : {}),\n ...(diff ? { diff } : {}),\n });\n bus.emit({ type: 'agent-done', runId: run.runId, exitCode: result.exitCode });\n\n if (bundle.execution.autoApply) {\n logger.info(`run ${run.runId}: autoApply enabled, applying`);\n await this.apply(run.runId);\n }\n }\n\n private async fail(run: RunRecord, message: string): Promise<void> {\n run.status = 'error';\n run.error = message;\n run.updatedAt = new Date().toISOString();\n await this.deps.store.saveRun(run);\n this.deps.bus.emit({ type: 'agent-error', runId: run.runId, message });\n }\n\n private async isAgentAvailable(\n agentConfig: AgentConfig,\n adapter: AgentAdapter,\n ctx: AgentLaunchContext,\n ): Promise<boolean> {\n const key = availabilityCacheKey(agentConfig);\n const now = Date.now();\n const cached = this.availabilityCache.get(key);\n if (cached && cached.expiresAt > now) return cached.ok;\n\n const ok = await adapter.isAvailable(ctx);\n this.availabilityCache.set(key, {\n ok,\n expiresAt: now + (ok ? POSITIVE_AVAILABILITY_CACHE_MS : NEGATIVE_AVAILABILITY_CACHE_MS),\n });\n return ok;\n }\n\n /**\n * Merge a finished run's sandbox changes back into the working tree. Reports\n * conflicts, commits on success, records revert metadata, and cleans up the\n * sandbox.\n */\n async apply(runId: string): Promise<ApplyResponse> {\n const { store, bus, logger } = this.deps;\n const run = await store.getRun(runId);\n if (!run) throw new Error(`Unknown run: ${runId}`);\n if (!run.repoRoot || !run.sandbox) {\n throw new Error(`Run ${runId} has no git sandbox to apply.`);\n }\n\n bus.emit({ type: 'apply-started', runId });\n const git = new Git(run.repoRoot);\n const previousHead = await git.headCommit();\n const message = `${COMMIT_PREFIX} run ${runId}: ${truncate(run.prompt, 72)}`;\n\n try {\n let commit: string | undefined;\n\n if (run.sandbox.isolation === 'worktree') {\n const diff = (await store.readArtifact(runId, 'diff.patch')) ?? '';\n const applied = await git.applyPatch(diff);\n if (!applied.ok) return await this.applyConflict(run, applied.conflicts);\n commit = diff.trim() ? await git.commit(message) : previousHead;\n await this.cleanupSandbox(run);\n } else if (run.sandbox.isolation === 'branch') {\n // Changes are already staged in the repo on the clicksmith branch.\n if (await git.hasChanges()) await git.commit(message);\n if (run.baseBranch) await git.switchTo(run.baseBranch);\n const merged = await git.merge(run.sandbox.branch!, message);\n if (!merged.ok) return await this.applyConflict(run, merged.conflicts);\n commit = await git.headCommit();\n await git.deleteBranch(run.sandbox.branch!);\n } else {\n // inplace: commit whatever the agent changed in the working tree.\n commit = (await git.hasChanges()) ? await git.commit(message) : previousHead;\n }\n\n run.status = 'applied';\n run.applied = { ...(commit ? { commit } : {}), at: new Date().toISOString() };\n run.revert = {\n previousHead,\n ...(commit && commit !== previousHead ? { appliedCommit: commit } : {}),\n instructions:\n commit && commit !== previousHead\n ? `git revert ${commit} # or: git reset --hard ${previousHead}`\n : 'No commit was created; nothing to revert.',\n };\n run.updatedAt = new Date().toISOString();\n await store.saveRun(run);\n\n bus.emit({ type: 'apply-done', runId, ...(commit ? { commit } : {}) });\n return { applied: true, ...(commit ? { commit } : {}) };\n } catch (err) {\n const messageText = err instanceof Error ? err.message : String(err);\n logger.error(`apply ${runId} failed`, messageText);\n run.status = 'apply-error';\n run.error = messageText;\n await store.saveRun(run);\n bus.emit({ type: 'apply-error', runId, message: messageText });\n return { applied: false };\n }\n }\n\n private async applyConflict(run: RunRecord, conflicts: string[]): Promise<ApplyResponse> {\n run.status = 'apply-error';\n run.error = `Apply conflicts in: ${conflicts.join(', ') || 'unknown files'}`;\n run.updatedAt = new Date().toISOString();\n await this.deps.store.saveRun(run);\n this.deps.bus.emit({\n type: 'apply-error',\n runId: run.runId,\n message: run.error,\n conflicts,\n });\n return { applied: false, conflicts };\n }\n\n private async cleanupSandbox(run: RunRecord): Promise<void> {\n if (!run.repoRoot || !run.sandbox) return;\n if (run.sandbox.isolation === 'worktree') {\n const git = new Git(run.repoRoot);\n await git.removeWorktree(run.sandbox.path, run.sandbox.branch ?? undefined);\n }\n }\n\n /**\n * Resolve the instruction file passed to the agent. Prefer the project's\n * rendered file if it exists; otherwise write a run-local one from the shared\n * template so every agent always has instructions.\n */\n private async resolveInstructionFile(run: RunRecord, agentConfig: AgentConfig): Promise<string> {\n const { config, store } = this.deps;\n if (config.repoRoot && agentConfig.instructions) {\n const projectFile = join(config.repoRoot, agentConfig.instructions.file);\n if (await fileExists(projectFile)) return projectFile;\n }\n const body = renderInstructionBody({ daemonPort: config.port });\n return store.writeArtifact(run.runId, 'AGENT_INSTRUCTIONS.md', body);\n }\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n const { access } = await import('node:fs/promises');\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function safe<T>(fn: () => Promise<T>): Promise<T | null> {\n try {\n return await fn();\n } catch {\n return null;\n }\n}\n\nfunction truncate(s: string, n: number): string {\n return s.length <= n ? s : `${s.slice(0, n - 1)}…`;\n}\n\nfunction unavailableMessage(agentConfig: AgentConfig): string {\n const checked = agentConfig.detect?.anyOf ?? [agentConfig.command];\n return (\n `Agent \"${agentConfig.id}\" is not available to the ClickSmith daemon. ` +\n `Checked: ${checked.join(', ')}. ` +\n `Install the agent CLI so it is on the daemon PATH, or set command/detect to an absolute path in .clicksmith/agents.config.json.`\n );\n}\n\nfunction availabilityCacheKey(agentConfig: AgentConfig): string {\n return [\n agentConfig.id,\n agentConfig.command,\n ...(agentConfig.detect?.anyOf ?? [agentConfig.command]),\n ].join('\\0');\n}\n\nfunction createLogBuffer(\n write: (chunk: string) => Promise<void>,\n onError: (err: unknown) => void,\n): { append: (chunk: string) => void; flush: () => Promise<void> } {\n let buffer = '';\n let timer: ReturnType<typeof setTimeout> | null = null;\n let flushChain = Promise.resolve();\n\n async function flush(): Promise<void> {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n if (!buffer) return flushChain;\n\n const chunk = buffer;\n buffer = '';\n flushChain = flushChain.then(() => write(chunk)).catch(onError);\n await flushChain;\n }\n\n return {\n append(chunk) {\n buffer += chunk;\n if (buffer.length >= LOG_FLUSH_BYTES) {\n void flush();\n } else if (!timer) {\n timer = setTimeout(() => void flush(), LOG_FLUSH_MS);\n }\n },\n flush,\n };\n}\n\nfunction buildAgentPrompt(input: {\n bundle: CaptureBundle;\n bundlePath: string;\n instructionFile: string;\n run: RunRecord;\n}): string {\n const { bundle, bundlePath, instructionFile, run } = input;\n const elementSummary = bundle.elements\n .map((element) => {\n const text = element.el.text || element.el.label || element.el.tag;\n const locator =\n element.locator.kind === 'source'\n ? `${element.locator.file}:${element.locator.line}`\n : element.locator.kind === 'attr'\n ? `${element.locator.attr}=${JSON.stringify(element.locator.value)}`\n : element.locator.kind === 'behavioral'\n ? `${element.locator.role} ${JSON.stringify(element.locator.name)}`\n : element.locator.selector;\n return `#${element.id}: <${element.el.tag}> ${JSON.stringify(text)} | ${element.locator.kind}: ${locator}`;\n })\n .join('\\n');\n const sourceHints = bundle.elements\n .flatMap((element) => {\n if (element.locator.kind === 'source') {\n return [`#${element.id} source ${element.locator.file}:${element.locator.line}`];\n }\n if (element.locator.kind === 'attr') {\n return [\n `#${element.id} search ${element.locator.attr}=${JSON.stringify(element.locator.value)}`,\n ];\n }\n return [];\n })\n .join('\\n');\n const modeLine =\n bundle.execution.mode === 'edit'\n ? 'Edit mode: modify the smallest set of files in the working directory. Do not ask for confirmation.'\n : 'Plan mode: inspect files and produce a concise plan without modifying files.';\n\n return [\n `User request: ${bundle.prompt}`,\n `Route: ${bundle.app.route}`,\n `URL: ${bundle.app.url}`,\n `Bundle JSON: ${bundlePath}`,\n `Project instructions: ${instructionFile}`,\n `Working directory: ${run.sandbox?.path ?? run.repoRoot ?? '(none)'}`,\n `Isolation: ${run.isolation}`,\n '',\n 'Clicked elements:',\n elementSummary,\n ...(sourceHints ? ['', 'Fast lookup hints:', sourceHints] : []),\n '',\n modeLine,\n 'Use the numbered #N references. Prefer source hints and stable attributes before DOM structure.',\n ].join('\\n');\n}\n","import {\n appendElement,\n createSession,\n ExecutionOptionsSchema,\n finalizeSession,\n removeElement,\n touchSession,\n type ApplyResponse,\n type CaptureRequest,\n type CaptureResponse,\n type HealthResponse,\n type RemoveElementResponse,\n type Session,\n type SubmitRequest,\n type SubmitResponse,\n} from '@clicksmith/core';\nimport { EventBus } from './events.js';\nimport { FileStore } from './store.js';\nimport { RunManager, type RunManagerDeps } from './run-manager.js';\nimport type { DaemonConfig } from './config.js';\nimport type { EnrichmentProvider } from './enrichment.js';\nimport { version as DAEMON_VERSION } from './version.js';\n\nexport interface DaemonServiceOptions {\n config: DaemonConfig;\n enrichment?: EnrichmentProvider;\n binExists?: RunManagerDeps['binExists'];\n}\n\n/**\n * The framework-agnostic core of the daemon. The HTTP/WS server and tests both\n * drive this; it owns sessions, runs, the event bus, and persistence.\n */\nexport class DaemonService {\n readonly config: DaemonConfig;\n readonly store: FileStore;\n readonly bus: EventBus;\n readonly runs: RunManager;\n\n constructor(opts: DaemonServiceOptions) {\n this.config = opts.config;\n this.store = new FileStore(opts.config.storageRoot);\n this.bus = new EventBus();\n this.runs = new RunManager({\n store: this.store,\n config: opts.config,\n bus: this.bus,\n logger: opts.config.logger,\n ...(opts.enrichment ? { enrichment: opts.enrichment } : {}),\n ...(opts.binExists ? { binExists: opts.binExists } : {}),\n });\n }\n\n async init(): Promise<void> {\n await this.store.init();\n await this.store.cleanupExpired();\n }\n\n /* ------------------------------- capture ------------------------------ */\n\n /** Create or append to the active session for an app/route. */\n async capture(req: CaptureRequest): Promise<CaptureResponse> {\n const now = new Date();\n let session = req.sessionId ? await this.store.getSession(req.sessionId) : undefined;\n if (!session) {\n session = createSession({ app: req.app, now, ttlMs: this.config.ttlMs });\n } else {\n session = touchSession(session, now, this.config.ttlMs);\n }\n\n const { session: next, element } = appendElement(session, req.element, now);\n await this.store.saveSession(next);\n\n this.bus.emit({ type: 'capture-ack', sessionId: next.id, element });\n return { sessionId: next.id, element };\n }\n\n async removeElement(sessionId: string, elementId: number): Promise<RemoveElementResponse> {\n const session = await this.store.getSession(sessionId);\n if (!session) return { removed: false };\n const { session: next, removed } = removeElement(session, elementId);\n if (removed) {\n await this.store.saveSession(next);\n this.bus.emit({ type: 'element-removed', sessionId, elementId });\n }\n return { removed };\n }\n\n async getSession(id: string): Promise<Session | undefined> {\n return this.store.getSession(id);\n }\n\n /* ------------------------------- submit ------------------------------- */\n\n /** Finalize a session into a bundle and start a run. */\n async submit(req: SubmitRequest): Promise<SubmitResponse> {\n const session = await this.store.getSession(req.sessionId);\n if (!session) throw new NotFoundError(`Unknown session: ${req.sessionId}`);\n\n const execution = ExecutionOptionsSchema.parse(req.execution ?? {});\n const bundle = finalizeSession(session, {\n prompt: req.prompt,\n execution,\n ...(req.enrichment ? { enrichment: req.enrichment } : {}),\n });\n\n const submitted: Session = { ...session, status: 'submitted', prompt: req.prompt };\n await this.store.saveSession(submitted);\n\n const { run } = await this.runs.createRun(bundle);\n return { runId: run.runId, bundle };\n }\n\n async apply(runId: string): Promise<ApplyResponse> {\n return this.runs.apply(runId);\n }\n\n /* ------------------------------- health ------------------------------- */\n\n async health(): Promise<HealthResponse> {\n const sessions = await this.store.listSessions();\n return {\n ok: true,\n name: 'clicksmith-daemon',\n version: DAEMON_VERSION,\n host: this.config.host,\n port: this.config.port,\n repoRoot: this.config.repoRoot,\n activeSessions: sessions.filter((s) => s.status === 'active').length,\n };\n }\n}\n\nexport class NotFoundError extends Error {\n readonly code = 'NOT_FOUND';\n}\n","import Fastify, { type FastifyInstance } from 'fastify';\nimport websocket from '@fastify/websocket';\nimport {\n CaptureRequestSchema,\n SubmitRequestSchema,\n type ClientMessage,\n type ServerEvent,\n} from '@clicksmith/core';\nimport { type DaemonService, NotFoundError } from './daemon-service.js';\nimport { RefusalError } from './run-manager.js';\n\ntype SubscribeMessage = Extract<ClientMessage, { type: 'subscribe' }> & { runId?: string };\n\n/**\n * Build the Fastify app exposing ClickSmith's HTTP + WebSocket surface on top\n * of a {@link DaemonService}. Binds loopback only; CORS is opened for localhost\n * so the browser extension can talk to it.\n */\nexport async function buildServer(service: DaemonService): Promise<FastifyInstance> {\n const app = Fastify({ logger: false });\n\n // Minimal CORS for the extension (loopback only).\n app.addHook('onRequest', async (req, reply) => {\n reply.header('Access-Control-Allow-Origin', '*');\n reply.header('Access-Control-Allow-Methods', 'GET,POST,DELETE,OPTIONS');\n reply.header('Access-Control-Allow-Headers', 'content-type');\n if (req.method === 'OPTIONS') {\n reply.code(204).send();\n }\n });\n\n await app.register(websocket);\n\n /* ------------------------------- HTTP -------------------------------- */\n\n app.get('/health', async () => service.health());\n\n app.post('/capture', async (req, reply) => {\n const parsed = CaptureRequestSchema.safeParse(req.body);\n if (!parsed.success) return reply.code(400).send({ error: parsed.error.message });\n return service.capture(parsed.data);\n });\n\n app.post('/submit', async (req, reply) => {\n const parsed = SubmitRequestSchema.safeParse(req.body);\n if (!parsed.success) return reply.code(400).send({ error: parsed.error.message });\n try {\n return await service.submit(parsed.data);\n } catch (err) {\n if (err instanceof NotFoundError) return reply.code(404).send({ error: err.message });\n if (err instanceof RefusalError)\n return reply.code(409).send({ error: err.message, code: err.code });\n throw err;\n }\n });\n\n app.post<{ Params: { runId: string } }>('/apply/:runId', async (req, reply) => {\n try {\n return await service.apply(req.params.runId);\n } catch (err) {\n return reply.code(404).send({ error: err instanceof Error ? err.message : String(err) });\n }\n });\n\n app.get<{ Params: { id: string } }>('/session/:id', async (req, reply) => {\n const session = await service.getSession(req.params.id);\n if (!session) return reply.code(404).send({ error: `Unknown session: ${req.params.id}` });\n return session;\n });\n\n app.delete<{ Params: { sessionId: string; elementId: string } }>(\n '/element/:sessionId/:elementId',\n async (req) => {\n const elementId = Number.parseInt(req.params.elementId, 10);\n return service.removeElement(req.params.sessionId, elementId);\n },\n );\n\n /* ----------------------------- WebSocket ----------------------------- */\n\n app.get('/ws', { websocket: true }, (socket) => {\n const send = (event: ServerEvent) => {\n if (socket.readyState === socket.OPEN) socket.send(JSON.stringify(event));\n };\n const unsubscribe = service.bus.subscribe(send);\n\n socket.on('message', (raw: Buffer) => {\n let msg: ClientMessage;\n try {\n msg = JSON.parse(raw.toString()) as ClientMessage;\n } catch {\n return;\n }\n if (msg.type === 'ping') socket.send(JSON.stringify({ type: 'pong' }));\n else if (msg.type === 'subscribe') {\n // Replay only the run/session the client asks for. Unscoped replay can\n // flood reconnecting extension workers with unrelated historical logs.\n const sub = msg as SubscribeMessage;\n for (const event of service.bus.replay({ runId: sub.runId, sessionId: sub.sessionId })) {\n send(event);\n }\n }\n });\n\n socket.on('close', unsubscribe);\n socket.on('error', unsubscribe);\n });\n\n return app;\n}\n"],"mappings":"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","names":[]}
|