{"version":3,"file":"init-project.d.ts","sourceRoot":"","sources":["../../src/core/init-project.ts"],"names":[],"mappings":"AASA,KAAK,sBAAsB,GAAG,qBAAqB,GAAG,kBAAkB,CAAC;AAUzE,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IACjC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,0BAA0B;IAC1C,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AA4VD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,0BAA+B,GAAG,iBAAiB,CAuGlH","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join, relative, resolve } from \"node:path\";\nimport { CONFIG_DIR_NAME, getExamplesPath } from \"../config.js\";\nimport { findContextFileInDir } from \"./context-files.js\";\n\nconst SUBAGENT_EXTENSION_SETTING = \"./extensions/subagent\";\n\ntype AgentTemplateName = \"planner\" | \"scout\" | \"worker\" | \"reviewer\" | \"security\" | \"pentester\" | \"librarian\";\n\ntype InitProjectWarningCode = \"context-file-exists\" | \"invalid-settings\";\n\nconst PROTOCOL_CONTEXT_FILE = \"JENSEN_PROTOCOL.md\";\n\ninterface AgentTemplate {\n\tname: AgentTemplateName;\n\tdescription: string;\n\tbody: string;\n}\n\nexport interface InitProjectWarning {\n\tcode: InitProjectWarningCode;\n\tpath: string;\n\tmessage: string;\n}\n\nexport interface InitProjectResult {\n\tcreatedDirectories: string[];\n\tcreatedFiles: string[];\n\tupdatedFiles: string[];\n\tskippedFiles: string[];\n\twarnings: InitProjectWarning[];\n\toutput: string;\n}\n\nexport interface InitProjectScaffoldOptions {\n\tincludeProtocol?: boolean;\n}\n\nconst AGENT_TEMPLATES: readonly AgentTemplate[] = [\n\t{\n\t\tname: \"planner\",\n\t\tdescription: \"Plan one bounded slice without implementing it\",\n\t\tbody: [\n\t\t\t\"# Planner\",\n\t\t\t\"\",\n\t\t\t\"You plan one bounded slice at a time.\",\n\t\t\t\"\",\n\t\t\t\"Responsibilities:\",\n\t\t\t\"- restate the exact slice objective\",\n\t\t\t\"- define success criteria, no-touch boundaries, stop conditions, and verification needs\",\n\t\t\t\"- break the work into small, ordered, testable steps\",\n\t\t\t\"- stay grounded in repository truth\",\n\t\t\t\"\",\n\t\t\t\"Rules:\",\n\t\t\t\"- do not implement code\",\n\t\t\t\"- do not approve final closure\",\n\t\t\t\"- do not widen scope\",\n\t\t\t\"- call out blockers and dependencies explicitly\",\n\t\t].join(\"\\n\"),\n\t},\n\t{\n\t\tname: \"scout\",\n\t\tdescription: \"Inspect repository truth and report grounded findings\",\n\t\tbody: [\n\t\t\t\"# Scout\",\n\t\t\t\"\",\n\t\t\t\"You gather repo-grounded facts for a bounded task.\",\n\t\t\t\"\",\n\t\t\t\"Responsibilities:\",\n\t\t\t\"- inspect the exact seams, files, symbols, and runtime expectations relevant to the ask\",\n\t\t\t\"- distinguish required behavior from optional or speculative ideas\",\n\t\t\t\"- cite concrete files and symbols\",\n\t\t\t\"\",\n\t\t\t\"Rules:\",\n\t\t\t\"- do not implement changes\",\n\t\t\t\"- do not widen scope\",\n\t\t\t\"- report uncertainty precisely instead of guessing\",\n\t\t].join(\"\\n\"),\n\t},\n\t{\n\t\tname: \"worker\",\n\t\tdescription: \"Execute a bounded implementation slice with validation\",\n\t\tbody: [\n\t\t\t\"# Worker\",\n\t\t\t\"\",\n\t\t\t\"You implement exactly the assigned bounded slice.\",\n\t\t\t\"\",\n\t\t\t\"Responsibilities:\",\n\t\t\t\"- make the minimum correct code and test changes\",\n\t\t\t\"- preserve existing architecture and abstractions\",\n\t\t\t\"- validate the slice with focused checks\",\n\t\t\t\"- report exact files changed and commands run\",\n\t\t\t\"\",\n\t\t\t\"Rules:\",\n\t\t\t\"- do not widen scope\",\n\t\t\t\"- do not silently redesign architecture\",\n\t\t\t\"- do not self-certify final closure\",\n\t\t].join(\"\\n\"),\n\t},\n\t{\n\t\tname: \"reviewer\",\n\t\tdescription: \"Audit correctness, regressions, and scope discipline\",\n\t\tbody: [\n\t\t\t\"# Reviewer\",\n\t\t\t\"\",\n\t\t\t\"You audit delivered work for correctness and scope discipline.\",\n\t\t\t\"\",\n\t\t\t\"Focus on:\",\n\t\t\t\"- correctness and regressions\",\n\t\t\t\"- adherence to the requested slice\",\n\t\t\t\"- missing validation or evidence gaps\",\n\t\t\t\"- mismatches between implementation and repo truth\",\n\t\t\t\"\",\n\t\t\t\"Rules:\",\n\t\t\t\"- do not implement follow-up changes yourself\",\n\t\t\t\"- be explicit about blockers vs nits\",\n\t\t].join(\"\\n\"),\n\t},\n\t{\n\t\tname: \"security\",\n\t\tdescription: \"Review trust boundaries and risky capability expansion\",\n\t\tbody: [\n\t\t\t\"# Security\",\n\t\t\t\"\",\n\t\t\t\"You audit trust boundaries, secrets handling, and risky capability growth.\",\n\t\t\t\"\",\n\t\t\t\"Focus on:\",\n\t\t\t\"- privilege expansion and unsafe defaults\",\n\t\t\t\"- destructive or surprising file, process, or network behavior\",\n\t\t\t\"- insecure trust of repo-controlled content\",\n\t\t\t\"- missing operator confirmations where they matter\",\n\t\t\t\"\",\n\t\t\t\"Rules:\",\n\t\t\t\"- do not widen into general architecture redesign\",\n\t\t\t\"- report concrete risks and residual exposure clearly\",\n\t\t].join(\"\\n\"),\n\t},\n\t{\n\t\tname: \"pentester\",\n\t\tdescription: \"Attack the delivered slice for practical exploit paths\",\n\t\tbody: [\n\t\t\t\"# Pentester\",\n\t\t\t\"\",\n\t\t\t\"You probe the delivered slice for practical abuse cases.\",\n\t\t\t\"\",\n\t\t\t\"Focus on:\",\n\t\t\t\"- ways a real user or attacker could bypass assumptions\",\n\t\t\t\"- malformed input, edge cases, and unsafe state transitions\",\n\t\t\t\"- practical exploitability instead of theoretical style concerns\",\n\t\t\t\"\",\n\t\t\t\"Rules:\",\n\t\t\t\"- do not implement fixes yourself\",\n\t\t\t\"- keep findings concrete, testable, and adversarial\",\n\t\t].join(\"\\n\"),\n\t},\n\t{\n\t\tname: \"librarian\",\n\t\tdescription: \"Reconcile docs, decisions, and workflow truth for closure\",\n\t\tbody: [\n\t\t\t\"# Librarian\",\n\t\t\t\"\",\n\t\t\t\"You reconcile closure truth after a slice is complete.\",\n\t\t\t\"\",\n\t\t\t\"Responsibilities:\",\n\t\t\t\"- verify changed files, validation, and residual risks are recorded clearly\",\n\t\t\t\"- keep roadmap, notes, and handoff artifacts aligned with reality\",\n\t\t\t\"- surface unfinished follow-ups instead of hiding them\",\n\t\t\t\"\",\n\t\t\t\"Rules:\",\n\t\t\t\"- do not implement product code\",\n\t\t\t\"- do not claim closure without evidence\",\n\t\t].join(\"\\n\"),\n\t},\n] as const;\n\nfunction buildProjectOverlay(projectName: string): string {\n\treturn [\n\t\t`# ${projectName}`,\n\t\t\"\",\n\t\t\"This repository uses Jensen with a minimal project-local scaffold.\",\n\t\t\"\",\n\t\t\"## Local Harness Scaffold\",\n\t\t\"- Project settings: `.jensen/settings.json`\",\n\t\t\"- Project-local subagent extension: `.jensen/extensions/subagent/`\",\n\t\t\"- Project-local agents: `.jensen/agents/`\",\n\t\t\"\",\n\t\t\"## Project Overlay Guidance\",\n\t\t\"Use this file for repo-specific facts only:\",\n\t\t\"- project identity and product phase\",\n\t\t\"- architecture notes and ADR links\",\n\t\t\"- roadmap, TODO, and workflow truth\",\n\t\t\"- repo-specific constraints and approved exceptions\",\n\t\t\"\",\n\t\t\"Do not duplicate the global Jensen operating model here.\",\n\t\t'Prefer role-pure delegated work, and use project-local agents with `agentScope: \"both\"` or `\"project\"` when appropriate.',\n\t].join(\"\\n\");\n}\n\nfunction buildAgentMarkdown(template: AgentTemplate): string {\n\treturn [\"---\", `name: ${template.name}`, `description: ${template.description}`, \"---\", \"\", template.body, \"\"].join(\n\t\t\"\\n\",\n\t);\n}\n\nfunction buildProtocolWorkspaceMarker(projectName: string): string {\n\treturn [\n\t\t`# ${projectName} Jensen-Protocol Workspace`,\n\t\t\"\",\n\t\t\"This file marks this directory as a Jensen-Protocol workspace boundary.\",\n\t\t\"\",\n\t\t\"## Workspace Role\",\n\t\t\"- Jensen-Protocol is the backend/service backbone for this workspace.\",\n\t\t\"- The nearest ancestor `.jensen/JENSEN_PROTOCOL.md` is loaded as Protocol context for agents working here.\",\n\t\t\"\",\n\t\t\"## Record Here\",\n\t\t\"Use this file for Protocol-specific workspace truth only:\",\n\t\t\"- backend and service architecture notes with links to ADRs or canonical docs\",\n\t\t\"- deployment, runtime, and operator workflow boundaries\",\n\t\t\"- project-specific conventions for Protocol services, workers, and scheduled jobs\",\n\t\t\"- links to the current source of truth for infrastructure and operations\",\n\t\t\"\",\n\t\t\"Keep this file minimal and current.\",\n\t\t\"Do not duplicate global Jensen law, generic infrastructure templates, or speculative plans.\",\n\t].join(\"\\n\");\n}\n\nfunction buildSettingsJson(): string {\n\treturn `${JSON.stringify({ extensions: [SUBAGENT_EXTENSION_SETTING] }, null, 2)}\\n`;\n}\n\nfunction toRelativePath(rootDir: string, targetPath: string): string {\n\tconst relativePath = relative(rootDir, targetPath);\n\treturn relativePath.length > 0 ? relativePath : \".\";\n}\n\nfunction ensureDirectory(dirPath: string, rootDir: string, createdDirectories: string[]): void {\n\tif (existsSync(dirPath)) {\n\t\treturn;\n\t}\n\tmkdirSync(dirPath, { recursive: true });\n\tcreatedDirectories.push(toRelativePath(rootDir, dirPath));\n}\n\nfunction writeFileIfMissing(\n\tfilePath: string,\n\tcontent: string,\n\trootDir: string,\n\tcreatedDirectories: string[],\n\tcreatedFiles: string[],\n\tskippedFiles: string[],\n): void {\n\tif (existsSync(filePath)) {\n\t\tskippedFiles.push(toRelativePath(rootDir, filePath));\n\t\treturn;\n\t}\n\tensureDirectory(resolve(filePath, \"..\"), rootDir, createdDirectories);\n\twriteFileSync(filePath, content, \"utf-8\");\n\tcreatedFiles.push(toRelativePath(rootDir, filePath));\n}\n\nfunction copyExampleFileIfMissing(\n\tsourcePath: string,\n\ttargetPath: string,\n\trootDir: string,\n\tcreatedDirectories: string[],\n\tcreatedFiles: string[],\n\tskippedFiles: string[],\n): void {\n\tif (existsSync(targetPath)) {\n\t\tskippedFiles.push(toRelativePath(rootDir, targetPath));\n\t\treturn;\n\t}\n\tensureDirectory(resolve(targetPath, \"..\"), rootDir, createdDirectories);\n\twriteFileSync(targetPath, readFileSync(sourcePath, \"utf-8\"), \"utf-8\");\n\tcreatedFiles.push(toRelativePath(rootDir, targetPath));\n}\n\nfunction updateSettingsFile(\n\tsettingsPath: string,\n\trootDir: string,\n\tcreatedDirectories: string[],\n\tcreatedFiles: string[],\n\tupdatedFiles: string[],\n\tskippedFiles: string[],\n\twarnings: InitProjectWarning[],\n): void {\n\tif (!existsSync(settingsPath)) {\n\t\tensureDirectory(resolve(settingsPath, \"..\"), rootDir, createdDirectories);\n\t\twriteFileSync(settingsPath, buildSettingsJson(), \"utf-8\");\n\t\tcreatedFiles.push(toRelativePath(rootDir, settingsPath));\n\t\treturn;\n\t}\n\n\tconst relativeSettingsPath = toRelativePath(rootDir, settingsPath);\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(settingsPath, \"utf-8\"));\n\t} catch (error) {\n\t\twarnings.push({\n\t\t\tcode: \"invalid-settings\",\n\t\t\tpath: relativeSettingsPath,\n\t\t\tmessage: `Skipped ${relativeSettingsPath}: existing settings.json is not valid JSON (${error instanceof Error ? error.message : String(error)}).`,\n\t\t});\n\t\tskippedFiles.push(relativeSettingsPath);\n\t\treturn;\n\t}\n\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\twarnings.push({\n\t\t\tcode: \"invalid-settings\",\n\t\t\tpath: relativeSettingsPath,\n\t\t\tmessage: `Skipped ${relativeSettingsPath}: existing settings.json must contain a JSON object.`,\n\t\t});\n\t\tskippedFiles.push(relativeSettingsPath);\n\t\treturn;\n\t}\n\n\tconst settings = { ...(parsed as Record<string, unknown>) };\n\tconst extensions = Array.isArray(settings.extensions)\n\t\t? settings.extensions.filter((entry): entry is string => typeof entry === \"string\")\n\t\t: [];\n\n\tif (extensions.includes(SUBAGENT_EXTENSION_SETTING)) {\n\t\tskippedFiles.push(relativeSettingsPath);\n\t\treturn;\n\t}\n\n\tsettings.extensions = [...extensions, SUBAGENT_EXTENSION_SETTING];\n\twriteFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\\n`, \"utf-8\");\n\tupdatedFiles.push(relativeSettingsPath);\n}\n\nfunction buildOutput(result: Omit<InitProjectResult, \"output\">): string {\n\tconst lines = [\"/init-project completed.\"];\n\n\tif (result.createdDirectories.length > 0) {\n\t\tlines.push(\"\", \"Created directories:\");\n\t\tfor (const dirPath of result.createdDirectories) {\n\t\t\tlines.push(`- ${dirPath}`);\n\t\t}\n\t}\n\n\tif (result.createdFiles.length > 0) {\n\t\tlines.push(\"\", \"Created files:\");\n\t\tfor (const filePath of result.createdFiles) {\n\t\t\tlines.push(`- ${filePath}`);\n\t\t}\n\t}\n\n\tif (result.updatedFiles.length > 0) {\n\t\tlines.push(\"\", \"Updated files:\");\n\t\tfor (const filePath of result.updatedFiles) {\n\t\t\tlines.push(`- ${filePath}`);\n\t\t}\n\t}\n\n\tif (result.skippedFiles.length > 0) {\n\t\tlines.push(\"\", \"Skipped existing files:\");\n\t\tfor (const filePath of result.skippedFiles) {\n\t\t\tlines.push(`- ${filePath}`);\n\t\t}\n\t}\n\n\tif (result.warnings.length > 0) {\n\t\tlines.push(\"\", \"Warnings:\");\n\t\tfor (const warning of result.warnings) {\n\t\t\tlines.push(`- ${warning.message}`);\n\t\t}\n\t}\n\n\tif (\n\t\tresult.createdDirectories.length === 0 &&\n\t\tresult.createdFiles.length === 0 &&\n\t\tresult.updatedFiles.length === 0 &&\n\t\tresult.warnings.length === 0\n\t) {\n\t\tlines.push(\"\", \"No changes were needed. The minimal scaffold is already present.\");\n\t} else {\n\t\tlines.push(\"\", \"Existing files were preserved. Safe to rerun.\");\n\t}\n\n\treturn lines.join(\"\\n\");\n}\n\nexport function initializeProjectScaffold(cwd: string, options: InitProjectScaffoldOptions = {}): InitProjectResult {\n\tconst projectRoot = resolve(cwd);\n\tconst projectName = projectRoot.split(/[/\\\\]/u).filter(Boolean).pop() ?? \"project\";\n\tconst configDir = join(projectRoot, CONFIG_DIR_NAME);\n\tconst agentsDir = join(configDir, \"agents\");\n\tconst extensionsDir = join(configDir, \"extensions\", \"subagent\");\n\tconst settingsPath = join(configDir, \"settings.json\");\n\tconst rootOverlayPath = join(projectRoot, \"JENSEN.md\");\n\tconst protocolContextPath = join(configDir, PROTOCOL_CONTEXT_FILE);\n\tconst examplesDir = getExamplesPath();\n\tconst exampleSubagentDir = join(examplesDir, \"extensions\", \"subagent\");\n\n\tconst createdDirectories: string[] = [];\n\tconst createdFiles: string[] = [];\n\tconst updatedFiles: string[] = [];\n\tconst skippedFiles: string[] = [];\n\tconst warnings: InitProjectWarning[] = [];\n\n\tensureDirectory(configDir, projectRoot, createdDirectories);\n\tensureDirectory(agentsDir, projectRoot, createdDirectories);\n\tensureDirectory(join(configDir, \"extensions\"), projectRoot, createdDirectories);\n\tensureDirectory(extensionsDir, projectRoot, createdDirectories);\n\n\tconst existingContextFile = findContextFileInDir(projectRoot);\n\tif (existingContextFile) {\n\t\twarnings.push({\n\t\t\tcode: \"context-file-exists\",\n\t\t\tpath: toRelativePath(projectRoot, existingContextFile.path),\n\t\t\tmessage: `Skipped JENSEN.md creation because ${toRelativePath(projectRoot, existingContextFile.path)} already exists at the project root.`,\n\t\t});\n\t\tskippedFiles.push(\"JENSEN.md\");\n\t} else {\n\t\twriteFileIfMissing(\n\t\t\trootOverlayPath,\n\t\t\tbuildProjectOverlay(projectName),\n\t\t\tprojectRoot,\n\t\t\tcreatedDirectories,\n\t\t\tcreatedFiles,\n\t\t\tskippedFiles,\n\t\t);\n\t}\n\n\tif (options.includeProtocol) {\n\t\twriteFileIfMissing(\n\t\t\tprotocolContextPath,\n\t\t\tbuildProtocolWorkspaceMarker(projectName),\n\t\t\tprojectRoot,\n\t\t\tcreatedDirectories,\n\t\t\tcreatedFiles,\n\t\t\tskippedFiles,\n\t\t);\n\t}\n\n\tupdateSettingsFile(\n\t\tsettingsPath,\n\t\tprojectRoot,\n\t\tcreatedDirectories,\n\t\tcreatedFiles,\n\t\tupdatedFiles,\n\t\tskippedFiles,\n\t\twarnings,\n\t);\n\n\tcopyExampleFileIfMissing(\n\t\tjoin(exampleSubagentDir, \"index.ts\"),\n\t\tjoin(extensionsDir, \"index.ts\"),\n\t\tprojectRoot,\n\t\tcreatedDirectories,\n\t\tcreatedFiles,\n\t\tskippedFiles,\n\t);\n\tcopyExampleFileIfMissing(\n\t\tjoin(exampleSubagentDir, \"agents.ts\"),\n\t\tjoin(extensionsDir, \"agents.ts\"),\n\t\tprojectRoot,\n\t\tcreatedDirectories,\n\t\tcreatedFiles,\n\t\tskippedFiles,\n\t);\n\n\tfor (const template of AGENT_TEMPLATES) {\n\t\twriteFileIfMissing(\n\t\t\tjoin(agentsDir, `${template.name}.md`),\n\t\t\tbuildAgentMarkdown(template),\n\t\t\tprojectRoot,\n\t\t\tcreatedDirectories,\n\t\t\tcreatedFiles,\n\t\t\tskippedFiles,\n\t\t);\n\t}\n\n\tconst resultWithoutOutput = {\n\t\tcreatedDirectories,\n\t\tcreatedFiles,\n\t\tupdatedFiles,\n\t\tskippedFiles,\n\t\twarnings,\n\t};\n\n\treturn {\n\t\t...resultWithoutOutput,\n\t\toutput: buildOutput(resultWithoutOutput),\n\t};\n}\n"]}