joycraft 0.6.14 → 0.6.16

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.
@@ -1,75 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/version.ts
4
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
5
- import { join, dirname } from "path";
6
- import { createHash } from "crypto";
7
- var STATE_PATH = join(".claude", ".joycraft", "state.json");
8
- var LEGACY_VERSION_FILE = ".joycraft-version";
9
- var HASH_LENGTH = 16;
10
- var DEFAULT_GITIGNORE_PROFILE = "shared";
11
- function parseGitignoreProfile(value) {
12
- const v = typeof value === "string" ? value.trim().toLowerCase() : value;
13
- return v === "shared" || v === "private" ? v : null;
14
- }
15
- function hashContent(content) {
16
- return createHash("sha256").update(content).digest("hex");
17
- }
18
- function truncateHash(hash) {
19
- return hash.slice(0, HASH_LENGTH);
20
- }
21
- function readVersion(dir) {
22
- const filePath = join(dir, STATE_PATH);
23
- if (!existsSync(filePath)) return null;
24
- try {
25
- const raw = readFileSync(filePath, "utf-8");
26
- const parsed = JSON.parse(raw);
27
- if (typeof parsed.version === "string" && typeof parsed.files === "object") {
28
- const profile = parseGitignoreProfile(parsed.gitignoreProfile);
29
- return {
30
- version: parsed.version,
31
- files: parsed.files,
32
- ...profile ? { gitignoreProfile: profile } : {}
33
- };
34
- }
35
- return null;
36
- } catch {
37
- return null;
38
- }
39
- }
40
- function writeVersion(dir, version, files, gitignoreProfile) {
41
- const filePath = join(dir, STATE_PATH);
42
- const profile = gitignoreProfile ?? readVersion(dir)?.gitignoreProfile;
43
- const truncated = {};
44
- for (const [path, hash] of Object.entries(files)) {
45
- truncated[path] = truncateHash(hash);
46
- }
47
- const data = {
48
- version,
49
- files: truncated,
50
- ...profile ? { gitignoreProfile: profile } : {}
51
- };
52
- mkdirSync(dirname(filePath), { recursive: true });
53
- writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
54
- }
55
- function getLevel(dir) {
56
- const hasAutofix = existsSync(join(dir, ".github", "workflows", "autofix.yml"));
57
- if (!hasAutofix) return 4;
58
- const claudeMdPath = join(dir, "CLAUDE.md");
59
- if (!existsSync(claudeMdPath)) return 4;
60
- const content = readFileSync(claudeMdPath, "utf-8");
61
- return content.includes("## External Validation") ? 5 : 4;
62
- }
63
-
64
- export {
65
- STATE_PATH,
66
- LEGACY_VERSION_FILE,
67
- DEFAULT_GITIGNORE_PROFILE,
68
- parseGitignoreProfile,
69
- hashContent,
70
- truncateHash,
71
- readVersion,
72
- writeVersion,
73
- getLevel
74
- };
75
- //# sourceMappingURL=chunk-TD65VH2W.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/version.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { createHash } from 'node:crypto';\n\n/**\n * Project-relative path to Joycraft's upgrade-state file.\n *\n * Hidden inside `.claude/` — the dir `init` always creates for every harness —\n * directly analogous to npm's own hidden lockfile at\n * `node_modules/.package-lock.json`. Never at the repo root, never committed\n * (init/upgrade gitignore it). The old root location was `.joycraft-version`\n * (see LEGACY_VERSION_FILE); `upgrade` migrates it on first run.\n */\nexport const STATE_PATH = join('.claude', '.joycraft', 'state.json');\n\n/** The pre-relocation root path. Kept only so `upgrade` can migrate it. */\nexport const LEGACY_VERSION_FILE = '.joycraft-version';\n\n/**\n * Length we truncate stored hashes to. Full SHA-256 is 64 hex chars; 16 hex\n * (64 bits) is ample to detect customization across ~100 managed files and\n * shrinks the state ~4×. Compare truncated-on-both-sides (see upgrade.ts).\n */\nconst HASH_LENGTH = 16;\n\n/**\n * How much of the Joycraft harness is tracked in git.\n * - `shared` — commit skills/agents/pi so teammates get the workflow (default).\n * - `private` — gitignore .claude/, .agents/, .pi/; track only CLAUDE.md,\n * AGENTS.md, and docs/.\n */\nexport type GitignoreProfile = 'shared' | 'private';\n\nexport const DEFAULT_GITIGNORE_PROFILE: GitignoreProfile = 'shared';\n\n/**\n * Narrow an arbitrary value to a GitignoreProfile, or null if unrecognized.\n * Strings are normalized (trim + lowercase) here so every call site — flag,\n * prompt, persisted state — gets the same case-insensitivity for free.\n */\nexport function parseGitignoreProfile(value: unknown): GitignoreProfile | null {\n const v = typeof value === 'string' ? value.trim().toLowerCase() : value;\n return v === 'shared' || v === 'private' ? v : null;\n}\n\nexport interface VersionInfo {\n version: string;\n files: Record<string, string>;\n /**\n * The gitignore profile chosen at init/upgrade. Absent on state written by\n * Joycraft versions before this field existed — treat absent as `shared`.\n */\n gitignoreProfile?: GitignoreProfile;\n}\n\nexport function hashContent(content: string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\n/** Truncate a (full) content hash to the stored length. Idempotent for already-short input. */\nexport function truncateHash(hash: string): string {\n return hash.slice(0, HASH_LENGTH);\n}\n\nexport function readVersion(dir: string): VersionInfo | null {\n const filePath = join(dir, STATE_PATH);\n if (!existsSync(filePath)) return null;\n try {\n const raw = readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(raw);\n if (typeof parsed.version === 'string' && typeof parsed.files === 'object') {\n // Sanitize the profile: ignore unknown/legacy values rather than\n // returning them (absent or bogus → undefined, callers default to shared).\n const profile = parseGitignoreProfile(parsed.gitignoreProfile);\n return {\n version: parsed.version,\n files: parsed.files,\n ...(profile ? { gitignoreProfile: profile } : {}),\n };\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport function writeVersion(\n dir: string,\n version: string,\n files: Record<string, string>,\n gitignoreProfile?: GitignoreProfile\n): void {\n const filePath = join(dir, STATE_PATH);\n // An omitted profile means \"no new decision\", not \"clear it\": preserve\n // whatever is already persisted so call sites that only refresh\n // version/hashes can never silently strip a saved choice.\n const profile = gitignoreProfile ?? readVersion(dir)?.gitignoreProfile;\n // Store truncated hashes — single source of truth for the on-disk shape.\n const truncated: Record<string, string> = {};\n for (const [path, hash] of Object.entries(files)) {\n truncated[path] = truncateHash(hash);\n }\n const data: VersionInfo = {\n version,\n files: truncated,\n ...(profile ? { gitignoreProfile: profile } : {}),\n };\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, JSON.stringify(data, null, 2) + '\\n', 'utf-8');\n}\n\n/**\n * Detect the current Joycraft harness level for a project directory.\n * Returns 5 if Level 5 artifacts (autofix workflow + External Validation) are present, 4 otherwise.\n */\nexport function getLevel(dir: string): number {\n const hasAutofix = existsSync(join(dir, '.github', 'workflows', 'autofix.yml'));\n if (!hasAutofix) return 4;\n const claudeMdPath = join(dir, 'CLAUDE.md');\n if (!existsSync(claudeMdPath)) return 4;\n const content = readFileSync(claudeMdPath, 'utf-8');\n return content.includes('## External Validation') ? 5 : 4;\n}\n"],"mappings":";;;AAAA,SAAS,cAAc,eAAe,YAAY,iBAAiB;AACnE,SAAS,MAAM,eAAe;AAC9B,SAAS,kBAAkB;AAWpB,IAAM,aAAa,KAAK,WAAW,aAAa,YAAY;AAG5D,IAAM,sBAAsB;AAOnC,IAAM,cAAc;AAUb,IAAM,4BAA8C;AAOpD,SAAS,sBAAsB,OAAyC;AAC7E,QAAM,IAAI,OAAO,UAAU,WAAW,MAAM,KAAK,EAAE,YAAY,IAAI;AACnE,SAAO,MAAM,YAAY,MAAM,YAAY,IAAI;AACjD;AAYO,SAAS,YAAY,SAAyB;AACnD,SAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAC1D;AAGO,SAAS,aAAa,MAAsB;AACjD,SAAO,KAAK,MAAM,GAAG,WAAW;AAClC;AAEO,SAAS,YAAY,KAAiC;AAC3D,QAAM,WAAW,KAAK,KAAK,UAAU;AACrC,MAAI,CAAC,WAAW,QAAQ,EAAG,QAAO;AAClC,MAAI;AACF,UAAM,MAAM,aAAa,UAAU,OAAO;AAC1C,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,OAAO,OAAO,YAAY,YAAY,OAAO,OAAO,UAAU,UAAU;AAG1E,YAAM,UAAU,sBAAsB,OAAO,gBAAgB;AAC7D,aAAO;AAAA,QACL,SAAS,OAAO;AAAA,QAChB,OAAO,OAAO;AAAA,QACd,GAAI,UAAU,EAAE,kBAAkB,QAAQ,IAAI,CAAC;AAAA,MACjD;AAAA,IACF;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,aACd,KACA,SACA,OACA,kBACM;AACN,QAAM,WAAW,KAAK,KAAK,UAAU;AAIrC,QAAM,UAAU,oBAAoB,YAAY,GAAG,GAAG;AAEtD,QAAM,YAAoC,CAAC;AAC3C,aAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,cAAU,IAAI,IAAI,aAAa,IAAI;AAAA,EACrC;AACA,QAAM,OAAoB;AAAA,IACxB;AAAA,IACA,OAAO;AAAA,IACP,GAAI,UAAU,EAAE,kBAAkB,QAAQ,IAAI,CAAC;AAAA,EACjD;AACA,YAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,gBAAc,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,MAAM,OAAO;AACvE;AAMO,SAAS,SAAS,KAAqB;AAC5C,QAAM,aAAa,WAAW,KAAK,KAAK,WAAW,aAAa,aAAa,CAAC;AAC9E,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,eAAe,KAAK,KAAK,WAAW;AAC1C,MAAI,CAAC,WAAW,YAAY,EAAG,QAAO;AACtC,QAAM,UAAU,aAAa,cAAc,OAAO;AAClD,SAAO,QAAQ,SAAS,wBAAwB,IAAI,IAAI;AAC1D;","names":[]}