code-foundry 1.39.1 → 1.39.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,20 @@
1
1
  #!/usr/bin/env sh
2
+ # Generated by Code Foundry — see src/commands/sync.mjs.
2
3
  set -eu
3
4
 
5
+ if [ -z "$(git diff --cached --name-only)" ]; then
6
+ exit 0
7
+ fi
8
+
9
+ # Prefer a runtime the repository already depends on. A repository opts into
10
+ # the gate by depending on code-foundry; one that does not keeps the
11
+ # whitespace guard and stays offline. Nothing is fetched at commit time.
12
+ if [ -x node_modules/.bin/code-foundry ]; then
13
+ exec node_modules/.bin/code-foundry pre-commit
14
+ fi
15
+
16
+ if [ -f src/runtime.mjs ]; then
17
+ exec node src/runtime.mjs pre-commit
18
+ fi
19
+
4
20
  git diff --cached --check
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.39.3](https://github.com/0xPlayerOne/code-foundry/compare/v1.39.2...v1.39.3) (2026-10-01)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **hooks:** run the gate from a local dependency, not npx ([#688](https://github.com/0xPlayerOne/code-foundry/issues/688)) ([d256b69](https://github.com/0xPlayerOne/code-foundry/commit/d256b69b8f89f0041a01c53cc51af32fe582b5dc))
9
+
10
+ ## [1.39.2](https://github.com/0xPlayerOne/code-foundry/compare/v1.39.1...v1.39.2) (2026-10-01)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **hooks:** delegate the generated pre-commit hook to the runtime ([#684](https://github.com/0xPlayerOne/code-foundry/issues/684)) ([5d9a571](https://github.com/0xPlayerOne/code-foundry/commit/5d9a5714aeff0d48397e3be51b22e08395bfb5ce))
16
+
3
17
  ## [1.39.1](https://github.com/0xPlayerOne/code-foundry/compare/v1.39.0...v1.39.1) (2026-10-01)
4
18
 
5
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-foundry",
3
- "version": "1.39.1",
3
+ "version": "1.39.3",
4
4
  "description": "A fast, language-aware repository factory for agent-ready workflows, testing, security, and release automation.",
5
5
  "homepage": "https://github.com/0xPlayerOne/code-foundry#readme",
6
6
  "bugs": {
package/src/cli.mjs CHANGED
@@ -271,6 +271,16 @@ async function main() {
271
271
  } catch (error) {
272
272
  fail(error instanceof Error ? error.message : String(error))
273
273
  }
274
+ } else if (command === 'pre-commit') {
275
+ // The generated `.githooks/pre-commit` calls this. Without a CLI entry
276
+ // point the language-aware gate in runtime-core is unreachable from a hook.
277
+ try {
278
+ process.chdir(target)
279
+ const { preCommit } = await import('./runtime-core.mjs')
280
+ preCommit()
281
+ } catch (error) {
282
+ fail(error instanceof Error ? error.message : String(error))
283
+ }
274
284
  } else fail(`unknown command: ${command}`)
275
285
  }
276
286
 
@@ -40,6 +40,20 @@ export function doctor(root, options = {}) {
40
40
  }
41
41
  const hooks = git(target, ['config', '--get', 'core.hooksPath'])
42
42
  if (hooks !== '.githooks') warn('Git hooks are not enabled; run `npx code-foundry init`')
