code-foundry 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,67 +1,5 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
- git diff --cached --check
5
- changed=$(git diff --cached --name-only)
6
-
7
- has_script() {
8
- [ -f package.json ] && node -e 'const p=require("./package.json"); process.exit(p.scripts?.[process.argv[1]] ? 0 : 1)' "$1"
9
- }
10
-
11
- package_manager() {
12
- if [ -f bun.lock ] || [ -f bun.lockb ]; then echo bun
13
- elif [ -f pnpm-lock.yaml ]; then echo pnpm
14
- elif [ -f yarn.lock ]; then echo yarn
15
- else echo npm
16
- fi
17
- }
18
-
19
- run_lint_staged() {
20
- if ! has_script lint-staged; then return 1; fi
21
- case "$(package_manager)" in
22
- bun) bun run lint-staged ;;
23
- pnpm) corepack pnpm run lint-staged ;;
24
- yarn) corepack yarn run lint-staged ;;
25
- npm) npm run lint-staged ;;
26
- esac
27
- }
28
-
29
- if printf '%s\n' "$changed" | grep -Eq '\.(js|jsx|ts|tsx|json|md|mdx|yml|yaml)$'; then
30
- if ! run_lint_staged; then
31
- bash .github/scripts/ci.sh format
32
- bash .github/scripts/ci.sh lint
33
- fi
34
- fi
35
-
36
- if printf '%s\n' "$changed" | grep -Eq '\.rs$|(^|/)Cargo\.toml$'; then
37
- cargo fmt --check
38
- cargo clippy --all-targets -- -D warnings
39
- fi
40
-
41
- python_files=()
42
- python_config=false
43
- while IFS= read -r file; do
44
- case "$file" in
45
- *.py)
46
- [ -f "$file" ] && python_files+=("$file")
47
- ;;
48
- pyproject.toml|requirements.txt|requirements-dev.txt)
49
- python_config=true
50
- ;;
51
- esac
52
- done <<< "$changed"
53
-
54
- if [ "$python_config" = true ] || [ "${#python_files[@]}" -gt 0 ]; then
55
- ruff_targets=(.)
56
- [ "$python_config" = true ] || ruff_targets=("${python_files[@]}")
57
- if [ -x .venv/bin/ruff ]; then
58
- .venv/bin/ruff format --check "${ruff_targets[@]}"
59
- .venv/bin/ruff check "${ruff_targets[@]}"
60
- elif command -v ruff >/dev/null 2>&1; then
61
- ruff format --check "${ruff_targets[@]}"
62
- ruff check "${ruff_targets[@]}"
63
- else
64
- echo "Python files changed but Ruff is not installed; run the template setup first." >&2
65
- exit 1
66
- fi
67
- fi
4
+ repo_root="$(git rev-parse --show-toplevel)"
5
+ exec bash "$repo_root/.github/scripts/pre-commit.sh" "$@"
@@ -7,6 +7,7 @@ profile="${REPO_FOUNDRY_PROFILE:-auto}"
7
7
  protection=false
8
8
  dry_run=false
9
9
  prune=false
10
+ force=false
10
11
  languages="${REPO_FOUNDRY_LANGUAGES:-auto}"
11
12
  features="${REPO_FOUNDRY_FEATURES:-all}"
12
13
  package_manager="${REPO_FOUNDRY_PACKAGE_MANAGER:-auto}"
@@ -41,6 +42,7 @@ Options:
41
42
  --license-file PATH Use an exact custom license file
42
43
  --npm-publish Enable npm publication in the release workflow
43
44
  --dry-run Preview changes without writing files
45
+ --force Replace protected standard docs/templates
44
46
  --prune Remove disabled standard workflows (never custom workflows)
45
47
  --protection Synchronize main branch required checks
46
48
  --no-bootstrap Skip mise/hooks/doctor bootstrap after init
@@ -66,6 +68,7 @@ while [ "$#" -gt 0 ]; do
66
68
  --license-file) license_file="${2:?missing license file}"; shift 2 ;;
