code-foundry 0.25.0 → 0.27.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.
Files changed (47) hide show
  1. package/.githooks/pre-commit +3 -4
  2. package/.github/CONTRIBUTING.md +10 -13
  3. package/.github/actions/setup/action.yml +50 -3
  4. package/.github/code-foundry.yml +6 -1
  5. package/.github/licenses/AGPL-3.0-or-later.txt +661 -0
  6. package/.github/licenses/GPL-3.0-or-later.txt +674 -0
  7. package/.github/licenses/MIT.txt +4 -16
  8. package/.github/workflows/ci.yml +20 -23
  9. package/.github/workflows/ci_self-ci.yml +1 -1
  10. package/.github/workflows/codeql.yml +23 -29
  11. package/.github/workflows/codeql_self-ci.yml +1 -1
  12. package/.github/workflows/draft-pr.yml +2 -2
  13. package/.github/workflows/release-pr.yml +4 -4
  14. package/.github/workflows/release.yml +17 -29
  15. package/.github/workflows/security.yml +24 -19
  16. package/.github/workflows/security_self-ci.yml +1 -1
  17. package/.github/workflows/test.yml +20 -20
  18. package/.github/workflows/test_self-ci.yml +1 -1
  19. package/AGENTS.md +13 -10
  20. package/CHANGELOG.md +28 -0
  21. package/README.md +19 -8
  22. package/docs/CONFIGURATION.md +11 -2
  23. package/docs/INITIALIZATION.md +9 -5
  24. package/docs/RELEASES.md +5 -0
  25. package/docs/WORKFLOWS.md +12 -5
  26. package/package.json +7 -3
  27. package/src/cli.mjs +29 -25
  28. package/src/commands/doctor.mjs +70 -0
  29. package/src/commands/sync.mjs +224 -0
  30. package/src/lib/config.mjs +38 -0
  31. package/src/lib/profile.mjs +114 -0
  32. package/src/runtime.mjs +316 -0
  33. package/.github/scripts/bootstrap.sh +0 -21
  34. package/.github/scripts/changed-files.sh +0 -96
  35. package/.github/scripts/ci.sh +0 -641
  36. package/.github/scripts/codeql-languages.sh +0 -91
  37. package/.github/scripts/doctor.sh +0 -98
  38. package/.github/scripts/format-fast-path.sh +0 -80
  39. package/.github/scripts/init-repo.sh +0 -264
  40. package/.github/scripts/pre-commit.sh +0 -67
  41. package/.github/scripts/profile.sh +0 -186
  42. package/.github/scripts/security.sh +0 -214
  43. package/.github/scripts/sitecustomize.py +0 -17
  44. package/.github/scripts/sync-codeowners.sh +0 -76
  45. package/.github/scripts/sync-protection.sh +0 -138
  46. package/.github/scripts/sync-template.sh +0 -744
  47. package/.github/scripts/turbo-cache-probe.sh +0 -102
@@ -61,15 +61,15 @@ jobs:
61
61
  path: .code-foundry
62
62
  sparse-checkout: |
63
63
  .github/actions
64
- .github/scripts
64
+ src/lib
65
+ src/runtime.mjs
65
66
  - name: Install runtime
66
67
  run: |
67
- mkdir -p .github/actions .github/scripts
68
+ mkdir -p .github/actions
68
69
  cp -R .code-foundry/.github/actions/. .github/actions/
69
- cp -R .code-foundry/.github/scripts/. .github/scripts/
70
70
  - name: Detect
71
71
  id: applicability
72
- run: bash .github/scripts/ci.sh task_profile unit >> "$GITHUB_OUTPUT"
72
+ run: node .code-foundry/src/runtime.mjs ci task_profile unit >> "$GITHUB_OUTPUT"
73
73
  - name: Setup
74
74
  if: steps.applicability.outputs.applicable == 'true'
75
75
  uses: ./.github/actions/setup
@@ -87,7 +87,7 @@ jobs:
87
87
  task-rust: ${{ steps.applicability.outputs.rust }}
88
88
  - name: Unit
89
89
  if: steps.applicability.outputs.applicable == 'true'
90
- run: bash .github/scripts/ci.sh unit
90
+ run: node .code-foundry/src/runtime.mjs ci unit
91
91
  - name: Upload coverage
