{"version":3,"file":"style-system.cjs","names":["path","fs","spawn","os","createHarnessTelemetry","requireDoomConfigContext","HARNESS_EVENT","DOOM_CONFIG_SERVICE","connectDoomCordisHost"],"sources":["../../src/extensions/styleSystem.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { requireDoomConfigContext } from '@agimon-ai/doompi-config/piContext';\nimport { DOOM_CONFIG_SERVICE } from '@agimon-ai/doompi-core/config';\nimport { connectDoomCordisHost } from '@agimon-ai/doompi-core/cordis-host';\nimport {\n  createHarnessTelemetry,\n  HARNESS_EVENT,\n  type HarnessTelemetry,\n} from '@agimon-ai/doompi-core/runtime-log-sink-telemetry';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ToolResultEvent } from '@earendil-works/pi-coding-agent';\n\ninterface StoryRenderRequest {\n  storyPath: string;\n  projectPath: string;\n  componentName: string;\n  storyName: string;\n}\n\ninterface StyleSystemResult {\n  imagePath?: string;\n  storyFilePath?: string;\n  componentTitle?: string;\n  renderedStory?: string;\n  colorScheme?: string;\n  viewport?: { width: number; height: number };\n  dimensions?: string;\n}\n\nconst STYLE_SYSTEM_PACKAGE = 'style-system';\nconst STYLE_SYSTEM_CONFIG_FILE = `${STYLE_SYSTEM_PACKAGE}.config.yaml`;\nconst DEFAULT_STORY_NAME = 'Playground';\nconst STORY_FILE_PATTERN = /\\.stories\\.(?:ts|tsx)$/;\nconst EXPORT_STORY_PATTERN = /export\\s+const\\s+([A-Z][A-Za-z0-9_]*)\\b/g;\nconst TOOL_TIMEOUT_MS = 120_000;\nconst PACKAGE_SOURCE = '@agimon-ai/doompi/style-system';\n\nexport function eventPath(event: ToolResultEvent, cwd: string): string | undefined {\n  if (event.isError || (event.toolName !== 'write' && event.toolName !== 'edit')) return undefined;\n  const input = event.input as { path?: unknown };\n  if (typeof input.path !== 'string' || !STORY_FILE_PATTERN.test(input.path)) return undefined;\n  return path.resolve(cwd, input.path);\n}\n\nfunction findStyleSystemProject(startFile: string, root: string): string | undefined {\n  let directory = path.dirname(startFile);\n  const resolvedRoot = path.resolve(root);\n  while (directory.startsWith(resolvedRoot)) {\n    if (fs.existsSync(path.join(directory, STYLE_SYSTEM_CONFIG_FILE))) {\n      return path.relative(resolvedRoot, directory) || '.';\n    }\n    const parent = path.dirname(directory);\n    if (parent === directory) break;\n    directory = parent;\n  }\n  return undefined;\n}\n\nfunction inferComponentName(storyPath: string, content: string): string {\n  const title = content.match(/title\\s*:\\s*['\"]([^'\"]+)['\"]/)?.[1]?.trim();\n  if (title) return title;\n  return path.basename(storyPath).replace(/\\.stories\\.(?:ts|tsx)$/, '');\n}\n\nfunction inferStoryName(content: string): string {\n  if (/export\\s+const\\s+Playground\\b/.test(content)) return DEFAULT_STORY_NAME;\n  const firstExportedStory = EXPORT_STORY_PATTERN.exec(content)?.[1];\n  EXPORT_STORY_PATTERN.lastIndex = 0;\n  return firstExportedStory ?? DEFAULT_STORY_NAME;\n}\n\nexport function inferStoryRenderRequest(storyPath: string, root: string): StoryRenderRequest | undefined {\n  if (!STORY_FILE_PATTERN.test(storyPath) || !fs.existsSync(storyPath)) return undefined;\n  const projectPath = findStyleSystemProject(storyPath, root);\n  if (!projectPath) return undefined;\n  const content = fs.readFileSync(storyPath, 'utf8');\n  return {\n    storyPath,\n    projectPath,\n    componentName: inferComponentName(storyPath, content),\n    storyName: inferStoryName(content),\n  };\n}\n\nexport function parseStyleSystemOutput(stdout: string): StyleSystemResult {\n  const text = stdout.trim();\n  if (!text) throw new Error(`${STYLE_SYSTEM_PACKAGE} returned no output`);\n  return JSON.parse(text) as StyleSystemResult;\n}\n\n/* v8 ignore next 57 */\nasync function runStyleSystem(request: StoryRenderRequest, root: string): Promise<StyleSystemResult> {\n  const args = [\n    'exec',\n    STYLE_SYSTEM_PACKAGE,\n    'get-ui-component',\n    '--component-name',\n    request.componentName,\n    '--app-path',\n    request.projectPath,\n    '--story-name',\n    request.storyName,\n  ];\n\n  return new Promise((resolve, reject) => {\n    const child = spawn('pnpm', args, {\n      cwd: root,\n      env: { ...process.env, LOG_LEVEL: process.env.LOG_LEVEL ?? 'info' },\n      stdio: ['ignore', 'pipe', 'pipe'],\n    });\n    let stdout = '';\n    let stderr = '';\n    const timer = setTimeout(() => child.kill('SIGTERM'), TOOL_TIMEOUT_MS);\n    child.stdout.on('data', (chunk: Buffer) => {\n      stdout += chunk.toString();\n    });\n    child.stderr.on('data', (chunk: Buffer) => {\n      stderr += chunk.toString();\n    });\n    child.once('error', (error) => {\n      clearTimeout(timer);\n      reject(error);\n    });\n    child.once('exit', (code) => {\n      clearTimeout(timer);\n      if (code !== 0) {\n        reject(new Error(stderr.trim() || `${STYLE_SYSTEM_PACKAGE} exited with code ${code ?? 'unknown'}`));\n        return;\n      }\n      try {\n        resolve(parseStyleSystemOutput(stdout));\n      } catch (error) {\n        reject(error);\n      }\n    });\n  });\n}\n\nexport async function copyImageToHarnessTemp(\n  imagePath: string,\n  request: StoryRenderRequest,\n  outputDirectory: string = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-harness-style-system-')),\n): Promise<string> {\n  const targetDirectory = path.join(outputDirectory, `${STYLE_SYSTEM_PACKAGE}-visuals`);\n  await fs.promises.mkdir(targetDirectory, { recursive: true, mode: 0o700 });\n  const baseName = path\n    .basename(request.storyPath)\n    .replace(/[^a-zA-Z0-9.-]/g, '-')\n    .replace(/\\.stories\\./, '.');\n  const targetPath = path.join(targetDirectory, `${Date.now()}-${baseName}.png`);\n  await fs.promises.copyFile(imagePath, targetPath);\n  await fs.promises.chmod(targetPath, 0o600);\n  return targetPath;\n}\n\nexport function successMessage(\n  request: StoryRenderRequest,\n  result: StyleSystemResult,\n  copiedImagePath: string,\n): string {\n  const details = [\n    `Image path: ${copiedImagePath}`,\n    `Story file: ${result.storyFilePath ?? request.storyPath}`,\n    `Component: ${result.componentTitle ?? request.componentName}`,\n    `Story: ${result.renderedStory ?? request.storyName}`,\n    result.colorScheme ? `Color scheme: ${result.colorScheme}` : undefined,\n    result.dimensions ? `Dimensions: ${result.dimensions}` : undefined,\n  ].filter((line): line is string => Boolean(line));\n\n  return [\n    'Style-system visual generated for the story you just changed.',\n    ...details,\n    'The image is saved and ready to inspect: call the read tool on the image path above to view it.',\n    'If the layout, spacing, color, overflow, or hierarchy looks wrong, adjust the UI and story, then let this hook render it again.',\n  ].join('\\n');\n}\n\nexport function failureMessage(request: StoryRenderRequest, error: unknown): string {\n  const message = error instanceof Error ? error.message : String(error);\n  return [\n    `Your file change was applied, but ${STYLE_SYSTEM_PACKAGE} could not generate a visual for it.`,\n    `Story file: ${request.storyPath}`,\n    `Command: pnpm exec ${STYLE_SYSTEM_PACKAGE} get-ui-component --component-name ${JSON.stringify(request.componentName)} --app-path ${JSON.stringify(request.projectPath)} --story-name ${JSON.stringify(request.storyName)}`,\n    `Error: ${message}`,\n    'Do not treat the UI as visually verified. Run the command above to investigate.',\n  ].join('\\n');\n}\n\n/* v8 ignore next 23 */\n// Pi's extension loader resolves this default export and rejects the module\n// without it (\"Extension does not export a valid factory function\"). This must\n// stay a default export; see the pi-extension override in vibe-lint.config.yaml.\nexport function registerStyleSystemVisuals(\n  pi: ExtensionAPI,\n  telemetry: HarnessTelemetry = createHarnessTelemetry(),\n  cordisContext: () => Context,\n): void {\n  pi.on('tool_result', async (event, ctx) => {\n    const changedPath = eventPath(event, ctx.cwd);\n    if (!changedPath) return undefined;\n\n    const harness = requireDoomConfigContext(cordisContext()).harness;\n    const root = harness.root ?? ctx.cwd;\n    const request = inferStoryRenderRequest(changedPath, root);\n    if (!request) return undefined;\n    const statusKey = `style-system:${event.toolCallId}`;\n    if (ctx.hasUI) ctx.ui.setStatus(statusKey, `Rendering ${request.componentName} visual...`);\n\n    try {\n      const result = await runStyleSystem(request, root);\n      if (!result.imagePath) throw new Error(`${STYLE_SYSTEM_PACKAGE} did not return an imagePath`);\n      const copiedImagePath = await copyImageToHarnessTemp(\n        result.imagePath,\n        request,\n        harness.temporaryDirectory ?? fs.mkdtempSync(path.join(os.tmpdir(), 'agent-harness-style-system-')),\n      );\n      return {\n        content: [...event.content, { type: 'text' as const, text: successMessage(request, result, copiedImagePath) }],\n      };\n    } catch (error) {\n      void telemetry.recordWarning(HARNESS_EVENT.styleSystemRenderFailed, error, {\n        'style_system.component': request.componentName,\n      });\n      // The tool itself succeeded; only this hook failed. Surface the reason without\n      // reporting the write as failed, which would invite a retry of a change that landed.\n      return {\n        content: [...event.content, { type: 'text' as const, text: failureMessage(request, error) }],\n      };\n    } finally {\n      if (ctx.hasUI) ctx.ui.setStatus(statusKey, undefined);\n    }\n  });\n}\n\ninterface StyleSystemPluginConfig {\n  readonly pi: ExtensionAPI;\n  readonly telemetry: HarnessTelemetry;\n}\n\nfunction styleSystemPlugin(cordis: Context, { pi, telemetry }: StyleSystemPluginConfig): void {\n  let activeContext: Context | undefined;\n  cordis.inject([DOOM_CONFIG_SERVICE], (context) => {\n    activeContext = context;\n    return () => {\n      if (activeContext === context) activeContext = undefined;\n    };\n  });\n  registerStyleSystemVisuals(pi, telemetry, () => {\n    if (!activeContext) throw new Error('Doom style-system runtime is waiting for the session config service.');\n    return activeContext;\n  });\n}\n\nexport default async function styleSystemVisuals(\n  pi: ExtensionAPI,\n  telemetry: HarnessTelemetry = createHarnessTelemetry(),\n): Promise<void> {\n  const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n  const fiber = connection.root.plugin(styleSystemPlugin, { pi, telemetry });\n  try {\n    await fiber;\n  } catch (error) {\n    await fiber.dispose();\n    await connection.dispose();\n    throw error;\n  }\n  let disposal: Promise<void> | undefined;\n  pi.on(\n    'session_shutdown',\n    () =>\n      (disposal ??= (async () => {\n        try {\n          await fiber.dispose();\n        } finally {\n          await connection.dispose();\n        }\n      })()),\n  );\n}\n"],"mappings":"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"}