@agntk/agent-harness 0.1.5 → 0.1.7
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/README.md +104 -43
- package/dist/{analytics-RPT73WNM.js → analytics-L24W3B7U.js} +1 -2
- package/dist/{auto-processor-SDAJF67T.js → auto-processor-QIRUOGEI.js} +1 -2
- package/dist/{chunk-NOJW5KG2.js → chunk-5CO5JTYT.js} +2 -2
- package/dist/chunk-5CO5JTYT.js.map +1 -0
- package/dist/{chunk-UMXPOYZR.js → chunk-7MSZVP7A.js} +2 -2
- package/dist/{chunk-SEHAQTBO.js → chunk-IFVCU33I.js} +2 -2
- package/dist/{chunk-P74KXHA4.js → chunk-LACZLSC4.js} +3 -3
- package/dist/chunk-LACZLSC4.js.map +1 -0
- package/dist/{chunk-M6PDMK2O.js → chunk-LBT43BZA.js} +2 -2
- package/dist/{chunk-2ENYRENZ.js → chunk-PMFAYKBD.js} +1 -1
- package/dist/chunk-PMFAYKBD.js.map +1 -0
- package/dist/{chunk-GX2RCSFJ.js → chunk-QMOIVORH.js} +1 -1
- package/dist/chunk-QMOIVORH.js.map +1 -0
- package/dist/{chunk-PTQ37NRI.js → chunk-QU566LZE.js} +3 -3
- package/dist/chunk-QU566LZE.js.map +1 -0
- package/dist/{chunk-IZ6UZ3ZL.js → chunk-WCYBFALM.js} +10 -3
- package/dist/chunk-WCYBFALM.js.map +1 -0
- package/dist/{chunk-NBEAK63K.js → chunk-Z4HEHWFM.js} +2 -2
- package/dist/chunk-Z4HEHWFM.js.map +1 -0
- package/dist/cli/index.js +150 -165
- package/dist/cli/index.js.map +1 -1
- package/dist/{config-2O6S2YJO.js → config-PYSS3QY6.js} +1 -2
- package/dist/{context-loader-XCZ5EXNG.js → context-loader-RSXXFW5R.js} +1 -2
- package/dist/{conversation-OPLE23IM.js → conversation-OKBO4L66.js} +3 -4
- package/dist/{cost-tracker-RS3W7SVY.js → cost-tracker-NZRZEHVA.js} +1 -2
- package/dist/{delegate-ZJCIADNN.js → delegate-YOGVA3HB.js} +5 -6
- package/dist/{emotional-state-VQVRA6ED.js → emotional-state-IN4ZUL2Q.js} +1 -2
- package/dist/{emotional-state-VQVRA6ED.js.map → emotional-state-IN4ZUL2Q.js.map} +1 -1
- package/dist/{env-discovery-2BLVMAIM.js → env-discovery-PXBRE5FX.js} +1 -2
- package/dist/{env-discovery-2BLVMAIM.js.map → env-discovery-PXBRE5FX.js.map} +1 -1
- package/dist/{export-2HEAAOUF.js → export-GYLWROMB.js} +1 -2
- package/dist/{export-2HEAAOUF.js.map → export-GYLWROMB.js.map} +1 -1
- package/dist/{graph-5MKRTC3J.js → graph-LEEO37L3.js} +2 -3
- package/dist/{harness-XSBQBY7T.js → harness-NU2UU6J4.js} +5 -6
- package/dist/{health-NZ6WNIMV.js → health-HL2JYHIY.js} +1 -2
- package/dist/{indexer-YKSGUVYT.js → indexer-L5UC6J2V.js} +1 -2
- package/dist/{instinct-learner-CWVMLUWX.js → instinct-learner-EECG4L24.js} +3 -4
- package/dist/{intake-M5NRR6QR.js → intake-SVJKFHTL.js} +1 -2
- package/dist/{intelligence-UW4TCOC7.js → intelligence-BANUEAI4.js} +4 -7
- package/dist/intelligence-BANUEAI4.js.map +1 -0
- package/dist/{journal-KN265YLU.js → journal-O4SEANIC.js} +3 -4
- package/dist/{loader-BOCVXVCH.js → loader-27PLDCOJ.js} +1 -2
- package/dist/{mcp-WTQJJZAO.js → mcp-JSIUJJZV.js} +1 -2
- package/dist/{mcp-discovery-WPAQFL6S.js → mcp-discovery-DG3RQYLM.js} +1 -2
- package/dist/{mcp-discovery-WPAQFL6S.js.map → mcp-discovery-DG3RQYLM.js.map} +1 -1
- package/dist/{mcp-installer-KV3XZRRF.js → mcp-installer-X2TJ2S2G.js} +1 -2
- package/dist/{mcp-installer-KV3XZRRF.js.map → mcp-installer-X2TJ2S2G.js.map} +1 -1
- package/dist/{metrics-KXGNFAAB.js → metrics-2MNINXNQ.js} +1 -2
- package/dist/{primitive-registry-HOJMUFBT.js → primitive-registry-ZMGGXSO5.js} +1 -2
- package/dist/{primitive-registry-HOJMUFBT.js.map → primitive-registry-ZMGGXSO5.js.map} +1 -1
- package/dist/{project-discovery-C4UMD7JI.js → project-discovery-FQLAZKEM.js} +1 -2
- package/dist/project-discovery-FQLAZKEM.js.map +1 -0
- package/dist/{provider-SXPQZ74H.js → provider-75AKTYGB.js} +2 -3
- package/dist/{rate-limiter-RLRVM325.js → rate-limiter-PH5DCVU4.js} +1 -2
- package/dist/{rule-engine-I4AFQSSR.js → rule-engine-DM26S77N.js} +1 -2
- package/dist/{rule-engine-I4AFQSSR.js.map → rule-engine-DM26S77N.js.map} +1 -1
- package/dist/{scaffold-ZY4XWINP.js → scaffold-LA54KYKJ.js} +3 -4
- package/dist/{scaffold-ZY4XWINP.js.map → scaffold-LA54KYKJ.js.map} +1 -1
- package/dist/{scheduler-TYOQKO4C.js → scheduler-MFBVGFZQ.js} +8 -9
- package/dist/{scheduler-TYOQKO4C.js.map → scheduler-MFBVGFZQ.js.map} +1 -1
- package/dist/{search-4IYM525O.js → search-6Y6NCOLQ.js} +1 -2
- package/dist/search-6Y6NCOLQ.js.map +1 -0
- package/dist/{semantic-search-G624D6CI.js → semantic-search-FN6FZIXI.js} +1 -2
- package/dist/semantic-search-FN6FZIXI.js.map +1 -0
- package/dist/{serve-QFUZWOU3.js → serve-TQLN4OTP.js} +6 -7
- package/dist/serve-TQLN4OTP.js.map +1 -0
- package/dist/{sessions-CZGVXKQE.js → sessions-G6SZZXWS.js} +1 -2
- package/dist/{sources-RW5DT56F.js → sources-7LDYO5GK.js} +1 -2
- package/dist/{starter-packs-76YUVHEU.js → starter-packs-OR7NI5NA.js} +1 -2
- package/dist/{starter-packs-76YUVHEU.js.map → starter-packs-OR7NI5NA.js.map} +1 -1
- package/dist/{state-GMXILIHW.js → state-25IQEC5C.js} +1 -2
- package/dist/{state-merge-NKO5FRBA.js → state-merge-E333OEIQ.js} +1 -2
- package/dist/{state-merge-NKO5FRBA.js.map → state-merge-E333OEIQ.js.map} +1 -1
- package/dist/{telemetry-MVDNGJEC.js → telemetry-RS2JZUZP.js} +1 -2
- package/dist/{tool-executor-KEYQLO4M.js → tool-executor-6I5PHQDY.js} +3 -4
- package/dist/{tools-EB3BHRRF.js → tools-NDFJNVHK.js} +2 -3
- package/dist/{types-NYKB2DN3.js → types-NPJZAI72.js} +1 -2
- package/dist/{universal-installer-7MFCJUW7.js → universal-installer-LCAZHFZR.js} +2 -3
- package/dist/universal-installer-LCAZHFZR.js.map +1 -0
- package/dist/{validator-LZXBFEPV.js → validator-LM7RZWSH.js} +1 -2
- package/dist/{verification-gate-ALSJVKSW.js → verification-gate-2O6DF2B7.js} +1 -2
- package/dist/verification-gate-2O6DF2B7.js.map +1 -0
- package/dist/{versioning-Z3XNE2Q2.js → versioning-WEGF6KJG.js} +1 -2
- package/dist/versioning-WEGF6KJG.js.map +1 -0
- package/dist/{watcher-CSHVDOCM.js → watcher-GZWQSWZ6.js} +1 -2
- package/dist/{watcher-CSHVDOCM.js.map → watcher-GZWQSWZ6.js.map} +1 -1
- package/dist/{web-server-7NGOTK7J.js → web-server-SJ6NS5IX.js} +4 -5
- package/package.json +1 -9
- package/dist/agent-framework-CMFC3VJM.js +0 -344
- package/dist/agent-framework-CMFC3VJM.js.map +0 -1
- package/dist/chunk-2ENYRENZ.js.map +0 -1
- package/dist/chunk-DGUM43GV.js +0 -11
- package/dist/chunk-FD55B3IO.js +0 -204
- package/dist/chunk-FD55B3IO.js.map +0 -1
- package/dist/chunk-GX2RCSFJ.js.map +0 -1
- package/dist/chunk-IZ6UZ3ZL.js.map +0 -1
- package/dist/chunk-MSO7DKBK.js +0 -2123
- package/dist/chunk-MSO7DKBK.js.map +0 -1
- package/dist/chunk-NBEAK63K.js.map +0 -1
- package/dist/chunk-NOJW5KG2.js.map +0 -1
- package/dist/chunk-P74KXHA4.js.map +0 -1
- package/dist/chunk-PTQ37NRI.js.map +0 -1
- package/dist/chunk-RPBC2QOA.js +0 -235
- package/dist/chunk-RPBC2QOA.js.map +0 -1
- package/dist/chunk-XVFVTDE6.js +0 -98
- package/dist/chunk-XVFVTDE6.js.map +0 -1
- package/dist/chunk-ZZJOFKAT.js +0 -13
- package/dist/config-LLQZYN2Q.js +0 -11
- package/dist/harness-ABKZWP47.js +0 -11
- package/dist/index.d.ts +0 -3634
- package/dist/index.js +0 -13797
- package/dist/index.js.map +0 -1
- package/dist/intelligence-UW4TCOC7.js.map +0 -1
- package/dist/project-discovery-C4UMD7JI.js.map +0 -1
- package/dist/provider-LQHQX7Z7.js +0 -26
- package/dist/search-4IYM525O.js.map +0 -1
- package/dist/semantic-search-G624D6CI.js.map +0 -1
- package/dist/serve-QFUZWOU3.js.map +0 -1
- package/dist/tool-executor-KEYQLO4M.js.map +0 -1
- package/dist/tools-EB3BHRRF.js.map +0 -1
- package/dist/types-NYKB2DN3.js.map +0 -1
- package/dist/types-VRSXU4AM.js +0 -16
- package/dist/types-VRSXU4AM.js.map +0 -1
- package/dist/universal-installer-7MFCJUW7.js.map +0 -1
- package/dist/validator-LZXBFEPV.js.map +0 -1
- package/dist/verification-gate-ALSJVKSW.js.map +0 -1
- package/dist/versioning-Z3XNE2Q2.js.map +0 -1
- package/dist/web-server-7NGOTK7J.js.map +0 -1
- /package/dist/{analytics-RPT73WNM.js.map → analytics-L24W3B7U.js.map} +0 -0
- /package/dist/{auto-processor-SDAJF67T.js.map → auto-processor-QIRUOGEI.js.map} +0 -0
- /package/dist/{chunk-UMXPOYZR.js.map → chunk-7MSZVP7A.js.map} +0 -0
- /package/dist/{chunk-SEHAQTBO.js.map → chunk-IFVCU33I.js.map} +0 -0
- /package/dist/{chunk-M6PDMK2O.js.map → chunk-LBT43BZA.js.map} +0 -0
- /package/dist/{chunk-DGUM43GV.js.map → config-PYSS3QY6.js.map} +0 -0
- /package/dist/{chunk-ZZJOFKAT.js.map → context-loader-RSXXFW5R.js.map} +0 -0
- /package/dist/{config-2O6S2YJO.js.map → conversation-OKBO4L66.js.map} +0 -0
- /package/dist/{config-LLQZYN2Q.js.map → cost-tracker-NZRZEHVA.js.map} +0 -0
- /package/dist/{context-loader-XCZ5EXNG.js.map → delegate-YOGVA3HB.js.map} +0 -0
- /package/dist/{conversation-OPLE23IM.js.map → graph-LEEO37L3.js.map} +0 -0
- /package/dist/{cost-tracker-RS3W7SVY.js.map → harness-NU2UU6J4.js.map} +0 -0
- /package/dist/{delegate-ZJCIADNN.js.map → health-HL2JYHIY.js.map} +0 -0
- /package/dist/{graph-5MKRTC3J.js.map → indexer-L5UC6J2V.js.map} +0 -0
- /package/dist/{harness-ABKZWP47.js.map → instinct-learner-EECG4L24.js.map} +0 -0
- /package/dist/{harness-XSBQBY7T.js.map → intake-SVJKFHTL.js.map} +0 -0
- /package/dist/{health-NZ6WNIMV.js.map → journal-O4SEANIC.js.map} +0 -0
- /package/dist/{indexer-YKSGUVYT.js.map → loader-27PLDCOJ.js.map} +0 -0
- /package/dist/{instinct-learner-CWVMLUWX.js.map → mcp-JSIUJJZV.js.map} +0 -0
- /package/dist/{intake-M5NRR6QR.js.map → metrics-2MNINXNQ.js.map} +0 -0
- /package/dist/{journal-KN265YLU.js.map → provider-75AKTYGB.js.map} +0 -0
- /package/dist/{loader-BOCVXVCH.js.map → rate-limiter-PH5DCVU4.js.map} +0 -0
- /package/dist/{mcp-WTQJJZAO.js.map → sessions-G6SZZXWS.js.map} +0 -0
- /package/dist/{metrics-KXGNFAAB.js.map → sources-7LDYO5GK.js.map} +0 -0
- /package/dist/{provider-LQHQX7Z7.js.map → state-25IQEC5C.js.map} +0 -0
- /package/dist/{provider-SXPQZ74H.js.map → telemetry-RS2JZUZP.js.map} +0 -0
- /package/dist/{rate-limiter-RLRVM325.js.map → tool-executor-6I5PHQDY.js.map} +0 -0
- /package/dist/{sessions-CZGVXKQE.js.map → tools-NDFJNVHK.js.map} +0 -0
- /package/dist/{sources-RW5DT56F.js.map → types-NPJZAI72.js.map} +0 -0
- /package/dist/{state-GMXILIHW.js.map → validator-LM7RZWSH.js.map} +0 -0
- /package/dist/{telemetry-MVDNGJEC.js.map → web-server-SJ6NS5IX.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime/primitive-registry.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, renameSync, unlinkSync } from 'fs';\nimport { join, basename, dirname, relative } from 'path';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport { CORE_PRIMITIVE_DIRS } from '../core/types.js';\nimport { parseHarnessDocument } from '../primitives/loader.js';\nimport { log } from '../core/logger.js';\n\n// --- Manifest Types ---\n\nexport interface BundleManifest {\n /** Manifest format version */\n version: string;\n /** Bundle name (e.g., \"code-review-rules\") */\n name: string;\n /** Human-readable description */\n description: string;\n /** Author identifier */\n author: string;\n /** Semantic version (e.g., \"1.0.0\") */\n bundle_version: string;\n /** When this bundle was created */\n created: string;\n /** Primitive type(s) contained (e.g., [\"rules\", \"instincts\"]) */\n types: string[];\n /** Tags for search/discovery */\n tags: string[];\n /** Files included in this bundle (relative paths) */\n files: BundleFileEntry[];\n /** Optional dependencies (other bundle names) */\n dependencies?: string[];\n /** Optional registry URL this was published to */\n registry?: string;\n /** Optional license identifier */\n license?: string;\n}\n\nexport interface BundleFileEntry {\n path: string;\n type: string;\n id: string;\n l0: string;\n}\n\n// --- Registry Types ---\n\nexport interface RegistryConfig {\n /** Registry URL (HTTPS) */\n url: string;\n /** Optional auth token */\n token?: string;\n /** Optional name for display */\n name?: string;\n}\n\nexport interface BundleSearchResult {\n name: string;\n description: string;\n author: string;\n version: string;\n types: string[];\n tags: string[];\n download_url: string;\n}\n\nexport interface BundleSearchResponse {\n results: BundleSearchResult[];\n total: number;\n}\n\n// --- Install / Uninstall Types ---\n\nexport interface PrimitiveInstallResult {\n installed: boolean;\n name: string;\n files: string[];\n skipped: string[];\n errors: string[];\n manifest?: BundleManifest;\n}\n\nexport interface PrimitiveUninstallResult {\n uninstalled: boolean;\n name: string;\n archived: string[];\n dependents: string[];\n errors: string[];\n}\n\nexport interface PrimitiveUpdateResult {\n updated: boolean;\n name: string;\n added: string[];\n modified: string[];\n removed: string[];\n errors: string[];\n oldVersion?: string;\n newVersion?: string;\n}\n\n// --- Manifest Operations ---\n\n/**\n * Create a manifest.yaml for a set of primitive files.\n */\nexport function createManifest(\n harnessDir: string,\n options: {\n name: string;\n description: string;\n author?: string;\n version?: string;\n files: string[];\n tags?: string[];\n license?: string;\n },\n): BundleManifest {\n const types = new Set<string>();\n const fileEntries: BundleFileEntry[] = [];\n\n for (const filePath of options.files) {\n const absPath = filePath.startsWith('/') ? filePath : join(harnessDir, filePath);\n if (!existsSync(absPath)) continue;\n\n const relPath = relative(harnessDir, absPath);\n const dir = relPath.split('/')[0];\n const type = (CORE_PRIMITIVE_DIRS as readonly string[]).includes(dir) ? dir : 'custom';\n types.add(type);\n\n try {\n const doc = parseHarnessDocument(absPath);\n fileEntries.push({\n path: relPath,\n type,\n id: doc.frontmatter.id ?? basename(relPath, '.md'),\n l0: doc.l0 || '',\n });\n } catch {\n fileEntries.push({\n path: relPath,\n type,\n id: basename(relPath, '.md'),\n l0: '',\n });\n }\n }\n\n return {\n version: '1.0',\n name: options.name,\n description: options.description,\n author: options.author ?? 'unknown',\n bundle_version: options.version ?? '1.0.0',\n created: new Date().toISOString(),\n types: [...types],\n tags: options.tags ?? [],\n files: fileEntries,\n license: options.license,\n };\n}\n\n/**\n * Write a manifest to a YAML file.\n */\nexport function writeManifest(manifest: BundleManifest, outputPath: string): void {\n writeFileSync(outputPath, stringifyYaml(manifest), 'utf-8');\n}\n\n/**\n * Read and validate a manifest from a YAML file.\n */\nexport function readManifest(manifestPath: string): BundleManifest {\n if (!existsSync(manifestPath)) {\n throw new Error(`Manifest not found: ${manifestPath}`);\n }\n const content = readFileSync(manifestPath, 'utf-8');\n const parsed: unknown = parseYaml(content);\n\n if (typeof parsed !== 'object' || parsed === null) {\n throw new Error('Invalid manifest: not an object');\n }\n\n const manifest = parsed as Record<string, unknown>;\n if (typeof manifest.name !== 'string' || !manifest.name) {\n throw new Error('Invalid manifest: missing \"name\"');\n }\n if (typeof manifest.version !== 'string') {\n throw new Error('Invalid manifest: missing \"version\"');\n }\n if (!Array.isArray(manifest.files)) {\n throw new Error('Invalid manifest: missing \"files\" array');\n }\n\n return parsed as BundleManifest;\n}\n\n// --- Bundle Pack/Unpack ---\n\nexport interface PackedBundle {\n manifest: BundleManifest;\n files: Array<{ path: string; content: string }>;\n}\n\n/**\n * Pack a set of primitives into a bundle (manifest + file contents).\n */\nexport function packBundle(\n harnessDir: string,\n options: {\n name: string;\n description: string;\n author?: string;\n version?: string;\n files?: string[];\n types?: string[];\n tags?: string[];\n license?: string;\n },\n): PackedBundle {\n let filePaths: string[] = options.files ?? [];\n\n // If types specified (or no types and no files), auto-collect all .md files from those dirs\n const types = (options.types && options.types.length > 0)\n ? options.types\n : (filePaths.length === 0 ? [...CORE_PRIMITIVE_DIRS] : []);\n if (types.length > 0 && filePaths.length === 0) {\n for (const type of types) {\n const dirPath = join(harnessDir, type);\n if (!existsSync(dirPath)) continue;\n const files = readdirSync(dirPath)\n .filter((f: string) => f.endsWith('.md') && !f.startsWith('_') && !f.startsWith('.'))\n .map((f: string) => join(type, f));\n filePaths.push(...files);\n }\n }\n\n const manifest = createManifest(harnessDir, {\n name: options.name,\n description: options.description,\n author: options.author,\n version: options.version,\n files: filePaths,\n tags: options.tags,\n license: options.license,\n });\n\n const files: Array<{ path: string; content: string }> = [];\n for (const entry of manifest.files) {\n const absPath = join(harnessDir, entry.path);\n if (existsSync(absPath)) {\n files.push({\n path: entry.path,\n content: readFileSync(absPath, 'utf-8'),\n });\n }\n }\n\n return { manifest, files };\n}\n\n/**\n * Write a packed bundle to a directory (manifest.yaml + files).\n */\nexport function writeBundleDir(bundle: PackedBundle, outputDir: string): void {\n mkdirSync(outputDir, { recursive: true });\n writeManifest(bundle.manifest, join(outputDir, 'manifest.yaml'));\n\n for (const file of bundle.files) {\n const targetPath = join(outputDir, file.path);\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileSync(targetPath, file.content, 'utf-8');\n }\n}\n\n/**\n * Read a packed bundle from a directory containing manifest.yaml.\n */\nexport function readBundleDir(bundleDir: string): PackedBundle {\n const manifestPath = join(bundleDir, 'manifest.yaml');\n const manifest = readManifest(manifestPath);\n\n const files: Array<{ path: string; content: string }> = [];\n for (const entry of manifest.files) {\n const filePath = join(bundleDir, entry.path);\n if (existsSync(filePath)) {\n files.push({\n path: entry.path,\n content: readFileSync(filePath, 'utf-8'),\n });\n }\n }\n\n return { manifest, files };\n}\n\n// --- Install from Bundle ---\n\n/**\n * Install primitives from a packed bundle into a harness directory.\n */\nexport function installBundle(\n harnessDir: string,\n bundle: PackedBundle,\n options?: { overwrite?: boolean; force?: boolean },\n): PrimitiveInstallResult {\n const overwrite = options?.overwrite ?? false;\n const result: PrimitiveInstallResult = {\n installed: false,\n name: bundle.manifest.name,\n files: [],\n skipped: [],\n errors: [],\n manifest: bundle.manifest,\n };\n\n // Check for dependents\n if (bundle.manifest.dependencies && bundle.manifest.dependencies.length > 0 && !options?.force) {\n const installedPath = join(harnessDir, '.installed');\n if (existsSync(installedPath)) {\n const installed = readInstalledManifests(harnessDir);\n const installedNames = new Set(installed.map((m) => m.name));\n const missing = bundle.manifest.dependencies.filter((d) => !installedNames.has(d));\n if (missing.length > 0) {\n result.errors.push(`Missing dependencies: ${missing.join(', ')}. Use --force to install anyway.`);\n return result;\n }\n }\n }\n\n for (const file of bundle.files) {\n const targetPath = join(harnessDir, file.path);\n\n if (existsSync(targetPath) && !overwrite) {\n result.skipped.push(file.path);\n continue;\n }\n\n try {\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileSync(targetPath, file.content, 'utf-8');\n result.files.push(file.path);\n } catch (err) {\n result.errors.push(`${file.path}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Record installation\n if (result.files.length > 0 || result.skipped.length > 0) {\n result.installed = true;\n recordInstallation(harnessDir, bundle.manifest);\n }\n\n return result;\n}\n\n// --- Uninstall ---\n\n/**\n * Uninstall (soft-delete) a previously installed bundle.\n * Moves files to archive/ instead of deleting.\n */\nexport function uninstallBundle(\n harnessDir: string,\n bundleName: string,\n options?: { hard?: boolean },\n): PrimitiveUninstallResult {\n const result: PrimitiveUninstallResult = {\n uninstalled: false,\n name: bundleName,\n archived: [],\n dependents: [],\n errors: [],\n };\n\n // Find the installed manifest\n const installed = readInstalledManifests(harnessDir);\n const manifest = installed.find((m) => m.name === bundleName);\n if (!manifest) {\n result.errors.push(`Bundle \"${bundleName}\" is not installed`);\n return result;\n }\n\n // Check if other installed bundles depend on this one\n const dependents = installed.filter(\n (m) => m.name !== bundleName && m.dependencies?.includes(bundleName),\n );\n if (dependents.length > 0) {\n result.dependents = dependents.map((m) => m.name);\n result.errors.push(\n `Cannot uninstall: ${dependents.map((m) => m.name).join(', ')} depend(s) on \"${bundleName}\"`,\n );\n return result;\n }\n\n const archiveDir = join(harnessDir, 'archive', 'uninstalled', bundleName);\n\n for (const entry of manifest.files) {\n const filePath = join(harnessDir, entry.path);\n if (!existsSync(filePath)) continue;\n\n try {\n if (options?.hard) {\n unlinkSync(filePath);\n } else {\n // Soft delete — move to archive\n const archivePath = join(archiveDir, entry.path);\n mkdirSync(dirname(archivePath), { recursive: true });\n renameSync(filePath, archivePath);\n }\n result.archived.push(entry.path);\n } catch (err) {\n result.errors.push(`${entry.path}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Remove installation record\n if (result.archived.length > 0) {\n result.uninstalled = true;\n removeInstallationRecord(harnessDir, bundleName);\n }\n\n return result;\n}\n\n// --- Update ---\n\n/**\n * Compare an installed bundle against a new version and produce a diff.\n */\nexport function diffBundle(\n harnessDir: string,\n newBundle: PackedBundle,\n): { added: string[]; modified: string[]; removed: string[]; unchanged: string[] } {\n const installed = readInstalledManifests(harnessDir);\n const existing = installed.find((m) => m.name === newBundle.manifest.name);\n\n const added: string[] = [];\n const modified: string[] = [];\n const removed: string[] = [];\n const unchanged: string[] = [];\n\n const existingFiles = new Set(existing?.files.map((f) => f.path) ?? []);\n const newFiles = new Set(newBundle.manifest.files.map((f) => f.path));\n\n // Check for added/modified files\n for (const file of newBundle.files) {\n const targetPath = join(harnessDir, file.path);\n if (!existingFiles.has(file.path)) {\n added.push(file.path);\n } else if (existsSync(targetPath)) {\n const currentContent = readFileSync(targetPath, 'utf-8');\n if (currentContent !== file.content) {\n modified.push(file.path);\n } else {\n unchanged.push(file.path);\n }\n } else {\n added.push(file.path);\n }\n }\n\n // Check for removed files\n for (const path of existingFiles) {\n if (!newFiles.has(path)) {\n removed.push(path);\n }\n }\n\n return { added, modified, removed, unchanged };\n}\n\n/**\n * Update an installed bundle to a new version.\n */\nexport function updateBundle(\n harnessDir: string,\n newBundle: PackedBundle,\n options?: { removeDeleted?: boolean },\n): PrimitiveUpdateResult {\n const removeDeleted = options?.removeDeleted ?? false;\n const diff = diffBundle(harnessDir, newBundle);\n\n const result: PrimitiveUpdateResult = {\n updated: false,\n name: newBundle.manifest.name,\n added: [],\n modified: [],\n removed: [],\n errors: [],\n };\n\n // Find old version\n const installed = readInstalledManifests(harnessDir);\n const existing = installed.find((m) => m.name === newBundle.manifest.name);\n result.oldVersion = existing?.bundle_version;\n result.newVersion = newBundle.manifest.bundle_version;\n\n // Write added/modified files\n for (const path of [...diff.added, ...diff.modified]) {\n const file = newBundle.files.find((f) => f.path === path);\n if (!file) continue;\n\n const targetPath = join(harnessDir, path);\n try {\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileSync(targetPath, file.content, 'utf-8');\n if (diff.added.includes(path)) {\n result.added.push(path);\n } else {\n result.modified.push(path);\n }\n } catch (err) {\n result.errors.push(`${path}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Handle removed files\n if (removeDeleted) {\n const archiveDir = join(harnessDir, 'archive', 'updated', newBundle.manifest.name);\n for (const path of diff.removed) {\n const filePath = join(harnessDir, path);\n if (!existsSync(filePath)) continue;\n\n try {\n const archivePath = join(archiveDir, path);\n mkdirSync(dirname(archivePath), { recursive: true });\n renameSync(filePath, archivePath);\n result.removed.push(path);\n } catch (err) {\n result.errors.push(`${path}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n\n if (result.added.length > 0 || result.modified.length > 0 || result.removed.length > 0) {\n result.updated = true;\n recordInstallation(harnessDir, newBundle.manifest);\n }\n\n return result;\n}\n\n// --- Installation Record ---\n\nconst INSTALLED_DIR = '.installed';\n\n/**\n * Record that a bundle was installed (writes manifest to .installed/).\n */\nfunction recordInstallation(harnessDir: string, manifest: BundleManifest): void {\n const installedDir = join(harnessDir, INSTALLED_DIR);\n mkdirSync(installedDir, { recursive: true });\n const manifestPath = join(installedDir, `${manifest.name}.yaml`);\n writeFileSync(manifestPath, stringifyYaml(manifest), 'utf-8');\n}\n\n/**\n * Remove an installation record.\n */\nfunction removeInstallationRecord(harnessDir: string, bundleName: string): void {\n const manifestPath = join(harnessDir, INSTALLED_DIR, `${bundleName}.yaml`);\n if (existsSync(manifestPath)) {\n unlinkSync(manifestPath);\n }\n}\n\n/**\n * Read all installed bundle manifests.\n */\nexport function readInstalledManifests(harnessDir: string): BundleManifest[] {\n const installedDir = join(harnessDir, INSTALLED_DIR);\n if (!existsSync(installedDir)) return [];\n\n const files = readdirSync(installedDir).filter((f: string) => f.endsWith('.yaml'));\n const manifests: BundleManifest[] = [];\n\n for (const file of files) {\n try {\n const manifest = readManifest(join(installedDir, file));\n manifests.push(manifest);\n } catch (err) {\n log.warn(`Failed to read installed manifest ${file}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n return manifests;\n}\n\n/**\n * List all installed bundles with summary info.\n */\nexport function listInstalledBundles(harnessDir: string): Array<{\n name: string;\n version: string;\n types: string[];\n fileCount: number;\n description: string;\n}> {\n return readInstalledManifests(harnessDir).map((m) => ({\n name: m.name,\n version: m.bundle_version,\n types: m.types,\n fileCount: m.files.length,\n description: m.description,\n }));\n}\n\n// --- Remote Registry Client ---\n\n/**\n * Fetch a bundle from a remote registry URL.\n */\nexport async function fetchRemoteBundle(url: string): Promise<PackedBundle> {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch bundle: ${response.status} ${response.statusText}`);\n }\n\n const contentType = response.headers.get('content-type') ?? '';\n\n if (contentType.includes('application/json')) {\n // JSON bundle format (legacy HarnessBundle format)\n const data: unknown = await response.json();\n if (typeof data !== 'object' || data === null || !('entries' in data)) {\n throw new Error('Invalid JSON bundle format');\n }\n\n const jsonBundle = data as { entries: Array<{ path: string; content: string }>; agent_name?: string };\n\n // Convert to PackedBundle with synthetic manifest\n const files = jsonBundle.entries;\n const types = new Set<string>();\n const fileEntries: BundleFileEntry[] = [];\n\n for (const entry of files) {\n const dir = entry.path.split('/')[0];\n const type = (CORE_PRIMITIVE_DIRS as readonly string[]).includes(dir) ? dir : 'custom';\n types.add(type);\n fileEntries.push({\n path: entry.path,\n type,\n id: basename(entry.path, '.md'),\n l0: '',\n });\n }\n\n const manifest: BundleManifest = {\n version: '1.0',\n name: jsonBundle.agent_name ?? 'remote-bundle',\n description: 'Imported from remote URL',\n author: 'unknown',\n bundle_version: '1.0.0',\n created: new Date().toISOString(),\n types: [...types],\n tags: [],\n files: fileEntries,\n };\n\n return { manifest, files };\n }\n\n // YAML manifest + files format (tar/zip would go here in future)\n // For now, treat as a single-file bundle\n const content = await response.text();\n const fileName = basename(new URL(url).pathname);\n\n const manifest: BundleManifest = {\n version: '1.0',\n name: basename(fileName, '.md'),\n description: `Downloaded from ${url}`,\n author: 'unknown',\n bundle_version: '1.0.0',\n created: new Date().toISOString(),\n types: ['custom'],\n tags: [],\n files: [{ path: fileName, type: 'custom', id: basename(fileName, '.md'), l0: '' }],\n };\n\n return { manifest, files: [{ path: fileName, content }] };\n}\n\n/**\n * Search a remote registry for bundles.\n */\nexport async function searchBundleRegistry(\n registryUrl: string,\n query: string,\n options?: { limit?: number; token?: string },\n): Promise<BundleSearchResponse> {\n const limit = options?.limit ?? 20;\n const searchUrl = `${registryUrl}/api/bundles?search=${encodeURIComponent(query)}&limit=${limit}`;\n\n const headers: Record<string, string> = { 'Accept': 'application/json' };\n if (options?.token) {\n headers['Authorization'] = `Bearer ${options.token}`;\n }\n\n const response = await fetch(searchUrl, { headers });\n if (!response.ok) {\n throw new Error(`Registry search failed: ${response.status} ${response.statusText}`);\n }\n\n return await response.json() as BundleSearchResponse;\n}\n\n/**\n * Fetch a bundle from a registry by name.\n */\nexport async function fetchFromRegistry(\n registryUrl: string,\n bundleName: string,\n options?: { version?: string; token?: string },\n): Promise<PackedBundle> {\n const version = options?.version ?? 'latest';\n const bundleUrl = `${registryUrl}/api/bundles/${encodeURIComponent(bundleName)}/versions/${version}/download`;\n\n const headers: Record<string, string> = {};\n if (options?.token) {\n headers['Authorization'] = `Bearer ${options.token}`;\n }\n\n const response = await fetch(bundleUrl, { headers });\n if (!response.ok) {\n throw new Error(`Failed to fetch bundle \"${bundleName}\": ${response.status} ${response.statusText}`);\n }\n\n const data: unknown = await response.json();\n return data as PackedBundle;\n}\n\n// --- Multi-Registry Support ---\n\nexport interface BundleSearchHit extends BundleSearchResult {\n /** Which registry URL this result came from */\n registryUrl: string;\n /** Display name of the registry */\n registryName: string;\n}\n\nexport interface MultiBundleSearchResponse {\n results: BundleSearchHit[];\n total: number;\n registriesSearched: number;\n errors: Array<{ registry: string; error: string }>;\n}\n\n/**\n * Search all configured registries for bundles.\n * Merges results, deduplicating by name (first registry wins).\n */\nexport async function searchConfiguredRegistries(\n registries: Array<{ url: string; name?: string; token?: string }>,\n query: string,\n options?: { limit?: number },\n): Promise<MultiBundleSearchResponse> {\n const limit = options?.limit ?? 20;\n const allResults: BundleSearchHit[] = [];\n const errors: Array<{ registry: string; error: string }> = [];\n const seenNames = new Set<string>();\n\n const searches = registries.map(async (reg) => {\n const displayName = reg.name ?? reg.url;\n try {\n const response = await searchBundleRegistry(reg.url, query, { limit, token: reg.token });\n return { registry: reg, displayName, response };\n } catch (err) {\n errors.push({\n registry: displayName,\n error: err instanceof Error ? err.message : String(err),\n });\n return null;\n }\n });\n\n const results = await Promise.allSettled(searches);\n\n for (const settled of results) {\n if (settled.status === 'fulfilled' && settled.value) {\n const { registry, displayName, response } = settled.value;\n for (const result of response.results) {\n if (!seenNames.has(result.name)) {\n seenNames.add(result.name);\n allResults.push({\n ...result,\n registryUrl: registry.url,\n registryName: displayName,\n });\n }\n }\n }\n }\n\n return {\n results: allResults.slice(0, limit),\n total: allResults.length,\n registriesSearched: registries.length,\n errors,\n };\n}\n\n/**\n * Install a bundle from configured registries by name.\n * Searches each registry in order, installs from the first match.\n */\nexport async function installFromRegistry(\n harnessDir: string,\n registries: Array<{ url: string; name?: string; token?: string }>,\n bundleName: string,\n options?: { version?: string; overwrite?: boolean; force?: boolean },\n): Promise<PrimitiveInstallResult & { registryUrl?: string }> {\n for (const reg of registries) {\n try {\n const bundle = await fetchFromRegistry(reg.url, bundleName, {\n version: options?.version,\n token: reg.token,\n });\n const result = installBundle(harnessDir, bundle, {\n overwrite: options?.overwrite,\n force: options?.force,\n });\n return { ...result, registryUrl: reg.url };\n } catch {\n // Try next registry\n continue;\n }\n }\n\n return {\n installed: false,\n name: bundleName,\n files: [],\n skipped: [],\n errors: [`Bundle \"${bundleName}\" not found in any configured registry`],\n };\n}\n"],"mappings":"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","names":["files","manifest"]}
|
|
1
|
+
{"version":3,"sources":["../src/runtime/primitive-registry.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, renameSync, unlinkSync } from 'fs';\nimport { join, basename, dirname, relative } from 'path';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport { CORE_PRIMITIVE_DIRS } from '../core/types.js';\nimport { parseHarnessDocument } from '../primitives/loader.js';\nimport { log } from '../core/logger.js';\n\n// --- Manifest Types ---\n\nexport interface BundleManifest {\n /** Manifest format version */\n version: string;\n /** Bundle name (e.g., \"code-review-rules\") */\n name: string;\n /** Human-readable description */\n description: string;\n /** Author identifier */\n author: string;\n /** Semantic version (e.g., \"1.0.0\") */\n bundle_version: string;\n /** When this bundle was created */\n created: string;\n /** Primitive type(s) contained (e.g., [\"rules\", \"instincts\"]) */\n types: string[];\n /** Tags for search/discovery */\n tags: string[];\n /** Files included in this bundle (relative paths) */\n files: BundleFileEntry[];\n /** Optional dependencies (other bundle names) */\n dependencies?: string[];\n /** Optional registry URL this was published to */\n registry?: string;\n /** Optional license identifier */\n license?: string;\n}\n\nexport interface BundleFileEntry {\n path: string;\n type: string;\n id: string;\n l0: string;\n}\n\n// --- Registry Types ---\n\nexport interface RegistryConfig {\n /** Registry URL (HTTPS) */\n url: string;\n /** Optional auth token */\n token?: string;\n /** Optional name for display */\n name?: string;\n}\n\nexport interface BundleSearchResult {\n name: string;\n description: string;\n author: string;\n version: string;\n types: string[];\n tags: string[];\n download_url: string;\n}\n\nexport interface BundleSearchResponse {\n results: BundleSearchResult[];\n total: number;\n}\n\n// --- Install / Uninstall Types ---\n\nexport interface PrimitiveInstallResult {\n installed: boolean;\n name: string;\n files: string[];\n skipped: string[];\n errors: string[];\n manifest?: BundleManifest;\n}\n\nexport interface PrimitiveUninstallResult {\n uninstalled: boolean;\n name: string;\n archived: string[];\n dependents: string[];\n errors: string[];\n}\n\nexport interface PrimitiveUpdateResult {\n updated: boolean;\n name: string;\n added: string[];\n modified: string[];\n removed: string[];\n errors: string[];\n oldVersion?: string;\n newVersion?: string;\n}\n\n// --- Manifest Operations ---\n\n/**\n * Create a manifest.yaml for a set of primitive files.\n */\nexport function createManifest(\n harnessDir: string,\n options: {\n name: string;\n description: string;\n author?: string;\n version?: string;\n files: string[];\n tags?: string[];\n license?: string;\n },\n): BundleManifest {\n const types = new Set<string>();\n const fileEntries: BundleFileEntry[] = [];\n\n for (const filePath of options.files) {\n const absPath = filePath.startsWith('/') ? filePath : join(harnessDir, filePath);\n if (!existsSync(absPath)) continue;\n\n const relPath = relative(harnessDir, absPath);\n const dir = relPath.split('/')[0];\n const type = (CORE_PRIMITIVE_DIRS as readonly string[]).includes(dir) ? dir : 'custom';\n types.add(type);\n\n try {\n const doc = parseHarnessDocument(absPath);\n fileEntries.push({\n path: relPath,\n type,\n id: doc.frontmatter.id ?? basename(relPath, '.md'),\n l0: doc.l0 || '',\n });\n } catch {\n fileEntries.push({\n path: relPath,\n type,\n id: basename(relPath, '.md'),\n l0: '',\n });\n }\n }\n\n return {\n version: '1.0',\n name: options.name,\n description: options.description,\n author: options.author ?? 'unknown',\n bundle_version: options.version ?? '1.0.0',\n created: new Date().toISOString(),\n types: [...types],\n tags: options.tags ?? [],\n files: fileEntries,\n license: options.license,\n };\n}\n\n/**\n * Write a manifest to a YAML file.\n */\nexport function writeManifest(manifest: BundleManifest, outputPath: string): void {\n writeFileSync(outputPath, stringifyYaml(manifest), 'utf-8');\n}\n\n/**\n * Read and validate a manifest from a YAML file.\n */\nexport function readManifest(manifestPath: string): BundleManifest {\n if (!existsSync(manifestPath)) {\n throw new Error(`Manifest not found: ${manifestPath}`);\n }\n const content = readFileSync(manifestPath, 'utf-8');\n const parsed: unknown = parseYaml(content);\n\n if (typeof parsed !== 'object' || parsed === null) {\n throw new Error('Invalid manifest: not an object');\n }\n\n const manifest = parsed as Record<string, unknown>;\n if (typeof manifest.name !== 'string' || !manifest.name) {\n throw new Error('Invalid manifest: missing \"name\"');\n }\n if (typeof manifest.version !== 'string') {\n throw new Error('Invalid manifest: missing \"version\"');\n }\n if (!Array.isArray(manifest.files)) {\n throw new Error('Invalid manifest: missing \"files\" array');\n }\n\n return parsed as BundleManifest;\n}\n\n// --- Bundle Pack/Unpack ---\n\nexport interface PackedBundle {\n manifest: BundleManifest;\n files: Array<{ path: string; content: string }>;\n}\n\n/**\n * Pack a set of primitives into a bundle (manifest + file contents).\n */\nexport function packBundle(\n harnessDir: string,\n options: {\n name: string;\n description: string;\n author?: string;\n version?: string;\n files?: string[];\n types?: string[];\n tags?: string[];\n license?: string;\n },\n): PackedBundle {\n let filePaths: string[] = options.files ?? [];\n\n // If types specified (or no types and no files), auto-collect all .md files from those dirs\n const types = (options.types && options.types.length > 0)\n ? options.types\n : (filePaths.length === 0 ? [...CORE_PRIMITIVE_DIRS] : []);\n if (types.length > 0 && filePaths.length === 0) {\n for (const type of types) {\n const dirPath = join(harnessDir, type);\n if (!existsSync(dirPath)) continue;\n const files = readdirSync(dirPath)\n .filter((f: string) => f.endsWith('.md') && !f.startsWith('_') && !f.startsWith('.'))\n .map((f: string) => join(type, f));\n filePaths.push(...files);\n }\n }\n\n const manifest = createManifest(harnessDir, {\n name: options.name,\n description: options.description,\n author: options.author,\n version: options.version,\n files: filePaths,\n tags: options.tags,\n license: options.license,\n });\n\n const files: Array<{ path: string; content: string }> = [];\n for (const entry of manifest.files) {\n const absPath = join(harnessDir, entry.path);\n if (existsSync(absPath)) {\n files.push({\n path: entry.path,\n content: readFileSync(absPath, 'utf-8'),\n });\n }\n }\n\n return { manifest, files };\n}\n\n/**\n * Write a packed bundle to a directory (manifest.yaml + files).\n */\nexport function writeBundleDir(bundle: PackedBundle, outputDir: string): void {\n mkdirSync(outputDir, { recursive: true });\n writeManifest(bundle.manifest, join(outputDir, 'manifest.yaml'));\n\n for (const file of bundle.files) {\n const targetPath = join(outputDir, file.path);\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileSync(targetPath, file.content, 'utf-8');\n }\n}\n\n/**\n * Read a packed bundle from a directory containing manifest.yaml.\n */\nexport function readBundleDir(bundleDir: string): PackedBundle {\n const manifestPath = join(bundleDir, 'manifest.yaml');\n const manifest = readManifest(manifestPath);\n\n const files: Array<{ path: string; content: string }> = [];\n for (const entry of manifest.files) {\n const filePath = join(bundleDir, entry.path);\n if (existsSync(filePath)) {\n files.push({\n path: entry.path,\n content: readFileSync(filePath, 'utf-8'),\n });\n }\n }\n\n return { manifest, files };\n}\n\n// --- Install from Bundle ---\n\n/**\n * Install primitives from a packed bundle into a harness directory.\n */\nexport function installBundle(\n harnessDir: string,\n bundle: PackedBundle,\n options?: { overwrite?: boolean; force?: boolean },\n): PrimitiveInstallResult {\n const overwrite = options?.overwrite ?? false;\n const result: PrimitiveInstallResult = {\n installed: false,\n name: bundle.manifest.name,\n files: [],\n skipped: [],\n errors: [],\n manifest: bundle.manifest,\n };\n\n // Check for dependents\n if (bundle.manifest.dependencies && bundle.manifest.dependencies.length > 0 && !options?.force) {\n const installedPath = join(harnessDir, '.installed');\n if (existsSync(installedPath)) {\n const installed = readInstalledManifests(harnessDir);\n const installedNames = new Set(installed.map((m) => m.name));\n const missing = bundle.manifest.dependencies.filter((d) => !installedNames.has(d));\n if (missing.length > 0) {\n result.errors.push(`Missing dependencies: ${missing.join(', ')}. Use --force to install anyway.`);\n return result;\n }\n }\n }\n\n for (const file of bundle.files) {\n const targetPath = join(harnessDir, file.path);\n\n if (existsSync(targetPath) && !overwrite) {\n result.skipped.push(file.path);\n continue;\n }\n\n try {\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileSync(targetPath, file.content, 'utf-8');\n result.files.push(file.path);\n } catch (err) {\n result.errors.push(`${file.path}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Record installation\n if (result.files.length > 0 || result.skipped.length > 0) {\n result.installed = true;\n recordInstallation(harnessDir, bundle.manifest);\n }\n\n return result;\n}\n\n// --- Uninstall ---\n\n/**\n * Uninstall (soft-delete) a previously installed bundle.\n * Moves files to archive/ instead of deleting.\n */\nexport function uninstallBundle(\n harnessDir: string,\n bundleName: string,\n options?: { hard?: boolean },\n): PrimitiveUninstallResult {\n const result: PrimitiveUninstallResult = {\n uninstalled: false,\n name: bundleName,\n archived: [],\n dependents: [],\n errors: [],\n };\n\n // Find the installed manifest\n const installed = readInstalledManifests(harnessDir);\n const manifest = installed.find((m) => m.name === bundleName);\n if (!manifest) {\n result.errors.push(`Bundle \"${bundleName}\" is not installed`);\n return result;\n }\n\n // Check if other installed bundles depend on this one\n const dependents = installed.filter(\n (m) => m.name !== bundleName && m.dependencies?.includes(bundleName),\n );\n if (dependents.length > 0) {\n result.dependents = dependents.map((m) => m.name);\n result.errors.push(\n `Cannot uninstall: ${dependents.map((m) => m.name).join(', ')} depend(s) on \"${bundleName}\"`,\n );\n return result;\n }\n\n const archiveDir = join(harnessDir, 'archive', 'uninstalled', bundleName);\n\n for (const entry of manifest.files) {\n const filePath = join(harnessDir, entry.path);\n if (!existsSync(filePath)) continue;\n\n try {\n if (options?.hard) {\n unlinkSync(filePath);\n } else {\n // Soft delete — move to archive\n const archivePath = join(archiveDir, entry.path);\n mkdirSync(dirname(archivePath), { recursive: true });\n renameSync(filePath, archivePath);\n }\n result.archived.push(entry.path);\n } catch (err) {\n result.errors.push(`${entry.path}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Remove installation record\n if (result.archived.length > 0) {\n result.uninstalled = true;\n removeInstallationRecord(harnessDir, bundleName);\n }\n\n return result;\n}\n\n// --- Update ---\n\n/**\n * Compare an installed bundle against a new version and produce a diff.\n */\nexport function diffBundle(\n harnessDir: string,\n newBundle: PackedBundle,\n): { added: string[]; modified: string[]; removed: string[]; unchanged: string[] } {\n const installed = readInstalledManifests(harnessDir);\n const existing = installed.find((m) => m.name === newBundle.manifest.name);\n\n const added: string[] = [];\n const modified: string[] = [];\n const removed: string[] = [];\n const unchanged: string[] = [];\n\n const existingFiles = new Set(existing?.files.map((f) => f.path) ?? []);\n const newFiles = new Set(newBundle.manifest.files.map((f) => f.path));\n\n // Check for added/modified files\n for (const file of newBundle.files) {\n const targetPath = join(harnessDir, file.path);\n if (!existingFiles.has(file.path)) {\n added.push(file.path);\n } else if (existsSync(targetPath)) {\n const currentContent = readFileSync(targetPath, 'utf-8');\n if (currentContent !== file.content) {\n modified.push(file.path);\n } else {\n unchanged.push(file.path);\n }\n } else {\n added.push(file.path);\n }\n }\n\n // Check for removed files\n for (const path of existingFiles) {\n if (!newFiles.has(path)) {\n removed.push(path);\n }\n }\n\n return { added, modified, removed, unchanged };\n}\n\n/**\n * Update an installed bundle to a new version.\n */\nexport function updateBundle(\n harnessDir: string,\n newBundle: PackedBundle,\n options?: { removeDeleted?: boolean },\n): PrimitiveUpdateResult {\n const removeDeleted = options?.removeDeleted ?? false;\n const diff = diffBundle(harnessDir, newBundle);\n\n const result: PrimitiveUpdateResult = {\n updated: false,\n name: newBundle.manifest.name,\n added: [],\n modified: [],\n removed: [],\n errors: [],\n };\n\n // Find old version\n const installed = readInstalledManifests(harnessDir);\n const existing = installed.find((m) => m.name === newBundle.manifest.name);\n result.oldVersion = existing?.bundle_version;\n result.newVersion = newBundle.manifest.bundle_version;\n\n // Write added/modified files\n for (const path of [...diff.added, ...diff.modified]) {\n const file = newBundle.files.find((f) => f.path === path);\n if (!file) continue;\n\n const targetPath = join(harnessDir, path);\n try {\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileSync(targetPath, file.content, 'utf-8');\n if (diff.added.includes(path)) {\n result.added.push(path);\n } else {\n result.modified.push(path);\n }\n } catch (err) {\n result.errors.push(`${path}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Handle removed files\n if (removeDeleted) {\n const archiveDir = join(harnessDir, 'archive', 'updated', newBundle.manifest.name);\n for (const path of diff.removed) {\n const filePath = join(harnessDir, path);\n if (!existsSync(filePath)) continue;\n\n try {\n const archivePath = join(archiveDir, path);\n mkdirSync(dirname(archivePath), { recursive: true });\n renameSync(filePath, archivePath);\n result.removed.push(path);\n } catch (err) {\n result.errors.push(`${path}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n\n if (result.added.length > 0 || result.modified.length > 0 || result.removed.length > 0) {\n result.updated = true;\n recordInstallation(harnessDir, newBundle.manifest);\n }\n\n return result;\n}\n\n// --- Installation Record ---\n\nconst INSTALLED_DIR = '.installed';\n\n/**\n * Record that a bundle was installed (writes manifest to .installed/).\n */\nfunction recordInstallation(harnessDir: string, manifest: BundleManifest): void {\n const installedDir = join(harnessDir, INSTALLED_DIR);\n mkdirSync(installedDir, { recursive: true });\n const manifestPath = join(installedDir, `${manifest.name}.yaml`);\n writeFileSync(manifestPath, stringifyYaml(manifest), 'utf-8');\n}\n\n/**\n * Remove an installation record.\n */\nfunction removeInstallationRecord(harnessDir: string, bundleName: string): void {\n const manifestPath = join(harnessDir, INSTALLED_DIR, `${bundleName}.yaml`);\n if (existsSync(manifestPath)) {\n unlinkSync(manifestPath);\n }\n}\n\n/**\n * Read all installed bundle manifests.\n */\nexport function readInstalledManifests(harnessDir: string): BundleManifest[] {\n const installedDir = join(harnessDir, INSTALLED_DIR);\n if (!existsSync(installedDir)) return [];\n\n const files = readdirSync(installedDir).filter((f: string) => f.endsWith('.yaml'));\n const manifests: BundleManifest[] = [];\n\n for (const file of files) {\n try {\n const manifest = readManifest(join(installedDir, file));\n manifests.push(manifest);\n } catch (err) {\n log.warn(`Failed to read installed manifest ${file}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n return manifests;\n}\n\n/**\n * List all installed bundles with summary info.\n */\nexport function listInstalledBundles(harnessDir: string): Array<{\n name: string;\n version: string;\n types: string[];\n fileCount: number;\n description: string;\n}> {\n return readInstalledManifests(harnessDir).map((m) => ({\n name: m.name,\n version: m.bundle_version,\n types: m.types,\n fileCount: m.files.length,\n description: m.description,\n }));\n}\n\n// --- Remote Registry Client ---\n\n/**\n * Fetch a bundle from a remote registry URL.\n */\nexport async function fetchRemoteBundle(url: string): Promise<PackedBundle> {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch bundle: ${response.status} ${response.statusText}`);\n }\n\n const contentType = response.headers.get('content-type') ?? '';\n\n if (contentType.includes('application/json')) {\n // JSON bundle format (legacy HarnessBundle format)\n const data: unknown = await response.json();\n if (typeof data !== 'object' || data === null || !('entries' in data)) {\n throw new Error('Invalid JSON bundle format');\n }\n\n const jsonBundle = data as { entries: Array<{ path: string; content: string }>; agent_name?: string };\n\n // Convert to PackedBundle with synthetic manifest\n const files = jsonBundle.entries;\n const types = new Set<string>();\n const fileEntries: BundleFileEntry[] = [];\n\n for (const entry of files) {\n const dir = entry.path.split('/')[0];\n const type = (CORE_PRIMITIVE_DIRS as readonly string[]).includes(dir) ? dir : 'custom';\n types.add(type);\n fileEntries.push({\n path: entry.path,\n type,\n id: basename(entry.path, '.md'),\n l0: '',\n });\n }\n\n const manifest: BundleManifest = {\n version: '1.0',\n name: jsonBundle.agent_name ?? 'remote-bundle',\n description: 'Imported from remote URL',\n author: 'unknown',\n bundle_version: '1.0.0',\n created: new Date().toISOString(),\n types: [...types],\n tags: [],\n files: fileEntries,\n };\n\n return { manifest, files };\n }\n\n // YAML manifest + files format (tar/zip would go here in future)\n // For now, treat as a single-file bundle\n const content = await response.text();\n const fileName = basename(new URL(url).pathname);\n\n const manifest: BundleManifest = {\n version: '1.0',\n name: basename(fileName, '.md'),\n description: `Downloaded from ${url}`,\n author: 'unknown',\n bundle_version: '1.0.0',\n created: new Date().toISOString(),\n types: ['custom'],\n tags: [],\n files: [{ path: fileName, type: 'custom', id: basename(fileName, '.md'), l0: '' }],\n };\n\n return { manifest, files: [{ path: fileName, content }] };\n}\n\n/**\n * Search a remote registry for bundles.\n */\nexport async function searchBundleRegistry(\n registryUrl: string,\n query: string,\n options?: { limit?: number; token?: string },\n): Promise<BundleSearchResponse> {\n const limit = options?.limit ?? 20;\n const searchUrl = `${registryUrl}/api/bundles?search=${encodeURIComponent(query)}&limit=${limit}`;\n\n const headers: Record<string, string> = { 'Accept': 'application/json' };\n if (options?.token) {\n headers['Authorization'] = `Bearer ${options.token}`;\n }\n\n const response = await fetch(searchUrl, { headers });\n if (!response.ok) {\n throw new Error(`Registry search failed: ${response.status} ${response.statusText}`);\n }\n\n return await response.json() as BundleSearchResponse;\n}\n\n/**\n * Fetch a bundle from a registry by name.\n */\nexport async function fetchFromRegistry(\n registryUrl: string,\n bundleName: string,\n options?: { version?: string; token?: string },\n): Promise<PackedBundle> {\n const version = options?.version ?? 'latest';\n const bundleUrl = `${registryUrl}/api/bundles/${encodeURIComponent(bundleName)}/versions/${version}/download`;\n\n const headers: Record<string, string> = {};\n if (options?.token) {\n headers['Authorization'] = `Bearer ${options.token}`;\n }\n\n const response = await fetch(bundleUrl, { headers });\n if (!response.ok) {\n throw new Error(`Failed to fetch bundle \"${bundleName}\": ${response.status} ${response.statusText}`);\n }\n\n const data: unknown = await response.json();\n return data as PackedBundle;\n}\n\n// --- Multi-Registry Support ---\n\nexport interface BundleSearchHit extends BundleSearchResult {\n /** Which registry URL this result came from */\n registryUrl: string;\n /** Display name of the registry */\n registryName: string;\n}\n\nexport interface MultiBundleSearchResponse {\n results: BundleSearchHit[];\n total: number;\n registriesSearched: number;\n errors: Array<{ registry: string; error: string }>;\n}\n\n/**\n * Search all configured registries for bundles.\n * Merges results, deduplicating by name (first registry wins).\n */\nexport async function searchConfiguredRegistries(\n registries: Array<{ url: string; name?: string; token?: string }>,\n query: string,\n options?: { limit?: number },\n): Promise<MultiBundleSearchResponse> {\n const limit = options?.limit ?? 20;\n const allResults: BundleSearchHit[] = [];\n const errors: Array<{ registry: string; error: string }> = [];\n const seenNames = new Set<string>();\n\n const searches = registries.map(async (reg) => {\n const displayName = reg.name ?? reg.url;\n try {\n const response = await searchBundleRegistry(reg.url, query, { limit, token: reg.token });\n return { registry: reg, displayName, response };\n } catch (err) {\n errors.push({\n registry: displayName,\n error: err instanceof Error ? err.message : String(err),\n });\n return null;\n }\n });\n\n const results = await Promise.allSettled(searches);\n\n for (const settled of results) {\n if (settled.status === 'fulfilled' && settled.value) {\n const { registry, displayName, response } = settled.value;\n for (const result of response.results) {\n if (!seenNames.has(result.name)) {\n seenNames.add(result.name);\n allResults.push({\n ...result,\n registryUrl: registry.url,\n registryName: displayName,\n });\n }\n }\n }\n }\n\n return {\n results: allResults.slice(0, limit),\n total: allResults.length,\n registriesSearched: registries.length,\n errors,\n };\n}\n\n/**\n * Install a bundle from configured registries by name.\n * Searches each registry in order, installs from the first match.\n */\nexport async function installFromRegistry(\n harnessDir: string,\n registries: Array<{ url: string; name?: string; token?: string }>,\n bundleName: string,\n options?: { version?: string; overwrite?: boolean; force?: boolean },\n): Promise<PrimitiveInstallResult & { registryUrl?: string }> {\n for (const reg of registries) {\n try {\n const bundle = await fetchFromRegistry(reg.url, bundleName, {\n version: options?.version,\n token: reg.token,\n });\n const result = installBundle(harnessDir, bundle, {\n overwrite: options?.overwrite,\n force: options?.force,\n });\n return { ...result, registryUrl: reg.url };\n } catch {\n // Try next registry\n continue;\n }\n }\n\n return {\n installed: false,\n name: bundleName,\n files: [],\n skipped: [],\n errors: [`Bundle \"${bundleName}\" not found in any configured registry`],\n };\n}\n"],"mappings":"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","names":["files","manifest"]}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import "./chunk-ZZJOFKAT.js";
|
|
4
3
|
|
|
5
4
|
// src/runtime/project-discovery.ts
|
|
6
5
|
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
@@ -243,4 +242,4 @@ function discoverProjectContext(options) {
|
|
|
243
242
|
export {
|
|
244
243
|
discoverProjectContext
|
|
245
244
|
};
|
|
246
|
-
//# sourceMappingURL=project-discovery-
|
|
245
|
+
//# sourceMappingURL=project-discovery-FQLAZKEM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/project-discovery.ts"],"sourcesContent":["import { existsSync, readFileSync, readdirSync } from 'fs';\nimport { join } from 'path';\n\n// --- Types ---\n\n/** A detected project characteristic */\nexport interface ProjectSignal {\n /** What was detected (e.g. \"TypeScript\", \"React\", \"Docker\") */\n name: string;\n /** Category of signal */\n category: 'language' | 'framework' | 'tool' | 'runtime' | 'database' | 'cloud' | 'testing';\n /** Source file that triggered the detection */\n source: string;\n /** Additional details */\n details?: string;\n}\n\n/** Suggested rule, skill, or MCP server */\nexport interface ProjectSuggestion {\n /** What type of thing to add */\n type: 'rule' | 'skill' | 'mcp-server';\n /** Human-readable suggestion */\n message: string;\n /** File to create (for rules/skills) or server query (for MCP) */\n target: string;\n /** Triggered by these signals */\n signals: string[];\n}\n\n/** Full project discovery result */\nexport interface ProjectDiscoveryResult {\n /** Detected project signals */\n signals: ProjectSignal[];\n /** Files that were examined */\n filesExamined: string[];\n /** Suggestions based on signals */\n suggestions: ProjectSuggestion[];\n}\n\n// --- Detection Functions ---\n\nfunction detectPackageJson(content: string): ProjectSignal[] {\n const signals: ProjectSignal[] = [];\n\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(content);\n } catch {\n return signals;\n }\n\n const allDeps = {\n ...(pkg.dependencies as Record<string, string> | undefined),\n ...(pkg.devDependencies as Record<string, string> | undefined),\n };\n\n // Language\n if (allDeps['typescript'] || existsSync('tsconfig.json')) {\n signals.push({ name: 'TypeScript', category: 'language', source: 'package.json' });\n }\n\n // Frameworks\n const frameworks: Record<string, { name: string; category: ProjectSignal['category'] }> = {\n 'react': { name: 'React', category: 'framework' },\n 'next': { name: 'Next.js', category: 'framework' },\n 'vue': { name: 'Vue', category: 'framework' },\n 'nuxt': { name: 'Nuxt', category: 'framework' },\n 'svelte': { name: 'Svelte', category: 'framework' },\n '@angular/core': { name: 'Angular', category: 'framework' },\n 'express': { name: 'Express', category: 'framework' },\n 'fastify': { name: 'Fastify', category: 'framework' },\n 'hono': { name: 'Hono', category: 'framework' },\n 'astro': { name: 'Astro', category: 'framework' },\n 'remix': { name: 'Remix', category: 'framework' },\n '@remix-run/node': { name: 'Remix', category: 'framework' },\n 'electron': { name: 'Electron', category: 'framework' },\n };\n\n for (const [dep, info] of Object.entries(frameworks)) {\n if (allDeps[dep]) {\n signals.push({ name: info.name, category: info.category, source: 'package.json', details: `v${allDeps[dep]}` });\n }\n }\n\n // Testing\n const testLibs: Record<string, string> = {\n 'vitest': 'Vitest',\n 'jest': 'Jest',\n 'mocha': 'Mocha',\n '@playwright/test': 'Playwright',\n 'cypress': 'Cypress',\n };\n\n for (const [dep, name] of Object.entries(testLibs)) {\n if (allDeps[dep]) {\n signals.push({ name, category: 'testing', source: 'package.json' });\n }\n }\n\n // Databases\n const dbLibs: Record<string, string> = {\n 'prisma': 'Prisma',\n '@prisma/client': 'Prisma',\n 'drizzle-orm': 'Drizzle',\n 'mongoose': 'MongoDB (Mongoose)',\n 'pg': 'PostgreSQL',\n 'mysql2': 'MySQL',\n 'better-sqlite3': 'SQLite',\n 'redis': 'Redis',\n 'ioredis': 'Redis',\n };\n\n for (const [dep, name] of Object.entries(dbLibs)) {\n if (allDeps[dep]) {\n signals.push({ name, category: 'database', source: 'package.json' });\n }\n }\n\n // Tools\n const tools: Record<string, string> = {\n 'eslint': 'ESLint',\n 'prettier': 'Prettier',\n 'tailwindcss': 'Tailwind CSS',\n 'storybook': 'Storybook',\n '@storybook/react': 'Storybook',\n 'docker-compose': 'Docker Compose',\n };\n\n for (const [dep, name] of Object.entries(tools)) {\n if (allDeps[dep]) {\n signals.push({ name, category: 'tool', source: 'package.json' });\n }\n }\n\n return signals;\n}\n\nfunction detectFromFiles(dir: string): ProjectSignal[] {\n const signals: ProjectSignal[] = [];\n const entries = new Set<string>();\n\n try {\n for (const e of readdirSync(dir)) {\n entries.add(e);\n }\n } catch {\n return signals;\n }\n\n // Config files\n if (entries.has('Dockerfile') || entries.has('docker-compose.yml') || entries.has('docker-compose.yaml')) {\n signals.push({ name: 'Docker', category: 'runtime', source: 'Dockerfile' });\n }\n\n if (entries.has('.github')) {\n signals.push({ name: 'GitHub Actions', category: 'tool', source: '.github/' });\n }\n\n if (entries.has('Makefile')) {\n signals.push({ name: 'Make', category: 'tool', source: 'Makefile' });\n }\n\n if (entries.has('pyproject.toml') || entries.has('setup.py') || entries.has('requirements.txt')) {\n signals.push({ name: 'Python', category: 'language', source: 'pyproject.toml' });\n }\n\n if (entries.has('Cargo.toml')) {\n signals.push({ name: 'Rust', category: 'language', source: 'Cargo.toml' });\n }\n\n if (entries.has('go.mod')) {\n signals.push({ name: 'Go', category: 'language', source: 'go.mod' });\n }\n\n if (entries.has('Gemfile')) {\n signals.push({ name: 'Ruby', category: 'language', source: 'Gemfile' });\n }\n\n if (entries.has('.terraform') || entries.has('main.tf')) {\n signals.push({ name: 'Terraform', category: 'cloud', source: 'main.tf' });\n }\n\n if (entries.has('serverless.yml') || entries.has('serverless.yaml')) {\n signals.push({ name: 'Serverless Framework', category: 'cloud', source: 'serverless.yml' });\n }\n\n if (entries.has('vercel.json')) {\n signals.push({ name: 'Vercel', category: 'cloud', source: 'vercel.json' });\n }\n\n if (entries.has('netlify.toml')) {\n signals.push({ name: 'Netlify', category: 'cloud', source: 'netlify.toml' });\n }\n\n if (entries.has('wrangler.toml') || entries.has('wrangler.jsonc')) {\n signals.push({ name: 'Cloudflare Workers', category: 'cloud', source: 'wrangler.toml' });\n }\n\n if (entries.has('.prisma') || entries.has('prisma')) {\n signals.push({ name: 'Prisma', category: 'database', source: 'prisma/' });\n }\n\n if (entries.has('supabase')) {\n signals.push({ name: 'Supabase', category: 'database', source: 'supabase/' });\n }\n\n return signals;\n}\n\n// --- Suggestion Engine ---\n\ninterface SuggestionRule {\n signals: string[];\n type: ProjectSuggestion['type'];\n message: string;\n target: string;\n}\n\nconst SUGGESTION_RULES: SuggestionRule[] = [\n {\n signals: ['TypeScript'],\n type: 'rule',\n message: 'Add a TypeScript coding standards rule',\n target: 'rules/typescript-standards.md',\n },\n {\n signals: ['React'],\n type: 'rule',\n message: 'Add React component patterns rule',\n target: 'rules/react-patterns.md',\n },\n {\n signals: ['Next.js'],\n type: 'skill',\n message: 'Add Next.js development skill',\n target: 'skills/nextjs.md',\n },\n {\n signals: ['Docker'],\n type: 'rule',\n message: 'Add Docker/containerization rule',\n target: 'rules/docker.md',\n },\n {\n signals: ['GitHub Actions'],\n type: 'skill',\n message: 'Add CI/CD pipeline skill',\n target: 'skills/ci-cd.md',\n },\n {\n signals: ['PostgreSQL', 'Prisma'],\n type: 'mcp-server',\n message: 'Install PostgreSQL MCP server for database access',\n target: 'postgres',\n },\n {\n signals: ['Supabase'],\n type: 'mcp-server',\n message: 'Install Supabase MCP server',\n target: 'supabase',\n },\n {\n signals: ['ESLint'],\n type: 'rule',\n message: 'Add linting standards rule',\n target: 'rules/linting.md',\n },\n {\n signals: ['Vitest', 'Jest'],\n type: 'rule',\n message: 'Add testing standards rule',\n target: 'rules/testing.md',\n },\n {\n signals: ['Tailwind CSS'],\n type: 'rule',\n message: 'Add styling conventions rule',\n target: 'rules/styling.md',\n },\n];\n\nfunction generateSuggestions(signals: ProjectSignal[]): ProjectSuggestion[] {\n const signalNames = new Set(signals.map((s) => s.name));\n const suggestions: ProjectSuggestion[] = [];\n\n for (const rule of SUGGESTION_RULES) {\n // Check if ANY of the required signals are present\n const matchedSignals = rule.signals.filter((s) => signalNames.has(s));\n if (matchedSignals.length > 0) {\n suggestions.push({\n type: rule.type,\n message: rule.message,\n target: rule.target,\n signals: matchedSignals,\n });\n }\n }\n\n return suggestions;\n}\n\n// --- Main Discovery ---\n\n/** Options for project discovery */\nexport interface ProjectDiscoveryOptions {\n /** Project directory to scan */\n dir?: string;\n}\n\n/**\n * Scan a project directory to detect its technology stack and suggest\n * rules, skills, and MCP servers.\n */\nexport function discoverProjectContext(options?: ProjectDiscoveryOptions): ProjectDiscoveryResult {\n const dir = options?.dir ?? process.cwd();\n const signals: ProjectSignal[] = [];\n const filesExamined: string[] = [];\n\n // Scan package.json\n const packageJsonPath = join(dir, 'package.json');\n if (existsSync(packageJsonPath)) {\n filesExamined.push(packageJsonPath);\n const content = readFileSync(packageJsonPath, 'utf-8');\n signals.push(...detectPackageJson(content));\n }\n\n // Scan directory for files/folders\n filesExamined.push(dir);\n signals.push(...detectFromFiles(dir));\n\n // Deduplicate signals by name\n const seen = new Set<string>();\n const uniqueSignals = signals.filter((s) => {\n if (seen.has(s.name)) return false;\n seen.add(s.name);\n return true;\n });\n\n // Generate suggestions\n const suggestions = generateSuggestions(uniqueSignals);\n\n return {\n signals: uniqueSignals,\n filesExamined,\n suggestions,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -11,8 +11,7 @@ import {
|
|
|
11
11
|
streamGenerate,
|
|
12
12
|
streamGenerateWithDetails,
|
|
13
13
|
streamWithMessages
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-ZZJOFKAT.js";
|
|
14
|
+
} from "./chunk-WCYBFALM.js";
|
|
16
15
|
export {
|
|
17
16
|
generate,
|
|
18
17
|
generateWithMessages,
|
|
@@ -25,4 +24,4 @@ export {
|
|
|
25
24
|
streamGenerateWithDetails,
|
|
26
25
|
streamWithMessages
|
|
27
26
|
};
|
|
28
|
-
//# sourceMappingURL=provider-
|
|
27
|
+
//# sourceMappingURL=provider-75AKTYGB.js.map
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
saveRateLimits,
|
|
10
10
|
tryAcquire
|
|
11
11
|
} from "./chunk-OC6YSTDX.js";
|
|
12
|
-
import "./chunk-ZZJOFKAT.js";
|
|
13
12
|
export {
|
|
14
13
|
checkRateLimit,
|
|
15
14
|
clearRateLimits,
|
|
@@ -19,4 +18,4 @@ export {
|
|
|
19
18
|
saveRateLimits,
|
|
20
19
|
tryAcquire
|
|
21
20
|
};
|
|
22
|
-
//# sourceMappingURL=rate-limiter-
|
|
21
|
+
//# sourceMappingURL=rate-limiter-PH5DCVU4.js.map
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
loadDirectory
|
|
5
5
|
} from "./chunk-2UVWCTAY.js";
|
|
6
6
|
import "./chunk-4TQQZILG.js";
|
|
7
|
-
import "./chunk-ZZJOFKAT.js";
|
|
8
7
|
|
|
9
8
|
// src/runtime/rule-engine.ts
|
|
10
9
|
import { existsSync } from "fs";
|
|
@@ -179,4 +178,4 @@ export {
|
|
|
179
178
|
loadRules,
|
|
180
179
|
parseRulesFromDoc
|
|
181
180
|
};
|
|
182
|
-
//# sourceMappingURL=rule-engine-
|
|
181
|
+
//# sourceMappingURL=rule-engine-DM26S77N.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime/rule-engine.ts"],"sourcesContent":["import { existsSync } from 'fs';\nimport { join } from 'path';\nimport { loadDirectory } from '../primitives/loader.js';\nimport type { HarnessDocument } from '../core/types.js';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type RuleAction = 'allow' | 'deny' | 'warn' | 'require_approval';\n\nexport interface ParsedRule {\n /** Source rule document ID */\n ruleId: string;\n /** What this rule regulates */\n subject: string;\n /** Whether it permits or blocks */\n action: RuleAction;\n /** Original directive text (for messages) */\n directive: string;\n /** Tags from the source document (for scoping) */\n tags: string[];\n}\n\nexport interface RuleCheckInput {\n /** The action being attempted (e.g., \"run\", \"tool_call\", \"delegate\") */\n action: string;\n /** Free-text description of what's being attempted */\n description?: string;\n /** Relevant tags or topics for the check */\n tags?: string[];\n /** Tool name if this is a tool call */\n toolName?: string;\n}\n\nexport interface RuleViolation {\n ruleId: string;\n directive: string;\n severity: 'deny' | 'warn' | 'require_approval';\n reason: string;\n}\n\nexport interface RuleCheckResult {\n allowed: boolean;\n violations: RuleViolation[];\n warnings: RuleViolation[];\n requiresApproval: boolean;\n /** Human-readable summary */\n summary: string;\n}\n\n// ─── Rule Parsing ────────────────────────────────────────────────────────────\n\n/**\n * Extract enforceable rules from a harness document.\n * Parses \"never\", \"must not\", \"do not\", \"always\", \"require\" directives\n * and converts them into structured rule objects.\n */\nexport function parseRulesFromDoc(doc: HarnessDocument): ParsedRule[] {\n const rules: ParsedRule[] = [];\n const text = (doc.l0 + '\\n' + doc.l1 + '\\n' + doc.body).trim();\n\n for (const line of text.split('\\n')) {\n const trimmed = line.trim().toLowerCase();\n if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('<!--')) continue;\n\n // Strip list markers\n const cleaned = trimmed.replace(/^[-*]\\s+/, '').replace(/^\\d+\\.\\s+/, '');\n\n // \"Never\" / \"Do not\" / \"Must not\" → deny\n const denyMatch = cleaned.match(\n /^(never|don'?t|do not|must not|should not|shouldn'?t)\\s+(.+)/,\n );\n if (denyMatch) {\n const subject = denyMatch[2].replace(/[.!]$/, '');\n // Check if this is an approval gate (e.g., \"never X without approval\")\n if (/without\\s+(explicit\\s+)?(human\\s+)?approval/.test(subject)) {\n rules.push({\n ruleId: doc.frontmatter.id,\n subject: subject.replace(/\\s+without\\s+(explicit\\s+)?(human\\s+)?approval.*$/, ''),\n action: 'require_approval',\n directive: cleaned,\n tags: doc.frontmatter.tags,\n });\n } else {\n rules.push({\n ruleId: doc.frontmatter.id,\n subject,\n action: 'deny',\n directive: cleaned,\n tags: doc.frontmatter.tags,\n });\n }\n continue;\n }\n\n // \"Always\" / \"Must\" / \"Require\" → allow (we note the requirement)\n const requireMatch = cleaned.match(\n /^(always|must|require|ensure)\\s+(.+)/,\n );\n if (requireMatch) {\n const subject = requireMatch[2].replace(/[.!]$/, '');\n // \"require approval\" / \"require explicit approval\" patterns\n if (/\\bapproval\\b/.test(subject) || /\\brequires?\\s+(explicit\\s+)?(human\\s+)?approval\\b/.test(subject)) {\n rules.push({\n ruleId: doc.frontmatter.id,\n subject,\n action: 'require_approval',\n directive: cleaned,\n tags: doc.frontmatter.tags,\n });\n } else {\n rules.push({\n ruleId: doc.frontmatter.id,\n subject,\n action: 'allow',\n directive: cleaned,\n tags: doc.frontmatter.tags,\n });\n }\n continue;\n }\n\n // \"Avoid\" → warn\n const warnMatch = cleaned.match(\n /^(avoid|prefer not to|try not to)\\s+(.+)/,\n );\n if (warnMatch) {\n rules.push({\n ruleId: doc.frontmatter.id,\n subject: warnMatch[2].replace(/[.!]$/, ''),\n action: 'warn',\n directive: cleaned,\n tags: doc.frontmatter.tags,\n });\n }\n }\n\n return rules;\n}\n\n/**\n * Load and parse all enforceable rules from a harness directory.\n * Loads all documents from the rules/ directory and extracts structured rules.\n */\nexport function loadRules(harnessDir: string): ParsedRule[] {\n const rulesDir = join(harnessDir, 'rules');\n if (!existsSync(rulesDir)) return [];\n\n const docs = loadDirectory(rulesDir);\n const rules: ParsedRule[] = [];\n\n for (const doc of docs) {\n if (doc.frontmatter.status !== 'active') continue;\n rules.push(...parseRulesFromDoc(doc));\n }\n\n return rules;\n}\n\n// ─── Rule Checking ───────────────────────────────────────────────────────────\n\n/**\n * Check whether an action violates any loaded rules.\n * Uses keyword overlap between the action description/tags and rule subjects.\n *\n * @param rules - Parsed rules from loadRules()\n * @param input - Description of the action being attempted\n * @returns Check result with violations, warnings, and approval requirements\n */\nexport function checkRules(rules: ParsedRule[], input: RuleCheckInput): RuleCheckResult {\n const violations: RuleViolation[] = [];\n const warnings: RuleViolation[] = [];\n let requiresApproval = false;\n\n // Build search text from input\n const searchText = [\n input.action,\n input.description ?? '',\n input.toolName ?? '',\n ...(input.tags ?? []),\n ].join(' ').toLowerCase();\n\n const searchWords = new Set(\n searchText.split(/\\s+/).filter((w) => w.length > 2),\n );\n\n for (const rule of rules) {\n // Check if this rule is relevant to the current action\n const relevance = computeRelevance(rule, searchWords, input);\n if (relevance < 0.3) continue;\n\n if (rule.action === 'deny') {\n violations.push({\n ruleId: rule.ruleId,\n directive: rule.directive,\n severity: 'deny',\n reason: `Action matches denied rule: \"${rule.directive}\" (relevance: ${relevance.toFixed(2)})`,\n });\n } else if (rule.action === 'warn') {\n warnings.push({\n ruleId: rule.ruleId,\n directive: rule.directive,\n severity: 'warn',\n reason: `Action matches warning rule: \"${rule.directive}\" (relevance: ${relevance.toFixed(2)})`,\n });\n } else if (rule.action === 'require_approval') {\n requiresApproval = true;\n violations.push({\n ruleId: rule.ruleId,\n directive: rule.directive,\n severity: 'require_approval',\n reason: `Action requires approval: \"${rule.directive}\" (relevance: ${relevance.toFixed(2)})`,\n });\n }\n }\n\n const denyViolations = violations.filter((v) => v.severity === 'deny');\n const allowed = denyViolations.length === 0 && !requiresApproval;\n\n let summary: string;\n if (allowed && warnings.length === 0) {\n summary = 'All rule checks passed.';\n } else if (allowed) {\n summary = `Allowed with ${warnings.length} warning(s): ${warnings.map((w) => w.directive).join('; ')}`;\n } else if (requiresApproval && denyViolations.length === 0) {\n summary = `Requires human approval: ${violations.filter((v) => v.severity === 'require_approval').map((v) => v.directive).join('; ')}`;\n } else {\n summary = `Blocked by ${denyViolations.length} rule violation(s): ${denyViolations.map((v) => v.directive).join('; ')}`;\n }\n\n return {\n allowed,\n violations,\n warnings,\n requiresApproval,\n summary,\n };\n}\n\n/**\n * Compute relevance score (0–1) between a rule and search context.\n * Uses word overlap between rule subject/tags and input words/tags.\n */\nfunction computeRelevance(\n rule: ParsedRule,\n searchWords: Set<string>,\n input: RuleCheckInput,\n): number {\n // Extract words from rule subject\n const ruleWords = rule.subject\n .toLowerCase()\n .split(/\\s+/)\n .filter((w) => w.length > 2);\n\n if (ruleWords.length === 0) return 0;\n\n // Count word overlap\n let matchCount = 0;\n for (const word of ruleWords) {\n if (searchWords.has(word)) {\n matchCount++;\n }\n }\n\n let score = matchCount / ruleWords.length;\n\n // Tag overlap boost\n if (input.tags && input.tags.length > 0) {\n const inputTags = new Set(input.tags.map((t) => t.toLowerCase()));\n const tagOverlap = rule.tags.filter((t) => inputTags.has(t.toLowerCase())).length;\n if (tagOverlap > 0) {\n score += 0.2 * (tagOverlap / Math.max(rule.tags.length, 1));\n }\n }\n\n return Math.min(score, 1.0);\n}\n\n/**\n * Convenience: load rules from disk and check an action in one call.\n */\nexport function enforceRules(\n harnessDir: string,\n input: RuleCheckInput,\n): RuleCheckResult {\n const rules = loadRules(harnessDir);\n return checkRules(rules, input);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/runtime/rule-engine.ts"],"sourcesContent":["import { existsSync } from 'fs';\nimport { join } from 'path';\nimport { loadDirectory } from '../primitives/loader.js';\nimport type { HarnessDocument } from '../core/types.js';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type RuleAction = 'allow' | 'deny' | 'warn' | 'require_approval';\n\nexport interface ParsedRule {\n /** Source rule document ID */\n ruleId: string;\n /** What this rule regulates */\n subject: string;\n /** Whether it permits or blocks */\n action: RuleAction;\n /** Original directive text (for messages) */\n directive: string;\n /** Tags from the source document (for scoping) */\n tags: string[];\n}\n\nexport interface RuleCheckInput {\n /** The action being attempted (e.g., \"run\", \"tool_call\", \"delegate\") */\n action: string;\n /** Free-text description of what's being attempted */\n description?: string;\n /** Relevant tags or topics for the check */\n tags?: string[];\n /** Tool name if this is a tool call */\n toolName?: string;\n}\n\nexport interface RuleViolation {\n ruleId: string;\n directive: string;\n severity: 'deny' | 'warn' | 'require_approval';\n reason: string;\n}\n\nexport interface RuleCheckResult {\n allowed: boolean;\n violations: RuleViolation[];\n warnings: RuleViolation[];\n requiresApproval: boolean;\n /** Human-readable summary */\n summary: string;\n}\n\n// ─── Rule Parsing ────────────────────────────────────────────────────────────\n\n/**\n * Extract enforceable rules from a harness document.\n * Parses \"never\", \"must not\", \"do not\", \"always\", \"require\" directives\n * and converts them into structured rule objects.\n */\nexport function parseRulesFromDoc(doc: HarnessDocument): ParsedRule[] {\n const rules: ParsedRule[] = [];\n const text = (doc.l0 + '\\n' + doc.l1 + '\\n' + doc.body).trim();\n\n for (const line of text.split('\\n')) {\n const trimmed = line.trim().toLowerCase();\n if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('<!--')) continue;\n\n // Strip list markers\n const cleaned = trimmed.replace(/^[-*]\\s+/, '').replace(/^\\d+\\.\\s+/, '');\n\n // \"Never\" / \"Do not\" / \"Must not\" → deny\n const denyMatch = cleaned.match(\n /^(never|don'?t|do not|must not|should not|shouldn'?t)\\s+(.+)/,\n );\n if (denyMatch) {\n const subject = denyMatch[2].replace(/[.!]$/, '');\n // Check if this is an approval gate (e.g., \"never X without approval\")\n if (/without\\s+(explicit\\s+)?(human\\s+)?approval/.test(subject)) {\n rules.push({\n ruleId: doc.frontmatter.id,\n subject: subject.replace(/\\s+without\\s+(explicit\\s+)?(human\\s+)?approval.*$/, ''),\n action: 'require_approval',\n directive: cleaned,\n tags: doc.frontmatter.tags,\n });\n } else {\n rules.push({\n ruleId: doc.frontmatter.id,\n subject,\n action: 'deny',\n directive: cleaned,\n tags: doc.frontmatter.tags,\n });\n }\n continue;\n }\n\n // \"Always\" / \"Must\" / \"Require\" → allow (we note the requirement)\n const requireMatch = cleaned.match(\n /^(always|must|require|ensure)\\s+(.+)/,\n );\n if (requireMatch) {\n const subject = requireMatch[2].replace(/[.!]$/, '');\n // \"require approval\" / \"require explicit approval\" patterns\n if (/\\bapproval\\b/.test(subject) || /\\brequires?\\s+(explicit\\s+)?(human\\s+)?approval\\b/.test(subject)) {\n rules.push({\n ruleId: doc.frontmatter.id,\n subject,\n action: 'require_approval',\n directive: cleaned,\n tags: doc.frontmatter.tags,\n });\n } else {\n rules.push({\n ruleId: doc.frontmatter.id,\n subject,\n action: 'allow',\n directive: cleaned,\n tags: doc.frontmatter.tags,\n });\n }\n continue;\n }\n\n // \"Avoid\" → warn\n const warnMatch = cleaned.match(\n /^(avoid|prefer not to|try not to)\\s+(.+)/,\n );\n if (warnMatch) {\n rules.push({\n ruleId: doc.frontmatter.id,\n subject: warnMatch[2].replace(/[.!]$/, ''),\n action: 'warn',\n directive: cleaned,\n tags: doc.frontmatter.tags,\n });\n }\n }\n\n return rules;\n}\n\n/**\n * Load and parse all enforceable rules from a harness directory.\n * Loads all documents from the rules/ directory and extracts structured rules.\n */\nexport function loadRules(harnessDir: string): ParsedRule[] {\n const rulesDir = join(harnessDir, 'rules');\n if (!existsSync(rulesDir)) return [];\n\n const docs = loadDirectory(rulesDir);\n const rules: ParsedRule[] = [];\n\n for (const doc of docs) {\n if (doc.frontmatter.status !== 'active') continue;\n rules.push(...parseRulesFromDoc(doc));\n }\n\n return rules;\n}\n\n// ─── Rule Checking ───────────────────────────────────────────────────────────\n\n/**\n * Check whether an action violates any loaded rules.\n * Uses keyword overlap between the action description/tags and rule subjects.\n *\n * @param rules - Parsed rules from loadRules()\n * @param input - Description of the action being attempted\n * @returns Check result with violations, warnings, and approval requirements\n */\nexport function checkRules(rules: ParsedRule[], input: RuleCheckInput): RuleCheckResult {\n const violations: RuleViolation[] = [];\n const warnings: RuleViolation[] = [];\n let requiresApproval = false;\n\n // Build search text from input\n const searchText = [\n input.action,\n input.description ?? '',\n input.toolName ?? '',\n ...(input.tags ?? []),\n ].join(' ').toLowerCase();\n\n const searchWords = new Set(\n searchText.split(/\\s+/).filter((w) => w.length > 2),\n );\n\n for (const rule of rules) {\n // Check if this rule is relevant to the current action\n const relevance = computeRelevance(rule, searchWords, input);\n if (relevance < 0.3) continue;\n\n if (rule.action === 'deny') {\n violations.push({\n ruleId: rule.ruleId,\n directive: rule.directive,\n severity: 'deny',\n reason: `Action matches denied rule: \"${rule.directive}\" (relevance: ${relevance.toFixed(2)})`,\n });\n } else if (rule.action === 'warn') {\n warnings.push({\n ruleId: rule.ruleId,\n directive: rule.directive,\n severity: 'warn',\n reason: `Action matches warning rule: \"${rule.directive}\" (relevance: ${relevance.toFixed(2)})`,\n });\n } else if (rule.action === 'require_approval') {\n requiresApproval = true;\n violations.push({\n ruleId: rule.ruleId,\n directive: rule.directive,\n severity: 'require_approval',\n reason: `Action requires approval: \"${rule.directive}\" (relevance: ${relevance.toFixed(2)})`,\n });\n }\n }\n\n const denyViolations = violations.filter((v) => v.severity === 'deny');\n const allowed = denyViolations.length === 0 && !requiresApproval;\n\n let summary: string;\n if (allowed && warnings.length === 0) {\n summary = 'All rule checks passed.';\n } else if (allowed) {\n summary = `Allowed with ${warnings.length} warning(s): ${warnings.map((w) => w.directive).join('; ')}`;\n } else if (requiresApproval && denyViolations.length === 0) {\n summary = `Requires human approval: ${violations.filter((v) => v.severity === 'require_approval').map((v) => v.directive).join('; ')}`;\n } else {\n summary = `Blocked by ${denyViolations.length} rule violation(s): ${denyViolations.map((v) => v.directive).join('; ')}`;\n }\n\n return {\n allowed,\n violations,\n warnings,\n requiresApproval,\n summary,\n };\n}\n\n/**\n * Compute relevance score (0–1) between a rule and search context.\n * Uses word overlap between rule subject/tags and input words/tags.\n */\nfunction computeRelevance(\n rule: ParsedRule,\n searchWords: Set<string>,\n input: RuleCheckInput,\n): number {\n // Extract words from rule subject\n const ruleWords = rule.subject\n .toLowerCase()\n .split(/\\s+/)\n .filter((w) => w.length > 2);\n\n if (ruleWords.length === 0) return 0;\n\n // Count word overlap\n let matchCount = 0;\n for (const word of ruleWords) {\n if (searchWords.has(word)) {\n matchCount++;\n }\n }\n\n let score = matchCount / ruleWords.length;\n\n // Tag overlap boost\n if (input.tags && input.tags.length > 0) {\n const inputTags = new Set(input.tags.map((t) => t.toLowerCase()));\n const tagOverlap = rule.tags.filter((t) => inputTags.has(t.toLowerCase())).length;\n if (tagOverlap > 0) {\n score += 0.2 * (tagOverlap / Math.max(rule.tags.length, 1));\n }\n }\n\n return Math.min(score, 1.0);\n}\n\n/**\n * Convenience: load rules from disk and check an action in one call.\n */\nexport function enforceRules(\n harnessDir: string,\n input: RuleCheckInput,\n): RuleCheckResult {\n const rules = loadRules(harnessDir);\n return checkRules(rules, input);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
writeDefaultConfig
|
|
5
5
|
} from "./chunk-EC42HQQH.js";
|
|
6
6
|
import "./chunk-4TQQZILG.js";
|
|
7
|
-
import "./chunk-ZZJOFKAT.js";
|
|
8
7
|
|
|
9
8
|
// src/cli/scaffold.ts
|
|
10
9
|
import { mkdirSync, writeFileSync, readFileSync, readdirSync, existsSync } from "fs";
|
|
@@ -280,8 +279,8 @@ function generateSystemMd(harnessDir, agentName) {
|
|
|
280
279
|
}
|
|
281
280
|
async function generateCoreMd(agentName, purpose, options) {
|
|
282
281
|
try {
|
|
283
|
-
const { generate, getModel } = await import("./provider-
|
|
284
|
-
const { HarnessConfigSchema } = await import("./types-
|
|
282
|
+
const { generate, getModel } = await import("./provider-75AKTYGB.js");
|
|
283
|
+
const { HarnessConfigSchema } = await import("./types-NPJZAI72.js");
|
|
285
284
|
const config = HarnessConfigSchema.parse({
|
|
286
285
|
agent: { name: agentName, version: "0.1.0" },
|
|
287
286
|
model: {
|
|
@@ -344,4 +343,4 @@ export {
|
|
|
344
343
|
listTemplates,
|
|
345
344
|
scaffoldHarness
|
|
346
345
|
};
|
|
347
|
-
//# sourceMappingURL=scaffold-
|
|
346
|
+
//# sourceMappingURL=scaffold-LA54KYKJ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/scaffold.ts"],"sourcesContent":["import { mkdirSync, writeFileSync, readFileSync, readdirSync, existsSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport { writeDefaultConfig } from '../core/config.js';\n\nconst DIRECTORIES = [\n 'rules',\n 'instincts',\n 'skills',\n 'playbooks',\n 'workflows',\n 'tools',\n 'agents',\n 'intake',\n 'memory/sessions',\n 'memory/journal',\n];\n\n/**\n * Resolve the package root directory by walking up from import.meta.url\n * until we find package.json. Works in both dev (src/) and prod (dist/).\n */\nfunction getPackageRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 5; i++) {\n if (existsSync(join(dir, 'package.json'))) return dir;\n dir = dirname(dir);\n }\n // Fallback: assume 2 levels up from source\n return dirname(dirname(fileURLToPath(import.meta.url)));\n}\n\ninterface TemplateVars {\n agentName: string;\n purpose?: string;\n}\n\n/**\n * Apply template variables to a string.\n */\nfunction applyTemplate(content: string, vars: TemplateVars): string {\n const date = new Date().toISOString().split('T')[0];\n const defaultPurpose = `I am ${vars.agentName}, an autonomous AI agent. My purpose is to help my creator build, think, and ship.`;\n const purpose = vars.purpose\n ? `I am ${vars.agentName}. ${vars.purpose}`\n : defaultPurpose;\n return content\n .replace(/\\{\\{AGENT_NAME\\}\\}/g, vars.agentName)\n .replace(/\\{\\{PURPOSE\\}\\}/g, purpose)\n .replace(/\\{\\{DATE\\}\\}/g, date);\n}\n\n/**\n * Copy default primitives from defaults/ directory into the target harness.\n */\nfunction copyDefaults(targetDir: string, vars: TemplateVars): void {\n const defaultsDir = join(getPackageRoot(), 'defaults');\n if (!existsSync(defaultsDir)) return;\n\n const primitiveDirs = ['rules', 'instincts', 'skills', 'playbooks', 'agents', 'tools', 'workflows'];\n for (const dir of primitiveDirs) {\n const srcDir = join(defaultsDir, dir);\n if (!existsSync(srcDir)) continue;\n const files = readdirSync(srcDir).filter((f) => f.endsWith('.md'));\n for (const file of files) {\n const content = readFileSync(join(srcDir, file), 'utf-8');\n writeFileSync(join(targetDir, dir, file), applyTemplate(content, vars), 'utf-8');\n }\n }\n}\n\n/**\n * Load a template file and apply substitutions. Returns null if not found.\n */\nfunction loadTemplate(templateName: string, fileName: string, vars: TemplateVars): string | null {\n const templatePath = join(getPackageRoot(), 'templates', templateName, fileName);\n if (!existsSync(templatePath)) return null;\n return applyTemplate(readFileSync(templatePath, 'utf-8'), vars);\n}\n\nexport interface ScaffoldOptions {\n template?: string;\n /** Custom CORE.md content — overrides template */\n coreContent?: string;\n /** Agent purpose description (stored as comment in CORE.md when no LLM generation) */\n purpose?: string;\n}\n\nexport function scaffoldHarness(targetDir: string, agentName: string, options?: ScaffoldOptions): void {\n if (existsSync(targetDir)) {\n throw new Error(`Directory already exists: ${targetDir}`);\n }\n\n const template = options?.template ?? 'base';\n const vars: TemplateVars = { agentName, purpose: options?.purpose };\n\n // Create directory structure\n mkdirSync(targetDir, { recursive: true });\n for (const dir of DIRECTORIES) {\n mkdirSync(join(targetDir, dir), { recursive: true });\n }\n\n // --- CORE.md (custom > template > inline fallback) ---\n if (options?.coreContent) {\n writeFileSync(join(targetDir, 'CORE.md'), options.coreContent);\n } else {\n const templateContent = loadTemplate(template, 'CORE.md', vars);\n writeFileSync(\n join(targetDir, 'CORE.md'),\n templateContent ?? applyTemplate(`# {{AGENT_NAME}}\n\n## Purpose\n{{PURPOSE}}\n\n## Values\n- **Honesty**: I tell the truth, even when it's uncomfortable.\n- **Action**: I bias toward doing, not discussing.\n- **Autonomy**: I act independently within my boundaries.\n- **Growth**: I learn from every interaction.\n- **Protection**: I guard my creator's time, money, and reputation.\n\n## Ethics\n- I never deceive my creator or others.\n- I never take irreversible actions without confirmation.\n- I never expose secrets, credentials, or private information.\n- I escalate when uncertain rather than guessing.\n`, vars)\n );\n }\n\n // --- SYSTEM.md (from template, or inline fallback) ---\n const systemContent = loadTemplate(template, 'SYSTEM.md', vars);\n writeFileSync(\n join(targetDir, 'SYSTEM.md'),\n systemContent ?? `# System\n\nYou are ${vars.agentName}. This file defines how you boot and operate.\n\n## Boot Sequence\n1. Load CORE.md — your identity (never changes)\n2. Load state.md — where you left off\n3. Load memory/scratch.md — current working memory\n4. Load indexes — scan all primitive directories\n5. Load relevant files based on current task\n\n## File Ownership\n| Owner | Files | Can Modify |\n|-------|-------|------------|\n| Human | CORE.md, rules/*, config.yaml | Only human edits |\n| Agent | instincts/*, memory/sessions/*, state.md (goals) | During/after interactions |\n| Infrastructure | */_index.md, memory/journal/* | Auto-scripts only |\n\n## Context Loading Strategy\n- L0 (~5 tokens): One-line summary — decides relevance\n- L1 (~50-100 tokens): Paragraph — enough to work with\n- L2 (full body): Complete content — loaded only when actively needed\n- Always load CORE + state + scratch first\n- Load primitives at the appropriate level based on token budget\n`\n );\n\n // --- config.yaml (from template, or use writeDefaultConfig) ---\n const configContent = loadTemplate(template, 'config.yaml', vars);\n writeFileSync(join(targetDir, 'config.yaml'), configContent ?? writeDefaultConfig(targetDir, agentName));\n\n // --- state.md ---\n writeFileSync(\n join(targetDir, 'state.md'),\n `# Agent State\n\n## Mode\nidle\n\n## Goals\n\n## Active Workflows\n\n## Last Interaction\n${new Date().toISOString()}\n\n## Unfinished Business\n`\n );\n\n // --- memory/scratch.md ---\n writeFileSync(join(targetDir, 'memory', 'scratch.md'), '');\n\n // --- Copy default primitives from defaults/ directory ---\n copyDefaults(targetDir, vars);\n\n // --- README.md (the in-scaffold quickstart, the FIRST thing a non-coder reads) ---\n writeFileSync(\n join(targetDir, 'README.md'),\n `# ${agentName}\n\nYou just created an agent. The agent IS this folder — every file is part\nof its identity, behavior, knowledge, and memory.\n\n## Try these in order\n\n\\`\\`\\`bash\nharness run \"What can you do?\" # see what's loaded\nharness run \"Help me decide between two options: A or B\"\nharness run \"Plan a weekend project for me\" # watch it qualify before answering\n\\`\\`\\`\n\nUse it for a few days with varied prompts. Then:\n\n\\`\\`\\`bash\nharness journal # synthesize today's sessions and find patterns\nharness learn --install # promote learned patterns into instincts\n\\`\\`\\`\n\nThe agent gets measurably better the more you use it. Every interaction\nis journaled, patterns become instincts, and instincts change behavior\non the next run. **No retraining, no fine-tuning, no code.** You're\nediting markdown.\n\n## What's in this folder\n\n| File / dir | Owner | What it is |\n|---|---|---|\n| \\`CORE.md\\` | human | Identity. Who is this agent? Frozen. |\n| \\`SYSTEM.md\\` | human | Boot instructions. How does it operate? |\n| \\`config.yaml\\` | human | Model, runtime, MCP servers, budgets |\n| \\`state.md\\` | mixed | Live state: mode, goals, last interaction |\n| \\`rules/\\` | human | Hard boundaries the agent must respect |\n| \\`skills/\\` | mixed | Capabilities + how to think about using them |\n| \\`playbooks/\\` | mixed | Adaptive guidance for outcomes |\n| \\`instincts/\\` | agent | Reflexive behaviors learned from sessions |\n| \\`workflows/\\` | infra | Cron-driven automations |\n| \\`tools/\\` | extern | HTTP/API tool definitions |\n| \\`agents/\\` | extern | Sub-agent roster |\n| \\`memory/sessions/\\` | agent | Auto-captured interaction records |\n| \\`memory/journal/\\` | infra | Daily synthesized reflections |\n\nOpen any file and edit it. Save. Run \\`harness run \"...\"\\` again and the\nagent reads your change. That's the loop.\n\n## Going further\n\n\\`\\`\\`bash\nharness doctor # check scaffold health\nharness graph # see how primitives reference each other\nharness info # what's loaded in the context budget right now\nharness mcp discover # find MCP tools already installed on your machine\nharness mcp search <q> # browse the MCP registry for new tools\nharness install <url> # install a skill, agent, or rule from a URL\n\\`\\`\\`\n\nTools come from MCP servers — install one with \\`harness mcp install\\`.\n\n## When something feels off\n\n- \\`harness validate\\` — check the harness structure for errors\n- \\`harness doctor\\` — same, but auto-fix what it can\n- \\`harness contradictions\\` — check rules and instincts for conflicts\n- \\`harness dead-primitives\\` — find files you haven't used in a while\n\nThe agent journal in \\`memory/journal/\\` is the most interesting place\nto look — it's where the agent reflects on what you've been doing\ntogether. Read it once a week.\n`,\n );\n\n // --- .gitignore ---\n writeFileSync(\n join(targetDir, '.gitignore'),\n `memory/scratch.md\nmemory/context.jsonl\nmemory/context.md\nmemory/sessions/*\nmemory/journal/*\n!memory/sessions/.gitkeep\n!memory/journal/.gitkeep\n.env\n`\n );\n\n // Create .gitkeep files\n writeFileSync(join(targetDir, 'memory', 'sessions', '.gitkeep'), '');\n writeFileSync(join(targetDir, 'memory', 'journal', '.gitkeep'), '');\n}\n\n/**\n * Generate SYSTEM.md content from the actual directory structure of a harness.\n * Scans for primitives and reflects the real structure.\n */\nexport function generateSystemMd(harnessDir: string, agentName: string): string {\n const primitiveDirs = ['rules', 'instincts', 'skills', 'playbooks', 'workflows', 'tools', 'agents'];\n const sections: string[] = [];\n\n sections.push(`# System\\n`);\n sections.push(`You are ${agentName}. This file defines how you boot and operate.\\n`);\n\n // Boot sequence\n sections.push(`## Boot Sequence\n1. Load CORE.md — your identity (never changes)\n2. Load state.md — where you left off\n3. Load memory/scratch.md — current working memory\n4. Load indexes — scan all primitive directories\n5. Load relevant files based on current task\\n`);\n\n // Directory structure\n sections.push(`## Directory Structure\\n`);\n\n for (const dir of primitiveDirs) {\n const dirPath = join(harnessDir, dir);\n if (!existsSync(dirPath)) continue;\n\n const files = readdirSync(dirPath).filter((f) => f.endsWith('.md') && !f.startsWith('_'));\n if (files.length === 0) {\n sections.push(`- \\`${dir}/\\` — (empty)`);\n } else {\n sections.push(`- \\`${dir}/\\` — ${files.length} file(s): ${files.map((f) => f.replace('.md', '')).join(', ')}`);\n }\n }\n\n // Memory\n const sessionsDir = join(harnessDir, 'memory', 'sessions');\n const journalDir = join(harnessDir, 'memory', 'journal');\n const sessionCount = existsSync(sessionsDir)\n ? readdirSync(sessionsDir).filter((f) => f.endsWith('.md')).length\n : 0;\n const journalCount = existsSync(journalDir)\n ? readdirSync(journalDir).filter((f) => f.endsWith('.md')).length\n : 0;\n\n sections.push(`- \\`memory/sessions/\\` — ${sessionCount} session(s)`);\n sections.push(`- \\`memory/journal/\\` — ${journalCount} entry/entries`);\n sections.push('');\n\n // File ownership\n sections.push(`## File Ownership\n| Owner | Files | Can Modify |\n|-------|-------|------------|\n| Human | CORE.md, rules/*, config.yaml | Only human edits |\n| Agent | instincts/*, memory/sessions/*, state.md (goals) | During/after interactions |\n| Infrastructure | */_index.md, memory/journal/* | Auto-scripts only |\\n`);\n\n // Context loading strategy\n sections.push(`## Context Loading Strategy\n- L0 (~5 tokens): One-line summary — decides relevance\n- L1 (~50-100 tokens): Paragraph — enough to work with\n- L2 (full body): Complete content — loaded only when actively needed\n- Always load CORE + state + scratch first\n- Load primitives at the appropriate level based on token budget\n`);\n\n return sections.join('\\n');\n}\n\n/**\n * Generate a rich CORE.md using an LLM, given an agent name and purpose description.\n * Returns the generated markdown content, or throws on failure.\n */\nexport async function generateCoreMd(\n agentName: string,\n purpose: string,\n options: { provider?: string; modelId?: string; apiKey?: string },\n): Promise<string> {\n try {\n const { generate, getModel } = await import('../llm/provider.js');\n const { HarnessConfigSchema } = await import('../core/types.js');\n\n const config = HarnessConfigSchema.parse({\n agent: { name: agentName, version: '0.1.0' },\n model: {\n provider: options.provider ?? 'openrouter',\n id: options.modelId ?? 'anthropic/claude-sonnet-4',\n },\n });\n\n const model = getModel(config, options.apiKey);\n const result = await generate({\n model,\n system: `You are a technical writer creating an identity document for an AI agent.\nThe document defines who the agent is, what it does, its values, and its ethical boundaries.\nWrite in first person from the agent's perspective. Be specific and practical, not generic.\nOutput ONLY the markdown content, no code fences.`,\n prompt: `Create a CORE.md identity document for an AI agent with:\n- Name: ${agentName}\n- Purpose: ${purpose}\n\nThe document should have these sections:\n# ${agentName}\n\n## Purpose\n(Detailed purpose based on the description — be specific to what this agent does)\n\n## Values\n(5-7 values tailored to this agent's purpose — not generic platitudes)\n\n## Ethics\n(4-6 ethical boundaries specific to this agent's domain)\n\n## Capabilities\n(3-5 key capabilities this agent should have based on its purpose)\n\n## Boundaries\n(3-5 things this agent should NOT do or areas where it should escalate)`,\n maxOutputTokens: 2000,\n maxRetries: 1,\n timeoutMs: 30000,\n });\n\n return result.text.trim();\n } catch (err: unknown) {\n if (err instanceof Error) throw err;\n throw new Error(String(err));\n }\n}\n\n/**\n * List available templates.\n */\nexport function listTemplates(): string[] {\n const templatesDir = join(getPackageRoot(), 'templates');\n if (!existsSync(templatesDir)) return [];\n return readdirSync(templatesDir).filter((f) => {\n try {\n return readdirSync(join(templatesDir, f)).length > 0;\n } catch {\n return false;\n }\n });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/scaffold.ts"],"sourcesContent":["import { mkdirSync, writeFileSync, readFileSync, readdirSync, existsSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport { writeDefaultConfig } from '../core/config.js';\n\nconst DIRECTORIES = [\n 'rules',\n 'instincts',\n 'skills',\n 'playbooks',\n 'workflows',\n 'tools',\n 'agents',\n 'intake',\n 'memory/sessions',\n 'memory/journal',\n];\n\n/**\n * Resolve the package root directory by walking up from import.meta.url\n * until we find package.json. Works in both dev (src/) and prod (dist/).\n */\nfunction getPackageRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 5; i++) {\n if (existsSync(join(dir, 'package.json'))) return dir;\n dir = dirname(dir);\n }\n // Fallback: assume 2 levels up from source\n return dirname(dirname(fileURLToPath(import.meta.url)));\n}\n\ninterface TemplateVars {\n agentName: string;\n purpose?: string;\n}\n\n/**\n * Apply template variables to a string.\n */\nfunction applyTemplate(content: string, vars: TemplateVars): string {\n const date = new Date().toISOString().split('T')[0];\n const defaultPurpose = `I am ${vars.agentName}, an autonomous AI agent. My purpose is to help my creator build, think, and ship.`;\n const purpose = vars.purpose\n ? `I am ${vars.agentName}. ${vars.purpose}`\n : defaultPurpose;\n return content\n .replace(/\\{\\{AGENT_NAME\\}\\}/g, vars.agentName)\n .replace(/\\{\\{PURPOSE\\}\\}/g, purpose)\n .replace(/\\{\\{DATE\\}\\}/g, date);\n}\n\n/**\n * Copy default primitives from defaults/ directory into the target harness.\n */\nfunction copyDefaults(targetDir: string, vars: TemplateVars): void {\n const defaultsDir = join(getPackageRoot(), 'defaults');\n if (!existsSync(defaultsDir)) return;\n\n const primitiveDirs = ['rules', 'instincts', 'skills', 'playbooks', 'agents', 'tools', 'workflows'];\n for (const dir of primitiveDirs) {\n const srcDir = join(defaultsDir, dir);\n if (!existsSync(srcDir)) continue;\n const files = readdirSync(srcDir).filter((f) => f.endsWith('.md'));\n for (const file of files) {\n const content = readFileSync(join(srcDir, file), 'utf-8');\n writeFileSync(join(targetDir, dir, file), applyTemplate(content, vars), 'utf-8');\n }\n }\n}\n\n/**\n * Load a template file and apply substitutions. Returns null if not found.\n */\nfunction loadTemplate(templateName: string, fileName: string, vars: TemplateVars): string | null {\n const templatePath = join(getPackageRoot(), 'templates', templateName, fileName);\n if (!existsSync(templatePath)) return null;\n return applyTemplate(readFileSync(templatePath, 'utf-8'), vars);\n}\n\nexport interface ScaffoldOptions {\n template?: string;\n /** Custom CORE.md content — overrides template */\n coreContent?: string;\n /** Agent purpose description (stored as comment in CORE.md when no LLM generation) */\n purpose?: string;\n}\n\nexport function scaffoldHarness(targetDir: string, agentName: string, options?: ScaffoldOptions): void {\n if (existsSync(targetDir)) {\n throw new Error(`Directory already exists: ${targetDir}`);\n }\n\n const template = options?.template ?? 'base';\n const vars: TemplateVars = { agentName, purpose: options?.purpose };\n\n // Create directory structure\n mkdirSync(targetDir, { recursive: true });\n for (const dir of DIRECTORIES) {\n mkdirSync(join(targetDir, dir), { recursive: true });\n }\n\n // --- CORE.md (custom > template > inline fallback) ---\n if (options?.coreContent) {\n writeFileSync(join(targetDir, 'CORE.md'), options.coreContent);\n } else {\n const templateContent = loadTemplate(template, 'CORE.md', vars);\n writeFileSync(\n join(targetDir, 'CORE.md'),\n templateContent ?? applyTemplate(`# {{AGENT_NAME}}\n\n## Purpose\n{{PURPOSE}}\n\n## Values\n- **Honesty**: I tell the truth, even when it's uncomfortable.\n- **Action**: I bias toward doing, not discussing.\n- **Autonomy**: I act independently within my boundaries.\n- **Growth**: I learn from every interaction.\n- **Protection**: I guard my creator's time, money, and reputation.\n\n## Ethics\n- I never deceive my creator or others.\n- I never take irreversible actions without confirmation.\n- I never expose secrets, credentials, or private information.\n- I escalate when uncertain rather than guessing.\n`, vars)\n );\n }\n\n // --- SYSTEM.md (from template, or inline fallback) ---\n const systemContent = loadTemplate(template, 'SYSTEM.md', vars);\n writeFileSync(\n join(targetDir, 'SYSTEM.md'),\n systemContent ?? `# System\n\nYou are ${vars.agentName}. This file defines how you boot and operate.\n\n## Boot Sequence\n1. Load CORE.md — your identity (never changes)\n2. Load state.md — where you left off\n3. Load memory/scratch.md — current working memory\n4. Load indexes — scan all primitive directories\n5. Load relevant files based on current task\n\n## File Ownership\n| Owner | Files | Can Modify |\n|-------|-------|------------|\n| Human | CORE.md, rules/*, config.yaml | Only human edits |\n| Agent | instincts/*, memory/sessions/*, state.md (goals) | During/after interactions |\n| Infrastructure | */_index.md, memory/journal/* | Auto-scripts only |\n\n## Context Loading Strategy\n- L0 (~5 tokens): One-line summary — decides relevance\n- L1 (~50-100 tokens): Paragraph — enough to work with\n- L2 (full body): Complete content — loaded only when actively needed\n- Always load CORE + state + scratch first\n- Load primitives at the appropriate level based on token budget\n`\n );\n\n // --- config.yaml (from template, or use writeDefaultConfig) ---\n const configContent = loadTemplate(template, 'config.yaml', vars);\n writeFileSync(join(targetDir, 'config.yaml'), configContent ?? writeDefaultConfig(targetDir, agentName));\n\n // --- state.md ---\n writeFileSync(\n join(targetDir, 'state.md'),\n `# Agent State\n\n## Mode\nidle\n\n## Goals\n\n## Active Workflows\n\n## Last Interaction\n${new Date().toISOString()}\n\n## Unfinished Business\n`\n );\n\n // --- memory/scratch.md ---\n writeFileSync(join(targetDir, 'memory', 'scratch.md'), '');\n\n // --- Copy default primitives from defaults/ directory ---\n copyDefaults(targetDir, vars);\n\n // --- README.md (the in-scaffold quickstart, the FIRST thing a non-coder reads) ---\n writeFileSync(\n join(targetDir, 'README.md'),\n `# ${agentName}\n\nYou just created an agent. The agent IS this folder — every file is part\nof its identity, behavior, knowledge, and memory.\n\n## Try these in order\n\n\\`\\`\\`bash\nharness run \"What can you do?\" # see what's loaded\nharness run \"Help me decide between two options: A or B\"\nharness run \"Plan a weekend project for me\" # watch it qualify before answering\n\\`\\`\\`\n\nUse it for a few days with varied prompts. Then:\n\n\\`\\`\\`bash\nharness journal # synthesize today's sessions and find patterns\nharness learn --install # promote learned patterns into instincts\n\\`\\`\\`\n\nThe agent gets measurably better the more you use it. Every interaction\nis journaled, patterns become instincts, and instincts change behavior\non the next run. **No retraining, no fine-tuning, no code.** You're\nediting markdown.\n\n## What's in this folder\n\n| File / dir | Owner | What it is |\n|---|---|---|\n| \\`CORE.md\\` | human | Identity. Who is this agent? Frozen. |\n| \\`SYSTEM.md\\` | human | Boot instructions. How does it operate? |\n| \\`config.yaml\\` | human | Model, runtime, MCP servers, budgets |\n| \\`state.md\\` | mixed | Live state: mode, goals, last interaction |\n| \\`rules/\\` | human | Hard boundaries the agent must respect |\n| \\`skills/\\` | mixed | Capabilities + how to think about using them |\n| \\`playbooks/\\` | mixed | Adaptive guidance for outcomes |\n| \\`instincts/\\` | agent | Reflexive behaviors learned from sessions |\n| \\`workflows/\\` | infra | Cron-driven automations |\n| \\`tools/\\` | extern | HTTP/API tool definitions |\n| \\`agents/\\` | extern | Sub-agent roster |\n| \\`memory/sessions/\\` | agent | Auto-captured interaction records |\n| \\`memory/journal/\\` | infra | Daily synthesized reflections |\n\nOpen any file and edit it. Save. Run \\`harness run \"...\"\\` again and the\nagent reads your change. That's the loop.\n\n## Going further\n\n\\`\\`\\`bash\nharness doctor # check scaffold health\nharness graph # see how primitives reference each other\nharness info # what's loaded in the context budget right now\nharness mcp discover # find MCP tools already installed on your machine\nharness mcp search <q> # browse the MCP registry for new tools\nharness install <url> # install a skill, agent, or rule from a URL\n\\`\\`\\`\n\nTools come from MCP servers — install one with \\`harness mcp install\\`.\n\n## When something feels off\n\n- \\`harness validate\\` — check the harness structure for errors\n- \\`harness doctor\\` — same, but auto-fix what it can\n- \\`harness contradictions\\` — check rules and instincts for conflicts\n- \\`harness dead-primitives\\` — find files you haven't used in a while\n\nThe agent journal in \\`memory/journal/\\` is the most interesting place\nto look — it's where the agent reflects on what you've been doing\ntogether. Read it once a week.\n`,\n );\n\n // --- .gitignore ---\n writeFileSync(\n join(targetDir, '.gitignore'),\n `memory/scratch.md\nmemory/context.jsonl\nmemory/context.md\nmemory/sessions/*\nmemory/journal/*\n!memory/sessions/.gitkeep\n!memory/journal/.gitkeep\n.env\n`\n );\n\n // Create .gitkeep files\n writeFileSync(join(targetDir, 'memory', 'sessions', '.gitkeep'), '');\n writeFileSync(join(targetDir, 'memory', 'journal', '.gitkeep'), '');\n}\n\n/**\n * Generate SYSTEM.md content from the actual directory structure of a harness.\n * Scans for primitives and reflects the real structure.\n */\nexport function generateSystemMd(harnessDir: string, agentName: string): string {\n const primitiveDirs = ['rules', 'instincts', 'skills', 'playbooks', 'workflows', 'tools', 'agents'];\n const sections: string[] = [];\n\n sections.push(`# System\\n`);\n sections.push(`You are ${agentName}. This file defines how you boot and operate.\\n`);\n\n // Boot sequence\n sections.push(`## Boot Sequence\n1. Load CORE.md — your identity (never changes)\n2. Load state.md — where you left off\n3. Load memory/scratch.md — current working memory\n4. Load indexes — scan all primitive directories\n5. Load relevant files based on current task\\n`);\n\n // Directory structure\n sections.push(`## Directory Structure\\n`);\n\n for (const dir of primitiveDirs) {\n const dirPath = join(harnessDir, dir);\n if (!existsSync(dirPath)) continue;\n\n const files = readdirSync(dirPath).filter((f) => f.endsWith('.md') && !f.startsWith('_'));\n if (files.length === 0) {\n sections.push(`- \\`${dir}/\\` — (empty)`);\n } else {\n sections.push(`- \\`${dir}/\\` — ${files.length} file(s): ${files.map((f) => f.replace('.md', '')).join(', ')}`);\n }\n }\n\n // Memory\n const sessionsDir = join(harnessDir, 'memory', 'sessions');\n const journalDir = join(harnessDir, 'memory', 'journal');\n const sessionCount = existsSync(sessionsDir)\n ? readdirSync(sessionsDir).filter((f) => f.endsWith('.md')).length\n : 0;\n const journalCount = existsSync(journalDir)\n ? readdirSync(journalDir).filter((f) => f.endsWith('.md')).length\n : 0;\n\n sections.push(`- \\`memory/sessions/\\` — ${sessionCount} session(s)`);\n sections.push(`- \\`memory/journal/\\` — ${journalCount} entry/entries`);\n sections.push('');\n\n // File ownership\n sections.push(`## File Ownership\n| Owner | Files | Can Modify |\n|-------|-------|------------|\n| Human | CORE.md, rules/*, config.yaml | Only human edits |\n| Agent | instincts/*, memory/sessions/*, state.md (goals) | During/after interactions |\n| Infrastructure | */_index.md, memory/journal/* | Auto-scripts only |\\n`);\n\n // Context loading strategy\n sections.push(`## Context Loading Strategy\n- L0 (~5 tokens): One-line summary — decides relevance\n- L1 (~50-100 tokens): Paragraph — enough to work with\n- L2 (full body): Complete content — loaded only when actively needed\n- Always load CORE + state + scratch first\n- Load primitives at the appropriate level based on token budget\n`);\n\n return sections.join('\\n');\n}\n\n/**\n * Generate a rich CORE.md using an LLM, given an agent name and purpose description.\n * Returns the generated markdown content, or throws on failure.\n */\nexport async function generateCoreMd(\n agentName: string,\n purpose: string,\n options: { provider?: string; modelId?: string; apiKey?: string },\n): Promise<string> {\n try {\n const { generate, getModel } = await import('../llm/provider.js');\n const { HarnessConfigSchema } = await import('../core/types.js');\n\n const config = HarnessConfigSchema.parse({\n agent: { name: agentName, version: '0.1.0' },\n model: {\n provider: options.provider ?? 'openrouter',\n id: options.modelId ?? 'anthropic/claude-sonnet-4',\n },\n });\n\n const model = getModel(config, options.apiKey);\n const result = await generate({\n model,\n system: `You are a technical writer creating an identity document for an AI agent.\nThe document defines who the agent is, what it does, its values, and its ethical boundaries.\nWrite in first person from the agent's perspective. Be specific and practical, not generic.\nOutput ONLY the markdown content, no code fences.`,\n prompt: `Create a CORE.md identity document for an AI agent with:\n- Name: ${agentName}\n- Purpose: ${purpose}\n\nThe document should have these sections:\n# ${agentName}\n\n## Purpose\n(Detailed purpose based on the description — be specific to what this agent does)\n\n## Values\n(5-7 values tailored to this agent's purpose — not generic platitudes)\n\n## Ethics\n(4-6 ethical boundaries specific to this agent's domain)\n\n## Capabilities\n(3-5 key capabilities this agent should have based on its purpose)\n\n## Boundaries\n(3-5 things this agent should NOT do or areas where it should escalate)`,\n maxOutputTokens: 2000,\n maxRetries: 1,\n timeoutMs: 30000,\n });\n\n return result.text.trim();\n } catch (err: unknown) {\n if (err instanceof Error) throw err;\n throw new Error(String(err));\n }\n}\n\n/**\n * List available templates.\n */\nexport function listTemplates(): string[] {\n const templatesDir = join(getPackageRoot(), 'templates');\n if (!existsSync(templatesDir)) return [];\n return readdirSync(templatesDir).filter((f) => {\n try {\n return readdirSync(join(templatesDir, f)).length > 0;\n } catch {\n return false;\n }\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,22 +3,22 @@
|
|
|
3
3
|
import {
|
|
4
4
|
listUnjournaled,
|
|
5
5
|
synthesizeJournal
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-Z4HEHWFM.js";
|
|
7
7
|
import {
|
|
8
8
|
learnFromSessions
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-LBT43BZA.js";
|
|
10
10
|
import {
|
|
11
11
|
recordRun
|
|
12
12
|
} from "./chunk-6EMOEYGU.js";
|
|
13
13
|
import {
|
|
14
14
|
createHarness
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-LACZLSC4.js";
|
|
16
16
|
import "./chunk-AGAAFJEO.js";
|
|
17
17
|
import "./chunk-5H34JPMB.js";
|
|
18
18
|
import {
|
|
19
19
|
delegateTo
|
|
20
|
-
} from "./chunk-
|
|
21
|
-
import "./chunk-
|
|
20
|
+
} from "./chunk-QU566LZE.js";
|
|
21
|
+
import "./chunk-5CO5JTYT.js";
|
|
22
22
|
import "./chunk-7GZ4D6V6.js";
|
|
23
23
|
import "./chunk-UDZIS2AQ.js";
|
|
24
24
|
import {
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
} from "./chunk-TAT6JU3X.js";
|
|
32
32
|
import "./chunk-JKMGYWXB.js";
|
|
33
33
|
import "./chunk-OC6YSTDX.js";
|
|
34
|
-
import "./chunk-
|
|
34
|
+
import "./chunk-PMFAYKBD.js";
|
|
35
35
|
import {
|
|
36
36
|
loadDirectory,
|
|
37
37
|
parseHarnessDocument
|
|
@@ -39,12 +39,11 @@ import {
|
|
|
39
39
|
import {
|
|
40
40
|
log
|
|
41
41
|
} from "./chunk-BSKDOFRT.js";
|
|
42
|
-
import "./chunk-
|
|
42
|
+
import "./chunk-WCYBFALM.js";
|
|
43
43
|
import {
|
|
44
44
|
loadConfig
|
|
45
45
|
} from "./chunk-EC42HQQH.js";
|
|
46
46
|
import "./chunk-4TQQZILG.js";
|
|
47
|
-
import "./chunk-ZZJOFKAT.js";
|
|
48
47
|
|
|
49
48
|
// src/runtime/scheduler.ts
|
|
50
49
|
import cron from "node-cron";
|
|
@@ -394,4 +393,4 @@ export {
|
|
|
394
393
|
Scheduler,
|
|
395
394
|
isQuietHours
|
|
396
395
|
};
|
|
397
|
-
//# sourceMappingURL=scheduler-
|
|
396
|
+
//# sourceMappingURL=scheduler-MFBVGFZQ.js.map
|