92
92
  if: >-
93
93
  always() &&
@@ -122,15 +122,15 @@ jobs:
122
122
  path: .code-foundry
123
123
  sparse-checkout: |
124
124
  .github/actions
125
- .github/scripts
125
+ src/lib
126
+ src/runtime.mjs
126
127
  - name: Install runtime
127
128
  run: |
128
- mkdir -p .github/actions .github/scripts
129
+ mkdir -p .github/actions
129
130
  cp -R .code-foundry/.github/actions/. .github/actions/
130
- cp -R .code-foundry/.github/scripts/. .github/scripts/
131
131
  - name: Detect
132
132
  id: applicability
133
- run: bash .github/scripts/ci.sh task_profile integration >> "$GITHUB_OUTPUT"
133
+ run: node .code-foundry/src/runtime.mjs ci task_profile integration >> "$GITHUB_OUTPUT"
134
134
  - name: Setup
135
135
  if: steps.applicability.outputs.applicable == 'true'
136
136
  uses: ./.github/actions/setup
@@ -148,7 +148,7 @@ jobs:
148
148
  task-rust: ${{ steps.applicability.outputs.rust }}
149
149
  - name: Integration
150
150
  if: steps.applicability.outputs.applicable == 'true'
151
- run: bash .github/scripts/ci.sh integration
151
+ run: node .code-foundry/src/runtime.mjs ci integration
152
152
 
153
153
  e2e:
154
154
  name: E2E
@@ -167,15 +167,15 @@ jobs:
167
167
  path: .code-foundry
168
168
  sparse-checkout: |
169
169
  .github/actions
170
- .github/scripts
170
+ src/lib
171
+ src/runtime.mjs
171
172
  - name: Install runtime
172
173
  run: |
173
- mkdir -p .github/actions .github/scripts
174
+ mkdir -p .github/actions
174
175
  cp -R .code-foundry/.github/actions/. .github/actions/
175
- cp -R .code-foundry/.github/scripts/. .github/scripts/
176
176
  - name: Detect
177
177
  id: applicability
178
- run: bash .github/scripts/ci.sh task_profile e2e >> "$GITHUB_OUTPUT"
178
+ run: node .code-foundry/src/runtime.mjs ci task_profile e2e >> "$GITHUB_OUTPUT"
179
179
  - name: Cache browser binaries
180
180
  if: steps.applicability.outputs.applicable == 'true' && steps.applicability.outputs.browser == 'true'
181
181
  uses: ./.github/actions/cache
@@ -205,7 +205,7 @@ jobs:
205
205
  task-rust: ${{ steps.applicability.outputs.rust }}
206
206
  - name: E2E
207
207
  if: steps.applicability.outputs.applicable == 'true'
208
- run: bash .github/scripts/ci.sh e2e
208
+ run: node .code-foundry/src/runtime.mjs ci e2e
209
209
 
210
210
  smoke:
211
211
  name: Smoke
@@ -224,15 +224,15 @@ jobs:
224
224
  path: .code-foundry
225
225
  sparse-checkout: |
226
226
  .github/actions
227
- .github/scripts
227
+ src/lib
228
+ src/runtime.mjs
228
229
  - name: Install runtime
229
230
  run: |
230
- mkdir -p .github/actions .github/scripts
231
+ mkdir -p .github/actions
231
232
  cp -R .code-foundry/.github/actions/. .github/actions/
232
- cp -R .code-foundry/.github/scripts/. .github/scripts/
233
233
  - name: Detect
234
234
  id: applicability
235
- run: bash .github/scripts/ci.sh task_profile smoke >> "$GITHUB_OUTPUT"
235
+ run: node .code-foundry/src/runtime.mjs ci task_profile smoke >> "$GITHUB_OUTPUT"
236
236
  - name: Setup
237
237
  if: steps.applicability.outputs.applicable == 'true'
238
238
  uses: ./.github/actions/setup
@@ -250,4 +250,4 @@ jobs:
250
250
  task-rust: ${{ steps.applicability.outputs.rust }}
251
251
  - name: Smoke
252
252
  if: steps.applicability.outputs.applicable == 'true'
253
- run: bash .github/scripts/ci.sh smoke
253
+ run: node .code-foundry/src/runtime.mjs ci smoke
@@ -16,7 +16,7 @@ jobs:
16
16
  uses: ./.github/workflows/test.yml
