joycraft 0.5.7 → 0.5.8
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 +2 -348
- package/dist/{chunk-G342HURJ.js → chunk-A2CQG5J5.js} +495 -61
- package/dist/{chunk-G342HURJ.js.map → chunk-A2CQG5J5.js.map} +1 -1
- package/dist/cli.js +3 -3
- package/dist/index.d.ts +6 -0
- package/dist/index.js +78 -7
- package/dist/index.js.map +1 -1
- package/dist/{init-7FUTURUY.js → init-QXG5BT4Y.js} +82 -11
- package/dist/init-QXG5BT4Y.js.map +1 -0
- package/dist/{init-autofix-K4B5BD5V.js → init-autofix-Y5DQOFEU.js} +2 -2
- package/dist/{upgrade-2Y7D2HCD.js → upgrade-VUOSXPR5.js} +2 -2
- package/package.json +1 -1
- package/dist/init-7FUTURUY.js.map +0 -1
- /package/dist/{init-autofix-K4B5BD5V.js.map → init-autofix-Y5DQOFEU.js.map} +0 -0
- /package/dist/{upgrade-2Y7D2HCD.js.map → upgrade-VUOSXPR5.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -10,15 +10,15 @@ var pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"
|
|
|
10
10
|
var program = new Command();
|
|
11
11
|
program.name("joycraft").description("Scaffold and upgrade AI development harnesses").version(pkg.version, "-v, --version");
|
|
12
12
|
program.command("init").description("Scaffold the Joycraft harness into the current project").argument("[dir]", "Target directory", ".").option("--force", "Overwrite existing files").action(async (dir, opts) => {
|
|
13
|
-
const { init } = await import("./init-
|
|
13
|
+
const { init } = await import("./init-QXG5BT4Y.js");
|
|
14
14
|
await init(dir, { force: opts.force ?? false });
|
|
15
15
|
});
|
|
16
16
|
program.command("upgrade").description("Upgrade installed Joycraft templates and skills to latest").argument("[dir]", "Target directory", ".").option("--yes", "Auto-accept all updates").action(async (dir, opts) => {
|
|
17
|
-
const { upgrade } = await import("./upgrade-
|
|
17
|
+
const { upgrade } = await import("./upgrade-VUOSXPR5.js");
|
|
18
18
|
await upgrade(dir, { yes: opts.yes ?? false });
|
|
19
19
|
});
|
|
20
20
|
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) => {
|
|
21
|
-
const { initAutofix } = await import("./init-autofix-
|
|
21
|
+
const { initAutofix } = await import("./init-autofix-Y5DQOFEU.js");
|
|
22
22
|
await initAutofix(dir, opts);
|
|
23
23
|
});
|
|
24
24
|
program.command("check-version").description("Check if a newer version of Joycraft is available").action(async () => {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
interface TestingStrategy {
|
|
2
|
+
backbone: 'playwright' | 'maestro' | 'api' | 'native';
|
|
3
|
+
testFormat: string;
|
|
4
|
+
layers: ('ui' | 'api' | 'logic' | 'static')[];
|
|
5
|
+
}
|
|
1
6
|
interface StackInfo {
|
|
2
7
|
language: string;
|
|
3
8
|
packageManager: string;
|
|
@@ -9,6 +14,7 @@ interface StackInfo {
|
|
|
9
14
|
deploy?: string;
|
|
10
15
|
};
|
|
11
16
|
framework?: string;
|
|
17
|
+
testingStrategy?: TestingStrategy;
|
|
12
18
|
}
|
|
13
19
|
declare function detectStack(dir: string): Promise<StackInfo>;
|
|
14
20
|
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
// src/detect.ts
|
|
2
|
-
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { readFileSync, existsSync, readdirSync } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
|
+
var WEB_FRAMEWORKS = /* @__PURE__ */ new Set(["Next.js", "Nuxt", "Remix", "React", "Vue", "Svelte"]);
|
|
5
|
+
var API_FRAMEWORKS = /* @__PURE__ */ new Set(["Express", "Fastify", "FastAPI", "Django", "Flask", "Gin", "Fiber", "Echo", "Actix", "Axum", "Rocket"]);
|
|
6
|
+
function buildTestingStrategy(backbone) {
|
|
7
|
+
switch (backbone) {
|
|
8
|
+
case "playwright":
|
|
9
|
+
return { backbone, testFormat: ".spec.ts", layers: ["ui", "api", "logic", "static"] };
|
|
10
|
+
case "maestro":
|
|
11
|
+
return { backbone, testFormat: ".yaml", layers: ["ui", "api", "logic", "static"] };
|
|
12
|
+
case "api":
|
|
13
|
+
return { backbone, testFormat: ".test.ts", layers: ["api", "logic", "static"] };
|
|
14
|
+
case "native":
|
|
15
|
+
return { backbone, testFormat: ".test.ts", layers: ["logic", "static"] };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
4
18
|
function readFile(path) {
|
|
5
19
|
try {
|
|
6
20
|
return readFileSync(path, "utf-8");
|
|
@@ -58,11 +72,23 @@ function detectNode(dir) {
|
|
|
58
72
|
else if (pkg.devDependencies?.["typescript"]) {
|
|
59
73
|
commands.typecheck = "tsc --noEmit";
|
|
60
74
|
}
|
|
75
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
76
|
+
let testingStrategy;
|
|
77
|
+
if (allDeps["react-native"]) {
|
|
78
|
+
testingStrategy = buildTestingStrategy("maestro");
|
|
79
|
+
} else if (framework && WEB_FRAMEWORKS.has(framework)) {
|
|
80
|
+
testingStrategy = buildTestingStrategy("playwright");
|
|
81
|
+
} else if (framework && API_FRAMEWORKS.has(framework)) {
|
|
82
|
+
testingStrategy = buildTestingStrategy("api");
|
|
83
|
+
} else {
|
|
84
|
+
testingStrategy = buildTestingStrategy("native");
|
|
85
|
+
}
|
|
61
86
|
return {
|
|
62
87
|
language: "node",
|
|
63
88
|
packageManager: pm,
|
|
64
89
|
commands,
|
|
65
|
-
framework
|
|
90
|
+
framework,
|
|
91
|
+
testingStrategy
|
|
66
92
|
};
|
|
67
93
|
}
|
|
68
94
|
function detectPythonFramework(content) {
|
|
@@ -90,6 +116,7 @@ function detectPython(dir) {
|
|
|
90
116
|
}
|
|
91
117
|
const framework = detectPythonFramework(pyproject);
|
|
92
118
|
const hasPytest = /pytest/i.test(pyproject);
|
|
119
|
+
const testingStrategy = framework && API_FRAMEWORKS.has(framework) ? buildTestingStrategy("api") : buildTestingStrategy("native");
|
|
93
120
|
return {
|
|
94
121
|
language: "python",
|
|
95
122
|
packageManager: pm,
|
|
@@ -98,12 +125,14 @@ function detectPython(dir) {
|
|
|
98
125
|
test: hasPytest ? `${run} pytest` : `${run} pytest`,
|
|
99
126
|
lint: `${run} ruff check .`
|
|
100
127
|
},
|
|
101
|
-
framework
|
|
128
|
+
framework,
|
|
129
|
+
testingStrategy
|
|
102
130
|
};
|
|
103
131
|
}
|
|
104
132
|
const requirements = readFile(join(dir, "requirements.txt"));
|
|
105
133
|
if (requirements !== null) {
|
|
106
134
|
const framework = detectPythonFramework(requirements);
|
|
135
|
+
const testingStrategy = framework && API_FRAMEWORKS.has(framework) ? buildTestingStrategy("api") : buildTestingStrategy("native");
|
|
107
136
|
return {
|
|
108
137
|
language: "python",
|
|
109
138
|
packageManager: "pip",
|
|
@@ -112,7 +141,8 @@ function detectPython(dir) {
|
|
|
112
141
|
test: "python -m pytest",
|
|
113
142
|
lint: "python -m ruff check ."
|
|
114
143
|
},
|
|
115
|
-
framework
|
|
144
|
+
framework,
|
|
145
|
+
testingStrategy
|
|
116
146
|
};
|
|
117
147
|
}
|
|
118
148
|
return null;
|
|
@@ -124,6 +154,7 @@ function detectRust(dir) {
|
|
|
124
154
|
if (/actix-web/.test(cargo)) framework = "Actix";
|
|
125
155
|
else if (/axum/.test(cargo)) framework = "Axum";
|
|
126
156
|
else if (/rocket/.test(cargo)) framework = "Rocket";
|
|
157
|
+
const testingStrategy = framework && API_FRAMEWORKS.has(framework) ? buildTestingStrategy("api") : buildTestingStrategy("native");
|
|
127
158
|
return {
|
|
128
159
|
language: "rust",
|
|
129
160
|
packageManager: "cargo",
|
|
@@ -132,7 +163,8 @@ function detectRust(dir) {
|
|
|
132
163
|
test: "cargo test",
|
|
133
164
|
lint: "cargo clippy"
|
|
134
165
|
},
|
|
135
|
-
framework
|
|
166
|
+
framework,
|
|
167
|
+
testingStrategy
|
|
136
168
|
};
|
|
137
169
|
}
|
|
138
170
|
function detectGo(dir) {
|
|
@@ -142,6 +174,7 @@ function detectGo(dir) {
|
|
|
142
174
|
if (/github\.com\/gin-gonic\/gin/.test(gomod)) framework = "Gin";
|
|
143
175
|
else if (/github\.com\/gofiber\/fiber/.test(gomod)) framework = "Fiber";
|
|
144
176
|
else if (/github\.com\/labstack\/echo/.test(gomod)) framework = "Echo";
|
|
177
|
+
const testingStrategy = framework && API_FRAMEWORKS.has(framework) ? buildTestingStrategy("api") : buildTestingStrategy("native");
|
|
145
178
|
return {
|
|
146
179
|
language: "go",
|
|
147
180
|
packageManager: "go",
|
|
@@ -150,7 +183,8 @@ function detectGo(dir) {
|
|
|
150
183
|
test: "go test ./...",
|
|
151
184
|
lint: "golangci-lint run"
|
|
152
185
|
},
|
|
153
|
-
framework
|
|
186
|
+
framework,
|
|
187
|
+
testingStrategy
|
|
154
188
|
};
|
|
155
189
|
}
|
|
156
190
|
function detectSwift(dir) {
|
|
@@ -162,7 +196,42 @@ function detectSwift(dir) {
|
|
|
162
196
|
commands: {
|
|
163
197
|
build: "swift build",
|
|
164
198
|
test: "swift test"
|
|
165
|
-
}
|
|
199
|
+
},
|
|
200
|
+
testingStrategy: buildTestingStrategy("native")
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function detectFlutter(dir) {
|
|
204
|
+
const pubspec = readFile(join(dir, "pubspec.yaml"));
|
|
205
|
+
if (pubspec === null) return null;
|
|
206
|
+
if (!/flutter/i.test(pubspec)) return null;
|
|
207
|
+
return {
|
|
208
|
+
language: "dart",
|
|
209
|
+
packageManager: "flutter",
|
|
210
|
+
commands: {
|
|
211
|
+
build: "flutter build",
|
|
212
|
+
test: "flutter test",
|
|
213
|
+
lint: "flutter analyze"
|
|
214
|
+
},
|
|
215
|
+
framework: "Flutter",
|
|
216
|
+
testingStrategy: buildTestingStrategy("maestro")
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function detectXcode(dir) {
|
|
220
|
+
try {
|
|
221
|
+
const entries = readdirSync(dir);
|
|
222
|
+
const hasXcode = entries.some((e) => e.endsWith(".xcodeproj") || e.endsWith(".xcworkspace"));
|
|
223
|
+
if (!hasXcode) return null;
|
|
224
|
+
} catch {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
language: "swift",
|
|
229
|
+
packageManager: "xcode",
|
|
230
|
+
commands: {
|
|
231
|
+
build: "xcodebuild build",
|
|
232
|
+
test: "xcodebuild test"
|
|
233
|
+
},
|
|
234
|
+
testingStrategy: buildTestingStrategy("maestro")
|
|
166
235
|
};
|
|
167
236
|
}
|
|
168
237
|
function detectMakefile(dir) {
|
|
@@ -194,7 +263,9 @@ async function detectStack(dir) {
|
|
|
194
263
|
detectPython,
|
|
195
264
|
detectRust,
|
|
196
265
|
detectGo,
|
|
266
|
+
detectFlutter,
|
|
197
267
|
detectSwift,
|
|
268
|
+
detectXcode,
|
|
198
269
|
detectMakefile,
|
|
199
270
|
detectDockerfile
|
|
200
271
|
];
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/detect.ts"],"sourcesContent":["import { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport interface StackInfo {\n language: string;\n packageManager: string;\n commands: {\n build?: string;\n test?: string;\n lint?: string;\n typecheck?: string;\n deploy?: string;\n };\n framework?: string;\n}\n\nfunction readFile(path: string): string | null {\n try {\n return readFileSync(path, 'utf-8');\n } catch {\n return null;\n }\n}\n\nfunction detectNodeFramework(pkg: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> }): string | undefined {\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (allDeps['next']) return 'Next.js';\n if (allDeps['nuxt']) return 'Nuxt';\n if (allDeps['@remix-run/node'] || allDeps['@remix-run/react']) return 'Remix';\n if (allDeps['express']) return 'Express';\n if (allDeps['fastify']) return 'Fastify';\n if (allDeps['react']) return 'React';\n if (allDeps['vue']) return 'Vue';\n if (allDeps['svelte']) return 'Svelte';\n return undefined;\n}\n\nfunction detectNodeTestFramework(pkg: { devDependencies?: Record<string, string>; dependencies?: Record<string, string> }): string | undefined {\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (allDeps['vitest']) return 'vitest';\n if (allDeps['jest']) return 'jest';\n if (allDeps['mocha']) return 'mocha';\n return undefined;\n}\n\nfunction detectNodePackageManager(dir: string): string {\n if (existsSync(join(dir, 'pnpm-lock.yaml'))) return 'pnpm';\n if (existsSync(join(dir, 'yarn.lock'))) return 'yarn';\n if (existsSync(join(dir, 'bun.lockb')) || existsSync(join(dir, 'bun.lock'))) return 'bun';\n return 'npm';\n}\n\nfunction detectNode(dir: string): StackInfo | null {\n const raw = readFile(join(dir, 'package.json'));\n if (raw === null) return null;\n\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(raw);\n } catch {\n return null;\n }\n\n const pm = detectNodePackageManager(dir);\n const run = pm === 'npm' ? 'npm run' : pm;\n const scripts = (pkg.scripts ?? {}) as Record<string, string>;\n const framework = detectNodeFramework(pkg as { dependencies?: Record<string, string>; devDependencies?: Record<string, string> });\n const testFramework = detectNodeTestFramework(pkg as { devDependencies?: Record<string, string>; dependencies?: Record<string, string> });\n\n const commands: StackInfo['commands'] = {};\n if (scripts.build) commands.build = `${run} build`;\n else commands.build = `${run} build`;\n if (scripts.test) commands.test = `${run} test`;\n else if (testFramework) commands.test = `${run} test`;\n else commands.test = `${pm === 'npm' ? 'npm' : pm} test`;\n if (scripts.lint) commands.lint = `${run} lint`;\n if (scripts.typecheck) commands.typecheck = `${run} typecheck`;\n else if ((pkg.devDependencies as Record<string, string> | undefined)?.['typescript']) {\n commands.typecheck = 'tsc --noEmit';\n }\n\n return {\n language: 'node',\n packageManager: pm,\n commands,\n framework,\n };\n}\n\nfunction detectPythonFramework(content: string): string | undefined {\n if (/fastapi/i.test(content)) return 'FastAPI';\n if (/django/i.test(content)) return 'Django';\n if (/flask/i.test(content)) return 'Flask';\n return undefined;\n}\n\nfunction detectPython(dir: string): StackInfo | null {\n const pyproject = readFile(join(dir, 'pyproject.toml'));\n if (pyproject !== null) {\n const isPoetry = /\\[tool\\.poetry\\]/.test(pyproject);\n const isUv = existsSync(join(dir, 'uv.lock'));\n\n let pm: string;\n let run: string;\n if (isUv) {\n pm = 'uv';\n run = 'uv run';\n } else if (isPoetry) {\n pm = 'poetry';\n run = 'poetry run';\n } else {\n pm = 'pip';\n run = 'python -m';\n }\n\n const framework = detectPythonFramework(pyproject);\n const hasPytest = /pytest/i.test(pyproject);\n\n return {\n language: 'python',\n packageManager: pm,\n commands: {\n build: `${pm === 'poetry' ? 'poetry' : pm} build`,\n test: hasPytest ? `${run} pytest` : `${run} pytest`,\n lint: `${run} ruff check .`,\n },\n framework,\n };\n }\n\n const requirements = readFile(join(dir, 'requirements.txt'));\n if (requirements !== null) {\n const framework = detectPythonFramework(requirements);\n return {\n language: 'python',\n packageManager: 'pip',\n commands: {\n build: 'pip install -e .',\n test: 'python -m pytest',\n lint: 'python -m ruff check .',\n },\n framework,\n };\n }\n\n return null;\n}\n\nfunction detectRust(dir: string): StackInfo | null {\n const cargo = readFile(join(dir, 'Cargo.toml'));\n if (cargo === null) return null;\n\n let framework: string | undefined;\n if (/actix-web/.test(cargo)) framework = 'Actix';\n else if (/axum/.test(cargo)) framework = 'Axum';\n else if (/rocket/.test(cargo)) framework = 'Rocket';\n\n return {\n language: 'rust',\n packageManager: 'cargo',\n commands: {\n build: 'cargo build',\n test: 'cargo test',\n lint: 'cargo clippy',\n },\n framework,\n };\n}\n\nfunction detectGo(dir: string): StackInfo | null {\n const gomod = readFile(join(dir, 'go.mod'));\n if (gomod === null) return null;\n\n let framework: string | undefined;\n if (/github\\.com\\/gin-gonic\\/gin/.test(gomod)) framework = 'Gin';\n else if (/github\\.com\\/gofiber\\/fiber/.test(gomod)) framework = 'Fiber';\n else if (/github\\.com\\/labstack\\/echo/.test(gomod)) framework = 'Echo';\n\n return {\n language: 'go',\n packageManager: 'go',\n commands: {\n build: 'go build ./...',\n test: 'go test ./...',\n lint: 'golangci-lint run',\n },\n framework,\n };\n}\n\nfunction detectSwift(dir: string): StackInfo | null {\n const pkg = readFile(join(dir, 'Package.swift'));\n if (pkg === null) return null;\n\n return {\n language: 'swift',\n packageManager: 'swift',\n commands: {\n build: 'swift build',\n test: 'swift test',\n },\n };\n}\n\nfunction detectMakefile(dir: string): StackInfo | null {\n const makefile = readFile(join(dir, 'Makefile'));\n if (makefile === null) return null;\n\n const commands: StackInfo['commands'] = {};\n commands.build = 'make build';\n if (/^test:/m.test(makefile)) commands.test = 'make test';\n if (/^lint:/m.test(makefile)) commands.lint = 'make lint';\n\n return {\n language: 'unknown',\n packageManager: 'make',\n commands,\n };\n}\n\nfunction detectDockerfile(dir: string): StackInfo | null {\n if (!existsSync(join(dir, 'Dockerfile'))) return null;\n\n return {\n language: 'unknown',\n packageManager: 'docker',\n commands: {\n build: 'docker build .',\n },\n };\n}\n\nexport async function detectStack(dir: string): Promise<StackInfo> {\n const detectors = [\n detectNode,\n detectPython,\n detectRust,\n detectGo,\n detectSwift,\n detectMakefile,\n detectDockerfile,\n ];\n\n for (const detect of detectors) {\n const result = detect(dir);\n if (result) return result;\n }\n\n return { language: 'unknown', packageManager: '', commands: {} };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/detect.ts"],"sourcesContent":["import { readFileSync, existsSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport interface TestingStrategy {\n backbone: 'playwright' | 'maestro' | 'api' | 'native';\n testFormat: string;\n layers: ('ui' | 'api' | 'logic' | 'static')[];\n}\n\nexport interface StackInfo {\n language: string;\n packageManager: string;\n commands: {\n build?: string;\n test?: string;\n lint?: string;\n typecheck?: string;\n deploy?: string;\n };\n framework?: string;\n testingStrategy?: TestingStrategy;\n}\n\nconst WEB_FRAMEWORKS = new Set(['Next.js', 'Nuxt', 'Remix', 'React', 'Vue', 'Svelte']);\nconst API_FRAMEWORKS = new Set(['Express', 'Fastify', 'FastAPI', 'Django', 'Flask', 'Gin', 'Fiber', 'Echo', 'Actix', 'Axum', 'Rocket']);\n\nfunction buildTestingStrategy(backbone: TestingStrategy['backbone']): TestingStrategy {\n switch (backbone) {\n case 'playwright':\n return { backbone, testFormat: '.spec.ts', layers: ['ui', 'api', 'logic', 'static'] };\n case 'maestro':\n return { backbone, testFormat: '.yaml', layers: ['ui', 'api', 'logic', 'static'] };\n case 'api':\n return { backbone, testFormat: '.test.ts', layers: ['api', 'logic', 'static'] };\n case 'native':\n return { backbone, testFormat: '.test.ts', layers: ['logic', 'static'] };\n }\n}\n\nfunction readFile(path: string): string | null {\n try {\n return readFileSync(path, 'utf-8');\n } catch {\n return null;\n }\n}\n\nfunction detectNodeFramework(pkg: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> }): string | undefined {\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (allDeps['next']) return 'Next.js';\n if (allDeps['nuxt']) return 'Nuxt';\n if (allDeps['@remix-run/node'] || allDeps['@remix-run/react']) return 'Remix';\n if (allDeps['express']) return 'Express';\n if (allDeps['fastify']) return 'Fastify';\n if (allDeps['react']) return 'React';\n if (allDeps['vue']) return 'Vue';\n if (allDeps['svelte']) return 'Svelte';\n return undefined;\n}\n\nfunction detectNodeTestFramework(pkg: { devDependencies?: Record<string, string>; dependencies?: Record<string, string> }): string | undefined {\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (allDeps['vitest']) return 'vitest';\n if (allDeps['jest']) return 'jest';\n if (allDeps['mocha']) return 'mocha';\n return undefined;\n}\n\nfunction detectNodePackageManager(dir: string): string {\n if (existsSync(join(dir, 'pnpm-lock.yaml'))) return 'pnpm';\n if (existsSync(join(dir, 'yarn.lock'))) return 'yarn';\n if (existsSync(join(dir, 'bun.lockb')) || existsSync(join(dir, 'bun.lock'))) return 'bun';\n return 'npm';\n}\n\nfunction detectNode(dir: string): StackInfo | null {\n const raw = readFile(join(dir, 'package.json'));\n if (raw === null) return null;\n\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(raw);\n } catch {\n return null;\n }\n\n const pm = detectNodePackageManager(dir);\n const run = pm === 'npm' ? 'npm run' : pm;\n const scripts = (pkg.scripts ?? {}) as Record<string, string>;\n const framework = detectNodeFramework(pkg as { dependencies?: Record<string, string>; devDependencies?: Record<string, string> });\n const testFramework = detectNodeTestFramework(pkg as { devDependencies?: Record<string, string>; dependencies?: Record<string, string> });\n\n const commands: StackInfo['commands'] = {};\n if (scripts.build) commands.build = `${run} build`;\n else commands.build = `${run} build`;\n if (scripts.test) commands.test = `${run} test`;\n else if (testFramework) commands.test = `${run} test`;\n else commands.test = `${pm === 'npm' ? 'npm' : pm} test`;\n if (scripts.lint) commands.lint = `${run} lint`;\n if (scripts.typecheck) commands.typecheck = `${run} typecheck`;\n else if ((pkg.devDependencies as Record<string, string> | undefined)?.['typescript']) {\n commands.typecheck = 'tsc --noEmit';\n }\n\n const allDeps = { ...(pkg.dependencies as Record<string, string> | undefined), ...(pkg.devDependencies as Record<string, string> | undefined) };\n let testingStrategy: TestingStrategy;\n if (allDeps['react-native']) {\n testingStrategy = buildTestingStrategy('maestro');\n } else if (framework && WEB_FRAMEWORKS.has(framework)) {\n testingStrategy = buildTestingStrategy('playwright');\n } else if (framework && API_FRAMEWORKS.has(framework)) {\n testingStrategy = buildTestingStrategy('api');\n } else {\n testingStrategy = buildTestingStrategy('native');\n }\n\n return {\n language: 'node',\n packageManager: pm,\n commands,\n framework,\n testingStrategy,\n };\n}\n\nfunction detectPythonFramework(content: string): string | undefined {\n if (/fastapi/i.test(content)) return 'FastAPI';\n if (/django/i.test(content)) return 'Django';\n if (/flask/i.test(content)) return 'Flask';\n return undefined;\n}\n\nfunction detectPython(dir: string): StackInfo | null {\n const pyproject = readFile(join(dir, 'pyproject.toml'));\n if (pyproject !== null) {\n const isPoetry = /\\[tool\\.poetry\\]/.test(pyproject);\n const isUv = existsSync(join(dir, 'uv.lock'));\n\n let pm: string;\n let run: string;\n if (isUv) {\n pm = 'uv';\n run = 'uv run';\n } else if (isPoetry) {\n pm = 'poetry';\n run = 'poetry run';\n } else {\n pm = 'pip';\n run = 'python -m';\n }\n\n const framework = detectPythonFramework(pyproject);\n const hasPytest = /pytest/i.test(pyproject);\n\n const testingStrategy = framework && API_FRAMEWORKS.has(framework)\n ? buildTestingStrategy('api')\n : buildTestingStrategy('native');\n\n return {\n language: 'python',\n packageManager: pm,\n commands: {\n build: `${pm === 'poetry' ? 'poetry' : pm} build`,\n test: hasPytest ? `${run} pytest` : `${run} pytest`,\n lint: `${run} ruff check .`,\n },\n framework,\n testingStrategy,\n };\n }\n\n const requirements = readFile(join(dir, 'requirements.txt'));\n if (requirements !== null) {\n const framework = detectPythonFramework(requirements);\n const testingStrategy = framework && API_FRAMEWORKS.has(framework)\n ? buildTestingStrategy('api')\n : buildTestingStrategy('native');\n\n return {\n language: 'python',\n packageManager: 'pip',\n commands: {\n build: 'pip install -e .',\n test: 'python -m pytest',\n lint: 'python -m ruff check .',\n },\n framework,\n testingStrategy,\n };\n }\n\n return null;\n}\n\nfunction detectRust(dir: string): StackInfo | null {\n const cargo = readFile(join(dir, 'Cargo.toml'));\n if (cargo === null) return null;\n\n let framework: string | undefined;\n if (/actix-web/.test(cargo)) framework = 'Actix';\n else if (/axum/.test(cargo)) framework = 'Axum';\n else if (/rocket/.test(cargo)) framework = 'Rocket';\n\n const testingStrategy = framework && API_FRAMEWORKS.has(framework)\n ? buildTestingStrategy('api')\n : buildTestingStrategy('native');\n\n return {\n language: 'rust',\n packageManager: 'cargo',\n commands: {\n build: 'cargo build',\n test: 'cargo test',\n lint: 'cargo clippy',\n },\n framework,\n testingStrategy,\n };\n}\n\nfunction detectGo(dir: string): StackInfo | null {\n const gomod = readFile(join(dir, 'go.mod'));\n if (gomod === null) return null;\n\n let framework: string | undefined;\n if (/github\\.com\\/gin-gonic\\/gin/.test(gomod)) framework = 'Gin';\n else if (/github\\.com\\/gofiber\\/fiber/.test(gomod)) framework = 'Fiber';\n else if (/github\\.com\\/labstack\\/echo/.test(gomod)) framework = 'Echo';\n\n const testingStrategy = framework && API_FRAMEWORKS.has(framework)\n ? buildTestingStrategy('api')\n : buildTestingStrategy('native');\n\n return {\n language: 'go',\n packageManager: 'go',\n commands: {\n build: 'go build ./...',\n test: 'go test ./...',\n lint: 'golangci-lint run',\n },\n framework,\n testingStrategy,\n };\n}\n\nfunction detectSwift(dir: string): StackInfo | null {\n const pkg = readFile(join(dir, 'Package.swift'));\n if (pkg === null) return null;\n\n return {\n language: 'swift',\n packageManager: 'swift',\n commands: {\n build: 'swift build',\n test: 'swift test',\n },\n testingStrategy: buildTestingStrategy('native'),\n };\n}\n\nfunction detectFlutter(dir: string): StackInfo | null {\n const pubspec = readFile(join(dir, 'pubspec.yaml'));\n if (pubspec === null) return null;\n if (!/flutter/i.test(pubspec)) return null;\n\n return {\n language: 'dart',\n packageManager: 'flutter',\n commands: {\n build: 'flutter build',\n test: 'flutter test',\n lint: 'flutter analyze',\n },\n framework: 'Flutter',\n testingStrategy: buildTestingStrategy('maestro'),\n };\n}\n\nfunction detectXcode(dir: string): StackInfo | null {\n // Check for .xcodeproj or .xcworkspace directories\n try {\n const entries = readdirSync(dir);\n const hasXcode = entries.some(e => e.endsWith('.xcodeproj') || e.endsWith('.xcworkspace'));\n if (!hasXcode) return null;\n } catch {\n return null;\n }\n\n return {\n language: 'swift',\n packageManager: 'xcode',\n commands: {\n build: 'xcodebuild build',\n test: 'xcodebuild test',\n },\n testingStrategy: buildTestingStrategy('maestro'),\n };\n}\n\nfunction detectMakefile(dir: string): StackInfo | null {\n const makefile = readFile(join(dir, 'Makefile'));\n if (makefile === null) return null;\n\n const commands: StackInfo['commands'] = {};\n commands.build = 'make build';\n if (/^test:/m.test(makefile)) commands.test = 'make test';\n if (/^lint:/m.test(makefile)) commands.lint = 'make lint';\n\n return {\n language: 'unknown',\n packageManager: 'make',\n commands,\n };\n}\n\nfunction detectDockerfile(dir: string): StackInfo | null {\n if (!existsSync(join(dir, 'Dockerfile'))) return null;\n\n return {\n language: 'unknown',\n packageManager: 'docker',\n commands: {\n build: 'docker build .',\n },\n };\n}\n\nexport async function detectStack(dir: string): Promise<StackInfo> {\n const detectors = [\n detectNode,\n detectPython,\n detectRust,\n detectGo,\n detectFlutter,\n detectSwift,\n detectXcode,\n detectMakefile,\n detectDockerfile,\n ];\n\n for (const detect of detectors) {\n const result = detect(dir);\n if (result) return result;\n }\n\n return { language: 'unknown', packageManager: '', commands: {} };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -6,15 +6,29 @@ import {
|
|
|
6
6
|
import {
|
|
7
7
|
SKILLS,
|
|
8
8
|
TEMPLATES
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-A2CQG5J5.js";
|
|
10
10
|
|
|
11
11
|
// src/init.ts
|
|
12
|
-
import { mkdirSync as mkdirSync2, existsSync as existsSync3, writeFileSync as writeFileSync2, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
|
|
12
|
+
import { mkdirSync as mkdirSync2, existsSync as existsSync3, writeFileSync as writeFileSync2, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync } from "fs";
|
|
13
13
|
import { join as join3, basename, resolve, dirname } from "path";
|
|
14
14
|
|
|
15
15
|
// src/detect.ts
|
|
16
|
-
import { readFileSync, existsSync } from "fs";
|
|
16
|
+
import { readFileSync, existsSync, readdirSync } from "fs";
|
|
17
17
|
import { join } from "path";
|
|
18
|
+
var WEB_FRAMEWORKS = /* @__PURE__ */ new Set(["Next.js", "Nuxt", "Remix", "React", "Vue", "Svelte"]);
|
|
19
|
+
var API_FRAMEWORKS = /* @__PURE__ */ new Set(["Express", "Fastify", "FastAPI", "Django", "Flask", "Gin", "Fiber", "Echo", "Actix", "Axum", "Rocket"]);
|
|
20
|
+
function buildTestingStrategy(backbone) {
|
|
21
|
+
switch (backbone) {
|
|
22
|
+
case "playwright":
|
|
23
|
+
return { backbone, testFormat: ".spec.ts", layers: ["ui", "api", "logic", "static"] };
|
|
24
|
+
case "maestro":
|
|
25
|
+
return { backbone, testFormat: ".yaml", layers: ["ui", "api", "logic", "static"] };
|
|
26
|
+
case "api":
|
|
27
|
+
return { backbone, testFormat: ".test.ts", layers: ["api", "logic", "static"] };
|
|
28
|
+
case "native":
|
|
29
|
+
return { backbone, testFormat: ".test.ts", layers: ["logic", "static"] };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
18
32
|
function readFile(path) {
|
|
19
33
|
try {
|
|
20
34
|
return readFileSync(path, "utf-8");
|
|
@@ -72,11 +86,23 @@ function detectNode(dir) {
|
|
|
72
86
|
else if (pkg.devDependencies?.["typescript"]) {
|
|
73
87
|
commands.typecheck = "tsc --noEmit";
|
|
74
88
|
}
|
|
89
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
90
|
+
let testingStrategy;
|
|
91
|
+
if (allDeps["react-native"]) {
|
|
92
|
+
testingStrategy = buildTestingStrategy("maestro");
|
|
93
|
+
} else if (framework && WEB_FRAMEWORKS.has(framework)) {
|
|
94
|
+
testingStrategy = buildTestingStrategy("playwright");
|
|
95
|
+
} else if (framework && API_FRAMEWORKS.has(framework)) {
|
|
96
|
+
testingStrategy = buildTestingStrategy("api");
|
|
97
|
+
} else {
|
|
98
|
+
testingStrategy = buildTestingStrategy("native");
|
|
99
|
+
}
|
|
75
100
|
return {
|
|
76
101
|
language: "node",
|
|
77
102
|
packageManager: pm,
|
|
78
103
|
commands,
|
|
79
|
-
framework
|
|
104
|
+
framework,
|
|
105
|
+
testingStrategy
|
|
80
106
|
};
|
|
81
107
|
}
|
|
82
108
|
function detectPythonFramework(content) {
|
|
@@ -104,6 +130,7 @@ function detectPython(dir) {
|
|
|
104
130
|
}
|
|
105
131
|
const framework = detectPythonFramework(pyproject);
|
|
106
132
|
const hasPytest = /pytest/i.test(pyproject);
|
|
133
|
+
const testingStrategy = framework && API_FRAMEWORKS.has(framework) ? buildTestingStrategy("api") : buildTestingStrategy("native");
|
|
107
134
|
return {
|
|
108
135
|
language: "python",
|
|
109
136
|
packageManager: pm,
|
|
@@ -112,12 +139,14 @@ function detectPython(dir) {
|
|
|
112
139
|
test: hasPytest ? `${run} pytest` : `${run} pytest`,
|
|
113
140
|
lint: `${run} ruff check .`
|
|
114
141
|
},
|
|
115
|
-
framework
|
|
142
|
+
framework,
|
|
143
|
+
testingStrategy
|
|
116
144
|
};
|
|
117
145
|
}
|
|
118
146
|
const requirements = readFile(join(dir, "requirements.txt"));
|
|
119
147
|
if (requirements !== null) {
|
|
120
148
|
const framework = detectPythonFramework(requirements);
|
|
149
|
+
const testingStrategy = framework && API_FRAMEWORKS.has(framework) ? buildTestingStrategy("api") : buildTestingStrategy("native");
|
|
121
150
|
return {
|
|
122
151
|
language: "python",
|
|
123
152
|
packageManager: "pip",
|
|
@@ -126,7 +155,8 @@ function detectPython(dir) {
|
|
|
126
155
|
test: "python -m pytest",
|
|
127
156
|
lint: "python -m ruff check ."
|
|
128
157
|
},
|
|
129
|
-
framework
|
|
158
|
+
framework,
|
|
159
|
+
testingStrategy
|
|
130
160
|
};
|
|
131
161
|
}
|
|
132
162
|
return null;
|
|
@@ -138,6 +168,7 @@ function detectRust(dir) {
|
|
|
138
168
|
if (/actix-web/.test(cargo)) framework = "Actix";
|
|
139
169
|
else if (/axum/.test(cargo)) framework = "Axum";
|
|
140
170
|
else if (/rocket/.test(cargo)) framework = "Rocket";
|
|
171
|
+
const testingStrategy = framework && API_FRAMEWORKS.has(framework) ? buildTestingStrategy("api") : buildTestingStrategy("native");
|
|
141
172
|
return {
|
|
142
173
|
language: "rust",
|
|
143
174
|
packageManager: "cargo",
|
|
@@ -146,7 +177,8 @@ function detectRust(dir) {
|
|
|
146
177
|
test: "cargo test",
|
|
147
178
|
lint: "cargo clippy"
|
|
148
179
|
},
|
|
149
|
-
framework
|
|
180
|
+
framework,
|
|
181
|
+
testingStrategy
|
|
150
182
|
};
|
|
151
183
|
}
|
|
152
184
|
function detectGo(dir) {
|
|
@@ -156,6 +188,7 @@ function detectGo(dir) {
|
|
|
156
188
|
if (/github\.com\/gin-gonic\/gin/.test(gomod)) framework = "Gin";
|
|
157
189
|
else if (/github\.com\/gofiber\/fiber/.test(gomod)) framework = "Fiber";
|
|
158
190
|
else if (/github\.com\/labstack\/echo/.test(gomod)) framework = "Echo";
|
|
191
|
+
const testingStrategy = framework && API_FRAMEWORKS.has(framework) ? buildTestingStrategy("api") : buildTestingStrategy("native");
|
|
159
192
|
return {
|
|
160
193
|
language: "go",
|
|
161
194
|
packageManager: "go",
|
|
@@ -164,7 +197,8 @@ function detectGo(dir) {
|
|
|
164
197
|
test: "go test ./...",
|
|
165
198
|
lint: "golangci-lint run"
|
|
166
199
|
},
|
|
167
|
-
framework
|
|
200
|
+
framework,
|
|
201
|
+
testingStrategy
|
|
168
202
|
};
|
|
169
203
|
}
|
|
170
204
|
function detectSwift(dir) {
|
|
@@ -176,7 +210,42 @@ function detectSwift(dir) {
|
|
|
176
210
|
commands: {
|
|
177
211
|
build: "swift build",
|
|
178
212
|
test: "swift test"
|
|
179
|
-
}
|
|
213
|
+
},
|
|
214
|
+
testingStrategy: buildTestingStrategy("native")
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function detectFlutter(dir) {
|
|
218
|
+
const pubspec = readFile(join(dir, "pubspec.yaml"));
|
|
219
|
+
if (pubspec === null) return null;
|
|
220
|
+
if (!/flutter/i.test(pubspec)) return null;
|
|
221
|
+
return {
|
|
222
|
+
language: "dart",
|
|
223
|
+
packageManager: "flutter",
|
|
224
|
+
commands: {
|
|
225
|
+
build: "flutter build",
|
|
226
|
+
test: "flutter test",
|
|
227
|
+
lint: "flutter analyze"
|
|
228
|
+
},
|
|
229
|
+
framework: "Flutter",
|
|
230
|
+
testingStrategy: buildTestingStrategy("maestro")
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function detectXcode(dir) {
|
|
234
|
+
try {
|
|
235
|
+
const entries = readdirSync(dir);
|
|
236
|
+
const hasXcode = entries.some((e) => e.endsWith(".xcodeproj") || e.endsWith(".xcworkspace"));
|
|
237
|
+
if (!hasXcode) return null;
|
|
238
|
+
} catch {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
language: "swift",
|
|
243
|
+
packageManager: "xcode",
|
|
244
|
+
commands: {
|
|
245
|
+
build: "xcodebuild build",
|
|
246
|
+
test: "xcodebuild test"
|
|
247
|
+
},
|
|
248
|
+
testingStrategy: buildTestingStrategy("maestro")
|
|
180
249
|
};
|
|
181
250
|
}
|
|
182
251
|
function detectMakefile(dir) {
|
|
@@ -208,7 +277,9 @@ async function detectStack(dir) {
|
|
|
208
277
|
detectPython,
|
|
209
278
|
detectRust,
|
|
210
279
|
detectGo,
|
|
280
|
+
detectFlutter,
|
|
211
281
|
detectSwift,
|
|
282
|
+
detectXcode,
|
|
212
283
|
detectMakefile,
|
|
213
284
|
detectDockerfile
|
|
214
285
|
];
|
|
@@ -620,7 +691,7 @@ async function init(dir, opts) {
|
|
|
620
691
|
const skillsDir = join3(targetDir, ".claude", "skills");
|
|
621
692
|
let existingSkills = [];
|
|
622
693
|
if (existsSync3(skillsDir)) {
|
|
623
|
-
existingSkills =
|
|
694
|
+
existingSkills = readdirSync2(skillsDir).filter((name) => {
|
|
624
695
|
if (name.startsWith("joycraft-")) return false;
|
|
625
696
|
if (name.startsWith(".")) return false;
|
|
626
697
|
const fullPath = join3(skillsDir, name);
|
|
@@ -810,4 +881,4 @@ function printSummary(result, stack, existingSkills = []) {
|
|
|
810
881
|
export {
|
|
811
882
|
init
|
|
812
883
|
};
|
|
813
|
-
//# sourceMappingURL=init-
|
|
884
|
+
//# sourceMappingURL=init-QXG5BT4Y.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/init.ts","../src/detect.ts","../src/improve-claude-md.ts","../src/agents-md.ts","../src/permissions.ts","../src/safeguard.ts"],"sourcesContent":["import { mkdirSync, existsSync, writeFileSync, readFileSync, readdirSync, statSync } from 'node:fs';\nimport { join, basename, resolve, dirname } from 'node:path';\nimport { detectStack } from './detect.js';\nimport { generateCLAUDEMd } from './improve-claude-md.js';\nimport { generateAgentsMd } from './agents-md.js';\nimport { generatePermissions } from './permissions.js';\nimport { installSafeguardHooks } from './safeguard.js';\nimport { SKILLS, TEMPLATES } from './bundled-files.js';\nimport { writeVersion, hashContent } from './version.js';\n\nexport interface InitOptions {\n force: boolean;\n}\n\ninterface InitResult {\n created: string[];\n skipped: string[];\n modified: string[];\n warnings: string[];\n}\n\nfunction ensureDir(dir: string): void {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n}\n\nfunction writeFile(path: string, content: string, force: boolean, result: InitResult): void {\n if (existsSync(path) && !force) {\n result.skipped.push(path);\n return;\n }\n writeFileSync(path, content, 'utf-8');\n result.created.push(path);\n}\n\nexport async function init(dir: string, opts: InitOptions): Promise<void> {\n const targetDir = resolve(dir);\n const result: InitResult = { created: [], skipped: [], modified: [], warnings: [] };\n\n // Detect stack\n const stack = await detectStack(targetDir);\n\n // 1. Create docs/ subdirectories\n const docsDirs = ['briefs', 'specs', 'discoveries', 'contracts', 'decisions', 'context'];\n for (const sub of docsDirs) {\n ensureDir(join(targetDir, 'docs', sub));\n }\n\n // 1b. Scan for existing non-Joycraft skills before copying ours\n const skillsDir = join(targetDir, '.claude', 'skills');\n let existingSkills: string[] = [];\n if (existsSync(skillsDir)) {\n existingSkills = readdirSync(skillsDir)\n .filter(name => {\n if (name.startsWith('joycraft-')) return false;\n if (name.startsWith('.')) return false;\n const fullPath = join(skillsDir, name);\n try {\n return statSync(fullPath).isDirectory();\n } catch {\n return false;\n }\n });\n }\n\n // 2. Copy skill files to .claude/skills/<name>/SKILL.md\n for (const [filename, content] of Object.entries(SKILLS)) {\n const skillName = filename.replace(/\\.md$/, '');\n const skillDir = join(skillsDir, skillName);\n ensureDir(skillDir);\n writeFile(join(skillDir, 'SKILL.md'), content, opts.force, result);\n }\n\n // 3. Copy template files to docs/templates/\n const templatesDir = join(targetDir, 'docs', 'templates');\n ensureDir(templatesDir);\n for (const [filename, content] of Object.entries(TEMPLATES)) {\n ensureDir(dirname(join(templatesDir, filename)));\n writeFile(join(templatesDir, filename), content, opts.force, result);\n }\n\n // 4. Handle CLAUDE.md — only create if missing, never modify existing (unless --force)\n const claudeMdPath = join(targetDir, 'CLAUDE.md');\n if (existsSync(claudeMdPath) && !opts.force) {\n result.skipped.push(claudeMdPath);\n } else {\n const projectName = basename(targetDir);\n const content = generateCLAUDEMd(projectName, stack, existingSkills);\n writeFileSync(claudeMdPath, content, 'utf-8');\n result.created.push(claudeMdPath);\n }\n\n // 5. Handle AGENTS.md — only create if missing, never modify existing (unless --force)\n const agentsMdPath = join(targetDir, 'AGENTS.md');\n if (existsSync(agentsMdPath) && !opts.force) {\n result.skipped.push(agentsMdPath);\n } else {\n const projectName = basename(targetDir);\n const content = generateAgentsMd(projectName, stack);\n writeFileSync(agentsMdPath, content, 'utf-8');\n result.created.push(agentsMdPath);\n }\n\n // 6. Write .joycraft-version with hashes of all managed files\n const fileHashes: Record<string, string> = {};\n for (const [filename, content] of Object.entries(SKILLS)) {\n const skillName = filename.replace(/\\.md$/, '');\n fileHashes[join('.claude', 'skills', skillName, 'SKILL.md')] = hashContent(content);\n }\n for (const [filename, content] of Object.entries(TEMPLATES)) {\n fileHashes[join('docs', 'templates', filename)] = hashContent(content);\n }\n writeVersion(targetDir, '0.1.0', fileHashes);\n\n // 7. Install version check hook\n const hooksDir = join(targetDir, '.claude', 'hooks');\n ensureDir(hooksDir);\n const hookScript = `// Joycraft version check — runs on Claude Code session start\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\ntry {\n const data = JSON.parse(readFileSync(join(process.cwd(), '.joycraft-version'), '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()).version;\n if (data.version !== latest) console.log('Joycraft ' + latest + ' available (you have ' + data.version + '). Run: npx joycraft upgrade');\n }\n} catch {}\n`;\n writeFile(join(hooksDir, 'joycraft-version-check.mjs'), hookScript, opts.force, result);\n\n // Update .claude/settings.json with SessionStart hook\n const settingsPath = join(targetDir, '.claude', 'settings.json');\n let settings: Record<string, unknown> = {};\n let settingsMalformed = false;\n if (existsSync(settingsPath)) {\n try {\n settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));\n } catch {\n settingsMalformed = true;\n result.warnings.push(\n 'settings.json exists but is malformed — skipping settings merge to protect your config.\\n' +\n ' Fix the JSON in .claude/settings.json and re-run init.'\n );\n }\n }\n if (!settingsMalformed) {\n if (!settings.hooks) settings.hooks = {};\n const hooksConfig = settings.hooks as Record<string, unknown>;\n if (!hooksConfig.SessionStart) hooksConfig.SessionStart = [];\n const sessionStartHooks = hooksConfig.SessionStart as Array<Record<string, unknown>>;\n const hasJoycraftHook = sessionStartHooks.some(h => {\n const innerHooks = h.hooks as Array<Record<string, unknown>> | undefined;\n return innerHooks?.some(ih => typeof ih.command === 'string' && ih.command.includes('joycraft'));\n });\n if (!hasJoycraftHook) {\n sessionStartHooks.push({\n matcher: '',\n hooks: [{\n type: 'command',\n command: 'node .claude/hooks/joycraft-version-check.mjs',\n }],\n });\n writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n', 'utf-8');\n result.created.push(settingsPath);\n }\n\n // 8. Generate and merge permission rules into settings.json\n const permissions = generatePermissions(stack);\n // Re-read settings in case it was just created by hook step\n if (existsSync(settingsPath)) {\n try {\n settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));\n } catch {\n result.warnings.push(\n 'settings.json became unreadable after hook merge — skipping permissions merge.\\n' +\n ' Fix the JSON in .claude/settings.json and re-run init.'\n );\n settingsMalformed = true;\n }\n }\n if (!settingsMalformed) {\n if (!settings.permissions) settings.permissions = {};\n const perms = settings.permissions as Record<string, string[]>;\n if (!perms.allow) perms.allow = [];\n if (!perms.deny) perms.deny = [];\n for (const rule of permissions.allow) {\n if (!perms.allow.includes(rule)) perms.allow.push(rule);\n }\n for (const rule of permissions.deny) {\n if (!perms.deny.includes(rule)) perms.deny.push(rule);\n }\n writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n', 'utf-8');\n }\n }\n\n // 9. Install safeguard hooks (PreToolUse deny-pattern blocking)\n const hookResult = installSafeguardHooks(targetDir, [], opts.force, settingsMalformed);\n result.created.push(...hookResult.created);\n result.skipped.push(...hookResult.skipped);\n\n // 10. Check .gitignore for .claude/ exclusion\n const gitignorePath = join(targetDir, '.gitignore');\n if (existsSync(gitignorePath)) {\n const gitignore = readFileSync(gitignorePath, 'utf-8');\n if (/^\\.claude\\/?$/m.test(gitignore) || /^\\.claude\\/\\*$/m.test(gitignore)) {\n result.warnings.push(\n '.claude/ is in your .gitignore — teammates won\\'t get Joycraft skills.\\n' +\n ' Add this line to .gitignore to fix: !.claude/skills/'\n );\n }\n }\n\n // 11. Print summary\n printSummary(result, stack, existingSkills);\n}\n\nfunction printSummary(result: InitResult, stack: import('./detect.js').StackInfo, existingSkills: string[] = []): void {\n console.log('\\nJoycraft initialized!\\n');\n\n if (stack.language !== 'unknown') {\n const fw = stack.framework ? ` + ${stack.framework}` : '';\n console.log(` Detected stack: ${stack.language}${fw} (${stack.packageManager})`);\n } else {\n console.log(' Detected stack: unknown (no recognized manifest found)');\n }\n\n if (result.created.length > 0) {\n console.log(`\\n Created ${result.created.length} file(s):`);\n for (const f of result.created) {\n console.log(` + ${f}`);\n }\n }\n\n if (result.modified.length > 0) {\n console.log(`\\n Modified ${result.modified.length} file(s):`);\n for (const f of result.modified) {\n console.log(` ~ ${f}`);\n }\n }\n\n if (result.skipped.length > 0) {\n console.log(`\\n Skipped ${result.skipped.length} file(s) (already exist, use --force to overwrite):`);\n for (const f of result.skipped) {\n console.log(` - ${f}`);\n }\n }\n\n if (result.warnings.length > 0) {\n console.log('\\n Warnings:');\n for (const w of result.warnings) {\n console.log(` ⚠ ${w}`);\n }\n }\n\n if (existingSkills.length > 0) {\n console.log(`\\n Found existing skills: ${existingSkills.join(', ')}. These are preserved — Joycraft is additive.`);\n }\n\n const hasExistingClaude = result.skipped.some(f => f.endsWith('CLAUDE.md'));\n\n console.log('\\n Next steps:');\n if (hasExistingClaude) {\n console.log(' 1. Run Claude Code and try /joycraft-tune to assess and improve your existing CLAUDE.md');\n } else {\n console.log(' 1. Review and customize the generated CLAUDE.md for your project');\n }\n console.log(' 2. Try /joycraft-new-feature to start building with the spec-driven workflow');\n console.log(' 3. Commit .claude/skills/ and docs/ so your team gets the same workflow');\n console.log('');\n}\n","import { readFileSync, existsSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport interface TestingStrategy {\n backbone: 'playwright' | 'maestro' | 'api' | 'native';\n testFormat: string;\n layers: ('ui' | 'api' | 'logic' | 'static')[];\n}\n\nexport interface StackInfo {\n language: string;\n packageManager: string;\n commands: {\n build?: string;\n test?: string;\n lint?: string;\n typecheck?: string;\n deploy?: string;\n };\n framework?: string;\n testingStrategy?: TestingStrategy;\n}\n\nconst WEB_FRAMEWORKS = new Set(['Next.js', 'Nuxt', 'Remix', 'React', 'Vue', 'Svelte']);\nconst API_FRAMEWORKS = new Set(['Express', 'Fastify', 'FastAPI', 'Django', 'Flask', 'Gin', 'Fiber', 'Echo', 'Actix', 'Axum', 'Rocket']);\n\nfunction buildTestingStrategy(backbone: TestingStrategy['backbone']): TestingStrategy {\n switch (backbone) {\n case 'playwright':\n return { backbone, testFormat: '.spec.ts', layers: ['ui', 'api', 'logic', 'static'] };\n case 'maestro':\n return { backbone, testFormat: '.yaml', layers: ['ui', 'api', 'logic', 'static'] };\n case 'api':\n return { backbone, testFormat: '.test.ts', layers: ['api', 'logic', 'static'] };\n case 'native':\n return { backbone, testFormat: '.test.ts', layers: ['logic', 'static'] };\n }\n}\n\nfunction readFile(path: string): string | null {\n try {\n return readFileSync(path, 'utf-8');\n } catch {\n return null;\n }\n}\n\nfunction detectNodeFramework(pkg: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> }): string | undefined {\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (allDeps['next']) return 'Next.js';\n if (allDeps['nuxt']) return 'Nuxt';\n if (allDeps['@remix-run/node'] || allDeps['@remix-run/react']) return 'Remix';\n if (allDeps['express']) return 'Express';\n if (allDeps['fastify']) return 'Fastify';\n if (allDeps['react']) return 'React';\n if (allDeps['vue']) return 'Vue';\n if (allDeps['svelte']) return 'Svelte';\n return undefined;\n}\n\nfunction detectNodeTestFramework(pkg: { devDependencies?: Record<string, string>; dependencies?: Record<string, string> }): string | undefined {\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (allDeps['vitest']) return 'vitest';\n if (allDeps['jest']) return 'jest';\n if (allDeps['mocha']) return 'mocha';\n return undefined;\n}\n\nfunction detectNodePackageManager(dir: string): string {\n if (existsSync(join(dir, 'pnpm-lock.yaml'))) return 'pnpm';\n if (existsSync(join(dir, 'yarn.lock'))) return 'yarn';\n if (existsSync(join(dir, 'bun.lockb')) || existsSync(join(dir, 'bun.lock'))) return 'bun';\n return 'npm';\n}\n\nfunction detectNode(dir: string): StackInfo | null {\n const raw = readFile(join(dir, 'package.json'));\n if (raw === null) return null;\n\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(raw);\n } catch {\n return null;\n }\n\n const pm = detectNodePackageManager(dir);\n const run = pm === 'npm' ? 'npm run' : pm;\n const scripts = (pkg.scripts ?? {}) as Record<string, string>;\n const framework = detectNodeFramework(pkg as { dependencies?: Record<string, string>; devDependencies?: Record<string, string> });\n const testFramework = detectNodeTestFramework(pkg as { devDependencies?: Record<string, string>; dependencies?: Record<string, string> });\n\n const commands: StackInfo['commands'] = {};\n if (scripts.build) commands.build = `${run} build`;\n else commands.build = `${run} build`;\n if (scripts.test) commands.test = `${run} test`;\n else if (testFramework) commands.test = `${run} test`;\n else commands.test = `${pm === 'npm' ? 'npm' : pm} test`;\n if (scripts.lint) commands.lint = `${run} lint`;\n if (scripts.typecheck) commands.typecheck = `${run} typecheck`;\n else if ((pkg.devDependencies as Record<string, string> | undefined)?.['typescript']) {\n commands.typecheck = 'tsc --noEmit';\n }\n\n const allDeps = { ...(pkg.dependencies as Record<string, string> | undefined), ...(pkg.devDependencies as Record<string, string> | undefined) };\n let testingStrategy: TestingStrategy;\n if (allDeps['react-native']) {\n testingStrategy = buildTestingStrategy('maestro');\n } else if (framework && WEB_FRAMEWORKS.has(framework)) {\n testingStrategy = buildTestingStrategy('playwright');\n } else if (framework && API_FRAMEWORKS.has(framework)) {\n testingStrategy = buildTestingStrategy('api');\n } else {\n testingStrategy = buildTestingStrategy('native');\n }\n\n return {\n language: 'node',\n packageManager: pm,\n commands,\n framework,\n testingStrategy,\n };\n}\n\nfunction detectPythonFramework(content: string): string | undefined {\n if (/fastapi/i.test(content)) return 'FastAPI';\n if (/django/i.test(content)) return 'Django';\n if (/flask/i.test(content)) return 'Flask';\n return undefined;\n}\n\nfunction detectPython(dir: string): StackInfo | null {\n const pyproject = readFile(join(dir, 'pyproject.toml'));\n if (pyproject !== null) {\n const isPoetry = /\\[tool\\.poetry\\]/.test(pyproject);\n const isUv = existsSync(join(dir, 'uv.lock'));\n\n let pm: string;\n let run: string;\n if (isUv) {\n pm = 'uv';\n run = 'uv run';\n } else if (isPoetry) {\n pm = 'poetry';\n run = 'poetry run';\n } else {\n pm = 'pip';\n run = 'python -m';\n }\n\n const framework = detectPythonFramework(pyproject);\n const hasPytest = /pytest/i.test(pyproject);\n\n const testingStrategy = framework && API_FRAMEWORKS.has(framework)\n ? buildTestingStrategy('api')\n : buildTestingStrategy('native');\n\n return {\n language: 'python',\n packageManager: pm,\n commands: {\n build: `${pm === 'poetry' ? 'poetry' : pm} build`,\n test: hasPytest ? `${run} pytest` : `${run} pytest`,\n lint: `${run} ruff check .`,\n },\n framework,\n testingStrategy,\n };\n }\n\n const requirements = readFile(join(dir, 'requirements.txt'));\n if (requirements !== null) {\n const framework = detectPythonFramework(requirements);\n const testingStrategy = framework && API_FRAMEWORKS.has(framework)\n ? buildTestingStrategy('api')\n : buildTestingStrategy('native');\n\n return {\n language: 'python',\n packageManager: 'pip',\n commands: {\n build: 'pip install -e .',\n test: 'python -m pytest',\n lint: 'python -m ruff check .',\n },\n framework,\n testingStrategy,\n };\n }\n\n return null;\n}\n\nfunction detectRust(dir: string): StackInfo | null {\n const cargo = readFile(join(dir, 'Cargo.toml'));\n if (cargo === null) return null;\n\n let framework: string | undefined;\n if (/actix-web/.test(cargo)) framework = 'Actix';\n else if (/axum/.test(cargo)) framework = 'Axum';\n else if (/rocket/.test(cargo)) framework = 'Rocket';\n\n const testingStrategy = framework && API_FRAMEWORKS.has(framework)\n ? buildTestingStrategy('api')\n : buildTestingStrategy('native');\n\n return {\n language: 'rust',\n packageManager: 'cargo',\n commands: {\n build: 'cargo build',\n test: 'cargo test',\n lint: 'cargo clippy',\n },\n framework,\n testingStrategy,\n };\n}\n\nfunction detectGo(dir: string): StackInfo | null {\n const gomod = readFile(join(dir, 'go.mod'));\n if (gomod === null) return null;\n\n let framework: string | undefined;\n if (/github\\.com\\/gin-gonic\\/gin/.test(gomod)) framework = 'Gin';\n else if (/github\\.com\\/gofiber\\/fiber/.test(gomod)) framework = 'Fiber';\n else if (/github\\.com\\/labstack\\/echo/.test(gomod)) framework = 'Echo';\n\n const testingStrategy = framework && API_FRAMEWORKS.has(framework)\n ? buildTestingStrategy('api')\n : buildTestingStrategy('native');\n\n return {\n language: 'go',\n packageManager: 'go',\n commands: {\n build: 'go build ./...',\n test: 'go test ./...',\n lint: 'golangci-lint run',\n },\n framework,\n testingStrategy,\n };\n}\n\nfunction detectSwift(dir: string): StackInfo | null {\n const pkg = readFile(join(dir, 'Package.swift'));\n if (pkg === null) return null;\n\n return {\n language: 'swift',\n packageManager: 'swift',\n commands: {\n build: 'swift build',\n test: 'swift test',\n },\n testingStrategy: buildTestingStrategy('native'),\n };\n}\n\nfunction detectFlutter(dir: string): StackInfo | null {\n const pubspec = readFile(join(dir, 'pubspec.yaml'));\n if (pubspec === null) return null;\n if (!/flutter/i.test(pubspec)) return null;\n\n return {\n language: 'dart',\n packageManager: 'flutter',\n commands: {\n build: 'flutter build',\n test: 'flutter test',\n lint: 'flutter analyze',\n },\n framework: 'Flutter',\n testingStrategy: buildTestingStrategy('maestro'),\n };\n}\n\nfunction detectXcode(dir: string): StackInfo | null {\n // Check for .xcodeproj or .xcworkspace directories\n try {\n const entries = readdirSync(dir);\n const hasXcode = entries.some(e => e.endsWith('.xcodeproj') || e.endsWith('.xcworkspace'));\n if (!hasXcode) return null;\n } catch {\n return null;\n }\n\n return {\n language: 'swift',\n packageManager: 'xcode',\n commands: {\n build: 'xcodebuild build',\n test: 'xcodebuild test',\n },\n testingStrategy: buildTestingStrategy('maestro'),\n };\n}\n\nfunction detectMakefile(dir: string): StackInfo | null {\n const makefile = readFile(join(dir, 'Makefile'));\n if (makefile === null) return null;\n\n const commands: StackInfo['commands'] = {};\n commands.build = 'make build';\n if (/^test:/m.test(makefile)) commands.test = 'make test';\n if (/^lint:/m.test(makefile)) commands.lint = 'make lint';\n\n return {\n language: 'unknown',\n packageManager: 'make',\n commands,\n };\n}\n\nfunction detectDockerfile(dir: string): StackInfo | null {\n if (!existsSync(join(dir, 'Dockerfile'))) return null;\n\n return {\n language: 'unknown',\n packageManager: 'docker',\n commands: {\n build: 'docker build .',\n },\n };\n}\n\nexport async function detectStack(dir: string): Promise<StackInfo> {\n const detectors = [\n detectNode,\n detectPython,\n detectRust,\n detectGo,\n detectFlutter,\n detectSwift,\n detectXcode,\n detectMakefile,\n detectDockerfile,\n ];\n\n for (const detect of detectors) {\n const result = detect(dir);\n if (result) return result;\n }\n\n return { language: 'unknown', packageManager: '', commands: {} };\n}\n","import type { StackInfo } from './detect.js';\n\ninterface Section {\n header: string;\n content: string;\n}\n\nfunction parseSections(markdown: string): Section[] {\n const lines = markdown.split('\\n');\n const sections: Section[] = [];\n let currentHeader = '';\n let currentLines: string[] = [];\n\n for (const line of lines) {\n if (line.startsWith('## ')) {\n if (currentHeader || currentLines.length > 0) {\n sections.push({ header: currentHeader, content: currentLines.join('\\n') });\n }\n currentHeader = line;\n currentLines = [];\n } else {\n currentLines.push(line);\n }\n }\n\n // Push the last section\n if (currentHeader || currentLines.length > 0) {\n sections.push({ header: currentHeader, content: currentLines.join('\\n') });\n }\n\n return sections;\n}\n\nfunction hasSection(sections: Section[], pattern: RegExp): boolean {\n return sections.some(s => pattern.test(s.header));\n}\n\nfunction generateCommandsBlock(stack: StackInfo): string {\n const lines: string[] = ['```bash'];\n if (stack.commands.build) lines.push(`# Build\\n${stack.commands.build}`);\n if (stack.commands.test) lines.push(`# Test\\n${stack.commands.test}`);\n if (stack.commands.lint) lines.push(`# Lint\\n${stack.commands.lint}`);\n if (stack.commands.typecheck) lines.push(`# Type check\\n${stack.commands.typecheck}`);\n if (stack.commands.deploy) lines.push(`# Deploy\\n${stack.commands.deploy}`);\n lines.push('```');\n return lines.join('\\n');\n}\n\nfunction generateBoundariesSection(): string {\n return `## Behavioral Boundaries\n\n### ALWAYS\n- Run tests and type-check before committing\n- Run tests before implementing new features -- confirm they fail first, then implement until they pass\n- Use \\`verb: concise message\\` format for commits\n- Commit after completing each discrete task (atomic commits)\n- Stage specific files by name (not \\`git add -A\\` or \\`git add .\\`)\n- Read \\`docs/context/\\` before making infrastructure or config changes\n- Follow existing code patterns and style\n\n### ASK FIRST\n- Pushing to remote\n- Creating or merging pull requests\n- Adding new dependencies\n- Modifying database schema or data models\n- Changing authentication or authorization flows\n- Any destructive git operation (force-push, reset --hard, branch deletion)\n\n### NEVER\n- Push directly to main/master without approval\n- Commit .env files, secrets, or credentials\n- Use --no-verify to skip hooks\n- Amend commits that have been pushed\n- Skip type-checking or linting\n- Commit code that doesn't build`;\n}\n\nfunction generateWorkflowSection(stack: StackInfo): string {\n return `## Development Workflow\n\n${generateCommandsBlock(stack)}`;\n}\n\nfunction generateArchitectureSection(): string {\n return `## Architecture\n\n_TODO: Add a brief description of your project's architecture and key directories._`;\n}\n\nfunction generateKeyFilesSection(): string {\n return `## Key Files\n\n| File | Purpose |\n|------|---------|\n| _TODO_ | _Add key files and their purposes_ |`;\n}\n\nfunction generateGotchasSection(): string {\n return `## Common Gotchas\n\n_TODO: Add any gotchas, quirks, or non-obvious behaviors that developers should know about._`;\n}\n\nfunction generateGettingStartedSection(): string {\n return `## Getting Started with Joycraft\n\nThis project uses [Joycraft](https://github.com/maksutovic/joycraft) for AI development workflow. Available skills:\n\n| Skill | Purpose |\n|-------|---------|\n| \\`/joycraft-tune\\` | Assess your harness, apply upgrades, see path to Level 5 |\n| \\`/joycraft-new-feature\\` | Interview -> Feature Brief -> Atomic Specs |\n| \\`/joycraft-interview\\` | Lightweight brainstorm — yap about ideas, get a structured summary |\n| \\`/joycraft-decompose\\` | Break a brief into small, testable specs |\n| \\`/joycraft-session-end\\` | Capture discoveries, verify, commit |\n| \\`/joycraft-implement-level5\\` | Set up Level 5 — autofix loop, holdout scenarios, scenario evolution |\n\nRun \\`/joycraft-tune\\` to see where your project stands and what to improve next.`;\n}\n\nfunction generateExternalValidationSection(): string {\n return `## External Validation\n\nThis project uses holdout scenario tests in a separate private repo.\n\n### NEVER\n- Access, read, or reference the scenarios repo\n- Mention scenario test names or contents\n- Modify the scenarios dispatch workflow to leak test information\n\nThe scenarios repo is deliberately invisible to you. This is the holdout guarantee — like a validation set in ML.`;\n}\n\nfunction generateProjectToolsSection(existingSkills: string[]): string {\n const MAX_LISTED = 10;\n let skillList: string;\n if (existingSkills.length <= MAX_LISTED) {\n skillList = existingSkills.join(', ');\n } else {\n skillList = existingSkills.slice(0, MAX_LISTED).join(', ') +\n `, and ${existingSkills.length - MAX_LISTED} more — see .claude/skills/`;\n }\n return `## Project Tools\n\nThis project has additional tools beyond Joycraft. Always check \\`.claude/skills/\\` for available skills: ${skillList}`;\n}\n\nexport function improveCLAUDEMd(existing: string, stack: StackInfo, existingSkills: string[] = []): string {\n const sections = parseSections(existing);\n const additions: string[] = [];\n\n if (!hasSection(sections, /behavioral\\s*boundar/i)) {\n additions.push(generateBoundariesSection());\n }\n\n if (!hasSection(sections, /development\\s*workflow/i) && !hasSection(sections, /workflow/i)) {\n additions.push(generateWorkflowSection(stack));\n }\n\n if (!hasSection(sections, /architecture/i)) {\n additions.push(generateArchitectureSection());\n }\n\n if (!hasSection(sections, /key\\s*files/i)) {\n additions.push(generateKeyFilesSection());\n }\n\n if (!hasSection(sections, /common\\s*gotchas/i) && !hasSection(sections, /gotchas/i)) {\n additions.push(generateGotchasSection());\n }\n\n if (!hasSection(sections, /getting\\s*started.*joycraft/i) && !hasSection(sections, /joycraft.*skills/i)) {\n additions.push(generateGettingStartedSection());\n }\n\n if (!hasSection(sections, /external\\s*validation/i)) {\n additions.push(generateExternalValidationSection());\n }\n\n if (existingSkills.length > 0 && !hasSection(sections, /project\\s*tools/i)) {\n additions.push(generateProjectToolsSection(existingSkills));\n }\n\n if (additions.length === 0) {\n return existing;\n }\n\n // Append missing sections\n const trimmed = existing.trimEnd();\n return trimmed + '\\n\\n' + additions.join('\\n\\n') + '\\n';\n}\n\nexport function generateCLAUDEMd(projectName: string, stack: StackInfo, existingSkills: string[] = []): string {\n const frameworkNote = stack.framework ? ` (${stack.framework})` : '';\n const langLabel = stack.language === 'unknown' ? '' : ` | **Stack:** ${stack.language}${frameworkNote}`;\n\n const lines: string[] = [\n `# ${projectName}`,\n '',\n `**Component:** _TODO: describe what this project is_${langLabel}`,\n '',\n '---',\n '',\n generateBoundariesSection(),\n '',\n generateWorkflowSection(stack),\n '',\n generateArchitectureSection(),\n '',\n generateKeyFilesSection(),\n '',\n generateGotchasSection(),\n '',\n generateGettingStartedSection(),\n '',\n ];\n\n if (existingSkills.length > 0) {\n lines.push(generateProjectToolsSection(existingSkills), '');\n }\n\n return lines.join('\\n');\n}\n","import type { StackInfo } from './detect.js';\n\ninterface Section {\n header: string;\n content: string;\n}\n\nfunction parseSections(markdown: string): Section[] {\n const lines = markdown.split('\\n');\n const sections: Section[] = [];\n let currentHeader = '';\n let currentLines: string[] = [];\n\n for (const line of lines) {\n if (line.startsWith('## ')) {\n if (currentHeader || currentLines.length > 0) {\n sections.push({ header: currentHeader, content: currentLines.join('\\n') });\n }\n currentHeader = line;\n currentLines = [];\n } else {\n currentLines.push(line);\n }\n }\n\n if (currentHeader || currentLines.length > 0) {\n sections.push({ header: currentHeader, content: currentLines.join('\\n') });\n }\n\n return sections;\n}\n\nfunction hasSection(sections: Section[], pattern: RegExp): boolean {\n return sections.some(s => pattern.test(s.header));\n}\n\nfunction generateCommandsBlock(stack: StackInfo): string {\n const lines: string[] = ['```bash'];\n if (stack.commands.build) lines.push(stack.commands.build);\n if (stack.commands.test) lines.push(stack.commands.test);\n if (stack.commands.lint) lines.push(stack.commands.lint);\n if (stack.commands.typecheck) lines.push(stack.commands.typecheck);\n if (stack.commands.deploy) lines.push(stack.commands.deploy);\n lines.push('```');\n return lines.join('\\n');\n}\n\nfunction generateBoundariesSection(): string {\n return `## Behavioral Boundaries\n\n### ALWAYS\n- Run tests and type-check before committing\n- Follow existing code patterns and style\n\n### ASK FIRST\n- Adding new dependencies\n- Changing auth or data models\n- Any destructive operation\n\n### NEVER\n- Push to main without approval\n- Skip tests or type-checking\n- Hardcode secrets or credentials`;\n}\n\nfunction generateDevelopmentSection(stack: StackInfo): string {\n return `## Development\n\n${generateCommandsBlock(stack)}`;\n}\n\nfunction generateArchitectureSection(): string {\n return `## Architecture\n\n_TODO: Add a compact directory tree and one-paragraph summary._`;\n}\n\nfunction generateKeyFilesSection(): string {\n return `## Key Files\n\n| File | Purpose |\n|------|---------|\n| _TODO_ | _Add key files_ |`;\n}\n\nexport function generateAgentsMd(projectName: string, stack: StackInfo): string {\n const frameworkNote = stack.framework ? ` (${stack.framework})` : '';\n const langLabel = stack.language === 'unknown' ? '' : ` | **Stack:** ${stack.language}${frameworkNote}`;\n\n const lines: string[] = [\n `# ${projectName}`,\n '',\n `**Component:** _TODO: describe what this project is_${langLabel}`,\n '',\n '> Auto-generated by Joycraft. See CLAUDE.md for detailed instructions.',\n '',\n '---',\n '',\n generateBoundariesSection(),\n '',\n generateArchitectureSection(),\n '',\n generateKeyFilesSection(),\n '',\n generateDevelopmentSection(stack),\n '',\n ];\n\n return lines.join('\\n');\n}\n\nexport function improveAgentsMd(existing: string, stack: StackInfo): string {\n const sections = parseSections(existing);\n const additions: string[] = [];\n\n if (!hasSection(sections, /behavioral\\s*boundar/i)) {\n additions.push(generateBoundariesSection());\n }\n\n if (!hasSection(sections, /architecture/i)) {\n additions.push(generateArchitectureSection());\n }\n\n if (!hasSection(sections, /key\\s*files/i)) {\n additions.push(generateKeyFilesSection());\n }\n\n if (!hasSection(sections, /development/i)) {\n additions.push(generateDevelopmentSection(stack));\n }\n\n if (additions.length === 0) {\n return existing;\n }\n\n const trimmed = existing.trimEnd();\n return trimmed + '\\n\\n' + additions.join('\\n\\n') + '\\n';\n}\n","import type { StackInfo } from './detect.js';\n\nexport interface PermissionRules {\n allow: string[];\n deny: string[];\n}\n\nexport function generatePermissions(stack: StackInfo): PermissionRules {\n // Default deny rules for ALL projects\n const deny: string[] = [\n 'Bash(rm -rf *)',\n 'Bash(git push --force *)',\n 'Bash(git push -f *)',\n 'Bash(git reset --hard *)',\n 'Edit(//.env*)',\n 'Edit(//*.pem)',\n 'Edit(//*.key)',\n 'Edit(//.git/**)',\n ];\n\n // Default allow rules\n const allow: string[] = [\n 'Bash(git status)',\n 'Bash(git diff *)',\n 'Bash(git log *)',\n 'Bash(git add *)',\n 'Bash(git commit *)',\n 'Bash(git checkout *)',\n 'Bash(git branch *)',\n ];\n\n // Stack-specific rules\n if (stack.language === 'node') {\n allow.push(`Bash(${stack.packageManager} *)`);\n if (stack.packageManager !== 'npm') deny.push('Bash(npm install *)');\n if (stack.packageManager !== 'yarn') deny.push('Bash(yarn add *)');\n if (stack.packageManager !== 'pnpm') deny.push('Bash(pnpm add *)');\n if (stack.packageManager !== 'bun') deny.push('Bash(bun add *)');\n if (stack.commands.test) allow.push(`Bash(${stack.commands.test})`);\n if (stack.commands.build) allow.push(`Bash(${stack.commands.build})`);\n if (stack.commands.lint) allow.push(`Bash(${stack.commands.lint})`);\n if (stack.commands.typecheck) allow.push(`Bash(${stack.commands.typecheck})`);\n }\n\n if (stack.language === 'python') {\n allow.push(`Bash(${stack.packageManager} *)`);\n if (stack.commands.test) allow.push(`Bash(${stack.commands.test})`);\n if (stack.commands.lint) allow.push(`Bash(${stack.commands.lint})`);\n if (stack.commands.build) allow.push(`Bash(${stack.commands.build})`);\n }\n\n if (stack.language === 'rust') {\n allow.push('Bash(cargo *)');\n }\n\n if (stack.language === 'go') {\n allow.push('Bash(go *)');\n }\n\n if (stack.language === 'swift') {\n allow.push('Bash(swift *)');\n allow.push('Bash(xcodebuild *)');\n }\n\n return { allow, deny };\n}\n","import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport interface SafeguardConfig {\n denyPatterns: string[];\n}\n\n/**\n * Generate default deny patterns from common dangerous operations.\n */\nexport function getDefaultDenyPatterns(): string[] {\n return [\n 'rm\\\\s+-rf\\\\s+/', // rm -rf with absolute path\n 'rm\\\\s+-rf\\\\s+\\\\.', // rm -rf . or ./\n 'git\\\\s+push\\\\s+--force', // force push\n 'git\\\\s+push\\\\s+-f\\\\b', // force push shorthand\n 'git\\\\s+reset\\\\s+--hard', // hard reset\n 'DROP\\\\s+TABLE', // SQL drop\n 'DROP\\\\s+DATABASE', // SQL drop\n 'TRUNCATE', // SQL truncate\n 'chmod\\\\s+777', // wide-open permissions\n 'curl.*\\\\|.*sh', // pipe curl to shell\n 'wget.*\\\\|.*sh', // pipe wget to shell\n ];\n}\n\n/**\n * Generate the hook script that blocks dangerous Bash commands.\n */\nexport function generateHookScript(): string {\n return `#!/bin/bash\n# Joycraft Safeguard — PreToolUse hook\n# Blocks dangerous Bash commands. Exit 2 = block the action.\n# Edit deny-patterns.txt to customize what's blocked.\n\nTOOL_NAME=\"$1\"\n# Read the full tool input from stdin\nINPUT=$(cat)\n\n# Only check Bash commands\nif [ \"$TOOL_NAME\" != \"Bash\" ]; then\n exit 0\nfi\n\n# Extract the command from the JSON input\nCOMMAND=$(echo \"$INPUT\" | grep -o '\"command\":\"[^\"]*\"' | head -1 | sed 's/\"command\":\"//;s/\"$//')\n\nif [ -z \"$COMMAND\" ]; then\n exit 0\nfi\n\nPATTERNS_FILE=\"$(dirname \"$0\")/deny-patterns.txt\"\n\nif [ ! -f \"$PATTERNS_FILE\" ]; then\n exit 0\nfi\n\nwhile IFS= read -r pattern || [ -n \"$pattern\" ]; do\n # Skip empty lines and comments\n [ -z \"$pattern\" ] && continue\n [[ \"$pattern\" == \\\\#* ]] && continue\n\n if echo \"$COMMAND\" | grep -qEi \"$pattern\"; then\n echo \"Blocked by Joycraft Safeguard: command matches deny pattern '$pattern'\"\n echo \"Edit .claude/hooks/joycraft/deny-patterns.txt to modify blocked patterns.\"\n exit 2\n fi\ndone < \"$PATTERNS_FILE\"\n\nexit 0\n`;\n}\n\n/**\n * Generate deny-patterns.txt content from default patterns + custom patterns.\n */\nexport function generateDenyPatternsFile(customPatterns: string[] = []): string {\n const lines = [\n '# Joycraft Safeguard — Deny Patterns',\n '# One regex pattern per line. Lines starting with # are comments.',\n '# Commands matching any pattern will be blocked (exit 2).',\n '# Edit this file to customize what\\'s blocked.',\n '',\n '# Destructive file operations',\n 'rm\\\\s+-rf\\\\s+/',\n 'rm\\\\s+-rf\\\\s+\\\\.',\n '',\n '# Dangerous git operations',\n 'git\\\\s+push\\\\s+--force',\n 'git\\\\s+push\\\\s+-f\\\\b',\n 'git\\\\s+reset\\\\s+--hard',\n '',\n '# SQL destruction',\n 'DROP\\\\s+TABLE',\n 'DROP\\\\s+DATABASE',\n 'TRUNCATE',\n '',\n '# System security',\n 'chmod\\\\s+777',\n 'curl.*\\\\|.*sh',\n 'wget.*\\\\|.*sh',\n ];\n\n if (customPatterns.length > 0) {\n lines.push('');\n lines.push('# Project-specific patterns (from risk interview)');\n for (const p of customPatterns) {\n lines.push(p);\n }\n }\n\n return lines.join('\\n') + '\\n';\n}\n\n/**\n * Install safeguard hooks into a project.\n */\nexport function installSafeguardHooks(\n targetDir: string,\n customPatterns: string[] = [],\n force: boolean = false,\n skipSettingsMerge: boolean = false\n): { created: string[]; skipped: string[] } {\n const result = { created: [] as string[], skipped: [] as string[] };\n const hooksDir = join(targetDir, '.claude', 'hooks', 'joycraft');\n\n if (!existsSync(hooksDir)) {\n mkdirSync(hooksDir, { recursive: true });\n }\n\n // Write hook script\n const hookPath = join(hooksDir, 'block-dangerous.sh');\n if (!existsSync(hookPath) || force) {\n writeFileSync(hookPath, generateHookScript(), { mode: 0o755 });\n result.created.push(hookPath);\n } else {\n result.skipped.push(hookPath);\n }\n\n // Write deny patterns\n const patternsPath = join(hooksDir, 'deny-patterns.txt');\n if (!existsSync(patternsPath) || force) {\n writeFileSync(patternsPath, generateDenyPatternsFile(customPatterns));\n result.created.push(patternsPath);\n } else {\n result.skipped.push(patternsPath);\n }\n\n // Register hook in settings.json (skip if caller detected malformed JSON)\n if (!skipSettingsMerge) {\n const settingsPath = join(targetDir, '.claude', 'settings.json');\n let settings: Record<string, unknown> = {};\n if (existsSync(settingsPath)) {\n try {\n settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));\n } catch {\n // Don't overwrite — caller should handle the warning\n return result;\n }\n }\n\n if (!settings.hooks) settings.hooks = {};\n const hooks = settings.hooks as Record<string, unknown[]>;\n if (!hooks.PreToolUse) hooks.PreToolUse = [];\n\n const preToolUse = hooks.PreToolUse as Array<Record<string, unknown>>;\n const hasJoycraftHook = preToolUse.some(h => {\n const innerHooks = h.hooks as Array<Record<string, unknown>> | undefined;\n return innerHooks?.some(ih => typeof ih.command === 'string' && ih.command.includes('joycraft'));\n });\n\n if (!hasJoycraftHook) {\n preToolUse.push({\n matcher: 'Bash',\n hooks: [{\n type: 'command',\n command: '.claude/hooks/joycraft/block-dangerous.sh',\n }],\n });\n writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\\n', 'utf-8');\n }\n }\n\n return result;\n}\n"],"mappings":"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","names":["mkdirSync","existsSync","writeFileSync","readFileSync","readdirSync","join","generateCommandsBlock","generateBoundariesSection","generateArchitectureSection","generateKeyFilesSection","existsSync","readFileSync","join","join","existsSync","readFileSync","existsSync","mkdirSync","writeFileSync","join","readdirSync","readFileSync"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
TEMPLATES
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-A2CQG5J5.js";
|
|
5
5
|
|
|
6
6
|
// src/init-autofix.ts
|
|
7
7
|
import { mkdirSync, existsSync, writeFileSync } from "fs";
|
|
@@ -111,4 +111,4 @@ function printSummary(result, dryRun, scenariosRepo) {
|
|
|
111
111
|
export {
|
|
112
112
|
initAutofix
|
|
113
113
|
};
|
|
114
|
-
//# sourceMappingURL=init-autofix-
|
|
114
|
+
//# sourceMappingURL=init-autofix-Y5DQOFEU.js.map
|