@standards-kit/conform 0.1.0

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.
Files changed (71) hide show
  1. package/dist/artifactregistry-QQWBMEQN.js +38 -0
  2. package/dist/artifactregistry-QQWBMEQN.js.map +1 -0
  3. package/dist/chunk-J5S6GRGW.js +314 -0
  4. package/dist/chunk-J5S6GRGW.js.map +1 -0
  5. package/dist/chunk-KHO6NIAI.js +1367 -0
  6. package/dist/chunk-KHO6NIAI.js.map +1 -0
  7. package/dist/chunk-M7G73Q6P.js +662 -0
  8. package/dist/chunk-M7G73Q6P.js.map +1 -0
  9. package/dist/chunk-P7TIZJ4C.js +85 -0
  10. package/dist/chunk-P7TIZJ4C.js.map +1 -0
  11. package/dist/chunk-RXA4FO7L.js +279 -0
  12. package/dist/chunk-RXA4FO7L.js.map +1 -0
  13. package/dist/cli.js +7432 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/cloudrun-O36R23SH.js +31 -0
  16. package/dist/cloudrun-O36R23SH.js.map +1 -0
  17. package/dist/cloudwatch-KSZ4A256.js +56 -0
  18. package/dist/cloudwatch-KSZ4A256.js.map +1 -0
  19. package/dist/dynamodb-5KVESCVJ.js +51 -0
  20. package/dist/dynamodb-5KVESCVJ.js.map +1 -0
  21. package/dist/ec2-HKPE6GZV.js +151 -0
  22. package/dist/ec2-HKPE6GZV.js.map +1 -0
  23. package/dist/ecs-OS3NJZTA.js +141 -0
  24. package/dist/ecs-OS3NJZTA.js.map +1 -0
  25. package/dist/elasticache-7TCRHYYM.js +151 -0
  26. package/dist/elasticache-7TCRHYYM.js.map +1 -0
  27. package/dist/elb-PEDLXW5R.js +151 -0
  28. package/dist/elb-PEDLXW5R.js.map +1 -0
  29. package/dist/generate-D4MFMOHP.js +28 -0
  30. package/dist/generate-D4MFMOHP.js.map +1 -0
  31. package/dist/iam-7H5HFWVQ.js +96 -0
  32. package/dist/iam-7H5HFWVQ.js.map +1 -0
  33. package/dist/iam-DJI64AGK.js +39 -0
  34. package/dist/iam-DJI64AGK.js.map +1 -0
  35. package/dist/index.js +7980 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/infra-UXM5XQX3.js +566 -0
  38. package/dist/infra-UXM5XQX3.js.map +1 -0
  39. package/dist/lambda-NFB5UILT.js +60 -0
  40. package/dist/lambda-NFB5UILT.js.map +1 -0
  41. package/dist/manifest-7AIL2FK2.js +23 -0
  42. package/dist/manifest-7AIL2FK2.js.map +1 -0
  43. package/dist/mcp-O5O7XVFG.js +204 -0
  44. package/dist/mcp-O5O7XVFG.js.map +1 -0
  45. package/dist/rds-KLG5O5SI.js +151 -0
  46. package/dist/rds-KLG5O5SI.js.map +1 -0
  47. package/dist/registry-V65CC7IN.js +15 -0
  48. package/dist/registry-V65CC7IN.js.map +1 -0
  49. package/dist/s3-2DH7PRVR.js +49 -0
  50. package/dist/s3-2DH7PRVR.js.map +1 -0
  51. package/dist/scan-EELS42BP.js +593 -0
  52. package/dist/scan-EELS42BP.js.map +1 -0
  53. package/dist/secretmanager-RDL62EFW.js +31 -0
  54. package/dist/secretmanager-RDL62EFW.js.map +1 -0
  55. package/dist/secretsmanager-MOOIHLAO.js +50 -0
  56. package/dist/secretsmanager-MOOIHLAO.js.map +1 -0
  57. package/dist/sns-Y36LVTWA.js +50 -0
  58. package/dist/sns-Y36LVTWA.js.map +1 -0
  59. package/dist/sqs-RRS3GRHK.js +61 -0
  60. package/dist/sqs-RRS3GRHK.js.map +1 -0
  61. package/dist/src-KZRTG3EU.js +45 -0
  62. package/dist/src-KZRTG3EU.js.map +1 -0
  63. package/dist/standards-RXK5G4IG.js +37 -0
  64. package/dist/standards-RXK5G4IG.js.map +1 -0
  65. package/dist/sync-RLYBGYNY.js +877 -0
  66. package/dist/sync-RLYBGYNY.js.map +1 -0
  67. package/dist/validate-AABLVQJS.js +327 -0
  68. package/dist/validate-AABLVQJS.js.map +1 -0
  69. package/dist/validator-6PL5I5EC.js +156 -0
  70. package/dist/validator-6PL5I5EC.js.map +1 -0
  71. package/package.json +91 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/infra/manifest.ts","../src/infra/arn.ts","../src/infra/gcp.ts","../src/infra/schemas.ts"],"sourcesContent":["/**\n * Manifest reader for infra scan\n *\n * Supports two formats:\n * 1. JSON: { \"project\": \"...\", \"resources\": [\"arn:...\", \"projects/...\"] }\n * 2. TXT: One resource per line, # for comments\n *\n * Resources can be:\n * - AWS ARNs: arn:aws:s3:::bucket-name\n * - GCP paths: projects/{project}/locations/{location}/services/{service}\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { isValidArn } from \"./arn.js\";\nimport { isValidGcpResource } from \"./gcp.js\";\nimport {\n isValidAccountKey,\n isLegacyManifestSchema,\n isMultiAccountManifestSchema,\n validateLegacyManifest,\n validateMultiAccountManifest,\n type AccountId,\n type LegacyManifest,\n type Manifest,\n type ManifestAccount,\n type MultiAccountManifest,\n} from \"./types.js\";\n\n/**\n * Check if a resource identifier is valid (AWS ARN or GCP path)\n */\nfunction isValidResource(resource: string): boolean {\n return isValidArn(resource) || isValidGcpResource(resource);\n}\n\n/**\n * Error thrown when manifest parsing fails\n */\nexport class ManifestError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ManifestError\";\n }\n}\n\n/**\n * Type guard: check if manifest is multi-account format (v2)\n */\nexport function isMultiAccountManifest(manifest: Manifest): manifest is MultiAccountManifest {\n return \"accounts\" in manifest && typeof manifest.accounts === \"object\";\n}\n\n/**\n * Type guard: check if manifest is legacy format (v1)\n */\nexport function isLegacyManifest(manifest: Manifest): manifest is LegacyManifest {\n return \"resources\" in manifest && Array.isArray(manifest.resources);\n}\n\n/**\n * Parse an account key (e.g., \"aws:111111111111\" or \"gcp:my-project\")\n *\n * @param key - The account key in format \"cloud:id\"\n * @returns Parsed AccountId or null if invalid\n */\nexport function parseAccountKey(key: string): AccountId | null {\n // Use schema validation first\n if (!isValidAccountKey(key)) {\n return null;\n }\n\n // Extract components (we know the format is valid from schema check)\n const colonIndex = key.indexOf(\":\");\n return {\n cloud: key.substring(0, colonIndex) as \"aws\" | \"gcp\",\n id: key.substring(colonIndex + 1),\n };\n}\n\n/**\n * Format an account key from cloud and id\n */\nexport function formatAccountKey(cloud: \"aws\" | \"gcp\", id: string): string {\n return `${cloud}:${id}`;\n}\n\n/**\n * Normalize a legacy manifest to multi-account format\n * This converts v1 manifests to v2 format for unified processing\n */\nexport function normalizeManifest(manifest: Manifest): MultiAccountManifest {\n if (isMultiAccountManifest(manifest)) {\n return manifest;\n }\n\n // Group resources by detected account\n const accounts: Record<string, ManifestAccount> = {};\n\n for (const resource of manifest.resources) {\n const accountKey = detectAccountFromResource(resource);\n if (accountKey in accounts) {\n accounts[accountKey].resources.push(resource);\n } else {\n accounts[accountKey] = { resources: [resource] };\n }\n }\n\n return {\n version: 2,\n project: manifest.project,\n accounts,\n };\n}\n\n/**\n * Detect the account key from a resource identifier\n * Extracts AWS account ID from ARN or GCP project from resource path\n */\nexport function detectAccountFromResource(resource: string): string {\n // Check for AWS ARN: arn:partition:service:region:account:resource\n if (resource.startsWith(\"arn:\")) {\n const parts = resource.split(\":\");\n if (parts.length >= 5) {\n const accountId = parts[4];\n // Some AWS resources (like S3 buckets) don't have account ID in the ARN\n if (accountId) {\n return formatAccountKey(\"aws\", accountId);\n }\n // For S3 buckets without account ID, use a placeholder\n return \"aws:unknown\";\n }\n }\n\n // Check for GCP resource path: projects/{project}/...\n const gcpRegex = /^projects\\/([^/]+)\\//;\n const gcpMatch = gcpRegex.exec(resource);\n if (gcpMatch) {\n return formatAccountKey(\"gcp\", gcpMatch[1]);\n }\n\n return \"unknown:unknown\";\n}\n\n/**\n * Get all resources from a manifest (flattened for v2 manifests)\n */\nexport function getAllResources(manifest: Manifest): string[] {\n if (isMultiAccountManifest(manifest)) {\n return Object.values(manifest.accounts).flatMap((account) => account.resources);\n }\n return manifest.resources;\n}\n\n/**\n * Read and parse a manifest file\n *\n * @param manifestPath - Path to the manifest file\n * @returns Parsed manifest with project name and resource ARNs\n */\nexport function readManifest(manifestPath: string): Manifest {\n if (!fs.existsSync(manifestPath)) {\n throw new ManifestError(`Manifest file not found: ${manifestPath}`);\n }\n\n const content = fs.readFileSync(manifestPath, \"utf-8\");\n const ext = path.extname(manifestPath).toLowerCase();\n\n if (ext === \".json\") {\n return parseJsonManifest(content, manifestPath);\n }\n\n if (ext === \".txt\") {\n return parseTxtManifest(content, manifestPath);\n }\n\n // Try JSON first, then TXT\n try {\n return parseJsonManifest(content, manifestPath);\n } catch {\n return parseTxtManifest(content, manifestPath);\n }\n}\n\n/**\n * Parse a JSON format manifest using Zod schema validation\n */\nfunction parseJsonManifest(content: string, manifestPath: string): Manifest {\n const data = parseJsonContent(content, manifestPath);\n\n // First validate basic structure\n if (!data || typeof data !== \"object\") {\n throw new ManifestError(`Manifest ${manifestPath} must be a JSON object`);\n }\n\n // Try multi-account (v2) format first using Zod schema\n if (isMultiAccountManifestSchema(data)) {\n return validateMultiAccountManifestWithResources(data, manifestPath);\n }\n\n // Try legacy (v1) format using Zod schema\n if (isLegacyManifestSchema(data)) {\n return validateLegacyManifestWithResources(data, manifestPath);\n }\n\n // Fallback to manual validation for better error messages\n return parseFallbackManifest(data as Record<string, unknown>, manifestPath);\n}\n\n/**\n * Fallback parser for manifests that don't match Zod schemas\n */\nfunction parseFallbackManifest(obj: Record<string, unknown>, manifestPath: string): Manifest {\n if (\"accounts\" in obj) {\n return parseMultiAccountManifestFallback(obj, manifestPath);\n }\n\n validateJsonStructure(obj, manifestPath);\n const resources = extractAndValidateResources(obj.resources as unknown[], manifestPath);\n const project = typeof obj.project === \"string\" ? obj.project : undefined;\n\n return { project, resources };\n}\n\n/**\n * Validate multi-account manifest and its resources\n */\nfunction validateMultiAccountManifestWithResources(\n data: unknown,\n manifestPath: string\n): MultiAccountManifest {\n try {\n const manifest = validateMultiAccountManifest(data);\n\n // Validate account keys and resources\n for (const [accountKey, account] of Object.entries(manifest.accounts)) {\n // Validate account key format (must be \"aws:xxx\" or \"gcp:xxx\")\n if (!isValidAccountKey(accountKey)) {\n throw new ManifestError(\n `Manifest ${manifestPath} has invalid account key: \"${accountKey}\". Expected format: \"aws:<account-id>\" or \"gcp:<project-id>\"`\n );\n }\n\n // Validate each resource is a valid ARN or GCP path\n const invalidResources = account.resources.filter((r) => !isValidResource(r));\n if (invalidResources.length > 0) {\n throw new ManifestError(\n `Manifest ${manifestPath} account \"${accountKey}\" contains invalid resources: ${invalidResources.join(\", \")}`\n );\n }\n }\n\n return manifest;\n } catch (error) {\n if (error instanceof ManifestError) {\n throw error;\n }\n // Convert Zod errors to ManifestError\n const message = error instanceof Error ? error.message : \"Unknown error\";\n throw new ManifestError(`Invalid manifest ${manifestPath}: ${message}`);\n }\n}\n\n/**\n * Validate legacy manifest and its resources\n */\nfunction validateLegacyManifestWithResources(\n data: unknown,\n manifestPath: string\n): LegacyManifest {\n try {\n const manifest = validateLegacyManifest(data);\n\n // Additionally validate each resource is a valid ARN or GCP path\n const invalidResources = manifest.resources.filter((r) => !isValidResource(r));\n if (invalidResources.length > 0) {\n throw new ManifestError(\n `Manifest ${manifestPath} contains invalid resources: ${invalidResources.join(\", \")}`\n );\n }\n\n return manifest;\n } catch (error) {\n if (error instanceof ManifestError) {\n throw error;\n }\n // Convert Zod errors to ManifestError\n const message = error instanceof Error ? error.message : \"Unknown error\";\n throw new ManifestError(`Invalid manifest ${manifestPath}: ${message}`);\n }\n}\n\n/**\n * Fallback parser for multi-account manifest with detailed error messages\n */\nfunction parseMultiAccountManifestFallback(\n obj: Record<string, unknown>,\n manifestPath: string\n): MultiAccountManifest {\n const accountsRaw = obj.accounts as Record<string, unknown>;\n const accounts: Record<string, ManifestAccount> = {};\n\n for (const [key, value] of Object.entries(accountsRaw)) {\n accounts[key] = parseAccountEntry(key, value, manifestPath);\n }\n\n const project = typeof obj.project === \"string\" ? obj.project : undefined;\n\n return { version: 2, project, accounts };\n}\n\n/**\n * Validate and parse a single account entry from manifest\n */\nfunction parseAccountEntry(\n key: string,\n value: unknown,\n manifestPath: string\n): ManifestAccount {\n const parsedKey = parseAccountKey(key);\n if (!parsedKey) {\n throw new ManifestError(\n `Manifest ${manifestPath} has invalid account key: \"${key}\". Expected format: \"aws:<account-id>\" or \"gcp:<project-id>\"`\n );\n }\n\n if (!value || typeof value !== \"object\") {\n throw new ManifestError(`Manifest ${manifestPath} account \"${key}\" must be an object`);\n }\n\n const accountObj = value as Record<string, unknown>;\n if (!Array.isArray(accountObj.resources)) {\n throw new ManifestError(`Manifest ${manifestPath} account \"${key}\" must have a \"resources\" array`);\n }\n\n const resources = extractAndValidateResources(accountObj.resources, manifestPath);\n const alias = typeof accountObj.alias === \"string\" ? accountObj.alias : undefined;\n\n return { alias, resources };\n}\n\nfunction parseJsonContent(content: string, manifestPath: string): unknown {\n try {\n return JSON.parse(content);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n throw new ManifestError(`Invalid JSON in manifest ${manifestPath}: ${message}`);\n }\n}\n\nfunction validateJsonStructure(data: unknown, manifestPath: string): void {\n if (!data || typeof data !== \"object\") {\n throw new ManifestError(`Manifest ${manifestPath} must be a JSON object`);\n }\n\n const obj = data as Record<string, unknown>;\n if (!Array.isArray(obj.resources)) {\n throw new ManifestError(`Manifest ${manifestPath} must have a \"resources\" array`);\n }\n}\n\nfunction extractAndValidateResources(items: unknown[], manifestPath: string): string[] {\n const resources: string[] = [];\n const invalidResources: string[] = [];\n\n for (const item of items) {\n if (typeof item !== \"string\") {\n throw new ManifestError(\n `Manifest ${manifestPath} contains non-string resource: ${JSON.stringify(item)}`\n );\n }\n if (!isValidResource(item)) {\n invalidResources.push(item);\n } else {\n resources.push(item);\n }\n }\n\n if (invalidResources.length > 0) {\n throw new ManifestError(\n `Manifest ${manifestPath} contains invalid resources: ${invalidResources.join(\", \")}`\n );\n }\n\n return resources;\n}\n\n/**\n * Parse a TXT format manifest (one resource per line, # for comments)\n */\nfunction parseTxtManifest(content: string, manifestPath: string): Manifest {\n const lines = content.split(\"\\n\");\n const resources: string[] = [];\n const invalidResources: { line: number; value: string }[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n // Skip empty lines and comments\n if (!line || line.startsWith(\"#\")) {\n continue;\n }\n\n if (!isValidResource(line)) {\n invalidResources.push({ line: i + 1, value: line });\n } else {\n resources.push(line);\n }\n }\n\n if (invalidResources.length > 0) {\n const details = invalidResources.map((a) => `line ${a.line}: \"${a.value}\"`).join(\", \");\n throw new ManifestError(`Manifest ${manifestPath} contains invalid resources: ${details}`);\n }\n\n return { resources };\n}\n","/**\n * ARN parsing utilities\n *\n * ARN format: arn:partition:service:region:account-id:resource\n * or: arn:partition:service:region:account-id:resource-type/resource-id\n * or: arn:partition:service:region:account-id:resource-type:resource-id\n */\n\nimport type { ParsedArn } from \"./types.js\";\n\ninterface ResourceParts {\n resourceType: string;\n resourceId: string;\n}\n\n/**\n * Validate that a string is a valid ARN format\n */\nexport function isValidArn(arn: string): boolean {\n if (!arn.startsWith(\"arn:\")) {\n return false;\n }\n const parts = arn.split(\":\");\n // ARN must have at least 6 parts: arn:partition:service:region:account:resource\n return parts.length >= 6;\n}\n\n/**\n * Parse an ARN string into its components\n */\nexport function parseArn(arn: string): ParsedArn | null {\n if (!isValidArn(arn)) {\n return null;\n }\n\n const parts = arn.split(\":\");\n const [, partition, service, region, accountId, ...resourceParts] = parts;\n\n // Resource can contain colons, so we need to rejoin\n const resource = resourceParts.join(\":\");\n\n // Parse resource type and ID based on service-specific patterns\n const { resourceType, resourceId } = parseResource(service, resource);\n\n return {\n cloud: \"aws\" as const,\n partition,\n service,\n region,\n accountId,\n resourceType,\n resourceId,\n raw: arn,\n };\n}\n\n// Service-specific parsers\nconst serviceParsers: Record<string, (resource: string) => ResourceParts> = {\n s3: parseS3Resource,\n lambda: parseLambdaResource,\n dynamodb: parseDynamoDBResource,\n sqs: (resource) => ({ resourceType: \"queue\", resourceId: resource }),\n sns: (resource) => ({ resourceType: \"topic\", resourceId: resource }),\n iam: parseIAMResource,\n secretsmanager: parseSecretsManagerResource,\n logs: parseLogsResource,\n};\n\nfunction parseS3Resource(resource: string): ResourceParts {\n if (resource.includes(\"/\")) {\n const [bucket, ...keyParts] = resource.split(\"/\");\n return { resourceType: \"object\", resourceId: `${bucket}/${keyParts.join(\"/\")}` };\n }\n return { resourceType: \"bucket\", resourceId: resource };\n}\n\nfunction parseLambdaResource(resource: string): ResourceParts {\n if (resource.startsWith(\"function:\")) {\n const funcName = resource.slice(\"function:\".length);\n const colonIndex = funcName.indexOf(\":\");\n const resourceId = colonIndex !== -1 ? funcName.slice(0, colonIndex) : funcName;\n return { resourceType: \"function\", resourceId };\n }\n if (resource.startsWith(\"layer:\")) {\n const rest = resource.slice(\"layer:\".length);\n const colonIndex = rest.indexOf(\":\");\n const resourceId = colonIndex !== -1 ? rest.slice(0, colonIndex) : rest;\n return { resourceType: \"layer\", resourceId };\n }\n return { resourceType: \"function\", resourceId: resource };\n}\n\nfunction parseDynamoDBResource(resource: string): ResourceParts {\n if (resource.startsWith(\"table/\")) {\n const rest = resource.slice(\"table/\".length);\n const indexPos = rest.indexOf(\"/index/\");\n const resourceType = indexPos !== -1 ? \"index\" : \"table\";\n return { resourceType, resourceId: rest };\n }\n return { resourceType: \"table\", resourceId: resource };\n}\n\nfunction parseIAMResource(resource: string): ResourceParts {\n const prefixes = [\"role/\", \"user/\", \"policy/\"];\n for (const prefix of prefixes) {\n if (resource.startsWith(prefix)) {\n return {\n resourceType: prefix.slice(0, -1),\n resourceId: resource.slice(prefix.length),\n };\n }\n }\n const colonIndex = resource.indexOf(\":\");\n if (colonIndex !== -1) {\n return {\n resourceType: resource.slice(0, colonIndex),\n resourceId: resource.slice(colonIndex + 1),\n };\n }\n return { resourceType: \"\", resourceId: resource };\n}\n\nfunction parseSecretsManagerResource(resource: string): ResourceParts {\n if (resource.startsWith(\"secret:\")) {\n return { resourceType: \"secret\", resourceId: resource.slice(\"secret:\".length) };\n }\n return { resourceType: \"secret\", resourceId: resource };\n}\n\nfunction parseLogsResource(resource: string): ResourceParts {\n if (resource.startsWith(\"log-group:\")) {\n let logGroupName = resource.slice(\"log-group:\".length);\n if (logGroupName.endsWith(\":*\")) {\n logGroupName = logGroupName.slice(0, -2);\n }\n return { resourceType: \"log-group\", resourceId: logGroupName };\n }\n return { resourceType: \"log-group\", resourceId: resource };\n}\n\nfunction parseGenericResource(resource: string): ResourceParts {\n if (resource.includes(\"/\")) {\n const slashIndex = resource.indexOf(\"/\");\n return {\n resourceType: resource.slice(0, slashIndex),\n resourceId: resource.slice(slashIndex + 1),\n };\n }\n if (resource.includes(\":\")) {\n const colonIndex = resource.indexOf(\":\");\n return {\n resourceType: resource.slice(0, colonIndex),\n resourceId: resource.slice(colonIndex + 1),\n };\n }\n return { resourceType: \"\", resourceId: resource };\n}\n\n/**\n * Parse the resource portion of an ARN into type and ID\n */\nfunction parseResource(service: string, resource: string): ResourceParts {\n const parser = serviceParsers[service] as ((r: string) => ResourceParts) | undefined;\n return parser ? parser(resource) : parseGenericResource(resource);\n}\n","/**\n * GCP resource path parsing utilities\n *\n * GCP resource paths follow patterns like:\n * - projects/{project}/locations/{location}/services/{service} (Cloud Run)\n * - projects/{project}/serviceAccounts/{email} (IAM Service Accounts)\n * - projects/{project}/secrets/{secret} (Secret Manager)\n * - projects/{project}/locations/{location}/repositories/{repo} (Artifact Registry)\n */\n\nimport type { ParsedGcpResource } from \"./types.js\";\n\n/**\n * Validate that a string is a valid GCP resource path\n */\nexport function isValidGcpResource(path: string): boolean {\n return path.startsWith(\"projects/\") && path.split(\"/\").length >= 3;\n}\n\n/**\n * Parse a GCP resource path into its components\n */\nexport function parseGcpResource(path: string): ParsedGcpResource | null {\n if (!isValidGcpResource(path)) {\n return null;\n }\n\n const parts = path.split(\"/\");\n if (parts[0] !== \"projects\" || parts.length < 3) {\n return null;\n }\n\n const project = parts[1];\n const result = parseResourcePath(parts.slice(2), path, project);\n return result;\n}\n\n/**\n * Parse the resource-specific part of the path\n */\nfunction parseResourcePath(\n parts: string[],\n raw: string,\n project: string\n): ParsedGcpResource | null {\n // Service Accounts: projects/{project}/serviceAccounts/{email}\n if (parts[0] === \"serviceAccounts\" && parts.length >= 2) {\n return gcpResource({\n project,\n service: \"iam\",\n location: \"global\",\n resourceType: \"serviceAccounts\",\n resourceId: parts.slice(1).join(\"/\"),\n raw,\n });\n }\n\n // Secrets: projects/{project}/secrets/{secret}\n if (parts[0] === \"secrets\" && parts.length >= 2) {\n return gcpResource({\n project,\n service: \"secretmanager\",\n location: \"global\",\n resourceType: \"secrets\",\n resourceId: parts[1],\n raw,\n });\n }\n\n // Location-based resources\n if (parts[0] === \"locations\" && parts.length >= 4) {\n const location = parts[1];\n const resourceType = parts[2];\n const resourceId = parts.slice(3).join(\"/\");\n\n const service = getServiceFromResourceType(resourceType);\n return gcpResource({\n project,\n service,\n location,\n resourceType,\n resourceId,\n raw,\n });\n }\n\n // Unknown format\n return null;\n}\n\n/**\n * Map resource types to GCP service names\n */\nfunction getServiceFromResourceType(resourceType: string): string {\n const serviceMap: Record<string, string> = {\n services: \"run\",\n repositories: \"artifactregistry\",\n functions: \"cloudfunctions\",\n buckets: \"storage\",\n instances: \"compute\",\n clusters: \"container\",\n };\n return serviceMap[resourceType] ?? resourceType;\n}\n\ninterface GcpResourceParams {\n project: string;\n service: string;\n location: string;\n resourceType: string;\n resourceId: string;\n raw: string;\n}\n\n/**\n * Create a ParsedGcpResource object\n */\nfunction gcpResource(params: GcpResourceParams): ParsedGcpResource {\n return { cloud: \"gcp\", ...params };\n}\n","/**\n * Zod schemas for runtime validation of infra manifests and resources\n *\n * These schemas validate external inputs like manifest files,\n * stack exports, ARNs, and GCP resource paths at runtime.\n */\n\nimport { z } from \"zod\";\n\n// =============================================================================\n// Cloud Provider Types\n// =============================================================================\n\n/**\n * Cloud provider schema\n */\nexport const CloudProviderSchema = z.enum([\"aws\", \"gcp\"]);\nexport type CloudProvider = z.infer<typeof CloudProviderSchema>;\n\n/**\n * Account key schema - format: \"provider:accountId\"\n * Examples: \"aws:123456789012\", \"gcp:my-project-id\"\n */\nexport const AccountKeySchema = z\n .string()\n .regex(\n /^(aws|gcp):.+$/,\n \"Invalid account key format. Expected: provider:accountId (e.g., aws:123456789012, gcp:my-project)\"\n );\nexport type AccountKey = z.infer<typeof AccountKeySchema>;\n\n// =============================================================================\n// AWS Resource Schemas\n// =============================================================================\n\n/**\n * ARN schema - validates AWS ARN format\n *\n * Format: arn:partition:service:region:account-id:resource\n */\nexport const ArnSchema = z\n .string()\n .regex(\n /^arn:(aws|aws-cn|aws-us-gov):[a-z0-9-]+:[a-z0-9-]*:[0-9]*:.+$/,\n \"Invalid ARN format. Expected: arn:partition:service:region:account-id:resource\"\n );\nexport type Arn = z.infer<typeof ArnSchema>;\n\n/**\n * Parsed ARN schema - components extracted from an ARN\n */\nexport const ParsedArnSchema = z.object({\n /** Cloud provider (always \"aws\" for ARNs) */\n cloud: z.literal(\"aws\"),\n\n /** AWS partition (aws, aws-cn, aws-us-gov) */\n partition: z.string(),\n\n /** AWS service (s3, lambda, rds, etc.) */\n service: z.string(),\n\n /** AWS region (empty for global services like S3, IAM) */\n region: z.string(),\n\n /** AWS account ID (empty for S3 buckets) */\n accountId: z.string(),\n\n /** Resource type (e.g., function, table, bucket) */\n resourceType: z.string(),\n\n /** Resource name/identifier */\n resourceId: z.string(),\n\n /** Original ARN string */\n raw: z.string(),\n});\nexport type ParsedArn = z.infer<typeof ParsedArnSchema>;\n\n// =============================================================================\n// GCP Resource Schemas\n// =============================================================================\n\n/**\n * GCP resource path schema - validates GCP resource path format\n *\n * Examples:\n * - projects/my-project/locations/us-central1/functions/my-func\n * - projects/my-project/topics/my-topic\n * - projects/my-project/subscriptions/my-sub\n */\nexport const GcpResourcePathSchema = z\n .string()\n .regex(\n /^projects\\/[^/]+\\/.+$/,\n \"Invalid GCP resource path format. Expected: projects/{project-id}/...\"\n );\nexport type GcpResourcePath = z.infer<typeof GcpResourcePathSchema>;\n\n/**\n * Parsed GCP resource schema - components extracted from a GCP resource path\n */\nexport const ParsedGcpResourceSchema = z.object({\n /** Cloud provider (always \"gcp\" for GCP resources) */\n cloud: z.literal(\"gcp\"),\n\n /** GCP project ID */\n project: z.string(),\n\n /** GCP service (run, iam, secretmanager, artifactregistry, etc.) */\n service: z.string(),\n\n /** Location/region (us-central1, global, etc.) */\n location: z.string(),\n\n /** Resource type (services, serviceAccounts, secrets, repositories, etc.) */\n resourceType: z.string(),\n\n /** Resource name/ID */\n resourceId: z.string(),\n\n /** Original resource path */\n raw: z.string(),\n});\nexport type ParsedGcpResource = z.infer<typeof ParsedGcpResourceSchema>;\n\n/**\n * Generic resource identifier - can be AWS ARN or GCP resource path\n */\nexport const ResourceIdentifierSchema = z.union([ArnSchema, GcpResourcePathSchema]);\nexport type ResourceIdentifier = z.infer<typeof ResourceIdentifierSchema>;\n\n// =============================================================================\n// Account Types\n// =============================================================================\n\n/**\n * Account identifier schema - parsed from account key\n */\nexport const AccountIdSchema = z.object({\n /** Cloud provider */\n cloud: CloudProviderSchema,\n\n /** AWS account ID or GCP project ID */\n id: z.string(),\n});\nexport type AccountId = z.infer<typeof AccountIdSchema>;\n\n/**\n * Account entry in a multi-account manifest\n */\nexport const ManifestAccountSchema = z.object({\n /** Optional human-readable alias for this account */\n alias: z.string().optional(),\n\n /** List of resource identifiers (ARNs or GCP resource paths) */\n resources: z.array(z.string()),\n});\nexport type ManifestAccount = z.infer<typeof ManifestAccountSchema>;\n\n// =============================================================================\n// Manifest Schemas\n// =============================================================================\n\n/**\n * V2 Multi-account manifest schema\n *\n * Resources are grouped by cloud account (AWS account ID or GCP project ID)\n */\nexport const MultiAccountManifestSchema = z.object({\n /** Manifest version - must be 2 for multi-account format */\n version: z.literal(2),\n\n /** Optional project name */\n project: z.string().optional(),\n\n /** Resources grouped by account key (e.g., \"aws:123456789012\", \"gcp:my-project\") */\n accounts: z.record(z.string(), ManifestAccountSchema),\n});\nexport type MultiAccountManifest = z.infer<typeof MultiAccountManifestSchema>;\n\n/**\n * Legacy manifest schema (v1) - flat array of resources\n */\nexport const LegacyManifestSchema = z.object({\n /** Optional manifest version (1 or undefined for legacy) */\n version: z.literal(1).optional(),\n\n /** Optional project name */\n project: z.string().optional(),\n\n /** Flat list of resource identifiers */\n resources: z.array(z.string()),\n});\nexport type LegacyManifest = z.infer<typeof LegacyManifestSchema>;\n\n/**\n * Any manifest schema - accepts either v1 or v2 format\n */\nexport const ManifestSchema = z.union([MultiAccountManifestSchema, LegacyManifestSchema]);\nexport type Manifest = z.infer<typeof ManifestSchema>;\n\n// =============================================================================\n// Scan Result Schemas\n// =============================================================================\n\n/**\n * Result of checking a single resource\n */\nexport const ResourceCheckResultSchema = z.object({\n /** The resource ARN or GCP path */\n arn: z.string(),\n\n /** Whether the resource exists */\n exists: z.boolean(),\n\n /** Error message if check failed */\n error: z.string().optional(),\n\n /** Service name (e.g., s3, lambda, run) */\n service: z.string(),\n\n /** Resource type (e.g., bucket, function) */\n resourceType: z.string(),\n\n /** Resource identifier */\n resourceId: z.string(),\n});\nexport type ResourceCheckResult = z.infer<typeof ResourceCheckResultSchema>;\n\n/**\n * Scan summary statistics\n */\nexport const InfraScanSummarySchema = z.object({\n /** Total resources checked */\n total: z.number().int().nonnegative(),\n\n /** Resources that exist */\n found: z.number().int().nonnegative(),\n\n /** Resources that don't exist */\n missing: z.number().int().nonnegative(),\n\n /** Resources that couldn't be checked (errors) */\n errors: z.number().int().nonnegative(),\n});\nexport type InfraScanSummary = z.infer<typeof InfraScanSummarySchema>;\n\n/**\n * Per-account scan results\n */\nconst AccountScanResultSchema = z.object({\n /** Account alias if provided */\n alias: z.string().optional(),\n\n /** Individual resource check results */\n results: z.array(ResourceCheckResultSchema),\n\n /** Summary statistics for this account */\n summary: InfraScanSummarySchema,\n});\nexport type AccountScanResult = z.infer<typeof AccountScanResultSchema>;\n\n/**\n * Full infrastructure scan result\n */\nexport const InfraScanResultSchema = z.object({\n /** Path to the manifest file */\n manifest: z.string(),\n\n /** Project name */\n project: z.string().optional(),\n\n /** Individual resource check results */\n results: z.array(ResourceCheckResultSchema),\n\n /** Summary statistics */\n summary: InfraScanSummarySchema,\n\n /** Per-account results (only present for multi-account manifests) */\n accountResults: z.record(z.string(), AccountScanResultSchema).optional(),\n});\nexport type InfraScanResult = z.infer<typeof InfraScanResultSchema>;\n\n// =============================================================================\n// Scan Options Schemas\n// =============================================================================\n\n/**\n * Options for programmatic API\n */\nexport interface ScanInfraOptions {\n /** Path to manifest file */\n manifestPath?: string;\n\n /** Path to config file */\n configPath?: string;\n\n /** Filter to specific account (by alias or account key like \"aws:123\") */\n account?: string;\n}\n\n/**\n * Options for CLI handler\n */\nexport type RunInfraScanOptions = ScanInfraOptions & {\n /** Output format */\n format?: \"text\" | \"json\";\n};\n\n// =============================================================================\n// Pulumi Stack Export Schemas\n// =============================================================================\n\n/**\n * Pulumi resource in stack export\n */\nexport const PulumiResourceSchema = z.object({\n urn: z.string().optional(),\n type: z.string().optional(),\n inputs: z.record(z.string(), z.unknown()).optional(),\n outputs: z.record(z.string(), z.unknown()).optional(),\n});\nexport type PulumiResource = z.infer<typeof PulumiResourceSchema>;\n\n/**\n * Pulumi stack export schema (simplified)\n */\nexport const PulumiStackExportSchema = z.object({\n version: z.number().optional(),\n deployment: z\n .object({\n manifest: z\n .object({\n time: z.string().optional(),\n magic: z.string().optional(),\n version: z.string().optional(),\n })\n .optional(),\n resources: z.array(PulumiResourceSchema).optional(),\n })\n .optional(),\n});\nexport type PulumiStackExport = z.infer<typeof PulumiStackExportSchema>;\n\n// =============================================================================\n// Validation Functions\n// =============================================================================\n\n/**\n * Validate an ARN string\n * @throws ZodError if invalid\n */\nexport function validateArn(arn: string): Arn {\n return ArnSchema.parse(arn);\n}\n\n/**\n * Check if a string is a valid ARN format\n */\nexport function isValidArnFormat(arn: string): boolean {\n return ArnSchema.safeParse(arn).success;\n}\n\n/**\n * Validate a GCP resource path\n * @throws ZodError if invalid\n */\nexport function validateGcpResourcePath(path: string): GcpResourcePath {\n return GcpResourcePathSchema.parse(path);\n}\n\n/**\n * Check if a string is a valid GCP resource path\n */\nexport function isValidGcpResourcePath(path: string): boolean {\n return GcpResourcePathSchema.safeParse(path).success;\n}\n\n/**\n * Validate an account key string\n * @throws ZodError if invalid\n */\nexport function validateAccountKey(key: string): AccountKey {\n return AccountKeySchema.parse(key);\n}\n\n/**\n * Check if a string is a valid account key\n */\nexport function isValidAccountKey(key: string): boolean {\n return AccountKeySchema.safeParse(key).success;\n}\n\n/**\n * Validate a legacy (v1) manifest\n * @throws ZodError if invalid\n */\nexport function validateLegacyManifest(data: unknown): LegacyManifest {\n return LegacyManifestSchema.parse(data);\n}\n\n/**\n * Validate a multi-account (v2) manifest\n * @throws ZodError if invalid\n */\nexport function validateMultiAccountManifest(data: unknown): MultiAccountManifest {\n return MultiAccountManifestSchema.parse(data);\n}\n\n/**\n * Validate any manifest format (v1 or v2)\n * @throws ZodError if invalid\n */\nexport function validateManifest(data: unknown): Manifest {\n return ManifestSchema.parse(data);\n}\n\n/**\n * Check if data is a valid multi-account (v2) manifest\n */\nexport function isMultiAccountManifestSchema(data: unknown): data is MultiAccountManifest {\n return MultiAccountManifestSchema.safeParse(data).success;\n}\n\n/**\n * Check if data is a valid legacy (v1) manifest\n */\nexport function isLegacyManifestSchema(data: unknown): data is LegacyManifest {\n return LegacyManifestSchema.safeParse(data).success;\n}\n\n/**\n * Validate a Pulumi stack export\n * @throws ZodError if invalid\n */\nexport function validateStackExport(data: unknown): PulumiStackExport {\n return PulumiStackExportSchema.parse(data);\n}\n\n"],"mappings":"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","names":["path","path"]}