17
17
  with:
18
18
  runtime-repository: 0xPlayerOne/code-foundry
19
- runtime-ref: v0.22.0
19
+ runtime-ref: staging
20
20
  runner: ubuntu-latest
21
21
  unit-runner: ubuntu-slim
22
22
  secrets: inherit
package/AGENTS.md CHANGED
@@ -7,7 +7,8 @@ They complement `CONTRIBUTING.md`. More specific instructions in nested `AGENTS.
7
7
  ## Mission
8
8
 
9
9
  - Keep formatting, linting, type checking, builds, tests, and coverage reproducible locally and in CI.
10
- - Prefer the versions pinned in `.mise.toml`.
10
+ - Prefer the repository's configured toolchain; `toolchain: auto` uses native
11
+ tools unless an existing `.mise.toml` is present.
11
12
  - Do not commit secrets, generated credentials, local environment files, or machine-specific paths.
12
13
  - Add tests for behavior changes and keep coverage thresholds explicit in the project configuration.
13
14
  - Make the smallest complete, well-tested change that solves the requested problem without disturbing unrelated work.
@@ -72,7 +73,9 @@ For normal feature work, branch from `staging` and target pull requests at `stag
72
73
 
73
74
  ## Toolchain and dependencies
74
75
 
75
- - Use the versions pinned in `.mise.toml`; run `mise install` when needed.
76
+ - Follow `toolchain: auto` in `.github/code-foundry.yml`; use native tools by
77
+ default and reuse mise only when the repository already has `.mise.toml`.
78
+ - If `toolchain: mise` is selected, run `mise install` before validation.
76
79
  - Use the package manager indicated by the existing lockfile:
77
80
  - `bun.lock` or `bun.lockb` → Bun
78
81
  - `pnpm-lock.yaml` → pnpm
@@ -88,14 +91,14 @@ For normal feature work, branch from `staging` and target pull requests at `stag
88
91
  Use the shared scripts when present. They detect supported tools and skip inapplicable checks:
89
92
 
90
93
  ```sh
91
- bash .github/scripts/ci.sh format
92
- bash .github/scripts/ci.sh lint
93
- bash .github/scripts/ci.sh type_check
94
- bash .github/scripts/ci.sh build
95
- bash .github/scripts/ci.sh unit
96
- bash .github/scripts/ci.sh integration
97
- bash .github/scripts/ci.sh e2e
98
- bash .github/scripts/ci.sh smoke
94
+ node src/runtime.mjs ci format
95
+ node src/runtime.mjs ci lint
96
+ node src/runtime.mjs ci type_check
97
+ node src/runtime.mjs ci build
98
+ node src/runtime.mjs ci unit
99
+ node src/runtime.mjs ci integration
100
+ node src/runtime.mjs ci e2e
101
+ node src/runtime.mjs ci smoke
99
102
  Security and dependency audits run through the GitHub Security workflow.