67
69
  --npm-publish) npm_publish=true; shift ;;
68
70
  --dry-run) dry_run=true; shift ;;
71
+ --force) force=true; shift ;;
69
72
  --prune) prune=true; shift ;;
70
73
  --protection) protection=true; shift ;;
71
74
  --no-bootstrap) bootstrap=false; shift ;;
@@ -110,6 +113,7 @@ sync_args=(
110
113
  if [ -n "$license_file" ]; then sync_args+=(--license-file "$license_file"); fi
111
114
  if [ "$dry_run" = true ]; then sync_args+=(--check); else sync_args+=(--apply); fi
112
115
  if [ "$prune" = true ]; then sync_args+=(--prune); fi
116
+ if [ "$force" = true ]; then sync_args+=(--force); fi
113
117
  bash "$sync_script" "${sync_args[@]}"
114
118
 
115
119
  if [ "$dry_run" = true ]; then
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ git diff --cached --check
5
+ changed=$(git diff --cached --name-only)
6
+
7
+ has_script() {
8
+ [ -f package.json ] && node -e 'const p=require("./package.json"); process.exit(p.scripts?.[process.argv[1]] ? 0 : 1)' "$1"
9
+ }
10
+
11
+ package_manager() {
12
+ if [ -f bun.lock ] || [ -f bun.lockb ]; then echo bun
13
+ elif [ -f pnpm-lock.yaml ]; then echo pnpm
14
+ elif [ -f yarn.lock ]; then echo yarn
15
+ else echo npm
16
+ fi
17
+ }
18
+
19
+ run_lint_staged() {
20
+ if ! has_script lint-staged; then return 1; fi
21
+ case "$(package_manager)" in
22
+ bun) bun run lint-staged ;;
23
+ pnpm) corepack pnpm run lint-staged ;;
24
+ yarn) corepack yarn run lint-staged ;;
25
+ npm) npm run lint-staged ;;
26
+ esac
27
+ }
28
+
29
+ if printf '%s\n' "$changed" | grep -Eq '\.(js|jsx|ts|tsx|json|md|mdx|yml|yaml)$'; then
30
+ if ! run_lint_staged; then
31
+ bash .github/scripts/ci.sh format
32
+ bash .github/scripts/ci.sh lint
33
+ fi
34
+ fi
35
+
36
+ if printf '%s\n' "$changed" | grep -Eq '\.rs$|(^|/)Cargo\.toml$'; then
37
+ cargo fmt --check
38
+ cargo clippy --all-targets -- -D warnings
39
+ fi
40
+
41
+ python_files=()
42
+ python_config=false
43
+ while IFS= read -r file; do
44
+ case "$file" in
45
+ *.py)
46
+ [ -f "$file" ] && python_files+=("$file")
47
+ ;;
48
+ pyproject.toml|requirements.txt|requirements-dev.txt)
49
+ python_config=true
50
+ ;;
51
+ esac
52
+ done <<< "$changed"
53
+
54
+ if [ "$python_config" = true ] || [ "${#python_files[@]}" -gt 0 ]; then
55
+ ruff_targets=(.)
56
+ [ "$python_config" = true ] || ruff_targets=("${python_files[@]}")
57
+ if [ -x .venv/bin/ruff ]; then
58
+ .venv/bin/ruff format --check "${ruff_targets[@]}"
59
+ .venv/bin/ruff check "${ruff_targets[@]}"
60
+ elif command -v ruff >/dev/null 2>&1; then
61
+ ruff format --check "${ruff_targets[@]}"
62
+ ruff check "${ruff_targets[@]}"
63
+ else
64
+ echo "Python files changed but Ruff is not installed; run the template setup first." >&2
65
+ exit 1
66
+ fi
67
+ fi
@@ -18,6 +18,7 @@ Options:
18
18
  --check Preview changes (default)
19
19
  --apply Apply changes
20
20
  --prune Remove disabled standard workflows