@@ -0,0 +1,85 @@
1
+ // ../core/src/types.ts
2
+ var ViolationBuilder = {
3
+ create(options) {
4
+ return {
5
+ rule: options.rule,
6
+ tool: options.tool,
7
+ message: options.message,
8
+ severity: options.severity,
9
+ ...options.file && { file: options.file },
10
+ ...options.line && { line: options.line },
11
+ ...options.column && { column: options.column },
12
+ ...options.code && { code: options.code }
13
+ };
14
+ },
15
+ error(rule, tool, message, code) {
16
+ return { rule, tool, message, severity: "error", code };
17
+ },
18
+ warning(rule, tool, message, code) {
19
+ return { rule, tool, message, severity: "warning", code };
20
+ }
21
+ };
22
+ var CheckResultBuilder = {
23
+ pass(name, rule, duration) {
24
+ return {
25
+ name,
26
+ rule,
27
+ passed: true,
28
+ violations: [],
29
+ skipped: false,
30
+ duration
31
+ };
32
+ },
33
+ fail(name, rule, violations, duration) {
34
+ return {
35
+ name,
36
+ rule,
37
+ passed: false,
38
+ violations,
39
+ skipped: false,
40
+ duration
41
+ };
42
+ },
43
+ skip(name, rule, reason, duration) {
44
+ return {
45
+ name,
46
+ rule,
47
+ passed: true,
48
+ violations: [],
49
+ skipped: true,
50
+ skipReason: reason,
51
+ duration
52
+ };
53
+ },
54
+ fromViolations(name, rule, violations, duration) {
55
+ return violations.length === 0 ? CheckResultBuilder.pass(name, rule, duration) : CheckResultBuilder.fail(name, rule, violations, duration);
56
+ }
57
+ };
58
+ var DomainResultBuilder = {
59
+ fromChecks(domain, checks) {
60
+ const violationCount = checks.reduce((sum, check) => sum + check.violations.length, 0);
61
+ const allPassed = checks.every((check) => check.passed || check.skipped);
62
+ const allSkipped = checks.length === 0 || checks.every((check) => check.skipped);
63
+ let status = "fail";
64
+ if (allSkipped) {
65
+ status = "skip";
66
+ } else if (allPassed) {
67
+ status = "pass";
68
+ }
69
+ return { domain, status, checks, violationCount };
70
+ }
71
+ };
72
+ var ExitCode = {
73
+ SUCCESS: 0,
74
+ VIOLATIONS_FOUND: 1,
75
+ CONFIG_ERROR: 2,
76
+ RUNTIME_ERROR: 3
77
+ };
78
+
79
+ export {
80
+ ViolationBuilder,
81
+ CheckResultBuilder,
82
+ DomainResultBuilder,
83
+ ExitCode
84
+ };
85
+ //# sourceMappingURL=chunk-P7TIZJ4C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../core/src/types.ts"],"sourcesContent":["/**\n * Shared types for standards-kit\n */\n\n// =============================================================================\n// Core Types\n// =============================================================================\n\n/** Severity levels for violations */\nexport type Severity = \"error\" | \"warning\";\n\n/** Status for domain results */\nexport type DomainStatus = \"pass\" | \"fail\" | \"skip\";\n\n/** A single violation found by a check */\nexport interface Violation {\n rule: string;\n tool: string;\n file?: string;\n line?: number;\n column?: number;\n message: string;\n code?: string;\n severity: Severity;\n}\n\n/** Result of running a single check */\nexport interface CheckResult {\n name: string;\n rule: string;\n passed: boolean;\n violations: Violation[];\n skipped: boolean;\n skipReason?: string;\n duration?: number;\n}\n\n/** Result of running all checks in a domain */\nexport interface DomainResult {\n domain: string;\n status: DomainStatus;\n checks: CheckResult[];\n violationCount: number;\n}\n\n/** Full result of conform check or conform audit */\nexport interface FullResult {\n version: string;\n configPath: string;\n domains: Record<string, DomainResult>;\n summary: {\n totalViolations: number;\n exitCode: number;\n };\n}\n\n// =============================================================================\n// Tool Interface\n// =============================================================================\n\n/** Interface for tool runners (ESLint, Ruff, tsc, etc.) */\nexport interface IToolRunner {\n /** Display name of the tool */\n readonly name: string;\n /** Rule category (e.g., \"code.linting\") */\n readonly rule: string;\n /** Tool identifier for violations */\n readonly toolId: string;\n /** Config file patterns to look for */\n readonly configFiles: string[];\n\n /** Run the tool and return check results */\n run(projectRoot: string): Promise<CheckResult>;\n\n /** Audit that config exists without running the tool */\n audit(projectRoot: string): Promise<CheckResult>;\n}\n\n// =============================================================================\n// Builders\n// =============================================================================\n\n/** Options for creating a violation */\nexport interface ViolationOptions {\n rule: string;\n tool: string;\n message: string;\n severity: Severity;\n file?: string;\n line?: number;\n column?: number;\n code?: string;\n}\n\n/** Builder for creating Violation objects */\nexport const ViolationBuilder = {\n create(options: ViolationOptions): Violation {\n return {\n rule: options.rule,\n tool: options.tool,\n message: options.message,\n severity: options.severity,\n ...(options.file && { file: options.file }),\n ...(options.line && { line: options.line }),\n ...(options.column && { column: options.column }),\n ...(options.code && { code: options.code }),\n };\n },\n\n error(rule: string, tool: string, message: string, code?: string): Violation {\n return { rule, tool, message, severity: \"error\", code };\n },\n\n warning(rule: string, tool: string, message: string, code?: string): Violation {\n return { rule, tool, message, severity: \"warning\", code };\n },\n};\n\n/** Builder for creating CheckResult objects */\nexport const CheckResultBuilder = {\n pass(name: string, rule: string, duration?: number): CheckResult {\n return {\n name,\n rule,\n passed: true,\n violations: [],\n skipped: false,\n duration,\n };\n },\n\n fail(name: string, rule: string, violations: Violation[], duration?: number): CheckResult {\n return {\n name,\n rule,\n passed: false,\n violations,\n skipped: false,\n duration,\n };\n },\n\n skip(name: string, rule: string, reason: string, duration?: number): CheckResult {\n return {\n name,\n rule,\n passed: true,\n violations: [],\n skipped: true,\n skipReason: reason,\n duration,\n };\n },\n\n fromViolations(\n name: string,\n rule: string,\n violations: Violation[],\n duration?: number\n ): CheckResult {\n return violations.length === 0\n ? CheckResultBuilder.pass(name, rule, duration)\n : CheckResultBuilder.fail(name, rule, violations, duration);\n },\n};\n\n/** Builder for creating DomainResult objects */\nexport const DomainResultBuilder = {\n fromChecks(domain: string, checks: CheckResult[]): DomainResult {\n const violationCount = checks.reduce((sum, check) => sum + check.violations.length, 0);\n const allPassed = checks.every((check) => check.passed || check.skipped);\n const allSkipped = checks.length === 0 || checks.every((check) => check.skipped);\n\n let status: DomainStatus = \"fail\";\n if (allSkipped) {\n status = \"skip\";\n } else if (allPassed) {\n status = \"pass\";\n }\n\n return { domain, status, checks, violationCount };\n },\n};\n\n// =============================================================================\n// Exit Codes\n// =============================================================================\n\nexport const ExitCode = {\n SUCCESS: 0,\n VIOLATIONS_FOUND: 1,\n CONFIG_ERROR: 2,\n RUNTIME_ERROR: 3,\n} as const;\n\nexport type ExitCodeType = (typeof ExitCode)[keyof typeof ExitCode];\n"],"mappings":";AA+FO,IAAM,mBAAmB;AAAA,EAC9B,OAAO,SAAsC;AAC3C,WAAO;AAAA,MACL,MAAM,QAAQ;AAAA,MACd,MAAM,QAAQ;AAAA,MACd,SAAS,QAAQ;AAAA,MACjB,UAAU,QAAQ;AAAA,MAClB,GAAI,QAAQ,QAAQ,EAAE,MAAM,QAAQ,KAAK;AAAA,MACzC,GAAI,QAAQ,QAAQ,EAAE,MAAM,QAAQ,KAAK;AAAA,MACzC,GAAI,QAAQ,UAAU,EAAE,QAAQ,QAAQ,OAAO;AAAA,MAC/C,GAAI,QAAQ,QAAQ,EAAE,MAAM,QAAQ,KAAK;AAAA,IAC3C;AAAA,EACF;AAAA,EAEA,MAAM,MAAc,MAAc,SAAiB,MAA0B;AAC3E,WAAO,EAAE,MAAM,MAAM,SAAS,UAAU,SAAS,KAAK;AAAA,EACxD;AAAA,EAEA,QAAQ,MAAc,MAAc,SAAiB,MAA0B;AAC7E,WAAO,EAAE,MAAM,MAAM,SAAS,UAAU,WAAW,KAAK;AAAA,EAC1D;AACF;AAGO,IAAM,qBAAqB;AAAA,EAChC,KAAK,MAAc,MAAc,UAAgC;AAC/D,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,YAAY,CAAC;AAAA,MACb,SAAS;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EAEA,KAAK,MAAc,MAAc,YAAyB,UAAgC;AACxF,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA,SAAS;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EAEA,KAAK,MAAc,MAAc,QAAgB,UAAgC;AAC/E,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,YAAY,CAAC;AAAA,MACb,SAAS;AAAA,MACT,YAAY;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA,EAEA,eACE,MACA,MACA,YACA,UACa;AACb,WAAO,WAAW,WAAW,IACzB,mBAAmB,KAAK,MAAM,MAAM,QAAQ,IAC5C,mBAAmB,KAAK,MAAM,MAAM,YAAY,QAAQ;AAAA,EAC9D;AACF;AAGO,IAAM,sBAAsB;AAAA,EACjC,WAAW,QAAgB,QAAqC;AAC9D,UAAM,iBAAiB,OAAO,OAAO,CAAC,KAAK,UAAU,MAAM,MAAM,WAAW,QAAQ,CAAC;AACrF,UAAM,YAAY,OAAO,MAAM,CAAC,UAAU,MAAM,UAAU,MAAM,OAAO;AACvE,UAAM,aAAa,OAAO,WAAW,KAAK,OAAO,MAAM,CAAC,UAAU,MAAM,OAAO;AAE/E,QAAI,SAAuB;AAC3B,QAAI,YAAY;AACd,eAAS;AAAA,IACX,WAAW,WAAW;AACpB,eAAS;AAAA,IACX;AAEA,WAAO,EAAE,QAAQ,QAAQ,QAAQ,eAAe;AAAA,EAClD;AACF;AAMO,IAAM,WAAW;AAAA,EACtB,SAAS;AAAA,EACT,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,eAAe;AACjB;","names":[]}
@@ -0,0 +1,279 @@
1
+ // src/mcp/standards/fetcher.ts
2
+ import * as fs from "fs";
3
+ import * as os from "os";
4
+ import * as path from "path";
5
+ import { execa } from "execa";
6
+ var DEFAULT_OWNER = "palindrom-ai";
7
+ var DEFAULT_REPO = "standards";
8
+ var CACHE_DIR = path.join(os.tmpdir(), "cm-standards-cache");
9
+ function parseGitHubSource(source) {
10
+ const remainder = source.slice(7);
11
+ const atIndex = remainder.indexOf("@");
12
+ const ownerRepo = atIndex !== -1 ? remainder.slice(0, atIndex) : remainder;
13
+ const ref = atIndex !== -1 ? remainder.slice(atIndex + 1) : void 0;
14
+ const slashIndex = ownerRepo.indexOf("/");
15
+ if (slashIndex === -1) {
16
+ throw new StandardsError(`Invalid GitHub source format: ${source}. Expected github:owner/repo`);
17
+ }
18
+ const owner = ownerRepo.slice(0, slashIndex);
19
+ const repo = ownerRepo.slice(slashIndex + 1);
20
+ if (!owner || !repo) {
21
+ throw new StandardsError(`Invalid GitHub source format: ${source}. Expected github:owner/repo`);
22
+ }
23
+ return { type: "github", owner, repo, ref };
24
+ }
25
+ function parseSource(source) {
26
+ if (source.startsWith("github:")) {
27
+ return parseGitHubSource(source);
28
+ }
29
+ return { type: "local", path: source };
30
+ }
31
+ var StandardsError = class extends Error {
32
+ constructor(message) {
33
+ super(message);
34
+ this.name = "StandardsError";
35
+ }
36
+ };
37
+ function detectAuthMethod() {
38
+ if (process.env.CM_REGISTRY_TOKEN || process.env.GITHUB_TOKEN) {
39
+ return "token";
40
+ }
41
+ if (process.env.SSH_AUTH_SOCK) {
42
+ return "ssh";
43
+ }
44
+ return "none";
45
+ }
46
+ function getAuthToken() {
47
+ return process.env.CM_REGISTRY_TOKEN ?? process.env.GITHUB_TOKEN;
48
+ }
49
+ function buildGitHubUrl(auth, owner, repo) {
50
+ switch (auth) {
51
+ case "ssh":
52
+ return `git@github.com:${owner}/${repo}.git`;
53
+ case "token": {
54
+ const token = getAuthToken();
55
+ if (token) {
56
+ return `https://x-access-token:${token}@github.com/${owner}/${repo}.git`;
57
+ }
58
+ return `https://github.com/${owner}/${repo}.git`;
59
+ }
60
+ case "none":
61
+ default:
62
+ return `https://github.com/${owner}/${repo}.git`;
63
+ }
64
+ }
65
+ async function updateExistingRepo(repoDir) {
66
+ try {
67
+ await execa("git", ["pull", "--ff-only"], { cwd: repoDir, timeout: 3e4 });
68
+ return true;
69
+ } catch {
70
+ fs.rmSync(repoDir, { recursive: true, force: true });
71
+ return false;
72
+ }
73
+ }
74
+ async function cloneRepo(repoDir, owner, repo, ref) {
75
+ fs.mkdirSync(CACHE_DIR, { recursive: true });
76
+ const auth = detectAuthMethod();
77
+ const url = buildGitHubUrl(auth, owner, repo);
78
+ try {
79
+ const args = ["clone", "--depth", "1"];
80
+ if (ref) {
81
+ args.push("--branch", ref);
82
+ }
83
+ args.push(url, repoDir);
84
+ await execa("git", args, {
85
+ timeout: 3e4
86
+ });
87
+ } catch (error) {
88
+ const message = error instanceof Error ? error.message : String(error);
89
+ if (message.includes("timed out")) {
90
+ throw new StandardsError(`Standards repo clone timed out after 30 seconds`);
91
+ }
92
+ throw new StandardsError(`Failed to clone standards repo: ${message}`);
93
+ }
94
+ }
95
+ async function fetchGitHubRepo(owner, repo, ref) {
96
+ const cacheKey = ref ? `${owner}-${repo}-${ref}` : `${owner}-${repo}`;
97
+ const repoDir = path.join(CACHE_DIR, cacheKey);
98
+ if (fs.existsSync(repoDir)) {
99
+ await updateExistingRepo(repoDir);
100
+ }
101
+ if (!fs.existsSync(repoDir)) {
102
+ await cloneRepo(repoDir, owner, repo, ref);
103
+ }
104
+ return repoDir;
105
+ }
106
+ function resolveLocalPath(localPath, basePath) {
107
+ if (path.isAbsolute(localPath)) {
108
+ return localPath;
109
+ }
110
+ const base = basePath ?? process.cwd();
111
+ return path.resolve(base, localPath);
112
+ }
113
+ async function fetchStandardsRepoFromSource(source, basePath) {
114
+ const parsed = parseSource(source);
115
+ if (parsed.type === "local") {
116
+ const resolvedPath = resolveLocalPath(parsed.path, basePath);
117
+ if (!fs.existsSync(resolvedPath)) {
118
+ throw new StandardsError(`Local standards path does not exist: ${resolvedPath}`);
119
+ }
120
+ return resolvedPath;
121
+ }
122
+ return fetchGitHubRepo(parsed.owner, parsed.repo, parsed.ref);
123
+ }
124
+ async function fetchStandardsRepo() {
125
+ return fetchGitHubRepo(DEFAULT_OWNER, DEFAULT_REPO);
126
+ }
127
+ function getGuidelinesDir(repoPath) {
128
+ return path.join(repoPath, "guidelines");
129
+ }
130
+ function getRulesetsDir(repoPath) {
131
+ return path.join(repoPath, "rulesets");
132
+ }
133
+
134
+ // src/mcp/standards/parser.ts
135
+ import * as fs2 from "fs";
136
+ import * as path2 from "path";
137
+ import matter from "gray-matter";
138
+ import { z } from "zod";
139
+ var frontmatterSchema = z.object({
140
+ id: z.string(),
141
+ title: z.string(),
142
+ category: z.string(),
143
+ priority: z.number(),
144
+ tags: z.array(z.string())
145
+ });
146
+ function parseGuideline(fileContent, filename) {
147
+ const { data, content } = matter(fileContent);
148
+ const result = frontmatterSchema.safeParse(data);
149
+ if (!result.success) {
150
+ const errors = result.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
151
+ throw new StandardsError(`Invalid frontmatter in ${filename}: ${errors}`);
152
+ }
153
+ return {
154
+ ...result.data,
155
+ content: content.trim()
156
+ };
157
+ }
158
+ function loadAllGuidelines(guidelinesDir) {
159
+ if (!fs2.existsSync(guidelinesDir)) {
160
+ throw new StandardsError(`Guidelines directory not found: ${guidelinesDir}`);
161
+ }
162
+ const files = fs2.readdirSync(guidelinesDir).filter((f) => f.endsWith(".md"));
163
+ const guidelines = [];
164
+ for (const file of files) {
165
+ const filePath = path2.join(guidelinesDir, file);
166
+ const content = fs2.readFileSync(filePath, "utf-8");
167
+ try {
168
+ guidelines.push(parseGuideline(content, file));
169
+ } catch (error) {
170
+ console.warn(`Warning: Failed to parse guideline ${file}: ${error}`);
171
+ }
172
+ }
173
+ return guidelines;
174
+ }
175
+ function loadGuideline(guidelinesDir, id) {
176
+ const filePath = path2.join(guidelinesDir, `${id}.md`);
177
+ if (!fs2.existsSync(filePath)) {
178
+ return null;
179
+ }
180
+ const content = fs2.readFileSync(filePath, "utf-8");
181
+ return parseGuideline(content, `${id}.md`);
182
+ }
183
+ function toListItems(guidelines) {
184
+ return guidelines.map((g) => ({
185
+ id: g.id,
186
+ title: g.title,
187
+ tags: g.tags,
188
+ category: g.category
189
+ }));
190
+ }
191
+ function loadRuleset(rulesetsDir, id) {
192
+ const filePath = path2.join(rulesetsDir, `${id}.toml`);
193
+ if (!fs2.existsSync(filePath)) {
194
+ return null;
195
+ }
196
+ const content = fs2.readFileSync(filePath, "utf-8");
197
+ return { id, content };
198
+ }
199
+ function listRulesets(rulesetsDir) {
200
+ if (!fs2.existsSync(rulesetsDir)) {
201
+ return [];
202
+ }
203
+ return fs2.readdirSync(rulesetsDir).filter((f) => f.endsWith(".toml")).map((f) => f.replace(".toml", ""));
204
+ }
205
+
206
+ // src/mcp/standards/matcher.ts
207
+ function parseContext(context) {
208
+ const words = context.toLowerCase().split(/[\s,.\-_/]+/).filter((word) => word.length > 1);
209
+ return [...new Set(words)];
210
+ }
211
+ function scoreGuideline(guideline, keywords) {
212
+ const tags = new Set(guideline.tags.map((t) => t.toLowerCase()));
213
+ let score = 0;
214
+ for (const keyword of keywords) {
215
+ if (tags.has(keyword)) {
216
+ score++;
217
+ }
218
+ }
219
+ const category = guideline.category.toLowerCase();
220
+ const id = guideline.id.toLowerCase();
221
+ for (const keyword of keywords) {
222
+ if (category.includes(keyword) || id.includes(keyword)) {
223
+ score += 0.5;
224
+ }
225
+ }
226
+ return score;
227
+ }
228
+ function matchGuidelines(guidelines, context, limit) {
229
+ const keywords = parseContext(context);
230
+ if (keywords.length === 0) {
231
+ return [];
232
+ }
233
+ const scored = guidelines.map((guideline) => ({
234
+ guideline,
235
+ score: scoreGuideline(guideline, keywords)
236
+ })).filter((m) => m.score > 0);
237
+ scored.sort((a, b) => {
238
+ if (b.score !== a.score) {
239
+ return b.score - a.score;
240
+ }
241
+ return a.guideline.priority - b.guideline.priority;
242
+ });
243
+ return limit ? scored.slice(0, limit) : scored;
244
+ }
245
+ function composeGuidelines(matches) {
246
+ if (matches.length === 0) {
247
+ return "No matching guidelines found for the given context.";
248
+ }
249
+ const sections = matches.map((m) => {
250
+ const { guideline } = m;
251
+ return `# ${guideline.title}
252
+
253
+ **Category:** ${guideline.category} | **Priority:** ${guideline.priority}
254
+ **Tags:** ${guideline.tags.join(", ")}
255
+
256
+ ${guideline.content}`;
257
+ });
258
+ return sections.join("\n\n---\n\n");
259
+ }
260
+
261
+ export {
262
+ StandardsError,
263
+ fetchStandardsRepoFromSource,
264
+ fetchStandardsRepo,
265
+ getGuidelinesDir,
266
+ getRulesetsDir,
267
+ frontmatterSchema,
268
+ parseGuideline,
269
+ loadAllGuidelines,
270
+ loadGuideline,
271
+ toListItems,
272
+ loadRuleset,
273
+ listRulesets,
274
+ parseContext,
275
+ scoreGuideline,
276
+ matchGuidelines,
277
+ composeGuidelines
278
+ };
279
+ //# sourceMappingURL=chunk-RXA4FO7L.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp/standards/fetcher.ts","../src/mcp/standards/parser.ts","../src/mcp/standards/matcher.ts"],"sourcesContent":["/**\n * Fetches the standards repository from GitHub or local filesystem\n */\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\n\nimport { execa } from \"execa\";\n\nconst DEFAULT_OWNER = \"palindrom-ai\";\nconst DEFAULT_REPO = \"standards\";\nconst CACHE_DIR = path.join(os.tmpdir(), \"cm-standards-cache\");\n\n/** Parsed GitHub source */\ninterface GitHubSource {\n type: \"github\";\n owner: string;\n repo: string;\n ref?: string;\n}\n\n/** Parsed local source */\ninterface LocalSource {\n type: \"local\";\n path: string;\n}\n\n/** Parsed source type */\ntype ParsedSource = GitHubSource | LocalSource;\n\n/** Parse github:owner/repo[@ref] format */\nfunction parseGitHubSource(source: string): GitHubSource {\n const remainder = source.slice(7); // Remove \"github:\"\n const atIndex = remainder.indexOf(\"@\");\n const ownerRepo = atIndex !== -1 ? remainder.slice(0, atIndex) : remainder;\n const ref = atIndex !== -1 ? remainder.slice(atIndex + 1) : undefined;\n const slashIndex = ownerRepo.indexOf(\"/\");\n\n if (slashIndex === -1) {\n throw new StandardsError(`Invalid GitHub source format: ${source}. Expected github:owner/repo`);\n }\n\n const owner = ownerRepo.slice(0, slashIndex);\n const repo = ownerRepo.slice(slashIndex + 1);\n\n if (!owner || !repo) {\n throw new StandardsError(`Invalid GitHub source format: ${source}. Expected github:owner/repo`);\n }\n\n return { type: \"github\", owner, repo, ref };\n}\n\n/**\n * Parse a source string into owner/repo/ref or local path.\n * Formats:\n * - \"github:owner/repo\" - GitHub repository\n * - \"github:owner/repo@ref\" - GitHub with branch/tag\n * - Local filesystem path (absolute or relative)\n */\nfunction parseSource(source: string): ParsedSource {\n if (source.startsWith(\"github:\")) {\n return parseGitHubSource(source);\n }\n return { type: \"local\", path: source };\n}\n\n/** Error class for standards fetching failures */\nexport class StandardsError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"StandardsError\";\n }\n}\n\n/** Authentication method for GitHub */\ntype AuthMethod = \"token\" | \"ssh\" | \"none\";\n\n/**\n * Detect authentication method based on environment variables.\n * Priority: CM_REGISTRY_TOKEN > GITHUB_TOKEN > SSH key detection > none\n */\nfunction detectAuthMethod(): AuthMethod {\n if (process.env.CM_REGISTRY_TOKEN || process.env.GITHUB_TOKEN) {\n return \"token\";\n }\n if (process.env.SSH_AUTH_SOCK) {\n return \"ssh\";\n }\n return \"none\";\n}\n\n/**\n * Get the authentication token from environment variables.\n */\nfunction getAuthToken(): string | undefined {\n return process.env.CM_REGISTRY_TOKEN ?? process.env.GITHUB_TOKEN;\n}\n\n/**\n * Build the git URL for a repository based on auth method.\n */\nfunction buildGitHubUrl(auth: AuthMethod, owner: string, repo: string): string {\n switch (auth) {\n case \"ssh\":\n return `git@github.com:${owner}/${repo}.git`;\n case \"token\": {\n const token = getAuthToken();\n if (token) {\n return `https://x-access-token:${token}@github.com/${owner}/${repo}.git`;\n }\n return `https://github.com/${owner}/${repo}.git`;\n }\n case \"none\":\n default:\n return `https://github.com/${owner}/${repo}.git`;\n }\n}\n\n/**\n * Update an existing cloned repository.\n */\nasync function updateExistingRepo(repoDir: string): Promise<boolean> {\n try {\n await execa(\"git\", [\"pull\", \"--ff-only\"], { cwd: repoDir, timeout: 30_000 });\n return true;\n } catch {\n // If update fails, remove the directory so it will be re-cloned\n fs.rmSync(repoDir, { recursive: true, force: true });\n return false;\n }\n}\n\n/**\n * Clone a repository from GitHub.\n */\nasync function cloneRepo(repoDir: string, owner: string, repo: string, ref?: string): Promise<void> {\n fs.mkdirSync(CACHE_DIR, { recursive: true });\n\n const auth = detectAuthMethod();\n const url = buildGitHubUrl(auth, owner, repo);\n\n try {\n const args = [\"clone\", \"--depth\", \"1\"];\n if (ref) {\n args.push(\"--branch\", ref);\n }\n args.push(url, repoDir);\n\n await execa(\"git\", args, {\n timeout: 30_000,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"timed out\")) {\n throw new StandardsError(`Standards repo clone timed out after 30 seconds`);\n }\n throw new StandardsError(`Failed to clone standards repo: ${message}`);\n }\n}\n\n/**\n * Fetch a GitHub repository, caching it locally.\n * Returns the path to the cached repository.\n */\nasync function fetchGitHubRepo(owner: string, repo: string, ref?: string): Promise<string> {\n const cacheKey = ref ? `${owner}-${repo}-${ref}` : `${owner}-${repo}`;\n const repoDir = path.join(CACHE_DIR, cacheKey);\n\n // If repo exists, try to update it\n if (fs.existsSync(repoDir)) {\n await updateExistingRepo(repoDir);\n }\n\n // Clone if it doesn't exist (either first time or after failed update)\n if (!fs.existsSync(repoDir)) {\n await cloneRepo(repoDir, owner, repo, ref);\n }\n\n return repoDir;\n}\n\n/**\n * Resolve a local source path to an absolute path.\n */\nfunction resolveLocalPath(localPath: string, basePath?: string): string {\n if (path.isAbsolute(localPath)) {\n return localPath;\n }\n const base = basePath ?? process.cwd();\n return path.resolve(base, localPath);\n}\n\n/**\n * Fetch the standards repository from a source string.\n * Supports:\n * - \"github:owner/repo\" - GitHub repository\n * - \"github:owner/repo@ref\" - GitHub with branch/tag\n * - Local filesystem path (absolute or relative)\n *\n * @param source - Source string to fetch from\n * @param basePath - Base path for resolving relative local paths (defaults to cwd)\n * @returns Path to the standards repository\n */\nexport async function fetchStandardsRepoFromSource(\n source: string,\n basePath?: string\n): Promise<string> {\n const parsed = parseSource(source);\n\n if (parsed.type === \"local\") {\n const resolvedPath = resolveLocalPath(parsed.path, basePath);\n if (!fs.existsSync(resolvedPath)) {\n throw new StandardsError(`Local standards path does not exist: ${resolvedPath}`);\n }\n return resolvedPath;\n }\n\n return fetchGitHubRepo(parsed.owner, parsed.repo, parsed.ref);\n}\n\n/**\n * Fetch the default standards repository, caching it locally.\n * Returns the path to the cached repository.\n */\nexport async function fetchStandardsRepo(): Promise<string> {\n return fetchGitHubRepo(DEFAULT_OWNER, DEFAULT_REPO);\n}\n\n/**\n * Get the path to the guidelines directory.\n */\nexport function getGuidelinesDir(repoPath: string): string {\n return path.join(repoPath, \"guidelines\");\n}\n\n/**\n * Get the path to the rulesets directory.\n */\nexport function getRulesetsDir(repoPath: string): string {\n return path.join(repoPath, \"rulesets\");\n}\n","/**\n * Parser for guideline markdown files with YAML frontmatter\n */\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport matter from \"gray-matter\";\nimport { z } from \"zod\";\n\nimport { type Guideline, type GuidelineListItem, type Ruleset } from \"./types.js\";\nimport { StandardsError } from \"./fetcher.js\";\n\n/** Zod schema for validating guideline frontmatter */\nexport const frontmatterSchema = z.object({\n id: z.string(),\n title: z.string(),\n category: z.string(),\n priority: z.number(),\n tags: z.array(z.string()),\n});\n\n/**\n * Parse a guideline markdown file content into a Guideline object.\n */\nexport function parseGuideline(fileContent: string, filename: string): Guideline {\n const { data, content } = matter(fileContent);\n\n const result = frontmatterSchema.safeParse(data);\n if (!result.success) {\n const errors = result.error.errors.map((e) => `${e.path.join(\".\")}: ${e.message}`).join(\", \");\n throw new StandardsError(`Invalid frontmatter in ${filename}: ${errors}`);\n }\n\n return {\n ...result.data,\n content: content.trim(),\n };\n}\n\n/**\n * Load all guidelines from a directory.\n */\nexport function loadAllGuidelines(guidelinesDir: string): Guideline[] {\n if (!fs.existsSync(guidelinesDir)) {\n throw new StandardsError(`Guidelines directory not found: ${guidelinesDir}`);\n }\n\n const files = fs.readdirSync(guidelinesDir).filter((f) => f.endsWith(\".md\"));\n const guidelines: Guideline[] = [];\n\n for (const file of files) {\n const filePath = path.join(guidelinesDir, file);\n const content = fs.readFileSync(filePath, \"utf-8\");\n\n try {\n guidelines.push(parseGuideline(content, file));\n } catch (error) {\n // Skip files that fail to parse, log warning\n console.warn(`Warning: Failed to parse guideline ${file}: ${error}`);\n }\n }\n\n return guidelines;\n}\n\n/**\n * Load a single guideline by ID.\n */\nexport function loadGuideline(guidelinesDir: string, id: string): Guideline | null {\n const filePath = path.join(guidelinesDir, `${id}.md`);\n\n if (!fs.existsSync(filePath)) {\n return null;\n }\n\n const content = fs.readFileSync(filePath, \"utf-8\");\n return parseGuideline(content, `${id}.md`);\n}\n\n/**\n * Convert guidelines to list items (summary format).\n */\nexport function toListItems(guidelines: Guideline[]): GuidelineListItem[] {\n return guidelines.map((g) => ({\n id: g.id,\n title: g.title,\n tags: g.tags,\n category: g.category,\n }));\n}\n\n/**\n * Load a ruleset file by ID.\n */\nexport function loadRuleset(rulesetsDir: string, id: string): Ruleset | null {\n const filePath = path.join(rulesetsDir, `${id}.toml`);\n\n if (!fs.existsSync(filePath)) {\n return null;\n }\n\n const content = fs.readFileSync(filePath, \"utf-8\");\n return { id, content };\n}\n\n/**\n * List all available ruleset IDs.\n */\nexport function listRulesets(rulesetsDir: string): string[] {\n if (!fs.existsSync(rulesetsDir)) {\n return [];\n }\n\n return fs\n .readdirSync(rulesetsDir)\n .filter((f) => f.endsWith(\".toml\"))\n .map((f) => f.replace(\".toml\", \"\"));\n}\n","/**\n * Smart keyword matching logic for guidelines\n */\nimport { type Guideline, type MatchedGuideline } from \"./types.js\";\n\n/**\n * Parse a context string into keywords.\n * Extracts words, lowercases them, and removes duplicates.\n */\nexport function parseContext(context: string): string[] {\n const words = context\n .toLowerCase()\n .split(/[\\s,.\\-_/]+/)\n .filter((word) => word.length > 1);\n\n return [...new Set(words)];\n}\n\n/**\n * Score a guideline based on how many keywords match its tags.\n */\nexport function scoreGuideline(guideline: Guideline, keywords: string[]): number {\n const tags = new Set(guideline.tags.map((t) => t.toLowerCase()));\n let score = 0;\n\n for (const keyword of keywords) {\n if (tags.has(keyword)) {\n score++;\n }\n }\n\n // Also check if keyword appears in category or id\n const category = guideline.category.toLowerCase();\n const id = guideline.id.toLowerCase();\n\n for (const keyword of keywords) {\n if (category.includes(keyword) || id.includes(keyword)) {\n score += 0.5; // Partial match bonus\n }\n }\n\n return score;\n}\n\n/**\n * Match guidelines against a context string.\n * Returns guidelines sorted by score (descending) then priority (ascending).\n */\nexport function matchGuidelines(\n guidelines: Guideline[],\n context: string,\n limit?: number\n): MatchedGuideline[] {\n const keywords = parseContext(context);\n\n if (keywords.length === 0) {\n return [];\n }\n\n const scored = guidelines\n .map((guideline) => ({\n guideline,\n score: scoreGuideline(guideline, keywords),\n }))\n .filter((m) => m.score > 0);\n\n // Sort by score descending, then by priority ascending\n scored.sort((a, b) => {\n if (b.score !== a.score) {\n return b.score - a.score;\n }\n return a.guideline.priority - b.guideline.priority;\n });\n\n return limit ? scored.slice(0, limit) : scored;\n}\n\n/**\n * Compose matched guidelines into a single markdown document.\n */\nexport function composeGuidelines(matches: MatchedGuideline[]): string {\n if (matches.length === 0) {\n return \"No matching guidelines found for the given context.\";\n }\n\n const sections = matches.map((m) => {\n const { guideline } = m;\n return `# ${guideline.title}\\n\\n**Category:** ${guideline.category} | **Priority:** ${guideline.priority}\\n**Tags:** ${guideline.tags.join(\", \")}\\n\\n${guideline.content}`;\n });\n\n return sections.join(\"\\n\\n---\\n\\n\");\n}\n"],"mappings":"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","names":["fs","path"]}