100
103
  ```
101
104
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.27.0](https://github.com/0xPlayerOne/code-foundry/compare/v0.26.0...v0.27.0) (2026-07-28)
4
+
5
+
6
+ ### Features
7
+
8
+ * add typed javascript cli checks ([2d9464b](https://github.com/0xPlayerOne/code-foundry/commit/2d9464bcbef8aa51607cdf52a6a60e3fc1bdb054))
9
+ * make mise an optional toolchain ([e62f023](https://github.com/0xPlayerOne/code-foundry/commit/e62f0238f4ddc2d1b0f89e0f2b23bca3679b934e))
10
+ * make paid github security checks opt-in ([bbfe899](https://github.com/0xPlayerOne/code-foundry/commit/bbfe8993d88a8415129ce29bfa5d2181807232ba))
11
+ * skip irrelevant language configuration ([39a0003](https://github.com/0xPlayerOne/code-foundry/commit/39a00037ba92152926e7a89759f3255d86a4f26e))
12
+ * standardize optional github security policies ([2cf6bc3](https://github.com/0xPlayerOne/code-foundry/commit/2cf6bc3af161ed8757a03447aab16f3e7ea310d7))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * include runtime libraries in every workflow job ([ce94cd9](https://github.com/0xPlayerOne/code-foundry/commit/ce94cd96a32eff54048712e1bf4b2d34a868ba2e))
18
+ * include runtime libraries in workflow checkout ([4b2c252](https://github.com/0xPlayerOne/code-foundry/commit/4b2c2527c37c82b498c2b3457df5dd52ef7c694f))
19
+ * migrate stale managed docs during sync ([380bab1](https://github.com/0xPlayerOne/code-foundry/commit/380bab1334c9125f97bf333cc29c796e1a16d57d))
20
+ * preserve authored notice files ([342e666](https://github.com/0xPlayerOne/code-foundry/commit/342e666f4e218e094180f10ecdc5a24b85a27b28))
21
+ * preserve repository notice attribution ([7d43911](https://github.com/0xPlayerOne/code-foundry/commit/7d439117ec89882d8d06895528d94ed717ade471))
22
+ * use pinned bun lockfile format ([0f75faf](https://github.com/0xPlayerOne/code-foundry/commit/0f75faf2c7614cc7055cbb4f26a01b3cf1639f85))
23
+
24
+ ## [0.26.0](https://github.com/0xPlayerOne/code-foundry/compare/v0.25.0...v0.26.0) (2026-07-28)
25
+
26
+
27
+ ### Features
28
+
29
+ * default new repositories to gplv3 ([e200135](https://github.com/0xPlayerOne/code-foundry/commit/e200135939779693eb73a64aaf3587eccdc36ffc))
30
+
3
31
  ## [0.25.0](https://github.com/0xPlayerOne/code-foundry/compare/v0.24.1...v0.25.0) (2026-07-28)
4
32
 
5
33
 
package/README.md CHANGED
@@ -19,7 +19,7 @@ npx code-foundry init
19
19
  ```
20
20
 
21
21
  Initialization is repository-aware. It detects supported languages, package
22
- manager, profile, release strategy, and required mise tools, then writes the
22
+ manager, profile, release strategy, and toolchain preference, then writes the
23
23
  resolved choices to `.github/code-foundry.yml`.
24
24
 
25
25
  ```bash
@@ -39,7 +39,7 @@ contract. For normal updates, edit that file and run `npx code-foundry sync`.
39
39
  and Release.
40
40
  - A small `.githooks/pre-commit` launcher with language-aware formatting and
41
41
  linting.
42
- - `.mise.toml`, optional `mise.lock`, repository profile configuration, and
42
+ - An optional `.mise.toml`/`mise.lock`, repository profile configuration, and
43
43
  standard GitHub forms and policy files.
44
44
  - AGENTS instructions, CODEOWNERS, license/notice files, and reusable release
45
45
  configuration.
@@ -56,10 +56,13 @@ The generated configuration is the one place to control the baseline. See
56
56
  guide and [Initialization and synchronization](docs/INITIALIZATION.md) for
57
57
  the two-command workflow.
58
58
 
59
- New repositories default to AGPL-3.0-or-later. Synchronization preserves an
60
- existing license unless a replacement is explicitly selected. Authored
59
+ New repositories default to GPL-3.0-or-later. Existing projects preserve an
60
+ authored license unless a replacement is explicitly selected. Our maintained
61
+ repositories explicitly select AGPL-3.0-or-later. Authored
61
62
  documentation, application files, custom workflows, and existing `.mise.toml`
62
- files are preserved by default.
63
+ files are preserved by default. `toolchain: auto` uses an existing mise setup
64
+ when present and otherwise uses native language tooling; choose `native` or
65
+ `mise` explicitly when a repository needs a fixed policy.
63
66
 
64
67
  ## Workflow model
65
68
 
@@ -75,13 +78,21 @@ test runner. Rust uses `rustfmt`, Clippy with warnings as errors, and native
75
78
  Cargo tests. Python uses Ruff, uv/pip-compatible setup, and native Python
76
79
  tests. Solidity projects retain their native toolchain and test runner.
77
80
 
78
- CodeQL remains separate from CI and Security. Dependency Review is isolated in
79
- Security and is skipped where GitHub does not support it, while JavaScript,
80
- Python, and Rust audits remain available without GitHub Advanced Security.
81
+ CodeQL remains separate from CI and Security. With the default `codeql: auto`
82
+ and `dependency_review: auto` policies, public repositories use the free GitHub
83
+ security checks while private repositories skip them unless explicitly opted
84
+ in and supported by GitHub Advanced Security. JavaScript, Python, and Rust
85
+ audits remain available without Advanced Security. Code Foundry does not enable
86
+ GitHub Code Quality or other paid GitHub features.
81
87
 
82
88
  See [Workflow and CI conventions](docs/WORKFLOWS.md) for triggers, required
83
89
  checks, runners, coverage, caching, and custom workflow extensions.
84
90
 
91
+ The default contribution policy uses the `staging-release` workflow with
92
+ rebase merges. Repositories can set `merge_strategy: squash` or
93
+ `merge_strategy: merge` in `.github/code-foundry.yml` when their governance
94
+ requires another merge convention.
95
+
85
96
  ## Releases and publishing
86
97
 
87
98
  The standard flow promotes `staging` into `main`, lets Release Please open a
@@ -9,6 +9,10 @@ npx code-foundry init
9
9
  Initialization detects the repository and writes a fully resolved configuration.
10
10
  Edit that file directly, then run `npx code-foundry sync`.
11
11
 
12
+ The default `toolchain: auto` reuses an existing `.mise.toml`; otherwise it
13
+ selects native setup for the detected languages. Use `toolchain: native` to
14
+ prohibit mise or `toolchain: mise` to require it.
15
+
12
16
  ## Configuration flow
13
17
 
14
18
  ```text
