scripts/classify-primitives.mjs
scripts/classify-primitives.mjsBrowse 6 files
793 tokens
2,785 bytes
Token encoding: o200k_base
Snapshot 8beb6cd
← Back to SKILL.md
1#!/usr/bin/env node2/**3 * Classify changed paths against the primitives taxonomy.4 *5 * Usage:6 * node classify-primitives.mjs [--base <ref>] [--head <ref>]7 * git diff --name-only main...HEAD | node classify-primitives.mjs --stdin8 *9 * Defaults: base=main, head=HEAD (git diff --name-only base...head).10 */11import { execFileSync } from 'node:child_process'12import { createInterface } from 'node:readline'13import { classifyPaths, loadPrimitivesMap } from './primitives-map.mjs'14 15const args = process.argv.slice(2)16 17function readFlag(name, fallback) {18 const index = args.indexOf(name)19 if (index === -1) return fallback20 const value = args[index + 1]21 if (!value || value.startsWith('--')) {22 console.error(`missing value for ${name}`)23 process.exit(1)24 }25 return value26}27 28async function readStdinPaths() {29 const paths = []30 const rl = createInterface({ input: process.stdin, crlfDelay: Infinity })31 for await (const line of rl) {32 const trimmed = line.trim()33 if (trimmed) paths.push(trimmed)34 }35 return paths36}37 38function readGitPaths(base, head) {39 const output = execFileSync(40 'git',41 ['diff', '--name-only', `${base}...${head}`],42 { encoding: 'utf8' },43 )44 return output45 .split(/\r?\n/)46 .map((line) => line.trim())47 .filter(Boolean)48}49 50const useStdin = args.includes('--stdin')51const jsonOut = args.includes('--json')52const base = readFlag('--base', 'main')53const head = readFlag('--head', 'HEAD')54 55const paths = useStdin ? await readStdinPaths() : readGitPaths(base, head)56const map = loadPrimitivesMap()57const { matched, unmatched } = classifyPaths(paths, map)58 59if (jsonOut) {60 console.log(61 JSON.stringify(62 {63 base: useStdin ? null : base,64 head: useStdin ? null : head,65 pathCount: paths.length,66 matched: matched.map((entry) => ({67 id: entry.primitive.id,68 name: entry.primitive.name,69 group: entry.primitive.group ?? null,70 root: entry.root,71 files: entry.files,72 })),73 unmatched,74 },75 null,76 2,77 ),78 )79 process.exit(0)80}81 82if (paths.length === 0) {83 console.log('No changed paths.')84 process.exit(0)85}86 87console.log(88 useStdin89 ? `Classified ${paths.length} path(s) from stdin:\n`90 : `Classified ${paths.length} path(s) from ${base}...${head}:\n`,91)92 93for (const entry of matched) {94 console.log(95 `- ${entry.primitive.id} (${entry.primitive.group ?? 'ungrouped'}) via ${entry.root}`,96 )97 for (const file of entry.files.slice(0, 8)) {98 console.log(` ${file}`)99 }100 if (entry.files.length > 8) {101 console.log(` … +${entry.files.length - 8} more`)102 }103}104 105if (unmatched.length > 0) {106 console.log(`\nUnmatched (${unmatched.length}):`)107 for (const file of unmatched.slice(0, 20)) {108 console.log(` ${file}`)109 }110 if (unmatched.length > 20) {111 console.log(` … +${unmatched.length - 20} more`)112 }113}114