{"version":3,"file":"resource-loader.test.d.ts","sourceRoot":"","sources":["../../src/core/resource-loader.test.ts"],"names":[],"mappings":"","sourcesContent":["import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { DefaultResourceLoader } from \"./resource-loader.js\";\nimport { SettingsManager } from \"./settings-manager.js\";\nimport { loadSkillsFromDir } from \"./skills.js\";\n\nfunction createTempDir(): string {\n\treturn mkdtempSync(join(tmpdir(), \"jensen-context-\"));\n}\n\nfunction writeFile(path: string, content: string): void {\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, content, \"utf8\");\n}\n\nfunction createLoader(cwd: string, agentDir: string): DefaultResourceLoader {\n\treturn new DefaultResourceLoader({\n\t\tcwd,\n\t\tagentDir,\n\t\tsettingsManager: SettingsManager.inMemory(),\n\t});\n}\n\nconst tempDirs: string[] = [];\n\ndescribe(\"DefaultResourceLoader context files\", () => {\n\tafterEach(() => {\n\t\tfor (const dir of tempDirs.splice(0)) {\n\t\t\trmSync(dir, { recursive: true, force: true });\n\t\t}\n\t});\n\n\tit(\"loads JENSEN.md from the global directory and parent directories\", async () => {\n\t\tconst rootDir = createTempDir();\n\t\ttempDirs.push(rootDir);\n\n\t\tconst agentDir = join(rootDir, \"agent\");\n\t\tconst repoDir = join(rootDir, \"repo\");\n\t\tconst appDir = join(repoDir, \"app\");\n\n\t\tmkdirSync(agentDir, { recursive: true });\n\t\tmkdirSync(appDir, { recursive: true });\n\n\t\twriteFile(join(agentDir, \"JENSEN.md\"), \"global\");\n\t\twriteFile(join(repoDir, \"JENSEN.md\"), \"repo\");\n\t\twriteFile(join(appDir, \"JENSEN.md\"), \"app\");\n\n\t\tconst loader = createLoader(appDir, agentDir);\n\t\tawait loader.reload();\n\n\t\texpect(loader.getAgentsFiles().agentsFiles.map((file) => file.path)).toEqual([\n\t\t\tjoin(agentDir, \"JENSEN.md\"),\n\t\t\tjoin(repoDir, \"JENSEN.md\"),\n\t\t\tjoin(appDir, \"JENSEN.md\"),\n\t\t]);\n\t\texpect(loader.getAgentsFiles().agentsFiles.map((file) => file.content)).toEqual([\"global\", \"repo\", \"app\"]);\n\t\texpect(loader.getContextDiagnostics().diagnostics).toEqual([]);\n\t});\n\n\tit(\"prefers JENSEN.md over AGENTS.md when both exist in the same directory\", async () => {\n\t\tconst rootDir = createTempDir();\n\t\ttempDirs.push(rootDir);\n\n\t\tconst agentDir = join(rootDir, \"agent\");\n\t\tconst repoDir = join(rootDir, \"repo\");\n\n\t\tmkdirSync(agentDir, { recursive: true });\n\t\tmkdirSync(repoDir, { recursive: true });\n\n\t\twriteFile(join(repoDir, \"AGENTS.md\"), \"legacy\");\n\t\twriteFile(join(repoDir, \"JENSEN.md\"), \"canonical\");\n\n\t\tconst loader = createLoader(repoDir, agentDir);\n\t\tawait loader.reload();\n\n\t\texpect(loader.getAgentsFiles().agentsFiles).toHaveLength(1);\n\t\texpect(loader.getAgentsFiles().agentsFiles[0]).toEqual({\n\t\t\tpath: join(repoDir, \"JENSEN.md\"),\n\t\t\tcontent: \"canonical\",\n\t\t});\n\t\texpect(loader.getContextDiagnostics().diagnostics).toEqual([]);\n\t});\n\n\tit(\"loads Jensen-Protocol workspace context from the nearest marked workspace\", async () => {\n\t\tconst rootDir = createTempDir();\n\t\ttempDirs.push(rootDir);\n\n\t\tconst agentDir = join(rootDir, \"agent\");\n\t\tconst repoDir = join(rootDir, \"repo\");\n\t\tconst nestedWorkspaceDir = join(repoDir, \"apps\", \"nested\");\n\t\tconst appDir = join(nestedWorkspaceDir, \"service\");\n\n\t\tmkdirSync(agentDir, { recursive: true });\n\t\tmkdirSync(appDir, { recursive: true });\n\n\t\twriteFile(join(agentDir, \"JENSEN.md\"), \"global\");\n\t\twriteFile(join(repoDir, \"JENSEN.md\"), \"repo\");\n\t\twriteFile(join(repoDir, \".jensen\", \"JENSEN_PROTOCOL.md\"), \"repo protocol\");\n\t\twriteFile(join(nestedWorkspaceDir, \".jensen\", \"JENSEN_PROTOCOL.md\"), \"nested protocol\");\n\n\t\tconst loader = createLoader(appDir, agentDir);\n\t\tawait loader.reload();\n\n\t\texpect(loader.getAgentsFiles().agentsFiles.map((file) => file.path)).toEqual([\n\t\t\tjoin(agentDir, \"JENSEN.md\"),\n\t\t\tjoin(repoDir, \"JENSEN.md\"),\n\t\t\tjoin(nestedWorkspaceDir, \".jensen\", \"JENSEN_PROTOCOL.md\"),\n\t\t]);\n\t\texpect(loader.getAgentsFiles().agentsFiles.map((file) => file.content)).toEqual([\n\t\t\t\"global\",\n\t\t\t\"repo\",\n\t\t\t\"nested protocol\",\n\t\t]);\n\t\texpect(loader.getContextDiagnostics().diagnostics).toEqual([]);\n\t});\n\n\tit(\"falls back to AGENTS.md and reports a deprecation warning\", async () => {\n\t\tconst rootDir = createTempDir();\n\t\ttempDirs.push(rootDir);\n\n\t\tconst agentDir = join(rootDir, \"agent\");\n\t\tconst repoDir = join(rootDir, \"repo\");\n\n\t\tmkdirSync(agentDir, { recursive: true });\n\t\tmkdirSync(repoDir, { recursive: true });\n\n\t\twriteFile(join(repoDir, \"AGENTS.md\"), \"legacy\");\n\n\t\tconst loader = createLoader(repoDir, agentDir);\n\t\tawait loader.reload();\n\n\t\texpect(loader.getAgentsFiles().agentsFiles).toEqual([\n\t\t\t{\n\t\t\t\tpath: join(repoDir, \"AGENTS.md\"),\n\t\t\t\tcontent: \"legacy\",\n\t\t\t},\n\t\t]);\n\t\texpect(loader.getContextDiagnostics().diagnostics).toEqual([\n\t\t\t{\n\t\t\t\ttype: \"warning\",\n\t\t\t\tmessage: `AGENTS.md is deprecated. Rename ${join(repoDir, \"AGENTS.md\")} to JENSEN.md; AGENTS.md remains supported for now as a fallback.`,\n\t\t\t\tpath: join(repoDir, \"AGENTS.md\"),\n\t\t\t},\n\t\t]);\n\t});\n});\n\ndescribe(\"skill discovery\", () => {\n\tafterEach(() => {\n\t\tfor (const dir of tempDirs.splice(0)) {\n\t\t\trmSync(dir, { recursive: true, force: true });\n\t\t}\n\t});\n\n\tit(\"rejects a skill with an unregistered Cavecrew dependency before activation\", async () => {\n\t\tconst rootDir = createTempDir();\n\t\ttempDirs.push(rootDir);\n\t\tconst skillsDir = join(rootDir, \".jensen\", \"skills\", \"broken\");\n\t\twriteFile(\n\t\t\tjoin(skillsDir, \"SKILL.md\"),\n\t\t\t`---\\nname: broken\\ndescription: Broken skill.\\n---\\nUse cavecrew-investigator-typo.\\n`,\n\t\t);\n\t\tconst loader = createLoader(rootDir, join(rootDir, \"agent\"));\n\t\tawait loader.reload();\n\t\texpect(loader.getSkills().skills.some((skill) => skill.name === \"broken\")).toBe(false);\n\t\texpect(loader.getSkills().diagnostics).toEqual(\n\t\t\texpect.arrayContaining([\n\t\t\t\texpect.objectContaining({ type: \"error\", message: expect.stringContaining(\"SKILL_DEPENDENCY_INVALID\") }),\n\t\t\t]),\n\t\t);\n\t});\n\n\tit(\"loads only SKILL.md entrypoints and ignores README inventory docs\", () => {\n\t\tconst rootDir = createTempDir();\n\t\ttempDirs.push(rootDir);\n\n\t\tconst skillsDir = join(rootDir, \".jensen\", \"skills\");\n\t\twriteFile(join(skillsDir, \"README.md\"), \"# Skill inventory\\n\");\n\t\twriteFile(join(skillsDir, \"NOTES.md\"), \"# notes\\n\");\n\t\twriteFile(\n\t\t\tjoin(skillsDir, \"ui-flow-verifier\", \"SKILL.md\"),\n\t\t\t`---\nname: ui-flow-verifier\ndescription: Verify UI flow transitions.\n---\nUse this skill for UI verification.\n`,\n\t\t);\n\t\twriteFile(\n\t\t\tjoin(skillsDir, \"nested\", \"validate-unity-build\", \"SKILL.md\"),\n\t\t\t`---\nname: validate-unity-build\ndescription: Validate Unity project builds.\n---\nUse this skill for build verification.\n`,\n\t\t);\n\t\twriteFile(join(skillsDir, \"fixtures\", \"sample.md\"), \"# fixture\\n\");\n\t\twriteFile(join(skillsDir, \"notes\", \"debugging.md\"), \"# notes\\n\");\n\n\t\tconst result = loadSkillsFromDir({ dir: skillsDir, source: \"project\" });\n\n\t\texpect(result.skills.map((skill) => skill.name).sort()).toEqual([\"ui-flow-verifier\", \"validate-unity-build\"]);\n\t\texpect(result.skills.map((skill) => skill.filePath).sort()).toEqual([\n\t\t\tjoin(skillsDir, \"nested\", \"validate-unity-build\", \"SKILL.md\"),\n\t\t\tjoin(skillsDir, \"ui-flow-verifier\", \"SKILL.md\"),\n\t\t]);\n\t\texpect(result.diagnostics).toEqual([]);\n\t});\n});\n"]}