@@ -17,7 +21,7 @@ repository manifests and source
17
21
  v
18
22
  .github/code-foundry.yml
19
23
  |
20
- +--> mise and environment setup
24
+ +--> native or mise toolchain setup
21
25
  +--> standard workflow callers
22
26
  +--> runtime repository and version
23
27
  +--> release, license, cache, and coverage policy
@@ -30,13 +34,18 @@ repository manifests and source
30
34
  | `profile` | `auto`, `application`, `monorepo`, `minimal` | Repository shape |
31
35
  | `languages` | detected list | TypeScript, Rust, Python, Solidity |
32
36
  | `package_manager` | `bun`, `pnpm`, `yarn`, `npm`, `none` | JavaScript setup |
37
+ | `toolchain` | `auto`, `native`, `mise` | Environment setup policy; defaults to `auto` |
33
38
  | `features` | `all` or a list | Standard workflow callers |
39
+ | `codeql` | `auto`, `true`, `false` | CodeQL policy; public repositories default to enabled, non-public repositories default to disabled |
40
+ | `dependency_review` | `auto`, `true`, `false` | Dependency Review policy; public repositories default to enabled, non-public repositories default to disabled |
34
41
  | `prune_standard` | `true` or `false` | Remove disabled standard callers |
35
42
  | `runtime_repository` | `OWNER/REPO` | Reusable workflow source |
36
43
  | `runtime_ref` | tag or branch | Reusable workflow version |
37
44
  | `release_type` | `node`, `python`, `rust`, `simple`, `none` | Release strategy |
38
45
  | `npm_publish` | `true` or `false` | Opt into npm publication |
39
- | `license` | license name, `preserve`, `none` | License policy |
46
+ | `license` | `gpl-3.0-or-later`, `agpl-3.0-or-later`, `mit`, `preserve`, `none` | License policy; new repositories default to GPLv3 |
47
+ | `git_workflow` | `staging-release` | Branch/release model; the standard model promotes `staging` into `main` |
48
+ | `merge_strategy` | `rebase`, `squash`, `merge` | Preferred merge method for contribution and release PRs; defaults to `rebase` |
40
49
  | `runner` fields | GitHub runner names | Per-workflow runner policy |
41
50
 
42
51
  Supported features are `ci`, `codeql`, `security`, `test`, `draft-pr`,
