joycraft 0.6.13 → 0.6.15

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.
@@ -3,7 +3,7 @@ import {
3
3
  DEFAULT_GITIGNORE_PROFILE,
4
4
  STATE_PATH,
5
5
  parseGitignoreProfile
6
- } from "./chunk-TD65VH2W.js";
6
+ } from "./chunk-34IWIKXS.js";
7
7
 
8
8
  // src/gitignore.ts
9
9
  import { existsSync, readFileSync, writeFileSync } from "fs";
@@ -83,4 +83,4 @@ export {
83
83
  validateGitignoreFlag,
84
84
  resolveGitignoreProfile
85
85
  };
86
- //# sourceMappingURL=chunk-VIVJUY6J.js.map
86
+ //# sourceMappingURL=chunk-YE4LWG2O.js.map
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  PRIVATE_DIRS_DISPLAY
4
- } from "./chunk-VIVJUY6J.js";
5
- import "./chunk-TD65VH2W.js";
4
+ } from "./chunk-YE4LWG2O.js";
5
+ import "./chunk-34IWIKXS.js";
6
6
 
7
7
  // src/cli.ts
8
8
  import { Command } from "commander";
@@ -15,7 +15,7 @@ var GITIGNORE_OPTION_DESC = `Gitignore profile: 'shared' (commit skills) or 'pri
15
15
  var program = new Command();
16
16
  program.name("joycraft").description("Scaffold and upgrade AI development harnesses").version(pkg.version, "-v, --version");
17
17
  program.command("init").description("Scaffold the Joycraft harness into the current project").argument("[dir]", "Target directory", ".").option("--force", "Overwrite existing files").option("--gitignore <profile>", GITIGNORE_OPTION_DESC).action(async (dir, opts) => {
18
- const { init } = await import("./init-YJYJZUZU.js");
18
+ const { init } = await import("./init-LXSMLAY5.js");
19
19
  try {
20
20
  await init(dir, { force: opts.force ?? false, gitignore: opts.gitignore });
21
21
  } catch (err) {
@@ -24,7 +24,7 @@ program.command("init").description("Scaffold the Joycraft harness into the curr
24
24
  }
25
25
  });
26
26
  program.command("upgrade").description("Upgrade installed Joycraft templates and skills to latest").argument("[dir]", "Target directory", ".").option("--yes", "Auto-accept all updates").option("--gitignore <profile>", GITIGNORE_OPTION_DESC).action(async (dir, opts) => {
27
- const { upgrade } = await import("./upgrade-33NLD24D.js");
27
+ const { upgrade } = await import("./upgrade-P3JZS7NM.js");
28
28
  try {
29
29
  await upgrade(dir, { yes: opts.yes ?? false, gitignore: opts.gitignore });
30
30
  } catch (err) {
@@ -33,15 +33,16 @@ program.command("upgrade").description("Upgrade installed Joycraft templates and
33
33
  }
34
34
  });
35
35
  program.command("init-autofix").description("Set up the Level 5 auto-fix loop with holdout scenarios").argument("[dir]", "Target directory", ".").option("--scenarios-repo <name>", "Name for scenarios repo").option("--app-id <id>", "GitHub App ID for Joycraft Autofix").option("--force", "Overwrite existing workflow files").option("--dry-run", "Show what would be created without creating it").action(async (dir, opts) => {
36
- const { initAutofix } = await import("./init-autofix-4GUVGDXT.js");
36
+ const { initAutofix } = await import("./init-autofix-OZW5ITFI.js");
37
37
  await initAutofix(dir, opts);
38
38
  });
39
39
  program.command("check-version").description("Check if a newer version of Joycraft is available").action(async () => {
40
40
  try {
41
41
  const { readFileSync: readFileSync2, existsSync } = await import("fs");
42
42
  const { join: join2 } = await import("path");
43
- const { STATE_PATH, LEGACY_VERSION_FILE } = await import("./version-2FGZETKD.js");
44
- const statePath = existsSync(join2(process.cwd(), STATE_PATH)) ? join2(process.cwd(), STATE_PATH) : join2(process.cwd(), LEGACY_VERSION_FILE);
43
+ const { STATE_PATH, LEGACY_VERSION_FILE, LEGACY_CLAUDE_STATE_PATH } = await import("./version-OTDHPJBE.js");
44
+ const candidates = [STATE_PATH, LEGACY_CLAUDE_STATE_PATH, LEGACY_VERSION_FILE];
45
+ const statePath = candidates.map((p) => join2(process.cwd(), p)).find((p) => existsSync(p)) ?? join2(process.cwd(), STATE_PATH);
45
46
  const data = JSON.parse(readFileSync2(statePath, "utf-8"));
46
47
  const res = await fetch("https://registry.npmjs.org/joycraft/latest", { signal: AbortSignal.timeout(3e3) });
47
48
  if (res.ok) {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport { PRIVATE_DIRS_DISPLAY } from './gitignore.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));\n\nconst GITIGNORE_OPTION_DESC = `Gitignore profile: 'shared' (commit skills) or 'private' (gitignore ${PRIVATE_DIRS_DISPLAY})`;\n\nconst program = new Command();\n\nprogram\n .name('joycraft')\n .description('Scaffold and upgrade AI development harnesses')\n .version(pkg.version, '-v, --version');\n\nprogram\n .command('init')\n .description('Scaffold the Joycraft harness into the current project')\n .argument('[dir]', 'Target directory', '.')\n .option('--force', 'Overwrite existing files')\n .option('--gitignore <profile>', GITIGNORE_OPTION_DESC)\n .action(async (dir: string, opts: { force?: boolean; gitignore?: string }) => {\n const { init } = await import('./init.js');\n try {\n await init(dir, { force: opts.force ?? false, gitignore: opts.gitignore });\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('upgrade')\n .description('Upgrade installed Joycraft templates and skills to latest')\n .argument('[dir]', 'Target directory', '.')\n .option('--yes', 'Auto-accept all updates')\n .option('--gitignore <profile>', GITIGNORE_OPTION_DESC)\n .action(async (dir: string, opts: { yes?: boolean; gitignore?: string }) => {\n const { upgrade } = await import('./upgrade.js');\n try {\n await upgrade(dir, { yes: opts.yes ?? false, gitignore: opts.gitignore });\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('init-autofix')\n .description('Set up the Level 5 auto-fix loop with holdout scenarios')\n .argument('[dir]', 'Target directory', '.')\n .option('--scenarios-repo <name>', 'Name for scenarios repo')\n .option('--app-id <id>', 'GitHub App ID for Joycraft Autofix')\n .option('--force', 'Overwrite existing workflow files')\n .option('--dry-run', 'Show what would be created without creating it')\n .action(async (dir: string, opts: { scenariosRepo?: string; appId?: string; force?: boolean; dryRun?: boolean }) => {\n const { initAutofix } = await import('./init-autofix.js');\n await initAutofix(dir, opts);\n });\n\nprogram\n .command('check-version')\n .description('Check if a newer version of Joycraft is available')\n .action(async () => {\n try {\n const { readFileSync, existsSync } = await import('node:fs');\n const { join } = await import('node:path');\n const { STATE_PATH, LEGACY_VERSION_FILE } = await import('./version.js');\n // Prefer the hidden state; fall back to the legacy root file for projects\n // that have not been upgraded (which relocates it) yet.\n const statePath = existsSync(join(process.cwd(), STATE_PATH))\n ? join(process.cwd(), STATE_PATH)\n : join(process.cwd(), LEGACY_VERSION_FILE);\n const data = JSON.parse(readFileSync(statePath, 'utf-8'));\n const res = await fetch('https://registry.npmjs.org/joycraft/latest', { signal: AbortSignal.timeout(3000) });\n if (res.ok) {\n const latest = ((await res.json()) as { version: string }).version;\n if (data.version !== latest) {\n console.log(`Joycraft ${latest} available (you have ${data.version}). Run: npm install -g joycraft`);\n }\n }\n } catch {\n // Silent — don't block session start\n }\n });\n\n// Start update check immediately so it runs in parallel with the command\nconst updateCheckPromise = (async (): Promise<string | null> => {\n try {\n const res = await fetch('https://registry.npmjs.org/joycraft/latest', {\n signal: AbortSignal.timeout(3000)\n });\n if (res.ok) {\n const latest = ((await res.json()) as { version: string }).version;\n if (latest !== pkg.version) {\n return `\\nJoycraft ${latest} available (you have ${pkg.version}). Run: npm install -g joycraft`;\n }\n }\n } catch {\n // Silent — don't block or error on network issues\n }\n return null;\n})();\n\n// Print update nudge after every command\nprogram.hook('postAction', async () => {\n const message = await updateCheckPromise;\n if (message) {\n console.log(message);\n }\n});\n\n// Show help when no arguments provided\nif (process.argv.length <= 2) {\n program.outputHelp();\n process.exit(0);\n}\n\nprogram.parse();\n"],"mappings":"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","names":["readFileSync","join"]}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport { PRIVATE_DIRS_DISPLAY } from './gitignore.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));\n\nconst GITIGNORE_OPTION_DESC = `Gitignore profile: 'shared' (commit skills) or 'private' (gitignore ${PRIVATE_DIRS_DISPLAY})`;\n\nconst program = new Command();\n\nprogram\n .name('joycraft')\n .description('Scaffold and upgrade AI development harnesses')\n .version(pkg.version, '-v, --version');\n\nprogram\n .command('init')\n .description('Scaffold the Joycraft harness into the current project')\n .argument('[dir]', 'Target directory', '.')\n .option('--force', 'Overwrite existing files')\n .option('--gitignore <profile>', GITIGNORE_OPTION_DESC)\n .action(async (dir: string, opts: { force?: boolean; gitignore?: string }) => {\n const { init } = await import('./init.js');\n try {\n await init(dir, { force: opts.force ?? false, gitignore: opts.gitignore });\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('upgrade')\n .description('Upgrade installed Joycraft templates and skills to latest')\n .argument('[dir]', 'Target directory', '.')\n .option('--yes', 'Auto-accept all updates')\n .option('--gitignore <profile>', GITIGNORE_OPTION_DESC)\n .action(async (dir: string, opts: { yes?: boolean; gitignore?: string }) => {\n const { upgrade } = await import('./upgrade.js');\n try {\n await upgrade(dir, { yes: opts.yes ?? false, gitignore: opts.gitignore });\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n\nprogram\n .command('init-autofix')\n .description('Set up the Level 5 auto-fix loop with holdout scenarios')\n .argument('[dir]', 'Target directory', '.')\n .option('--scenarios-repo <name>', 'Name for scenarios repo')\n .option('--app-id <id>', 'GitHub App ID for Joycraft Autofix')\n .option('--force', 'Overwrite existing workflow files')\n .option('--dry-run', 'Show what would be created without creating it')\n .action(async (dir: string, opts: { scenariosRepo?: string; appId?: string; force?: boolean; dryRun?: boolean }) => {\n const { initAutofix } = await import('./init-autofix.js');\n await initAutofix(dir, opts);\n });\n\nprogram\n .command('check-version')\n .description('Check if a newer version of Joycraft is available')\n .action(async () => {\n try {\n const { readFileSync, existsSync } = await import('node:fs');\n const { join } = await import('node:path');\n const { STATE_PATH, LEGACY_VERSION_FILE, LEGACY_CLAUDE_STATE_PATH } = await import('./version.js');\n // Prefer the current state; fall back through the legacy locations for\n // projects not yet upgraded (upgrade relocates them): the interim\n // .claude/.joycraft/state.json, then the original repo-root file.\n const candidates = [STATE_PATH, LEGACY_CLAUDE_STATE_PATH, LEGACY_VERSION_FILE];\n const statePath =\n candidates.map((p) => join(process.cwd(), p)).find((p) => existsSync(p)) ??\n join(process.cwd(), STATE_PATH);\n const data = JSON.parse(readFileSync(statePath, 'utf-8'));\n const res = await fetch('https://registry.npmjs.org/joycraft/latest', { signal: AbortSignal.timeout(3000) });\n if (res.ok) {\n const latest = ((await res.json()) as { version: string }).version;\n if (data.version !== latest) {\n console.log(`Joycraft ${latest} available (you have ${data.version}). Run: npm install -g joycraft`);\n }\n }\n } catch {\n // Silent — don't block session start\n }\n });\n\n// Start update check immediately so it runs in parallel with the command\nconst updateCheckPromise = (async (): Promise<string | null> => {\n try {\n const res = await fetch('https://registry.npmjs.org/joycraft/latest', {\n signal: AbortSignal.timeout(3000)\n });\n if (res.ok) {\n const latest = ((await res.json()) as { version: string }).version;\n if (latest !== pkg.version) {\n return `\\nJoycraft ${latest} available (you have ${pkg.version}). Run: npm install -g joycraft`;\n }\n }\n } catch {\n // Silent — don't block or error on network issues\n }\n return null;\n})();\n\n// Print update nudge after every command\nprogram.hook('postAction', async () => {\n const message = await updateCheckPromise;\n if (message) {\n console.log(message);\n }\n});\n\n// Show help when no arguments provided\nif (process.argv.length <= 2) {\n program.outputHelp();\n process.exit(0);\n}\n\nprogram.parse();\n"],"mappings":"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","names":["readFileSync","join"]}
@@ -7,7 +7,7 @@ import {
7
7
  PRIVATE_UNTRACK_COMMAND,
8
8
  applyGitignoreProfile,
9
9
  resolveGitignoreProfile
10
- } from "./chunk-VIVJUY6J.js";
10
+ } from "./chunk-YE4LWG2O.js";
11
11
  import {
12
12
  CODEX_SKILLS,
13
13
  PI_AGENTS,
@@ -16,14 +16,15 @@ import {
16
16
  PI_SKILLS,
17
17
  SKILLS,
18
18
  TEMPLATES
19
- } from "./chunk-GM2T44P6.js";
19
+ } from "./chunk-XOMQIK4U.js";
20
20
  import {
21
21
  DEFAULT_GITIGNORE_PROFILE,
22
22
  STATE_PATH,
23
23
  hashContent,
24
24
  readVersion,
25
+ resolveHarnesses,
25
26
  writeVersion
26
- } from "./chunk-TD65VH2W.js";
27
+ } from "./chunk-34IWIKXS.js";
27
28
 
28
29
  // src/init.ts
29
30
  import { mkdirSync as mkdirSync2, existsSync as existsSync4, writeFileSync as writeFileSync2, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync, chmodSync } from "fs";
@@ -310,6 +311,10 @@ async function detectStack(dir) {
310
311
  // src/improve-claude-md.ts
311
312
  import { existsSync as existsSync2 } from "fs";
312
313
  import { join as join2 } from "path";
314
+ var PRIVATE_SETUP_NOTE_MARKER = "After cloning, run";
315
+ function generatePrivateSetupNote() {
316
+ return `> **Private setup:** The harness dirs (\`.claude/\`, \`.agents/\`, \`.pi/\`) are gitignored in this repo, so they aren't committed. ${PRIVATE_SETUP_NOTE_MARKER} \`npx joycraft init\` to regenerate the skill files locally.`;
317
+ }
313
318
  function generateCommandsBlock(stack) {
314
319
  const lines = ["```bash"];
315
320
  if (stack.commands.build) lines.push(`# Build
@@ -453,6 +458,9 @@ function generateCLAUDEMd(projectName, stack, existingSkills = [], opts) {
453
458
  generateGettingStartedSection(),
454
459
  ""
455
460
  ];
461
+ if (opts?.privateProfile) {
462
+ lines.push(generatePrivateSetupNote(), "");
463
+ }
456
464
  if (existingSkills.length > 0) {
457
465
  lines.push(generateProjectToolsSection(existingSkills), "");
458
466
  }
@@ -493,7 +501,7 @@ function generateKeyFilesSection2() {
493
501
  |------|---------|
494
502
  | _TODO_ | _Add key files_ |`;
495
503
  }
496
- function generateAgentsMd(projectName, stack) {
504
+ function generateAgentsMd(projectName, stack, privateProfile = false) {
497
505
  const frameworkNote = stack.framework ? ` (${stack.framework})` : "";
498
506
  const langLabel = stack.language === "unknown" ? "" : ` | **Stack:** ${stack.language}${frameworkNote}`;
499
507
  const lines = [
@@ -515,6 +523,9 @@ function generateAgentsMd(projectName, stack) {
515
523
  generateDevelopmentSection(stack),
516
524
  ""
517
525
  ];
526
+ if (privateProfile) {
527
+ lines.push(generatePrivateSetupNote(), "");
528
+ }
518
529
  return lines.join("\n");
519
530
  }
520
531
 
@@ -721,6 +732,14 @@ async function init(dir, opts) {
721
732
  const result = { created: [], skipped: [], modified: [], warnings: [] };
722
733
  const stack = await detectStack(targetDir);
723
734
  const isPi = existsSync4(join4(targetDir, ".pi"));
735
+ const harnesses = await resolveHarnesses(process.stdin.isTTY === true);
736
+ const wants = (h) => harnesses.includes(h);
737
+ if (harnesses.length === 0) {
738
+ console.log(
739
+ "\nNo harness selected \u2014 Joycraft will not install any skills.\nPlease run init again and select at least one harness (claude, codex, pi)."
740
+ );
741
+ return;
742
+ }
724
743
  const { profile: gitignoreProfile } = await resolveGitignoreProfile({
725
744
  flag: opts.gitignore,
726
745
  persisted: readVersion(targetDir)?.gitignoreProfile,
@@ -730,7 +749,7 @@ async function init(dir, opts) {
730
749
  ensureDir(join4(targetDir, "docs", "context"));
731
750
  const skillsDir = join4(targetDir, ".claude", "skills");
732
751
  let existingSkills = [];
733
- if (existsSync4(skillsDir)) {
752
+ if (wants("claude") && existsSync4(skillsDir)) {
734
753
  existingSkills = readdirSync2(skillsDir).filter((name) => {
735
754
  if (name.startsWith("joycraft-")) return false;
736
755
  if (name.startsWith(".")) return false;
@@ -742,60 +761,53 @@ async function init(dir, opts) {
742
761
  }
743
762
  });
744
763
  }
745
- for (const [filename, content] of Object.entries(SKILLS)) {
746
- const skillName = filename.replace(/\.md$/, "");
747
- const skillDir = join4(skillsDir, skillName);
748
- ensureDir(skillDir);
749
- writeFile(join4(skillDir, "SKILL.md"), content, opts.force, result);
764
+ if (wants("claude")) {
765
+ for (const [filename, content] of Object.entries(SKILLS)) {
766
+ const skillName = filename.replace(/\.md$/, "");
767
+ const skillDir = join4(skillsDir, skillName);
768
+ ensureDir(skillDir);
769
+ writeFile(join4(skillDir, "SKILL.md"), content, opts.force, result);
770
+ }
750
771
  }
751
- const codexSkillsDir = join4(targetDir, ".agents", "skills");
752
- let existingCodexSkills = [];
753
- if (existsSync4(codexSkillsDir)) {
754
- existingCodexSkills = readdirSync2(codexSkillsDir).filter((name) => {
755
- if (name.startsWith("joycraft-")) return false;
756
- if (name.startsWith(".")) return false;
757
- const fullPath = join4(codexSkillsDir, name);
758
- try {
759
- return statSync(fullPath).isDirectory();
760
- } catch {
761
- return false;
762
- }
763
- });
772
+ if (wants("codex")) {
773
+ const codexSkillsDir = join4(targetDir, ".agents", "skills");
774
+ for (const [filename, content] of Object.entries(CODEX_SKILLS)) {
775
+ const skillName = filename.replace(/\.md$/, "");
776
+ const skillDir = join4(codexSkillsDir, skillName);
777
+ ensureDir(skillDir);
778
+ writeFile(join4(skillDir, "SKILL.md"), content, opts.force, result);
779
+ }
764
780
  }
765
- for (const [filename, content] of Object.entries(CODEX_SKILLS)) {
766
- const skillName = filename.replace(/\.md$/, "");
767
- const skillDir = join4(codexSkillsDir, skillName);
768
- ensureDir(skillDir);
769
- writeFile(join4(skillDir, "SKILL.md"), content, opts.force, result);
770
- }
771
- const piSkillsDir = join4(targetDir, ".pi", "skills");
772
- for (const [filename, content] of Object.entries(PI_SKILLS)) {
773
- const skillName = filename.replace(/\.md$/, "");
774
- const skillDir = join4(piSkillsDir, skillName);
775
- ensureDir(skillDir);
776
- writeFile(join4(skillDir, "SKILL.md"), content, opts.force, result);
777
- }
778
- const piScriptsDir = join4(targetDir, ".pi", "scripts", "joycraft");
779
- ensureDir(piScriptsDir);
780
- for (const [name, content] of Object.entries(PI_SCRIPTS)) {
781
- const scriptPath = join4(piScriptsDir, name);
782
- writeFile(scriptPath, content, opts.force, result);
783
- if (name !== "README.md") {
784
- try {
785
- chmodSync(scriptPath, 493);
786
- } catch {
781
+ if (wants("pi")) {
782
+ const piSkillsDir = join4(targetDir, ".pi", "skills");
783
+ for (const [filename, content] of Object.entries(PI_SKILLS)) {
784
+ const skillName = filename.replace(/\.md$/, "");
785
+ const skillDir = join4(piSkillsDir, skillName);
786
+ ensureDir(skillDir);
787
+ writeFile(join4(skillDir, "SKILL.md"), content, opts.force, result);
788
+ }
789
+ const piScriptsDir = join4(targetDir, ".pi", "scripts", "joycraft");
790
+ ensureDir(piScriptsDir);
791
+ for (const [name, content] of Object.entries(PI_SCRIPTS)) {
792
+ const scriptPath = join4(piScriptsDir, name);
793
+ writeFile(scriptPath, content, opts.force, result);
794
+ if (name !== "README.md") {
795
+ try {
796
+ chmodSync(scriptPath, 493);
797
+ } catch {
798
+ }
787
799
  }
788
800
  }
789
- }
790
- const piExtDir = join4(targetDir, ".pi", "extensions");
791
- ensureDir(piExtDir);
792
- for (const [name, content] of Object.entries(PI_EXTENSIONS)) {
793
- writeFile(join4(piExtDir, name), content, opts.force, result);
794
- }
795
- const piAgentsDir = join4(targetDir, ".pi", "agents");
796
- ensureDir(piAgentsDir);
797
- for (const [name, content] of Object.entries(PI_AGENTS)) {
798
- writeFile(join4(piAgentsDir, name), content, opts.force, result);
801
+ const piExtDir = join4(targetDir, ".pi", "extensions");
802
+ ensureDir(piExtDir);
803
+ for (const [name, content] of Object.entries(PI_EXTENSIONS)) {
804
+ writeFile(join4(piExtDir, name), content, opts.force, result);
805
+ }
806
+ const piAgentsDir = join4(targetDir, ".pi", "agents");
807
+ ensureDir(piAgentsDir);
808
+ for (const [name, content] of Object.entries(PI_AGENTS)) {
809
+ writeFile(join4(piAgentsDir, name), content, opts.force, result);
810
+ }
799
811
  }
800
812
  const templatesDir = join4(targetDir, "docs", "templates");
801
813
  ensureDir(templatesDir);
@@ -808,7 +820,9 @@ async function init(dir, opts) {
808
820
  result.skipped.push(claudeMdPath);
809
821
  } else {
810
822
  const projectName = basename(targetDir);
811
- const content = generateCLAUDEMd(projectName, stack, existingSkills);
823
+ const content = generateCLAUDEMd(projectName, stack, existingSkills, {
824
+ privateProfile: gitignoreProfile === "private"
825
+ });
812
826
  writeFileSync2(claudeMdPath, content, "utf-8");
813
827
  result.created.push(claudeMdPath);
814
828
  }
@@ -817,40 +831,47 @@ async function init(dir, opts) {
817
831
  result.skipped.push(agentsMdPath);
818
832
  } else {
819
833
  const projectName = basename(targetDir);
820
- const content = generateAgentsMd(projectName, stack);
834
+ const content = generateAgentsMd(projectName, stack, gitignoreProfile === "private");
821
835
  writeFileSync2(agentsMdPath, content, "utf-8");
822
836
  result.created.push(agentsMdPath);
823
837
  }
824
838
  const fileHashes = {};
825
- for (const [filename, content] of Object.entries(SKILLS)) {
826
- const skillName = filename.replace(/\.md$/, "");
827
- fileHashes[join4(".claude", "skills", skillName, "SKILL.md")] = hashContent(content);
839
+ if (wants("claude")) {
840
+ for (const [filename, content] of Object.entries(SKILLS)) {
841
+ const skillName = filename.replace(/\.md$/, "");
842
+ fileHashes[join4(".claude", "skills", skillName, "SKILL.md")] = hashContent(content);
843
+ }
828
844
  }
829
- for (const [filename, content] of Object.entries(CODEX_SKILLS)) {
830
- const skillName = filename.replace(/\.md$/, "");
831
- fileHashes[join4(".agents", "skills", skillName, "SKILL.md")] = hashContent(content);
845
+ if (wants("codex")) {
846
+ for (const [filename, content] of Object.entries(CODEX_SKILLS)) {
847
+ const skillName = filename.replace(/\.md$/, "");
848
+ fileHashes[join4(".agents", "skills", skillName, "SKILL.md")] = hashContent(content);
849
+ }
832
850
  }
833
851
  for (const [filename, content] of Object.entries(TEMPLATES)) {
834
852
  fileHashes[join4("docs", "templates", filename)] = hashContent(content);
835
853
  }
836
- for (const [filename, content] of Object.entries(PI_SKILLS)) {
837
- const skillName = filename.replace(/\.md$/, "");
838
- fileHashes[join4(".pi", "skills", skillName, "SKILL.md")] = hashContent(content);
839
- }
840
- for (const [name, content] of Object.entries(PI_SCRIPTS)) {
841
- fileHashes[join4(".pi", "scripts", "joycraft", name)] = hashContent(content);
842
- }
843
- for (const [name, content] of Object.entries(PI_EXTENSIONS)) {
844
- fileHashes[join4(".pi", "extensions", name)] = hashContent(content);
845
- }
846
- for (const [name, content] of Object.entries(PI_AGENTS)) {
847
- fileHashes[join4(".pi", "agents", name)] = hashContent(content);
854
+ if (wants("pi")) {
855
+ for (const [filename, content] of Object.entries(PI_SKILLS)) {
856
+ const skillName = filename.replace(/\.md$/, "");
857
+ fileHashes[join4(".pi", "skills", skillName, "SKILL.md")] = hashContent(content);
858
+ }
859
+ for (const [name, content] of Object.entries(PI_SCRIPTS)) {
860
+ fileHashes[join4(".pi", "scripts", "joycraft", name)] = hashContent(content);
861
+ }
862
+ for (const [name, content] of Object.entries(PI_EXTENSIONS)) {
863
+ fileHashes[join4(".pi", "extensions", name)] = hashContent(content);
864
+ }
865
+ for (const [name, content] of Object.entries(PI_AGENTS)) {
866
+ fileHashes[join4(".pi", "agents", name)] = hashContent(content);
867
+ }
848
868
  }
849
- writeVersion(targetDir, getPackageVersion(), fileHashes, gitignoreProfile);
869
+ writeVersion(targetDir, getPackageVersion(), fileHashes, gitignoreProfile, harnesses);
850
870
  applyGitignoreProfile(targetDir, gitignoreProfile);
851
- const hooksDir = join4(targetDir, ".claude", "hooks");
852
- ensureDir(hooksDir);
853
- const hookScript = `// Joycraft version check \u2014 runs on Claude Code session start
871
+ if (wants("claude")) {
872
+ const hooksDir = join4(targetDir, ".claude", "hooks");
873
+ ensureDir(hooksDir);
874
+ const hookScript = `// Joycraft version check \u2014 runs on Claude Code session start
854
875
  import { readFileSync } from 'node:fs';
855
876
  import { join } from 'node:path';
856
877
  try {
@@ -862,69 +883,78 @@ try {
862
883
  }
863
884
  } catch {}
864
885
  `;
865
- writeFile(join4(hooksDir, "joycraft-version-check.mjs"), hookScript, opts.force, result);
866
- const settingsPath = join4(targetDir, ".claude", "settings.json");
867
- let settings = {};
868
- let settingsMalformed = false;
869
- if (existsSync4(settingsPath)) {
870
- try {
871
- settings = JSON.parse(readFileSync3(settingsPath, "utf-8"));
872
- } catch {
873
- settingsMalformed = true;
874
- result.warnings.push(
875
- "settings.json exists but is malformed \u2014 skipping settings merge to protect your config.\n Fix the JSON in .claude/settings.json and re-run init."
876
- );
877
- }
878
- }
879
- if (!settingsMalformed) {
880
- if (!settings.hooks) settings.hooks = {};
881
- const hooksConfig = settings.hooks;
882
- if (!hooksConfig.SessionStart) hooksConfig.SessionStart = [];
883
- const sessionStartHooks = hooksConfig.SessionStart;
884
- const hasJoycraftHook = sessionStartHooks.some((h) => {
885
- const innerHooks = h.hooks;
886
- return innerHooks?.some((ih) => typeof ih.command === "string" && ih.command.includes("joycraft"));
887
- });
888
- if (!hasJoycraftHook) {
889
- sessionStartHooks.push({
890
- matcher: "",
891
- hooks: [{
892
- type: "command",
893
- command: "node .claude/hooks/joycraft-version-check.mjs"
894
- }]
895
- });
896
- writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
897
- result.created.push(settingsPath);
898
- }
899
- const permissions = generatePermissions(stack);
886
+ writeFile(join4(hooksDir, "joycraft-version-check.mjs"), hookScript, opts.force, result);
887
+ const settingsPath = join4(targetDir, ".claude", "settings.json");
888
+ let settings = {};
889
+ let settingsMalformed = false;
900
890
  if (existsSync4(settingsPath)) {
901
891
  try {
902
892
  settings = JSON.parse(readFileSync3(settingsPath, "utf-8"));
903
893
  } catch {
894
+ settingsMalformed = true;
904
895
  result.warnings.push(
905
- "settings.json became unreadable after hook merge \u2014 skipping permissions merge.\n Fix the JSON in .claude/settings.json and re-run init."
896
+ "settings.json exists but is malformed \u2014 skipping settings merge to protect your config.\n Fix the JSON in .claude/settings.json and re-run init."
906
897
  );
907
- settingsMalformed = true;
908
898
  }
909
899
  }
910
900
  if (!settingsMalformed) {
911
- if (!settings.permissions) settings.permissions = {};
912
- const perms = settings.permissions;
913
- if (!perms.allow) perms.allow = [];
914
- if (!perms.deny) perms.deny = [];
915
- for (const rule of permissions.allow) {
916
- if (!perms.allow.includes(rule)) perms.allow.push(rule);
901
+ if (!settings.hooks) settings.hooks = {};
902
+ const hooksConfig = settings.hooks;
903
+ if (!hooksConfig.SessionStart) hooksConfig.SessionStart = [];
904
+ const sessionStartHooks = hooksConfig.SessionStart;
905
+ const hasJoycraftHook = sessionStartHooks.some((h) => {
906
+ const innerHooks = h.hooks;
907
+ return innerHooks?.some((ih) => typeof ih.command === "string" && ih.command.includes("joycraft"));
908
+ });
909
+ if (!settings.env) settings.env = {};
910
+ const env = settings.env;
911
+ const envMissing = !("CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS" in env);
912
+ if (envMissing) {
913
+ env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = "1";
914
+ }
915
+ if (!hasJoycraftHook) {
916
+ sessionStartHooks.push({
917
+ matcher: "",
918
+ hooks: [{
919
+ type: "command",
920
+ command: "node .claude/hooks/joycraft-version-check.mjs"
921
+ }]
922
+ });
917
923
  }
918
- for (const rule of permissions.deny) {
919
- if (!perms.deny.includes(rule)) perms.deny.push(rule);
924
+ if (!hasJoycraftHook || envMissing) {
925
+ writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
926
+ if (!result.created.includes(settingsPath)) result.created.push(settingsPath);
927
+ }
928
+ const permissions = generatePermissions(stack);
929
+ if (existsSync4(settingsPath)) {
930
+ try {
931
+ settings = JSON.parse(readFileSync3(settingsPath, "utf-8"));
932
+ } catch {
933
+ result.warnings.push(
934
+ "settings.json became unreadable after hook merge \u2014 skipping permissions merge.\n Fix the JSON in .claude/settings.json and re-run init."
935
+ );
936
+ settingsMalformed = true;
937
+ }
938
+ }
939
+ if (!settingsMalformed) {
940
+ if (!settings.permissions) settings.permissions = {};
941
+ const perms = settings.permissions;
942
+ if (!perms.allow) perms.allow = [];
943
+ if (!perms.deny) perms.deny = [];
944
+ for (const rule of permissions.allow) {
945
+ if (!perms.allow.includes(rule)) perms.allow.push(rule);
946
+ }
947
+ for (const rule of permissions.deny) {
948
+ if (!perms.deny.includes(rule)) perms.deny.push(rule);
949
+ }
950
+ writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
920
951
  }
921
- writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
922
952
  }
953
+ const hookResult = installSafeguardHooks(targetDir, [], opts.force, settingsMalformed);
954
+ result.created.push(...hookResult.created);
955
+ result.skipped.push(...hookResult.skipped);
923
956
  }
924
- const hookResult = installSafeguardHooks(targetDir, [], opts.force, settingsMalformed);
925
- result.created.push(...hookResult.created);
926
- result.skipped.push(...hookResult.skipped);
927
- if (gitignoreProfile === "shared") {
957
+ if (gitignoreProfile === "shared" && wants("claude")) {
928
958
  const gitignorePath = join4(targetDir, ".gitignore");
929
959
  if (existsSync4(gitignorePath)) {
930
960
  const gitignore = readFileSync3(gitignorePath, "utf-8");
@@ -935,10 +965,13 @@ try {
935
965
  }
936
966
  }
937
967
  }
938
- printSummary(result, stack, existingSkills, isPi, gitignoreProfile);
968
+ printSummary(result, stack, existingSkills, isPi, gitignoreProfile, harnesses);
939
969
  }
940
- function printSummary(result, stack, existingSkills = [], isPi = false, gitignoreProfile = DEFAULT_GITIGNORE_PROFILE) {
970
+ function printSummary(result, stack, existingSkills = [], isPi = false, gitignoreProfile = DEFAULT_GITIGNORE_PROFILE, harnesses = []) {
941
971
  console.log("\nJoycraft initialized!\n");
972
+ if (harnesses.length > 0) {
973
+ console.log(` Installed harnesses: ${harnesses.join(", ")}`);
974
+ }
942
975
  if (stack.language !== "unknown") {
943
976
  const fw = stack.framework ? ` + ${stack.framework}` : "";
944
977
  console.log(` Detected stack: ${stack.language}${fw} (${stack.packageManager})`);
@@ -1008,4 +1041,4 @@ function printSummary(result, stack, existingSkills = [], isPi = false, gitignor
1008
1041
  export {
1009
1042
  init
1010
1043
  };
1011
- //# sourceMappingURL=init-YJYJZUZU.js.map
1044
+ //# sourceMappingURL=init-LXSMLAY5.js.map