43
+ // A correctly enabled hook that enforces nothing is the failure this check
44
+ // could not see: the generated template used to be the whitespace guard
45
+ // alone while the real gate sat unused in the runtime. Reading only the
46
+ // hooksPath reports success for a hook that does nothing.
47
+ if (hooks === '.githooks') {
48
+ const hookFile = join(target, '.githooks/pre-commit')
49
+ if (!existsSync(hookFile)) warn('No .githooks/pre-commit found; run `npx code-foundry sync`')
50
+ else if (
51
+ !/code-foundry[@\w./-]*\s+(?:--yes\s+)?pre-commit/.test(readFileSync(hookFile, 'utf8'))
52
+ )
53
+ warn(
54
+ '.githooks/pre-commit does not delegate to the Code Foundry gate; run `npx code-foundry sync`'
55
+ )
56
+ }
43
57
  if (
44
58
  ['rust', 'python', 'solidity'].some((language) =>
45
59
  profile.languages.split(',').includes(language)
@@ -7,9 +7,11 @@ import {
7
7
  mkdirSync,
8
8
  readdirSync,
9
9
  readFileSync,
10
+ realpathSync,
10
11
  writeFileSync,
11
12
  } from 'node:fs'
12
13
  import { resolve } from 'node:path'
14
+ import { fileURLToPath } from 'node:url'
13
15
  import { spawnSync } from 'node:child_process'
14
16
  import { detectPackageManager, resolveProfile } from './lib/profile.mjs'
15
17
  import { configured, readConfig } from './lib/config.mjs'
@@ -1165,7 +1167,15 @@ function printProfile(command) {
1165
1167
  writeOutput('npm_publish', config.npm_publish ?? 'false')
1166
1168
  }
1167
1169
 
1168
- function preCommit() {
1170
+ /**
1171
+ * The local commit gate.
1172
+ *
1173
+ * Exported so the generated `.githooks/pre-commit` can reach it: the hook is
1174
+ * what turns this into an actual gate, and it used to run the whitespace
1175
+ * check alone. The generated hook is the consumer-visible contract, so keep
1176
+ * the two in step.
1177
+ */
1178
+ export function preCommit() {
1169
1179
  const changed = capture('git', ['diff', '--cached', '--name-only'])
1170
1180
  if (!changed) return
1171
1181
  const check = spawnSync('git', ['diff', '--cached', '--check'], { cwd: root, stdio: 'inherit' })
@@ -1173,6 +1183,10 @@ function preCommit() {
1173
1183
  if (/\.(js|jsx|ts|tsx|json|md|mdx|yml|yaml)$/.test(changed)) {
1174
1184
  ci('format')
1175
1185
  ci('lint')
1186
+ // Type errors are the fastest check that still needs a full toolchain, and
1187
+ // the most common reason a green-looking commit fails CI later. Run it
1188
+ // here while the dependency graph is warm.
1189
+ ci('type_check')
1176
1190
  }
1177
1191
  if (/\.rs$|(^|\/)Cargo\.toml$/.test(changed)) {
1178
1192
  run('cargo', ['fmt', '--check'])
@@ -1184,16 +1198,34 @@ function preCommit() {
1184
1198
  }
1185
1199
  }
1186
1200
 
1187
- const [area, task, ecosystem] = process.argv.slice(2)
1188
- try {
1189
- if (area === 'ci') ci(task)
1190
- else if (area === 'security') security(task, ecosystem)
1191
- else if (area === 'codeql') codeql()
1192
- else if (area === 'profile') printProfile(task)
1193
- else if (area === 'validation') validation(task)
1194
- else if (area === 'pre-commit') preCommit()
1195
- else throw new Error(`Unknown runtime command: ${area || '(missing)'}`)
1196
- } catch (error) {
1197
- console.error(error instanceof Error ? error.message : String(error))
1198
- process.exitCode = 1
1201
+ // Only dispatch when this module is the process entry point. The CLI imports
1202
+ // `preCommit` from here, and an unguarded dispatch would read argv and run the
1203
+ // gate a second time on import.
1204
+ const invokedDirectly =
1205
+ process.argv[1] !== undefined &&
1206
+ realpathSafe(process.argv[1]) === realpathSafe(fileURLToPath(import.meta.url))
1207
+
1208
+ /** @param {string} p @returns {string} */
1209
+ function realpathSafe(p) {
1210
+ try {
1211
+ return realpathSync(p)
1212
+ } catch {
1213
+ return p
1214
+ }
1215
+ }
1216
+
1217
+ if (invokedDirectly) {
1218
+ const [area, task, ecosystem] = process.argv.slice(2)
1219
+ try {
1220
+ if (area === 'ci') ci(task)
1221
+ else if (area === 'security') security(task, ecosystem)
1222
+ else if (area === 'codeql') codeql()
1223
+ else if (area === 'profile') printProfile(task)
1224
+ else if (area === 'validation') validation(task)
1225
+ else if (area === 'pre-commit') preCommit()
1226
+ else throw new Error(`Unknown runtime command: ${area || '(missing)'}`)
1227
+ } catch (error) {
1228
+ console.error(error instanceof Error ? error.message : String(error))
1229
+ process.exitCode = 1
1230
+ }
1199
1231
  }
@@ -72,6 +72,9 @@ pnpm-debug.log*
72
72
  .fleet/
73
73
  .opencode/
74
74
  .kluster/
75
+ .zcode/
76
+ .kiro/
77
+ .goose/
75
78
  .direnv/
76
79
  .envrc
77
80
  .vercel/