@@ -11,7 +11,7 @@ npx code-foundry doctor
11
11
  ```
12
12
 
13
13
  `init` detects supported languages, package manager, repository profile,
14
- release strategy, mise tools, and standard features. It writes the resolved
14
+ release strategy, toolchain preference, and standard features. It writes the resolved
15
15
  choices to `.github/code-foundry.yml`, initializes the local environment, and
16
16
  renders the standard baseline.
17
17
 
@@ -25,6 +25,9 @@ manifests, lockfiles, source extensions, workspace metadata, and existing
25
25
  project scripts. The generated values are explicit, so later syncs are stable
26
26
  until a maintainer changes the file.
27
27
 
28
+ New repositories receive the GNU GPLv3. Existing repositories with an authored
29
+ `LICENSE` keep that license unless the generated configuration is changed.
30
+
28
31
  ## Runtime selection
29
32
 
30
33
  Workflow callers use `runtime_repository` and `runtime_ref` from
@@ -37,7 +40,8 @@ Sync updates standard Code Foundry files only. It preserves application code,
37
40
  authored documentation, existing `.mise.toml` selections, and custom workflows
38
41
  such as deployment, search, Slither, or monitoring workflows.
39
42
 
40
- The environment bootstrap installs or reuses mise-managed tools and enables
41
- the repository hooks. Use `npx code-foundry doctor` when local setup needs to
42
- be checked; the CLI supplies the implementation without adding maintenance
43
- scripts to the consumer repository.
43
+ The environment bootstrap enables repository hooks and uses mise only when an
44
+ existing `.mise.toml` is present or `toolchain: mise` is selected. Otherwise it
45
+ uses the repository's native tools. Use `npx code-foundry doctor` when local
46
+ setup needs to be checked; the CLI supplies the implementation without adding
47
+ maintenance scripts to the consumer repository.
package/docs/RELEASES.md CHANGED
@@ -12,6 +12,11 @@ Keep commits Conventional Commit-shaped (`feat:`, `fix:`, `docs:`, `ci:`,
12
12
  `chore:`, and so on). Release Please uses them to select patch/minor/major
13
13
  versions and generate grouped changelog notes.
14
14
 
15
+ The default `merge_strategy` is `rebase`, matching the repository's linear
16
+ history policy. Set it to `squash` or `merge` in `.github/code-foundry.yml` when
17
+ that repository intentionally uses a different merge convention. The current
18
+ supported `git_workflow` is `staging-release`.
19
+
15
20
  The release workflow opens or updates a versioned PR after changes reach
16
21
  `main`. Merging that PR updates the changelog, creates the Git tag and GitHub
17
22
  Release, and triggers any configured package publication.
package/docs/WORKFLOWS.md CHANGED
@@ -46,11 +46,18 @@ ambiguous for mixed-language repositories.
46
46
 
47
47
  ## Security behavior
48
48
 
49
- CodeQL is a separate workflow using GitHub's official actions. Dependency
50
- Review is skipped where GitHub does not support it, while JavaScript, Python,
51
- and Rust audits remain available without Advanced Security. If GitHub default
52
- setup is enabled, disable its generated CodeQL workflow to avoid duplicate
53
- analysis.
49
+ CodeQL is a separate workflow using GitHub's official actions. The default
50
+ `codeql: auto` policy enables it for public repositories and skips it for
51
+ private repositories unless explicitly enabled and Advanced Security is
52
+ available. Dependency Review follows the same policy through
53
+ `dependency_review: auto`; JavaScript, Python, and Rust audits remain
54
+ available without Advanced Security. If GitHub default setup is enabled,
55
+ disable its generated CodeQL workflow to avoid duplicate analysis.
56
+
57
+ Code Foundry does not enable GitHub Code Quality or any paid GitHub feature.
58
+ The repository's format and lint jobs are ordinary CI checks. Set `codeql:
59
+ false` or `dependency_review: false` when a public repository also needs those
60
+ checks disabled.
54
61
 
55
62
  ## Branch protection
56
63
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-foundry",
3
- "version": "0.25.0",
3
+ "version": "0.27.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",
@@ -42,12 +42,16 @@
42
42
  "build": "node --check src/cli.mjs",
43
43
  "format:check": "node --check src/cli.mjs",
44
44
  "lint": "node --check src/cli.mjs",
45
- "test": "node --check src/cli.mjs",
46
- "type-check": "node --check src/cli.mjs",
45
+ "test": "node --test",
46
+ "type-check": "tsc --project jsconfig.json",
47
47
  "pack:check": "npm pack --dry-run",
48
48
  "prepublishOnly": "npm test && npm run pack:check"
49
49
  },
50
50
  "publishConfig": {
51
51
  "access": "public"
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^24.0.0",
55
+ "typescript": "^5.8.3"
52
56
  }
53
57
  }
package/src/cli.mjs CHANGED
@@ -1,12 +1,16 @@
1
1
  #!/usr/bin/env node
2
+ // @ts-check
2
3
 
3
- import { existsSync } from 'node:fs'
4
4
  import { resolve } from 'node:path'
5
- import { spawnSync } from 'node:child_process'
6
5
  import { fileURLToPath } from 'node:url'
6
+ import { doctor } from './commands/doctor.mjs'
7
+ import { syncRepository } from './commands/sync.mjs'
7
8
 
8
9
  const packageRoot = resolve(fileURLToPath(new URL('..', import.meta.url)))
9
10
 
11
+ /** @typedef {{ target: string, dryRun: boolean, force: boolean }} Options */
12
+ /** @typedef {{ command: string, options: Options }} ParsedArgs */
13
+
10
14
  const usage = `code-foundry — initialize and maintain agent-ready repositories
