@icebreakers/monorepo-templates 2.1.0 → 2.2.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 (104) hide show
  1. package/assets/.agents/skills/repoctl/SKILL.md +2 -0
  2. package/assets/.agents/skills/repoctl/references/commands.md +224 -13
  3. package/assets/.agents/skills/repoctl/references/config.md +70 -6
  4. package/assets/.agents/skills/repoctl/references/dependencies.md +37 -0
  5. package/assets/.agents/skills/repoctl/references/knip.md +34 -0
  6. package/assets/.agents/skills/repoctl/references/templates.md +23 -0
  7. package/assets/.github/workflows/ci.yml +10 -10
  8. package/assets/package.json +5 -9
  9. package/assets/pnpm-workspace.yaml +2 -1
  10. package/assets/repoctl.config.ts +1 -1
  11. package/assets/turbo.json +2 -1
  12. package/dist/cli.mjs +1 -1
  13. package/dist/index.d.mts +281 -1
  14. package/dist/index.mjs +1137 -74
  15. package/dist/{prepare-2VvFwYDz.mjs → prepare-BYNSxvoB.mjs} +76 -4
  16. package/package.json +2 -1
  17. package/template-data.mjs +24 -0
  18. package/templates/client/package.json +1 -1
  19. package/templates/next/README.md +31 -0
  20. package/templates/next/eslint.config.js +5 -0
  21. package/templates/next/gitignore +7 -0
  22. package/templates/next/next.config.ts +5 -0
  23. package/templates/next/package.json +36 -0
  24. package/templates/next/src/app/api/health/route.ts +5 -0
  25. package/templates/next/src/app/counter.tsx +19 -0
  26. package/templates/next/src/app/favicon.ico +0 -0
  27. package/templates/next/src/app/globals.css +109 -0
  28. package/templates/next/src/app/layout.tsx +17 -0
  29. package/templates/next/src/app/page.tsx +16 -0
  30. package/templates/next/stylelint.config.js +3 -0
  31. package/templates/next/test/counter.test.tsx +22 -0
  32. package/templates/next/tsconfig.json +40 -0
  33. package/templates/next/turbo.json +24 -0
  34. package/templates/next/vitest.config.js +9 -0
  35. package/templates/react-lib/LICENSE +21 -0
  36. package/templates/react-lib/README.md +52 -0
  37. package/templates/react-lib/eslint.config.js +5 -0
  38. package/templates/react-lib/gitignore +4 -0
  39. package/templates/react-lib/package.json +48 -0
  40. package/templates/react-lib/src/counter.tsx +28 -0
  41. package/templates/react-lib/src/index.ts +2 -0
  42. package/templates/react-lib/src/style.css +36 -0
  43. package/templates/react-lib/stylelint.config.js +3 -0
  44. package/templates/react-lib/test/counter.test.tsx +28 -0
  45. package/templates/react-lib/test-d/index.test-d.ts +11 -0
  46. package/templates/react-lib/tsconfig.app.json +15 -0
  47. package/templates/react-lib/tsconfig.build.json +12 -0
  48. package/templates/react-lib/tsconfig.json +4 -0
  49. package/templates/react-lib/tsconfig.node.json +11 -0
  50. package/templates/react-lib/vite.config.ts +22 -0
  51. package/templates/react-lib/vitest.config.ts +5 -0
  52. package/templates/react-vite/README.md +27 -0
  53. package/templates/react-vite/eslint.config.js +5 -0
  54. package/templates/react-vite/gitignore +6 -0
  55. package/templates/react-vite/index.html +13 -0
  56. package/templates/react-vite/package.json +36 -0
  57. package/templates/react-vite/src/app.tsx +22 -0
  58. package/templates/react-vite/src/main.tsx +15 -0
  59. package/templates/react-vite/src/style.css +95 -0
  60. package/templates/react-vite/stylelint.config.js +3 -0
  61. package/templates/react-vite/test/app.test.tsx +22 -0
  62. package/templates/react-vite/tsconfig.app.json +26 -0
  63. package/templates/react-vite/tsconfig.json +11 -0
  64. package/templates/react-vite/tsconfig.node.json +18 -0
  65. package/templates/react-vite/vite.config.ts +6 -0
  66. package/templates/react-vite/vitest.config.ts +9 -0
  67. package/templates/vitepress/.vitepress/navigation/routes.ts +7 -0
  68. package/templates/vitepress/reference/artifacts.md +51 -0
  69. package/templates/vitepress/reference/commands.md +149 -0
  70. package/templates/vitepress/reference/config.md +101 -7
  71. package/templates/vitepress/reference/dependencies.md +116 -0
  72. package/templates/vitepress/reference/dependency-admission.md +67 -0
  73. package/templates/vitepress/reference/devcontainer.md +25 -0
  74. package/templates/vitepress/reference/index.md +1 -0
  75. package/templates/vitepress/reference/install-security.md +51 -0
  76. package/templates/vitepress/reference/knip.md +55 -0
  77. package/templates/vitepress/reference/move.md +39 -0
  78. package/templates/vitepress/reference/organization-presets.md +89 -0
  79. package/templates/vitepress/reference/removal.md +42 -0
  80. package/templates/vitepress/reference/template-assets.md +39 -0
  81. package/templates/vitepress/reference/templates.md +372 -9
  82. package/templates/vitepress/start/diagnose.md +19 -2
  83. package/templates/vitepress/tasks/checks.md +149 -1
  84. package/templates/vitepress/tasks/ci.md +93 -1
  85. package/templates/vitepress/tasks/release.md +72 -6
  86. package/templates/vitepress/zh/reference/artifacts.md +51 -0
  87. package/templates/vitepress/zh/reference/commands.md +148 -0
  88. package/templates/vitepress/zh/reference/config.md +94 -2
  89. package/templates/vitepress/zh/reference/dependencies.md +116 -0
  90. package/templates/vitepress/zh/reference/dependency-admission.md +67 -0
  91. package/templates/vitepress/zh/reference/devcontainer.md +25 -0
  92. package/templates/vitepress/zh/reference/index.md +1 -0
  93. package/templates/vitepress/zh/reference/install-security.md +51 -0
  94. package/templates/vitepress/zh/reference/knip.md +55 -0
  95. package/templates/vitepress/zh/reference/move.md +39 -0
  96. package/templates/vitepress/zh/reference/organization-presets.md +89 -0
  97. package/templates/vitepress/zh/reference/removal.md +42 -0
  98. package/templates/vitepress/zh/reference/template-assets.md +19 -4
  99. package/templates/vitepress/zh/reference/templates.md +352 -9
  100. package/templates/vitepress/zh/start/diagnose.md +24 -13
  101. package/templates/vitepress/zh/tasks/checks.md +150 -0
  102. package/templates/vitepress/zh/tasks/ci.md +93 -1
  103. package/templates/vitepress/zh/tasks/release.md +72 -6
  104. package/templates/vue-lib/package.json +1 -1