21
+ --force Replace protected standard docs/templates
21
22
  EOF
22
23
  }
23
24
 
@@ -29,6 +30,7 @@ profile="${REPO_FOUNDRY_PROFILE:-auto}"
29
30
  languages="${REPO_FOUNDRY_LANGUAGES:-auto}"
30
31
  features="${REPO_FOUNDRY_FEATURES:-all}"
31
32
  prune=false
33
+ force=false
32
34
  languages_set=false
33
35
  profile_set=false
34
36
  features_set=false
@@ -107,6 +109,7 @@ while [ "$#" -gt 0 ]; do
107
109
  --check) mode="check"; shift ;;
108
110
  --apply) mode="apply"; shift ;;
109
111
  --prune) prune=true; shift ;;
112
+ --force) force=true; shift ;;
110
113
  -h|--help) usage; exit 0 ;;
111
114
  *) usage >&2; exit 2 ;;
112
115
  esac
@@ -159,9 +162,10 @@ contains_word "$valid_release_types" "$release_type" || {
159
162
  exit 2
160
163
  }
161
164
  case "$license" in
162
- preserve|agpl-3.0-or-later|mit|none) ;;
165
+ preserve|agpl-3.0-or-later|mit|custom|none) ;;
163
166
  *) printf 'Unsupported license: %s\n' "$license" >&2; exit 2 ;;
164
167
  esac
168
+ [ -z "$license_file" ] || license=custom
165
169
 
166
170
  if [ -d "$source" ] && [ -f "$source/.github/scripts/sync-template.sh" ]; then
167
171
  template_root="$source"