11
15
 
12
16
  Usage:
@@ -25,13 +29,17 @@ Options:
25
29
  -h, --help Show this help
26
30
  `
27
31
 
32
+ /** @param {string} message @returns {never} */
28
33
  function fail(message) {
29
34
  console.error(`code-foundry: ${message}`)
30
35
  process.exit(2)
31
36
  }
32
37
 
38
+ /** @param {string[]} argv @returns {ParsedArgs} */
33
39
  function parseArgs(argv) {
34
- const command = argv[0] && !argv[0].startsWith('-') ? argv.shift() : 'init'
40
+ const first = argv[0]
41
+ const hasCommand = Boolean(first && !first.startsWith('-'))
42
+ const command = hasCommand ? /** @type {string} */ (argv.shift()) : 'init'
35
43
  const options = { target: process.cwd(), dryRun: false, force: false }
36
44
 
37
45
  while (argv.length) {
@@ -52,34 +60,30 @@ function parseArgs(argv) {
52
60
  return { command, options }
53
61
  }
54
62
 
55
- function run(script, args, target) {
56
- const scriptPath = resolve(packageRoot, '.github/scripts', script)
57
- if (!existsSync(scriptPath)) fail(`package is missing ${script}`)
58
- const result = spawnSync('bash', [scriptPath, ...args], {
59
- cwd: target,
60
- stdio: 'inherit',
61
- env: process.env,
62
- })
63
- if (result.error) fail(result.error.message)
64
- if (result.status !== 0) process.exit(result.status ?? 1)
65
- }
66
-
67
63
  function main() {
68
64
  const { command, options } = parseArgs(process.argv.slice(2))
69
65
  const target = resolve(options.target)
70
- const common = ['--source', packageRoot, '--ref', 'main']
71
- if (options.dryRun) common.push('--check')
72
- else common.push('--apply')
73
- if (options.force) common.push('--force')
74
66
 
75
67
  if (command === 'init') {
76
- const initArgs = ['--source', packageRoot, '--ref', 'main']
77
- if (options.dryRun) initArgs.push('--dry-run')
78
- if (options.force) initArgs.push('--force')
79
- run('init-repo.sh', initArgs, target)
68
+ try {
69
+ syncRepository({ target, source: packageRoot, dryRun: options.dryRun, force: options.force, init: true })
70
+ } catch (error) {
71
+ fail(error instanceof Error ? error.message : String(error))
72
+ }
73
+ } else if (command === 'sync') {
74
+ try {
75
+ syncRepository({ target, source: packageRoot, dryRun: options.dryRun, force: options.force })
76
+ } catch (error) {
77
+ fail(error instanceof Error ? error.message : String(error))
78
+ }
79
+ } else if (command === 'doctor') {
80
+ try {
81
+ doctor(target)
82
+ } catch (error) {
83
+ console.error(`code-foundry: ${error instanceof Error ? error.message : String(error)}`)
84
+ process.exitCode = 1
85
+ }
80
86
  }
81
- else if (command === 'sync') run('sync-template.sh', common, target)
82
- else if (command === 'doctor') run('doctor.sh', [], target)
83
87
  else fail(`unknown command: ${command}`)
84
88
  }
85
89
 
@@ -0,0 +1,70 @@
1
+ // @ts-check
2
+
3
+ import { existsSync, readFileSync } from 'node:fs'
4
+ import { join, resolve } from 'node:path'
5
+ import { spawnSync } from 'node:child_process'
6
+ import { includesValue, readConfig } from '../lib/config.mjs'
7
+ import { resolveProfile } from '../lib/profile.mjs'
8
+
9
+ /** @param {string} root */
10
+ export function doctor(root) {
11
+ const target = resolve(root)
12
+ const config = readConfig(join(target, '.github/code-foundry.yml'))
13
+ const profile = resolveProfile(target)
14
+ let errors = 0
15
+ /** @param {string} message */
16
+ const error = (message) => { console.error(`ERROR: ${message}`); errors += 1 }
17
+ /** @param {string} message */
18
+ const warn = (message) => console.warn(`WARN: ${message}`)
19
+
20
+ const toolchain = config.toolchain ?? 'auto'
21
+ if (!['auto', 'native', 'mise'].includes(toolchain)) error(`unsupported toolchain: ${toolchain}`)
22
+ if (toolchain === 'mise' && !existsSync(join(target, '.mise.toml'))) error('.mise.toml is required when toolchain: mise')
23
+ if (toolchain === 'auto') {
24
+ console.log(existsSync(join(target, '.mise.toml'))
25
+ ? 'Toolchain: mise (detected from existing .mise.toml).'
26
+ : 'Toolchain: native (mise not configured).')
27
+ }
28
+ const hooks = git(target, ['config', '--get', 'core.hooksPath'])
29
+ if (hooks !== '.githooks') warn('Git hooks are not enabled; run `npx code-foundry init`')
30
+
31
+ const packageFile = join(target, 'package.json')
32
+ if (existsSync(packageFile)) {
33
+ let packageJson
34
+ try { packageJson = JSON.parse(readFileSync(packageFile, 'utf8')) }
35
+ catch { error('package.json is not valid JSON'); packageJson = {} }
36
+ const lockfiles = ['bun.lock', 'bun.lockb', 'pnpm-lock.yaml', 'yarn.lock', 'package-lock.json'].filter((file) => existsSync(join(target, file)))
37
+ const groups = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']
38
+ if (!lockfiles.length && groups.some((key) => Object.keys(packageJson[key] ?? {}).length)) error('package.json exists but no supported lockfile was found')
39
+ if (lockfiles.length > 1) error('multiple JavaScript lockfiles found; keep one package manager')
40
+ if (packageJson.packageManager && lockfiles.length) {
41
+ const declared = String(packageJson.packageManager).split('@')[0]
42
+ const actual = lockfiles[0]?.startsWith('bun') ? 'bun' : lockfiles[0]?.split('-')[0].replace('.yaml', '')
43
+ if (actual && declared !== actual) error(`packageManager (${declared}) does not match ${actual} lockfile`)
44
+ }
45
+ }
46
+
47
+ if (profile.languages.split(',').includes('rust') && !commandExists('cargo')) error('Cargo is required for this repository')
48
+ if (profile.languages.split(',').includes('rust')) {
49
+ const metadata = spawnSync('cargo', ['metadata', '--no-deps', '--format-version', '1'], { cwd: target, stdio: 'ignore' })
50
+ if (metadata.status !== 0) error('cargo metadata failed')
51
+ }
52
+ if (profile.languages.split(',').includes('python') && !commandExists('python') && !existsSync(join(target, '.venv/bin/python'))) error('Python is required for this repository')
53
+
54
+ for (const workflow of ['ci', 'codeql', 'security', 'test', 'draft-pr', 'release-pr', 'release']) {
55
+ if (includesValue(config.features ?? 'all', workflow) && !existsSync(join(target, `.github/workflows/${workflow}.yml`))) error(`missing enabled workflow: ${workflow}.yml`)
56
+ }
57
+ console.log('Remote CI, Test, Security, CodeQL, and release runtimes are loaded by reusable workflow wrappers.')
58
+ if (errors) throw new Error(`Repository doctor found ${errors} error(s).`)
59
+ console.log('Repository doctor passed.')
60
+ }
61
+
62
+ /** @param {string} root @param {string[]} args */
63
+ function git(root, args) {
64
+ return spawnSync('git', args, { cwd: root, encoding: 'utf8' }).stdout?.trim() ?? ''
65
+ }
66
+
67
+ /** @param {string} command */
68
+ function commandExists(command) {
69
+ return spawnSync(command, ['--version'], { stdio: 'ignore' }).status === 0
70
+ }