joycraft 0.5.20 → 0.6.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.
package/README.md CHANGED
@@ -52,8 +52,8 @@ Joycraft auto-detects your tech stack and creates:
52
52
 
53
53
  - **CLAUDE.md** with behavioral boundaries (Always / Ask First / Never) and correct build/test/lint commands
54
54
  - **AGENTS.md** for Codex compatibility
55
- - **14 skills** installed to `.claude/skills/` (Claude Code) and `.agents/skills/` (Codex) — see [Which skill do I need?](#which-skill-do-i-need) below
56
- - **docs/** structure: `briefs/`, `specs/`, `discoveries/`, `contracts/`, `decisions/`, `context/`
55
+ - **15 skills** installed to `.claude/skills/` (Claude Code) and `.agents/skills/` (Codex) — see [Which skill do I need?](#which-skill-do-i-need) below
56
+ - **docs/** structure: `docs/context/` is created up front; feature work lands in `docs/features/<slug>/{brief.md, research.md, design.md, specs/}` and deferred work in `docs/backlog/` — these are created lazily by the skills that write to them
57
57
  - **Context documents** in `docs/context/`: production map, dangerous assumptions, decision log, institutional knowledge, and troubleshooting guide
58
58
  - **Templates** including atomic spec, feature brief, implementation plan, boundary framework, and workflow templates for scenario generation and autofix loops
59
59
 
@@ -78,6 +78,7 @@ Frameworks auto-detected: Next.js, FastAPI, Django, Flask, Actix, Axum, Express,
78
78
  | Implement a spec with TDD | `/joycraft-implement` | Read spec → write failing tests → implement until green |
79
79
  | Run specs autonomously without hand-holding | `/joycraft-implement-level5` | Autofix loop + holdout scenario testing |
80
80
  | Verify an implementation independently | `/joycraft-verify` | Read-only subagent checks work against the spec |
81
+ | Set up Joycraft for a team | `/joycraft-collaborative-setup` | Scaffold `docs/areas/`, owner conventions, a team CONTRIBUTING doc |
81
82
 
82
83
  The core loop:
83
84
 
@@ -166,6 +167,54 @@ Joycraft synthesizes ideas and patterns from people doing extraordinary work in
166
167
  - **[Simon Willison](https://x.com/simonw)** for his [analysis of the Software Factory](https://simonwillison.net/2026/Feb/7/software-factory/) that helped contextualize StrongDM's approach for the broader community
167
168
  - **[Anthropic](https://www.anthropic.com/)** for Claude Code's skills, hooks, and CLAUDE.md system that makes tool-native AI development possible, and the [harness patterns for long-running agents](https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents)
168
169
 
170
+ ## Migration: Flat → Per-Feature Layout (v0.6+)
171
+
172
+ Starting in v0.6, Joycraft organizes feature artifacts into per-feature folders:
173
+
174
+ - `docs/briefs/<slug>.md` → `docs/features/<slug>/brief.md`
175
+ - `docs/research/<slug>.md` → `docs/features/<slug>/research.md`
176
+ - `docs/designs/<slug>.md` → `docs/features/<slug>/design.md`
177
+ - `docs/specs/<feature>/` → `docs/features/<slug>/specs/` (when `<feature>` matches a brief slug)
178
+
179
+ `npx joycraft upgrade` performs this migration automatically and forcefully on the first
180
+ post-upgrade run — no Y/N prompt. The CLI prints a summary of every move before applying it.
181
+ Spec directories under `docs/specs/` whose name doesn't match any brief slug (area-level specs
182
+ like bugfix folders) are left in place.
183
+
184
+ ### What you'll see on the first post-upgrade run
185
+
186
+ ```
187
+ Joycraft is migrating your docs/ to the new per-feature layout:
188
+
189
+ 2026-04-01-auth-redesign/
190
+ docs/briefs/2026-04-01-auth-redesign.md → docs/features/2026-04-01-auth-redesign/brief.md
191
+ docs/research/2026-04-01-auth-redesign.md → docs/features/2026-04-01-auth-redesign/research.md
192
+
193
+ Left in place — area-level specs (e.g., bugfix areas):
194
+ docs/specs/login-bugfix/
195
+
196
+ Migration complete. See the README section "Migration: Flat → Per-Feature Layout"
197
+ for context on what changed and why. If your project is a git repo, run
198
+ `git status` to inspect the moves before committing.
199
+ ```
200
+
201
+ ### Why forced (not opt-in)
202
+
203
+ All doc-producing skills (`joycraft-new-feature`, `joycraft-research`, `joycraft-design`,
204
+ `joycraft-decompose`, etc.) write to the new per-feature paths. Supporting both layouts
205
+ indefinitely would mean every skill carries dual-path branches; the forced migration keeps
206
+ the convention single and skills small.
207
+
208
+ ### Recovering / customizing
209
+
210
+ Every move is a plain filesystem move (no `git mv`). If you want a different organization
211
+ after the migration, you can `git mv` files anywhere — Joycraft only depends on the
212
+ `docs/features/<slug>/` shape for skills it ships, not on every doc living there. Git
213
+ history follows files via `git log --follow`.
214
+
215
+ If a brief and its destination already exist (re-running upgrade after a partial migration),
216
+ the move is skipped and reported. The migration is idempotent.
217
+
169
218
  ## Contributing
170
219
 
171
220
  Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide.
@@ -28,9 +28,25 @@ function writeVersion(dir, version, files) {
28
28
  writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
29
29
  }
30
30
 
31
+ // src/package-version.ts
32
+ import { readFileSync as readFileSync2 } from "fs";
33
+ import { fileURLToPath } from "url";
34
+ import { dirname, join as join2 } from "path";
35
+ var __dirname = dirname(fileURLToPath(import.meta.url));
36
+ function getPackageVersion() {
37
+ const pkgPath = join2(__dirname, "..", "package.json");
38
+ const raw = readFileSync2(pkgPath, "utf-8");
39
+ const pkg = JSON.parse(raw);
40
+ if (typeof pkg.version !== "string" || pkg.version.length === 0) {
41
+ throw new Error(`Joycraft package.json at ${pkgPath} is missing a version field`);
42
+ }
43
+ return pkg.version;
44
+ }
45
+
31
46
  export {
32
47
  hashContent,
33
48
  readVersion,
34
- writeVersion
49
+ writeVersion,
50
+ getPackageVersion
35
51
  };
36
- //# sourceMappingURL=chunk-VDQTLM2D.js.map
52
+ //# sourceMappingURL=chunk-4ZI7B4IW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/version.ts","../src/package-version.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { createHash } from 'node:crypto';\n\nconst VERSION_FILE = '.joycraft-version';\n\nexport interface VersionInfo {\n version: string;\n files: Record<string, string>;\n}\n\nexport function hashContent(content: string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\nexport function readVersion(dir: string): VersionInfo | null {\n const filePath = join(dir, VERSION_FILE);\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 return parsed as VersionInfo;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport function writeVersion(dir: string, version: string, files: Record<string, string>): void {\n const filePath = join(dir, VERSION_FILE);\n const data: VersionInfo = { version, files };\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","import { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport function getPackageVersion(): string {\n const pkgPath = join(__dirname, '..', 'package.json');\n const raw = readFileSync(pkgPath, 'utf-8');\n const pkg = JSON.parse(raw) as { version?: unknown };\n if (typeof pkg.version !== 'string' || pkg.version.length === 0) {\n throw new Error(`Joycraft package.json at ${pkgPath} is missing a version field`);\n }\n return pkg.version;\n}\n"],"mappings":";;;AAAA,SAAS,cAAc,eAAe,kBAAkB;AACxD,SAAS,YAAY;AACrB,SAAS,kBAAkB;AAE3B,IAAM,eAAe;AAOd,SAAS,YAAY,SAAyB;AACnD,SAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAC1D;AAEO,SAAS,YAAY,KAAiC;AAC3D,QAAM,WAAW,KAAK,KAAK,YAAY;AACvC,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;AAC1E,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,aAAa,KAAa,SAAiB,OAAqC;AAC9F,QAAM,WAAW,KAAK,KAAK,YAAY;AACvC,QAAM,OAAoB,EAAE,SAAS,MAAM;AAC3C,gBAAc,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,MAAM,OAAO;AACvE;;;AClCA,SAAS,gBAAAA,qBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,SAAS,QAAAC,aAAY;AAE9B,IAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAEjD,SAAS,oBAA4B;AAC1C,QAAM,UAAUA,MAAK,WAAW,MAAM,cAAc;AACpD,QAAM,MAAMD,cAAa,SAAS,OAAO;AACzC,QAAM,MAAM,KAAK,MAAM,GAAG;AAC1B,MAAI,OAAO,IAAI,YAAY,YAAY,IAAI,QAAQ,WAAW,GAAG;AAC/D,UAAM,IAAI,MAAM,4BAA4B,OAAO,6BAA6B;AAAA,EAClF;AACA,SAAO,IAAI;AACb;","names":["readFileSync","join"]}