@@ -229,6 +233,7 @@ files=(
229
233
  .github/scripts/doctor.sh
230
234
  .github/scripts/security.sh
231
235
  .github/scripts/sitecustomize.py
236
+ .github/scripts/pre-commit.sh
232
237
  .github/scripts/sync-template.sh
233
238
  .github/scripts/init-repo.sh
234
239
  .github/scripts/sync-codeowners.sh
@@ -268,6 +273,14 @@ standard_workflow() {
268
273
  esac
269
274
  }
270
275
 
276
+ protected_standard_file() {
277
+ case "$1" in
278
+ AGENTS.md|.github/CODE_OF_CONDUCT.md|.github/CONTRIBUTING.md|.github/PULL_REQUEST_TEMPLATE.md|.github/SECURITY.md|.github/ISSUE_TEMPLATE/*)
279
+ return 0 ;;
280
+ *) return 1 ;;
281
+ esac
282
+ }
283
+
271
284
  custom_workflows=()
272
285
  if [ -d .github/workflows ]; then
273
286
  while IFS= read -r workflow; do
@@ -292,6 +305,10 @@ for file in "${files[@]}"; do
292
305
  echo "Template file missing: $file" >&2
293
306
  exit 1
294
307
  fi
308
+ if [ "$force" != true ] && [ -f "$file" ] && protected_standard_file "$file"; then
309
+ printf 'Preserving existing authored file %s (use --force to refresh).\n' "$file"
310
+ continue
311
+ fi
295
312
  if { [ "$file" = LICENSE ] || [ "$file" = NOTICE ]; } && [ "$license" = preserve ] && [ -f "$file" ]; then
296
313
  continue
297
314
  fi
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.0](https://github.com/0xPlayerOne/code-foundry/compare/v0.4.0...v0.5.0) (2026-07-28)
4
+
5
+
6
+ ### Features
7
+
8
+ * **init:** preserve authored repository docs ([9fab688](https://github.com/0xPlayerOne/code-foundry/commit/9fab6880ad82762cf4e2eb7d813355ab22cdb905))
9
+
10
+ ## [0.4.0](https://github.com/0xPlayerOne/code-foundry/compare/v0.3.0...v0.4.0) (2026-07-28)
11
+
12
+
13
+ ### Features
14
+
15
+ * **hooks:** centralize generated pre-commit runtime ([8e8111c](https://github.com/0xPlayerOne/code-foundry/commit/8e8111c1b1b95fcb356fe1b1366e369393cbf946))
16
+
3
17
  ## [0.3.0](https://github.com/0xPlayerOne/code-foundry/compare/v0.2.0...v0.3.0) (2026-07-28)
4
18
 
5
19
 
package/README.md CHANGED
@@ -40,6 +40,10 @@ Initialization defaults to AGPL for new repositories. Synchronization defaults
40
40
  to `--license preserve`, so an existing repository's license is never replaced
41
41
  unless you explicitly select a license or provide `--license-file`.
42
42
 
43
+ The generated Git hook is intentionally a small launcher. It points at the
44
+ package-managed `.github/scripts/pre-commit.sh`, so repositories do not need to
45
+ maintain a second copy of the language-aware hook logic.
46
+
43
47
  ## Setup
44
48
 
45
49
  1. Install [mise](https://mise.jdx.dev/).
@@ -61,6 +65,10 @@ Use `bash .github/scripts/sync-template.sh --source ... --check` to preview diff
61
65
 
62
66
  The initializer is intentionally flag-driven so the same baseline can be used as a small repository package. Language selection controls CodeQL and records the repository profile; feature selection controls which standard workflows are installed. `auto` detects supported languages, while `all` enables every standard workflow. The resolver profiles the repository for language, package manager, project shape, release type, runners, and cache policy.
63
67
 
68
+ Existing authored documentation and issue/PR templates are preserved by
69
+ default. Pass `--force` when intentionally refreshing those files from the
70
+ baseline; repository-specific workflows and application files remain protected.
71
+
64
72
  ```bash
65
73
  # Preview a TypeScript + Python repository without changing files
66
74
  bash .github/scripts/init-repo.sh \
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-foundry",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "A fast, language-aware repository factory for agent-ready workflows, testing, security, and release automation.",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-or-later",
package/src/cli.mjs CHANGED
@@ -27,6 +27,7 @@ Init/sync options:
27
27
  --license-file PATH Use an exact custom license file
28
28
  --npm-publish Enable npm publication in the release workflow
29
29
  --dry-run Preview changes without writing files
30
+ --force Replace protected standard docs/templates
30
31
  --prune Remove disabled standard workflows
31
32
  --protection Synchronize main branch protections (init only)
32
33
  --no-bootstrap Skip mise/hooks/doctor bootstrap after init
@@ -56,6 +57,7 @@ function parseArgs(argv) {
56
57
  featuresSet: false,
57
58
  dryRun: false,
58
59
  prune: false,
60
+ force: false,
59
61
  protection: false,
60
62
  bootstrap: true,
61
63
  }
@@ -87,6 +89,7 @@ function parseArgs(argv) {
87
89
  continue
88
90
  }
89
91
  if (arg === '--dry-run') options.dryRun = true
92
+ else if (arg === '--force') options.force = true
90
93
  else if (arg === '--prune') options.prune = true
91
94
  else if (arg === '--protection') options.protection = true
92
95
  else if (arg === '--no-bootstrap') options.bootstrap = false
@@ -118,6 +121,7 @@ function main() {
118
121
  if (options.languagesSet) common.push('--languages', options.languages)
119
122
  if (options.featuresSet) common.push('--features', options.features)
120
123
  if (options.prune) common.push('--prune')
124
+ if (options.force) common.push('--force')
121
125
  if (options.dryRun) common.push('--check')
122
126
  else common.push('--apply')
123
127
 
@@ -136,6 +140,7 @@ function main() {
136
140
  if (options.protection) initArgs.push('--protection')
137
141
  if (options.dryRun) initArgs.push('--dry-run')
138
142
  if (options.prune) initArgs.push('--prune')
143
+ if (options.force) initArgs.push('--force')
139
144
  if (options.npmPublish) initArgs.push('--npm-publish')
140
145
  if (!options.bootstrap) initArgs.push('--no-bootstrap')
141
146
  run('init-repo.sh', initArgs, target)