@@ -0,0 +1,51 @@
1
+ # Installation security policy
2
+
3
+ `repo doctor security` inspects persisted pnpm policy and the current process environment without installing dependencies, approving builds, executing lifecycle scripts, or running pnpmfile hooks. It reports release waiting, trust downgrade checks, build decisions and version compatibility. `repo doctor` includes the same checks.
4
+
5
+ ```sh
6
+ repo doctor security --json
7
+ repo doctor security --pnpm-version 10.26.0 --json
8
+ repo doctor security --expectations policy.json --strict --json
9
+ repo doctor security --preset balanced > install-policy-plan.json
10
+ repo doctor security --apply install-policy-plan.json
11
+ ```
12
+
13
+ The audited version comes from an explicit exact version, observed launcher metadata, or the root `packageManager`. Declared-only evidence is a warning. Unknown, prerelease and future unsupported versions do not receive guessed defaults. `--pnpm-version` audits another version; it does not activate it. Applying a plan requires the current resolved version to match the plan.
14
+
15
+ Policy sources are shown by name, with pnpm version gates. Modern pnpm reads global `config.yaml`, `pnpm-workspace.yaml`, and `PNPM_CONFIG_` environment settings; old npmrc policy and package.json.pnpm declarations are flagged as inactive. Conflicting legacy sources, config dependencies and per-package configuration produce unknown results instead of inferred effective permission. CLI arguments of another pnpm invocation are not observable. External `onlyBuiltDependenciesFile` permissions are not loaded. Configuration interpolation is not expanded.
16
+
17
+ The JSON `schemaVersion: 1` report has `kind`, `workspaceDir`, `pnpm`, `settings`, `builds`, `checks`, `summary`, and `limitations`. Keys, status values and rule IDs are language independent. Invalid values are omitted; registry credentials, complete config files and Git artifact URLs are never included. Package selectors in policy arrays are reduced to package names; build decisions retain only a selector kind. Organization exception reasons are user-authored report text and should not contain secrets.
18
+
19
+ Build decisions distinguish allow, deny and pending declarations. `builds.override` describes global script behavior separately; an incompatible allow-all/map combination is unknown. `pendingPackages` means installed packages have not yet been built, which does not necessarily mean approval is missing. `ignoreScripts` does not disable pnpmfile hooks. Reading a report never approves a package.
20
+
21
+ ## Organization expectations
22
+
23
+ Use `installationSecurity` in `repoctl.config.ts`, or pass the same object in an explicit JSON file:
24
+
25
+ ```ts
26
+ export default {
27
+ installationSecurity: {
28
+ minimumReleaseAge: 1440,
29
+ trustPolicy: 'no-downgrade',
30
+ requireBuildApproval: true,
31
+ severity: 'warn',
32
+ exceptions: [
33
+ { key: 'minimumReleaseAgeExclude', package: '@acme/internal', reason: 'Published by the internal release pipeline' },
34
+ ],
35
+ },
36
+ }
37
+ ```
38
+
39
+ Expectations compare existing policy; they do not change it. A release-age expectation checks disabled waiting, non-strict fallback and missing-time bypass. Trust checks include trusted lockfiles, age cutoffs and the pnpm versions where missing-time bypass also affects trust. Exceptions must match exact pnpm selectors and include a nonblank reason. Missing reasons and unused reasons are visible. Warnings fail with `--strict`; `severity: 'fail'` makes expectation violations fail normally. Unknown fields, malformed expectations and null values are rejected.
40
+
41
+ Stable rules: `install-security-version`, `install-security-release-age`, `install-security-trust`, `install-security-builds`, `install-security-compatibility`, `install-security-config`, `install-security-expectation`, `install-security-exception`.
42
+
43
+ ## Optional additive preset
44
+
45
+ The `balanced` preset only proposes absent, supported policy keys: 1440 minutes of release waiting, strict age checks, no missing-time bypass, no trust downgrade, an empty build approval map, disabled allow-all, and strict dependency builds. Existing project/global/environment values remain explicit choices, including a zero release age. Historical approval lists are not mixed with a new map. An explicit allow-all policy prevents adding an incompatible map. This preset is a starting point; it does not override or certify existing policy.
46
+
47
+ Preview is read-only and includes added keys, preserved keys, a diff and hashes, never the complete YAML. Apply regenerates the exact plan, checks the workspace, version and input hashes, rejects linked inputs and stale plans, then performs an atomic replacement with a backup. Comments and unrelated bytes are retained. A repeated completed application does not write again. Failures before replacement preserve the current file; recoverable failures roll back. A cleanup failure reports that changes were applied and identifies retained backups. Inspect those files before removing them and generate a fresh plan after any manual recovery.
48
+
49
+ Public APIs: `inspectInstallSecurity`, `planInstallSecurityPreset`, `applyInstallSecurityPreset`, and their exported report/plan/options types. Parent doctor `--markdown`, `--out` and `--redact` options cannot be mixed with this subcommand; use its JSON output explicitly.
50
+
51
+ References: [pnpm dependency resolution](https://pnpm.io/settings/dependency-resolution), [dependency builds](https://pnpm.io/settings/build), [configuration](https://pnpm.io/configuring), [pnpm 10 settings](https://pnpm.io/10.x/settings).
@@ -0,0 +1,55 @@
1
+ ---
2
+ title: Unused code and dependencies
3
+ description: Explicit native Knip analysis with reviewed baselines.
4
+ ---
5
+
6
+ # Unused code and dependencies
7
+
8
+ `repo check knip` runs the workspace root's installed [Knip](https://knip.dev/) to report unused files, exports and dependencies, plus imports missing from dependency declarations. This is an explicit optional check: ordinary `repo check`, staged, full and affected checks keep their existing behavior.
9
+
10
+ ```bash
11
+ pnpm add -Dw knip@^6.39.0
12
+ pnpm exec repo check knip --recommend-config
13
+ pnpm exec repo check knip --dry-run --json
14
+ pnpm exec repo check knip --json
15
+ pnpm exec repo check knip --config knip.config.ts --strict
16
+ ```
17
+
18
+ Supported versions are `>=6.39.0 <7`. repoctl never downloads Knip, searches a parent/global installation, adds it as a runtime dependency, runs `--fix`, or removes code. Missing or unsupported tools produce installation guidance and fail the check. Native Jiti disk caching is disabled for this command. Native configuration files and plugins execute as they normally do in Knip; only use repository configurations you trust.
19
+
20
+ ## Native configuration
21
+
22
+ `--recommend-config` prints JSON suggestions for manual merging. It lists existing native configuration files without replacing or writing them. Recommendations cover pnpm workspace entries, library/CLI entrypoints, root tooling scripts and type tests, with explicit generated build, coverage, framework-cache and Worker-type exclusions.
23
+
24
+ Keep the project's [native configuration](https://knip.dev/reference/configuration) and framework plugins authoritative. Add dynamic imports or convention-based entrypoints through `entry` when a native plugin cannot infer them. Review example/fixture exclusions through native `ignoreFiles` or `ignoreIssues`; examples are not automatically exempted. Rules such as `"exports": "warn"` retain their native severity.
25
+
26
+ `--production` selects Knip's native production scope. `--strict` also enables production scope and checks dependency declarations per workspace, so dependencies declared only at the root cannot silently stand in for a package's own declarations. Review the native production entries before enabling either mode in CI.
27
+
28
+ The JSON report includes workspace, file, source location when available, finding category, native diagnostic metadata and `warn`/`error` severity. Native configuration/tag hints are included with their own severities. Exit codes are:
29
+
30
+ | Code | Meaning |
31
+ | ---- | ---------------------------------------------------------------------------------------------- |
32
+ | `0` | The selected policy passes; warnings and accepted baseline findings remain visible. |
33
+ | `1` | Error findings block the selected policy. |
34
+ | `2` | Analysis is incomplete, the local tool is unavailable/unsupported, or the baseline is invalid. |
35
+
36
+ `--json` on **`check knip`** emits the actual analysis. Add `--dry-run` to preview the tool command. These two shared options may appear before or after `knip`; other Knip options belong after the subcommand. Parent `check` execution modes/options cannot be combined with it. `--timeout <ms>` defaults to 120000.
37
+
38
+ ## Reviewed baselines
39
+
40
+ ```bash
41
+ # Review current findings, then explicitly create or update a baseline.
42
+ pnpm exec repo check knip --save-baseline knip-baseline.json --json
43
+ # CI fails only on new error findings; existing, added and fixed remain visible.
44
+ pnpm exec repo check knip --baseline knip-baseline.json --new-only --json
45
+ ```
46
+
47
+ Saving does not hide the current findings' exit status: a baseline may be saved successfully while the command exits `1`. Ordinary analysis never updates a baseline. `--save-baseline` cannot combine with comparison, new-only, dry-run or recommendation options. Without `--new-only`, comparison still blocks on every current error.
48
+
49
+ Finding identities include category, severity, workspace, file and symbol; source line shifts alone do not create new issues. Severity escalation is new. Baselines bind the exact Knip version, production/strict modes, root package name, native workspace set, primary configuration hashes, reported categories and enabled plugins. Missing, malformed, duplicate, tampered or incompatible baselines fail closed, including when no current errors are found.
50
+
51
+ Primary configuration includes the selected file, discovered standard Knip files, root `package.json#knip`, and pnpm workspace package patterns. Source changes and dependency fixes do not invalidate it. Imported configuration helpers, environment-dependent behavior and every plugin's auxiliary configuration cannot be fully fingerprinted; review and explicitly regenerate the baseline after changing those analysis policies.
52
+
53
+ Baseline output must stay inside the workspace with an existing, unlinked parent. Existing output must already be a valid baseline; configuration files and other arbitrary files cannot be overwritten. New files are created exclusively, updates are staged and checked for concurrent changes, and filesystem failures preserve the prior baseline. Review any retained `.repoctl-knip-*.tmp` paths reported after interruption or cleanup failure before retrying.
54
+
55
+ The exported APIs are `planKnipCheck(cwd, options)`, `runKnipCheck(cwd, options)`, `getKnipConfigurationSuggestions(cwd)` and `saveKnipBaseline(cwd, report, file)`. Options support `timeoutMs` and `AbortSignal`; incomplete or interrupted analyses cannot be saved. Config paths are relative to `cwd`; baseline paths are relative to the workspace root, including when invoked from a child package.
@@ -0,0 +1,39 @@
1
+ # Move or rename a workspace
2
+
3
+ `repo workspace move` previews a directory move, npm package rename, or both. Select one package by exact name or `./directory`; use `--to` for its workspace-relative directory and `--name` for its npm name.
4
+
5
+ ```bash
6
+ repo workspace move @acme/ui --to libs/ui --json > ../move-plan.json
7
+ repo workspace move @acme/ui --name @acme/design --json > ../rename-plan.json
8
+ repo workspace move ./packages/ui --to libs/design --name @acme/design --json > ../move-plan.json
9
+ repo workspace move --apply ../move-plan.json --json
10
+ ```
11
+
12
+ Preview is read-only. Save the JSON outside the selected directory and review the old/new identity, direct and transitive consumers, file contents, changed fields, blockers, and manual tasks before applying. A clean Git target and clean files to be updated are required. There is no force mode. `--apply` cannot be combined with selection or preview options.
13
+
14
+ The plan updates dependency keys in all four dependency sections, matching dependency metadata, `workspace:` and `npm:` aliases, and relative `workspace:`, `link:` and `file:` references. Alias keys remain stable unless they equal the renamed package's name. Plain semver edges use the shared workspace graph's local-candidate semantics; unresolved or nonlocal declarations appear in review candidates instead of being guessed. Matching `repository.directory` values are updated. Existing workspace globs remain; exact package entries are moved and a destination entry is added when needed. An excluded destination is rejected.
15
+
16
+ Tracked `tsconfig.json` and `tsconfig.*.json` files support JSONC comments, relative `extends`, project references, `files`/`include`/`exclude`, path aliases, and explicit `baseUrl`, `rootDir`/`rootDirs`, `outDir`, `declarationDir`, and `typeRoots`. Broader include/exclude globs whose coverage may change are preserved and reported for manual review; wildcard expansion is not analyzed. Config inheritance is not evaluated. When `paths` could depend on an inherited `baseUrl`, that field remains untouched with a manual task. Absolute paths, executable configurations and other configuration formats require review.
17
+
18
+ Source code is preserved. Manual tasks report file and line candidates for old names, old paths and relative string paths that may change meaning. This is a bounded scan of Git-tracked text, not an exhaustive import resolver: ignored/untracked files, binary files, files over 1 MiB, generated code, interpolated strings and dynamic references are outside its coverage. Symlink text is preserved and reported for review. Resolve these tasks before building; a renamed direct dependency still needs its source imports updated by the developer.
19
+
20
+ Root/outside paths, occupied destinations, duplicate or invalid names, links in the destination ancestry, overlapping workspaces, nested Git repositories, dirty inputs and stale plans are rejected. The operation inventories all selected files, including ignored files, and verifies hashes, file identity, workspace membership and Git HEAD before committing changes. A failure rolls back manifests and the directory when safe. Concurrent edits and replacement directories are preserved; recovery errors report the retained paths. A committed operation with cleanup failures returns `status: applied` and `cleanupPending`; inspect those files before cleanup. An unchanged successful plan can be applied again without writing.
21
+
22
+ Applications hold `.repoctl/workspace-move.lock` from the first fresh/replay check through cleanup or rollback. A concurrent application fails closed. After a process crash, verify that no writer is active and recover pending backups before manually removing a stale lock.
23
+
24
+ Registered template instances inside the selected directory move with it. The preview lists their IDs and old/new targets; IDs, source versions, generation parameters and baselines remain unchanged, including for package-name-only changes. Business edits remain local changes for subsequent template drift checks and three-way upgrades. Directory changes, manifest changes and instance targets commit together under the instance registry lock; failed registry writes restore files and directories before releasing it. Replay also checks the expected registry state. A pending upgrade recovery record blocks moving that instance: inspect `repo templates recover-upgrade <id>` first. Moving only part of a parent instance, or reusing another registered target (even a missing one), is rejected.
25
+
26
+ The freed original path can host a new project of the same template. Registration allocates a distinct ID under the lock, so the moved project and the new project retain independent upgrade histories. If the move commits but a registry lock cannot be removed, the result remains `applied` and includes its exact path in `cleanupPending`; inspect it and confirm no writer is active before removing it.
27
+
28
+ The command does not rewrite the lockfile or run install scripts. After resolving manual tasks, run:
29
+
30
+ ```bash
31
+ pnpm install --lockfile-only
32
+ pnpm install --frozen-lockfile
33
+ repo check
34
+ git diff
35
+ ```
36
+
37
+ Renaming a published package creates a new npm package identity. No publishing, deprecation, Git commit or Git push is performed by this command.
38
+
39
+ The built API exports `planWorkspaceMove(cwd, options)`, `applyWorkspaceMovePlan(cwd, plan)`, `WorkspaceMoveOptions`, `WorkspaceMovePlan` and `WorkspaceMoveResult`.
@@ -0,0 +1,89 @@
1
+ # Organization presets
2
+
3
+ An organization preset is an ordinary npm package containing a `repoctl.preset.json` file. It can share configuration, templates, capability recommendations and a small set of owned engineering files across independent repositories.
4
+
5
+ Install the package explicitly and keep its exact version in the workspace root `package.json`:
6
+
7
+ ```bash
8
+ pnpm add -Dw --save-exact @acme/repoctl-preset@1.2.3
9
+ ```
10
+
11
+ ```ts
12
+ import { defineMonorepoConfig } from 'repoctl'
13
+
14
+ export default defineMonorepoConfig({
15
+ presets: [{ packageName: '@acme/repoctl-preset', version: '1.2.3' }],
16
+ commands: { clean: { includePrivate: false } },
17
+ })
18
+ ```
19
+
20
+ The reference, dependency declaration and installed package identity must agree. Ranges and tags are rejected. Loading reads installed JSON only: it does not import the package entrypoint, execute package scripts, install dependencies, fetch templates or write a cache.
21
+
22
+ ## Package contract
23
+
24
+ Include the manifest and referenced assets in the published package's `files` list:
25
+
26
+ ```json
27
+ {
28
+ "schemaVersion": 1,
29
+ "requires": { "repoctl": ">=5.6.0 <6" },
30
+ "config": {
31
+ "commands": {
32
+ "clean": { "includePrivate": true },
33
+ "release": { "qualityScripts": ["build", "lint", "test"] }
34
+ }
35
+ },
36
+ "templates": {
37
+ "acme-sdk": {
38
+ "source": "templates/sdk",
39
+ "target": "packages/sdk",
40
+ "category": "library",
41
+ "label": "Acme SDK"
42
+ }
43
+ },
44
+ "capabilities": [{ "id": "playwright", "reason": "Browser interaction checks" }],
45
+ "assets": [{ "source": "assets/check.mjs", "target": "scripts/acme-check.mjs" }]
46
+ }
47
+ ```
48
+
49
+ Optional `extends` contains the same exact `{ packageName, version }` references. Each dependency must also have an exact declaration in the preset package's `dependencies` or `devDependencies` and be installed; published presets should use `dependencies` for required parent presets. Dependencies load before their declaring preset, followed by later top-level presets, the existing C12 project configuration, and explicit CLI options. Objects merge and arrays replace at preset boundaries. C12 retains its existing behavior within project configuration. A preset's `config` cannot introduce C12 metadata, further `presets`, `templatesDir` or `templateMap`; use the manifest's `extends` and `templates` fields instead.
50
+
51
+ Duplicate identical references are reported and applied once. Cycles, different versions of the same package, incompatible `requires.repoctl`, unknown config fields, unsafe asset paths and unsupported capability IDs block consuming commands. Resolution is limited to 64 packages and 16 dependency levels.
52
+
53
+ ```bash
54
+ repo presets inspect --json
55
+ repo config inspect --json
56
+ repo config inspect --command clean --set 'includePrivate=false' --json
57
+ repo templates --json
58
+ ```
59
+
60
+ Config inspection includes ordered `layers` and per-field `sources` with package names and versions. `effective.sources` explains command values including defaults and CLI overrides. The existing `effective.origins` union remains `default | project | cli`; preset values remain in its project category for compatibility. Config report redaction applies to every layer.
61
+
62
+ Templates enter the unified catalog in preset order; project template declarations take precedence. Each preset template uses the exact npm identity of the package declaring it. `templates` and `config inspect` only inspect declarations. Use `repo templates fetch acme-sdk` to prepare verified template bytes; actual creation can prepare them as well. A creation preview or `--offline` creation requires a previously verified cache entry. Registry configuration and integrity checks follow the [remote template source contract](./templates.md). The installed config package is not treated as a verified remote template cache.
63
+
64
+ Capability entries are recommendations. Loading or applying engineering assets does not install or apply capabilities; use the explicit `repo tooling` capability workflow after review.
65
+
66
+ ## Owned engineering files
67
+
68
+ ```bash
69
+ repo presets plan --json --out preset-plan.json
70
+ repo presets apply preset-plan.json --json
71
+ ```
72
+
73
+ Planning is read-only; `--out` exclusively creates a new review file. The plan contains package identities, input fingerprints, exact changes, diffs and ownership records. Applying is explicit, rejects changed inputs and tampered plans, and reapplying an already completed plan is a no-op. Commit `.repoctl/baselines/presets` with the managed files.
74
+
75
+ Allowed targets are supported root tooling configs, GitHub workflows and issue templates, specific VS Code configs, and engineering scripts. Business code, dependency manifests and internal repoctl metadata cannot be declared as targets. Package-internal symlinks, path traversal and nonportable filenames are rejected. A new preset cannot claim an existing file, even if its bytes match, or take over another preset's or repoctl's baseline. Multiple active presets claiming one target block the whole plan. Choose a new target when ownership is already established; first-version application does not transfer ownership.
76
+
77
+ To upgrade, explicitly install the new exact package version, update the reference, and review a new plan. The previous upstream baseline enables a three-way merge: nonoverlapping local edits survive; overlapping edits and local deletions block writes. Files removed from a preset declaration remain untouched with their ownership records; deletion or ownership transfer requires separate manual review.
78
+
79
+ A workspace lock covers validation, staging, writes and rollback. Baselines advance only after their managed files are written. Failure restores previous bytes; concurrent edits and their recovery backups are retained rather than overwritten. If interrupted, preserve `.repoctl-upgrade-*.bak`/`.tmp` files, verify that no writer is active, reconcile the named backups and baselines, and only then remove a stale `.repoctl/upgrade.lock` and create a new plan.
80
+
81
+ Public APIs are `resolveOrganizationPresets(cwd, references)`, `planOrganizationPresetAssets(cwd)` and `applyOrganizationPresetAssets(plan)`. The first returns structured preset diagnostics; config-consuming APIs map blocking preset diagnostics into configuration validation failures before writes.
82
+
83
+ ## Maintenance pull requests
84
+
85
+ After initial adoption, the [maintenance workflow](../tasks/checks.md) can update files from directly configured presets when their exact root dependency version changes. Commit the new dependency, matching `presets` reference and frozen lockfile together. The preparer checks both committed manifests, locked versions and installed package metadata, then uses the same three-way merge and transaction as explicit preset application. Unrelated dependency changes do not run asset validation. Conflicts and failed checks retain a blocked report; unchanged bytes and repeated runs produce no PR.
86
+
87
+ Export `repo maintenance workflow --out .github/workflows/repoctl-upgrade.yml` while the validated preset is installed. The workflow embeds fixed package/source/target triples. Its publisher reads committed package manifests and existing ownership records, verifies exact patch/baseline hashes, and obtains write credentials only after validation. A report cannot add its own authorized paths. When adding a new engineering file or changing its source, explicitly adopt/review it and export a newly reviewed workflow; the exporter protects existing workflow files. Changes in inherited-only presets, removals and ownership transfers require the explicit preset workflow. Business files never enter automatic maintenance.
88
+
89
+ The maintenance report keeps schema version 1: `versions` and `plan` retain their repoctl-only meaning, while optional `presets` contains preset versions, the selected plan and skipped new files. `planOrganizationPresetAssets(cwd, targets?)` supports an exact target subset; omitted targets retain the original all-assets behavior. Apply recomputes the same selection. A preset version with unchanged upstream bytes does not advance bookkeeping alone; later changes still merge from the last recorded upstream content.
@@ -0,0 +1,42 @@
1
+ # Safely remove a workspace package
2
+
3
+ `workspace remove` previews one exact package name or explicit workspace-relative directory. It includes root/private consumers, transitive dependency paths, directory contents (including ignored files), precise manifest edits, and source/configuration references for manual review.
4
+
5
+ ```bash
6
+ repo workspace remove @acme/legacy
7
+ repo workspace remove ./packages/legacy --dry-run --json > ../remove-plan.json
8
+ # Explicitly include removal of dependency fields in consumer manifests:
9
+ repo workspace remove @acme/legacy --remove-references --json > ../remove-plan.json
10
+ repo workspace remove --apply ../remove-plan.json --json
11
+ ```
12
+
13
+ Preview is always read-only; `--dry-run` makes that default explicit. Save the plan outside the selected package. A blocked preview still emits JSON and exits with code 1. Applying requires the separate `--apply` command; it cannot be combined with a target, `--remove-references` or `--dry-run`.
14
+
15
+ ## Selection and blockers
16
+
17
+ The initial version requires a Git repository with a commit and a selected directory free of staged, unstaged, and untracked changes. Ignored files are included in the reviewed inventory. The root package, outside paths, linked target ancestors, embedded repositories, and unselected nested workspaces cannot be removed. Ambiguous names require an explicit `./directory`. There is no force flag for these boundaries.
18
+
19
+ Consumers block removal by default. `--remove-references` plans only the exact dependency fields shown in the plan, plus matching `peerDependenciesMeta` and unused `dependenciesMeta` entries. Unrelated fields and dependency declarations are retained. Uncertain graph relationships that might reference the target, including unresolved catalog aliases, block application. Only workspaces with a single JSON manifest per package are supported initially.
20
+
21
+ The review scan inspects Git-tracked text files outside the selected package, using literal package-name/directory matches. It does not resolve imports or dynamic configuration. Untracked/ignored files, binary files, symlinks, hardlinks, files larger than 1 MiB, unsupported text extensions, and the lockfile are excluded. `review.scanned`, `review.matches`, and `review.skipped` describe this limited evidence; matches are manual candidates, not automatic edits. Check source imports, scripts, Turbo configuration, workspace globs, bundled dependency lists, documentation, and release configuration yourself.
22
+
23
+ ## Apply and recovery
24
+
25
+ Application regenerates the plan and rejects drift in manifests, workspace membership, Git HEAD, reviewed text, directory entries, file content, modification times, or link destinations. JSON plans include absolute paths and manifest contents; review them locally before sharing.
26
+
27
+ The workspace operation lock `.repoctl/workspace-remove.lock` covers replay checks, validation, writing, verification, rollback and cleanup. An overlapping removal fails as locked, including an identical plan. After a crash, verify that no writer is active and recover retained originals before manually removing the lock.
28
+
29
+ The transaction stages manifest replacements and moves the selected directory into a unique `node_modules/.cache/repoctl/removals/` operation directory. Recovery ancestors must be real directories. A cross-device move fails safely; no copy/delete fallback is attempted. Before commit, failures restore changed manifests and the directory when doing so is safe. Concurrent edits and a newly created target directory are preserved; the error reports retained original paths for manual recovery. After commit, cleanup failure returns `status: "applied"` with `cleanupPending` paths instead of claiming rollback. Inspect those exact paths before removing them; do not clear the shared cache indiscriminately.
30
+
31
+ For generated packages, removal preserves the instance ID, origin registry and retained template baselines. The plan and result identify affected instances, which appear as `missing` afterward. A retained registration still owns its path, so `repo new` refuses to reuse it and identifies the owning instance. Restore the original project from version control or a backup to resume that history, or choose a different unowned path for a new project. Rebuilding a template baseline reconstructs upstream template output; it does not recover business edits. Removal does not unregister template history.
32
+
33
+ The lockfile is never edited by this command. Finish explicitly with the repository's declared pnpm version:
34
+
35
+ ```bash
36
+ pnpm install --lockfile-only
37
+ pnpm install --frozen-lockfile
38
+ repo check --full
39
+ git diff
40
+ ```
41
+
42
+ Programmatic callers can use `planWorkspaceRemoval(cwd, { target, removeReferences })` and `applyWorkspaceRemovalPlan(cwd, plan)`. JSON plans use schema version 1. Reapplying an already applied plan returns `unchanged` only when the expected remaining workspace state still matches.
@@ -34,6 +34,45 @@ repo templates --check --json
34
34
 
35
35
  Health checks validate unique sources and targets, source directories, package manifests, metadata, and filtered temporary files.
36
36
 
37
+ ## Discover Custom Templates
38
+
39
+ Define local templates in `commands.create.templateMap`. Creation, interactive choices, list/detail output and health checks resolve the same catalog, including key, label, description, category, source directory and default target.
40
+
41
+ ```ts
42
+ import { fileURLToPath } from 'node:url'
43
+ import { defineMonorepoConfig } from 'repoctl'
44
+
45
+ export default defineMonorepoConfig({
46
+ commands: {
47
+ create: {
48
+ templateMap: {
49
+ 'internal-service': {
50
+ source: fileURLToPath(new URL('./templates/internal-service', import.meta.url)),
51
+ target: 'apps/internal-service',
52
+ label: 'Internal service',
53
+ category: 'service',
54
+ description: 'Company API service',
55
+ },
56
+ },
57
+ },
58
+ },
59
+ })
60
+ ```
61
+
62
+ ```bash
63
+ repo templates internal-service --json
64
+ repo templates --check --json
65
+ repo new payments --template internal-service --dry-run
66
+ ```
67
+
68
+ Absolute `source` paths allow installed and local templates to coexist. A relative `source` is resolved under `templatesDir`; that directory defaults to the installed template package. Setting `templatesDir: './templates'` replaces the root for every template, including built-ins, and resolves relative to the configuration file directory. Running from a nested pnpm workspace package finds the root configuration without shifting the template root. Generation destinations remain relative to the invocation directory.
69
+
70
+ String mappings remain supported: `templateMap: { custom: 'custom' }` is equivalent to `{ source: 'custom', target: 'custom' }`. Optional `label`, `description` and `category` add discoverable metadata. A non-empty `choices` array controls interactive order and inclusion; its names and descriptions also appear in list/detail output. Without it, all catalog entries are available. Existing built-in intent defaults are retained; configuring custom templates opens the catalog picker.
71
+
72
+ Overriding a built-in key is visible through `origin: 'custom'`, `overridesBuiltin: true` and its `configFile`/`configPath`. Invalid definitions and duplicate choices identify their configuration location. `repo templates --check` also detects duplicate sources/targets, missing source directories and missing `package.json`. List JSON remains an array; detail JSON remains an object, with additive catalog fields. Create JSON includes the selected entry as `templateInfo`.
73
+
74
+ Listing and health checks read declarations and files without generating output or executing template code. `--out` explicitly writes the chosen report. Programmatic consumers can use `resolveTemplateCatalog({ cwd })` and `checkTemplates({ cwd })`.
75
+
37
76
  ## Managed Assets
38
77
 
39
78
  `repo init` and `repo upgrade` synchronize root scripts, configuration, hooks, and release assets. Existing custom files are preserved unless overwrite behavior is explicitly selected.