codeforge-dev 1.14.2 → 2.0.1

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 (128) hide show
  1. package/{.devcontainer/config/defaults → .codeforge/config}/ccstatusline-settings.json +44 -6
  2. package/.codeforge/config/main-system-prompt.md +412 -0
  3. package/.codeforge/config/orchestrator-system-prompt.md +333 -0
  4. package/{.devcontainer/config/defaults → .codeforge/config}/settings.json +7 -2
  5. package/{.devcontainer/config → .codeforge}/file-manifest.json +15 -9
  6. package/{.devcontainer → .codeforge/scripts}/connect-external-terminal.sh +3 -1
  7. package/.devcontainer/.env.example +17 -5
  8. package/.devcontainer/.secrets.example +3 -0
  9. package/.devcontainer/CHANGELOG.md +215 -0
  10. package/.devcontainer/CLAUDE.md +26 -43
  11. package/.devcontainer/README.md +35 -20
  12. package/.devcontainer/devcontainer.json +36 -17
  13. package/.devcontainer/features/agent-browser/install.sh +3 -0
  14. package/.devcontainer/features/ast-grep/install.sh +3 -0
  15. package/.devcontainer/features/biome/install.sh +3 -0
  16. package/.devcontainer/features/ccburn/install.sh +2 -0
  17. package/.devcontainer/features/ccms/install.sh +2 -0
  18. package/.devcontainer/features/ccstatusline/README.md +7 -6
  19. package/.devcontainer/features/ccstatusline/install.sh +9 -4
  20. package/.devcontainer/features/ccusage/install.sh +2 -0
  21. package/.devcontainer/features/chromaterm/chromaterm.yml +2 -2
  22. package/.devcontainer/features/chromaterm/install.sh +2 -0
  23. package/.devcontainer/features/claude-code-native/README.md +47 -0
  24. package/.devcontainer/features/claude-code-native/devcontainer-feature.json +29 -0
  25. package/.devcontainer/features/claude-code-native/install.sh +131 -0
  26. package/.devcontainer/features/claude-monitor/install.sh +2 -0
  27. package/.devcontainer/features/claude-session-dashboard/README.md +2 -2
  28. package/.devcontainer/features/claude-session-dashboard/install.sh +3 -0
  29. package/.devcontainer/features/dprint/install.sh +2 -0
  30. package/.devcontainer/features/hadolint/install.sh +2 -0
  31. package/.devcontainer/features/kitty-terminfo/README.md +3 -1
  32. package/.devcontainer/features/kitty-terminfo/install.sh +2 -0
  33. package/.devcontainer/features/lsp-servers/install.sh +4 -0
  34. package/.devcontainer/features/mcp-qdrant/CHANGES.md +3 -3
  35. package/.devcontainer/features/mcp-qdrant/README.md +1 -0
  36. package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +1 -1
  37. package/.devcontainer/features/mcp-qdrant/install.sh +9 -2
  38. package/.devcontainer/features/mcp-qdrant/poststart-hook.sh +9 -2
  39. package/.devcontainer/features/notify-hook/devcontainer-feature.json +1 -1
  40. package/.devcontainer/features/notify-hook/install.sh +2 -0
  41. package/.devcontainer/features/ruff/install.sh +2 -0
  42. package/.devcontainer/features/shellcheck/install.sh +2 -0
  43. package/.devcontainer/features/shfmt/install.sh +2 -0
  44. package/.devcontainer/features/tmux/README.md +3 -3
  45. package/.devcontainer/features/tmux/install.sh +3 -1
  46. package/.devcontainer/features/tree-sitter/install.sh +4 -0
  47. package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +27 -11
  48. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/README.md +20 -6
  49. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/architect.md +182 -29
  50. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/bash-exec.md +9 -0
  51. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/claude-guide.md +13 -4
  52. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/debug-logs.md +24 -5
  53. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/dependency-analyst.md +16 -5
  54. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/documenter.md +412 -0
  55. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/explorer.md +18 -6
  56. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/generalist.md +36 -10
  57. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/git-archaeologist.md +10 -1
  58. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/implementer.md +260 -0
  59. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/investigator.md +262 -0
  60. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/migrator.md +10 -0
  61. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/perf-profiler.md +21 -5
  62. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/refactorer.md +18 -8
  63. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/researcher.md +23 -5
  64. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/security-auditor.md +20 -6
  65. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/spec-writer.md +12 -0
  66. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/statusline-config.md +12 -2
  67. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/test-writer.md +22 -7
  68. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py +9 -5
  69. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/redirect-builtin-agents.py +2 -5
  70. package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md +1 -1
  71. package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/advisory-test-runner.py +4 -2
  72. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/README.md +3 -2
  73. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py +89 -15
  74. package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/.claude-plugin/plugin.json +7 -0
  75. package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/README.md +125 -0
  76. package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/pr-review/SKILL.md +325 -0
  77. package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/ship/SKILL.md +314 -0
  78. package/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/.claude-plugin/plugin.json +5 -0
  79. package/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/README.md +52 -0
  80. package/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/skills/ps/SKILL.md +37 -0
  81. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/README.md +2 -2
  82. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py +80 -6
  83. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +4 -4
  84. package/.devcontainer/plugins/devs-marketplace/plugins/session-context/README.md +30 -14
  85. package/.devcontainer/plugins/devs-marketplace/plugins/session-context/hooks/hooks.json +13 -1
  86. package/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/collect-session-edits.py +44 -0
  87. package/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py +89 -10
  88. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json +1 -1
  89. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/README.md +19 -11
  90. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py +476 -282
  91. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/team/SKILL.md +4 -4
  92. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/SKILL.md +227 -0
  93. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/manual-worktree-commands.md +238 -0
  94. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/parallel-workflow-patterns.md +228 -0
  95. package/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-build/SKILL.md +2 -2
  96. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/scripts/ticket-linker.py +2 -2
  97. package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md +1 -1
  98. package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py +69 -31
  99. package/.devcontainer/scripts/check-setup.sh +5 -3
  100. package/.devcontainer/scripts/preflight.sh +113 -0
  101. package/.devcontainer/scripts/setup-aliases.sh +13 -8
  102. package/.devcontainer/scripts/setup-auth.sh +46 -0
  103. package/.devcontainer/scripts/setup-config.sh +29 -10
  104. package/.devcontainer/scripts/setup-migrate-claude.sh +80 -0
  105. package/.devcontainer/scripts/setup-migrate-codeforge.sh +60 -0
  106. package/.devcontainer/scripts/setup-plugins.sh +5 -5
  107. package/.devcontainer/scripts/setup-projects.sh +4 -2
  108. package/.devcontainer/scripts/setup-terminal.sh +3 -1
  109. package/.devcontainer/scripts/setup-update-claude.sh +22 -27
  110. package/.devcontainer/scripts/setup.sh +78 -5
  111. package/LICENSE.txt +14 -0
  112. package/README.md +82 -7
  113. package/package.json +4 -1
  114. package/setup.js +392 -21
  115. package/.devcontainer/config/defaults/main-system-prompt.md +0 -664
  116. package/.devcontainer/docs/configuration-reference.md +0 -93
  117. package/.devcontainer/docs/keybindings.md +0 -100
  118. package/.devcontainer/docs/optional-features.md +0 -64
  119. package/.devcontainer/docs/plugins.md +0 -176
  120. package/.devcontainer/docs/troubleshooting.md +0 -128
  121. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/doc-writer.md +0 -334
  122. package/.devcontainer/scripts/setup-symlink-claude.sh +0 -36
  123. /package/{.devcontainer/config/defaults → .codeforge/config}/keybindings.json +0 -0
  124. /package/{.devcontainer/config/defaults → .codeforge/config}/rules/session-search.md +0 -0
  125. /package/{.devcontainer/config/defaults → .codeforge/config}/rules/spec-workflow.md +0 -0
  126. /package/{.devcontainer/config/defaults → .codeforge/config}/rules/workspace-scope.md +0 -0
  127. /package/{.devcontainer/config/defaults → .codeforge/config}/writing-system-prompt.md +0 -0
  128. /package/{.devcontainer → .codeforge/scripts}/connect-external-terminal.ps1 +0 -0
package/README.md CHANGED
@@ -7,9 +7,16 @@
7
7
  [![npm downloads](https://img.shields.io/npm/dm/codeforge-dev)](https://www.npmjs.com/package/codeforge-dev)
8
8
  [![Node.js](https://img.shields.io/badge/node-%3E%3D14.0.0-brightgreen)](https://nodejs.org/)
9
9
  [![GitHub issues](https://img.shields.io/github/issues/AnExiledDev/CodeForge)](https://github.com/AnExiledDev/CodeForge/issues)
10
+ [![CI](https://github.com/AnExiledDev/CodeForge/actions/workflows/ci.yml/badge.svg)](https://github.com/AnExiledDev/CodeForge/actions/workflows/ci.yml)
10
11
 
11
12
  A curated development environment optimized for AI-powered coding with Claude Code. CodeForge comes pre-configured with language servers, code intelligence tools, and official Anthropic plugins to streamline your development workflow.
12
13
 
14
+ ## Why CodeForge?
15
+
16
+ Claude Code is powerful out of the box, but getting the most from it takes significant configuration — custom agents, safety plugins, code quality hooks, system prompts, and development tools that aren't obvious from the docs. CodeForge is a Claude Code power user's personal development environment, packaged so anyone can use it.
17
+
18
+ Instead of spending hours discovering and configuring advanced features like built-in agent replacement, automated code quality pipelines, or spec-driven workflows, you get a production-tested setup in one command. It's opinionated by design — every default reflects real daily use, not theoretical best practices.
19
+
13
20
  ## Installation
14
21
 
15
22
  Add CodeForge to any project:
@@ -23,8 +30,9 @@ This copies the `.devcontainer/` directory to your project. Then open in VS Code
23
30
  ### Options
24
31
 
25
32
  ```bash
26
- npx codeforge-dev --force # Overwrite existing .devcontainer directory
33
+ npx codeforge-dev --force # Smart update (preserves your customizations)
27
34
  npx codeforge-dev -f # Short form
35
+ npx codeforge-dev --reset # Fresh install (wipes .devcontainer, keeps .codeforge)
28
36
  ```
29
37
 
30
38
  ### Alternative Install Methods
@@ -41,7 +49,12 @@ npx codeforge-dev@1.2.3
41
49
  ## Prerequisites
42
50
 
43
51
  - **Docker Desktop** (or compatible container runtime like Podman)
44
- - **VS Code** with the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers), or **GitHub Codespaces**
52
+ - **A DevContainer client** any of:
53
+ - **VS Code** with the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
54
+ - **DevContainer CLI** — `npm install -g @devcontainers/cli` ([docs](https://containers.dev/supporting#devcontainer-cli))
55
+ - **GitHub Codespaces** — zero local setup
56
+ - **JetBrains Gateway** with [Dev Containers plugin](https://plugins.jetbrains.com/plugin/21962-dev-containers)
57
+ - **DevPod** — open-source, editor-agnostic ([devpod.sh](https://devpod.sh/))
45
58
  - **Claude Code authentication** — run `claude` on first start to authenticate
46
59
 
47
60
  ## What's Included
@@ -70,18 +83,80 @@ tree-sitter (JS/TS/Python), ast-grep, Pyright, TypeScript LSP
70
83
 
71
84
  tmux, agent-browser, claude-monitor, ccusage, ccburn, ccstatusline, ast-grep, tree-sitter, lsp-servers, biome, ruff, shfmt, shellcheck, hadolint, dprint, ccms, notify-hook, mcp-qdrant, chromaterm, kitty-terminfo, claude-session-dashboard
72
85
 
73
- ### Agents (17) & Skills (34)
86
+ ### Agents (17) & Skills (35)
87
+
88
+ The `agent-system` plugin includes 17 specialized agents (architect, explorer, test-writer, security-auditor, etc.). The `skill-engine` plugin provides 22 general coding skills, `spec-workflow` adds 8 spec lifecycle skills, and `ticket-workflow` provides 4 ticket management skills.
89
+
90
+ ## Architecture
91
+
92
+ CodeForge operates in three layers, each building on the one below:
93
+
94
+ ```
95
+ ┌──────────────────────────────────────────────┐
96
+ │ Claude Code │
97
+ │ AI assistant, tool execution, Agent Teams │
98
+ ├──────────────────────────────────────────────┤
99
+ │ CodeForge Layer │
100
+ │ Plugins · Agents · Skills · Hooks · Rules │
101
+ ├──────────────────────────────────────────────┤
102
+ │ DevContainer │
103
+ │ Runtimes · CLI Tools · LSP Servers │
104
+ └──────────────────────────────────────────────┘
105
+ ```
106
+
107
+ **DevContainer** — The foundation. A Python 3.14 container with Node.js, Rust, and Bun runtimes, plus 22 custom features that install development tools (ast-grep, tree-sitter, biome, ruff, and others).
108
+
109
+ **CodeForge Layer** — The intelligence. 17 plugins register hooks that validate commands, inject context, and enforce safety. 21 agents provide specialized personas. 38 skills offer on-demand reference material. System prompts and rules shape behavior.
74
110
 
75
- The `agent-system` plugin includes 17 specialized agents (architect, explorer, test-writer, security-auditor, etc.). The `skill-engine` plugin provides 21 general coding skills, `spec-workflow` adds 8 spec lifecycle skills, and `ticket-workflow` provides 4 ticket management skills.
111
+ **Claude Code** The AI assistant, executing tools and coordinating work. CodeForge enhances it through configuration replacing built-in subagents, adding safety guardrails, and wiring up quality checks that run automatically.
112
+
113
+ For the full architecture breakdown — hook pipeline, agent routing, skill loading, and design principles — see the [Architecture Reference](https://codeforge.core-directive.com/reference/architecture/).
114
+
115
+ ## Configuration
116
+
117
+ All configuration lives in `.devcontainer/` and deploys automatically on container start. Key files:
118
+
119
+ | File | What It Configures | User-Modifiable? |
120
+ |------|--------------------|------------------|
121
+ | `config/defaults/settings.json` | Model, plugins, permissions, environment variables | Yes |
122
+ | `config/defaults/main-system-prompt.md` | Claude's behavioral guidelines and directives | Yes |
123
+ | `config/defaults/keybindings.json` | Keyboard shortcuts | Yes |
124
+ | `config/defaults/ccstatusline-settings.json` | Terminal status bar widgets and layout | Yes |
125
+ | `config/file-manifest.json` | Which config files deploy and how they update | Yes |
126
+ | `devcontainer.json` | Container image, features, runtimes, ports | Yes |
127
+ | `.env` | Setup phase toggles (auth, plugins, aliases, etc.) | Yes |
128
+
129
+ Config files use SHA-256 change detection — your edits persist across container rebuilds unless the source changes. Set a file's overwrite mode to `"never"` in `file-manifest.json` to permanently preserve your customizations.
130
+
131
+ For the complete configuration guide, see the [documentation site](https://codeforge.core-directive.com/customization/configuration/).
76
132
 
77
133
  ## Quick Start
78
134
 
79
135
  1. **Install**: `npx codeforge-dev`
80
- 2. **Open in Container**: "Reopen in Container" in VS Code, or create a Codespace
136
+ 2. **Open in Container**:
137
+ - **VS Code**: "Reopen in Container" from the Command Palette
138
+ - **CLI**: `devcontainer up --workspace-folder .` then `docker exec -it <container> zsh`
139
+ - **Codespaces**: Create a Codespace from the repo
81
140
  3. **Authenticate**: Run `claude` and follow prompts
82
141
  4. **Start coding**: Run `cc`
83
142
 
84
- For full usage documentation — authentication, configuration, tools, agents, and keybindings — see [`.devcontainer/README.md`](.devcontainer/README.md).
143
+ CodeForge uses the open [Dev Containers specification](https://containers.dev/) — any compatible client works. For full usage documentation — authentication, configuration, tools, agents, and keybindings — see [`.devcontainer/README.md`](.devcontainer/README.md).
144
+
145
+ ## Contributing
146
+
147
+ We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md)
148
+ before submitting a pull request. All contributions require signing our
149
+ [Contributor License Agreement](CLA.md).
150
+
151
+ ## License
152
+
153
+ This project is licensed under the [GNU General Public License v3.0](LICENSE.txt).
154
+
155
+ **Commercial licensing** is available for organizations that need to use CodeForge
156
+ without GPL-3.0 obligations. Contact
157
+ [696222+AnExiledDev@users.noreply.github.com](mailto:696222+AnExiledDev@users.noreply.github.com)
158
+ or [open a GitHub issue](https://github.com/AnExiledDev/CodeForge/issues/new)
159
+ for terms.
85
160
 
86
161
  ## Development
87
162
 
@@ -102,7 +177,7 @@ npm publish
102
177
 
103
178
  ## Changelog
104
179
 
105
- See [CHANGELOG.md](.devcontainer/CHANGELOG.md) for release history. Current version: **1.14.0** (2026-02-23).
180
+ See [CHANGELOG.md](.devcontainer/CHANGELOG.md) for release history. Current version: **2.0.0**.
106
181
 
107
182
  ## Further Reading
108
183
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeforge-dev",
3
- "version": "1.14.2",
3
+ "version": "2.0.1",
4
4
  "description": "Complete development container that sets up Claude Code with modular devcontainer features, modern dev tools, and persistent configurations. Drop it into any project and get a production-ready AI development environment in minutes.",
5
5
  "main": "setup.js",
6
6
  "bin": {
@@ -8,6 +8,8 @@
8
8
  },
9
9
  "scripts": {
10
10
  "test": "node test.js",
11
+ "test:plugins": "pytest tests/ -v",
12
+ "test:all": "npm test && pytest tests/ -v",
11
13
  "prepublishOnly": "npm test",
12
14
  "docs:dev": "npm run dev --prefix docs",
13
15
  "docs:build": "npm run build --prefix docs",
@@ -30,6 +32,7 @@
30
32
  "license": "GPL-3.0",
31
33
  "files": [
32
34
  ".devcontainer/**/*",
35
+ ".codeforge/**/*",
33
36
  "setup.js",
34
37
  "README.md"
35
38
  ],
package/setup.js CHANGED
@@ -1,19 +1,16 @@
1
1
  #!/usr/bin/env node
2
+ // SPDX-License-Identifier: GPL-3.0-only
3
+ // Copyright (c) 2026 Marcus Krueger
2
4
 
3
- const fs = require("fs");
4
- const path = require("path");
5
+ const fs = require("node:fs");
6
+ const path = require("node:path");
7
+ const crypto = require("node:crypto");
5
8
 
6
9
  // ── Default preserve list ────────────────────────────────────────
7
- // Files in the package that should NOT overwrite user customizations.
10
+ // Files in .devcontainer that should NOT overwrite user customizations.
8
11
  // The package version is saved as <file>.codeforge-new for diffing.
9
- const DEFAULT_PRESERVE = [
10
- "config/defaults/settings.json",
11
- "config/defaults/main-system-prompt.md",
12
- "config/defaults/keybindings.json",
13
- "config/defaults/ccstatusline-settings.json",
14
- "config/file-manifest.json",
15
- ".codeforge-preserve",
16
- ];
12
+ // Note: .codeforge/ uses checksum-based preservation instead.
13
+ const DEFAULT_PRESERVE = [".codeforge-preserve"];
17
14
 
18
15
  // ── copyDirectory ────────────────────────────────────────────────
19
16
  // Simple recursive copy (used for fresh install and --reset).
@@ -54,6 +51,173 @@ function loadPreserveList(devcontainerDest) {
54
51
  return new Set([...DEFAULT_PRESERVE, ...custom]);
55
52
  }
56
53
 
54
+ // ── computeChecksum ──────────────────────────────────────────────
55
+ // Returns SHA-256 hex digest of a file's contents.
56
+ function computeChecksum(filePath) {
57
+ return crypto
58
+ .createHash("sha256")
59
+ .update(fs.readFileSync(filePath))
60
+ .digest("hex");
61
+ }
62
+
63
+ // ── generateChecksums ────────────────────────────────────────────
64
+ // Walks directory recursively, returns { relativePath: sha256hex } map.
65
+ // Skips .checksums/ and .markers/ directories.
66
+ function generateChecksums(dir) {
67
+ const checksums = {};
68
+
69
+ function walk(currentDir, relativeBase) {
70
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
71
+
72
+ for (const entry of entries) {
73
+ const fullPath = path.join(currentDir, entry.name);
74
+ const relativePath = relativeBase
75
+ ? `${relativeBase}/${entry.name}`
76
+ : entry.name;
77
+
78
+ if (entry.isDirectory()) {
79
+ if (entry.name === ".checksums" || entry.name === ".markers") {
80
+ continue;
81
+ }
82
+ walk(fullPath, relativePath);
83
+ } else {
84
+ checksums[relativePath] = computeChecksum(fullPath);
85
+ }
86
+ }
87
+ }
88
+
89
+ walk(dir, "");
90
+ return checksums;
91
+ }
92
+
93
+ // ── writeChecksums ───────────────────────────────────────────────
94
+ // Writes .checksums/<version>.json with version, timestamp, and file hashes.
95
+ function writeChecksums(codeforgeDir, version, checksums) {
96
+ const checksumsDir = path.join(codeforgeDir, ".checksums");
97
+ fs.mkdirSync(checksumsDir, { recursive: true });
98
+ const data = {
99
+ version,
100
+ generated: new Date().toISOString(),
101
+ files: checksums,
102
+ };
103
+ fs.writeFileSync(
104
+ path.join(checksumsDir, `${version}.json`),
105
+ JSON.stringify(data, null, "\t") + "\n",
106
+ );
107
+ }
108
+
109
+ // ── readChecksums ────────────────────────────────────────────────
110
+ // Reads latest version's checksums from .checksums/ dir.
111
+ // Returns { files: {} } if none found.
112
+ function readChecksums(codeforgeDir) {
113
+ const checksumsDir = path.join(codeforgeDir, ".checksums");
114
+ if (!fs.existsSync(checksumsDir)) {
115
+ return { files: {} };
116
+ }
117
+
118
+ const files = fs
119
+ .readdirSync(checksumsDir)
120
+ .filter((f) => f.endsWith(".json"))
121
+ .sort((a, b) => {
122
+ const pa = a.replace(".json", "").split(".").map(Number);
123
+ const pb = b.replace(".json", "").split(".").map(Number);
124
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
125
+ const diff = (pa[i] || 0) - (pb[i] || 0);
126
+ if (diff !== 0) return diff;
127
+ }
128
+ return 0;
129
+ });
130
+
131
+ if (files.length === 0) {
132
+ return { files: {} };
133
+ }
134
+
135
+ const latest = files[files.length - 1];
136
+ try {
137
+ return JSON.parse(
138
+ fs.readFileSync(path.join(checksumsDir, latest), "utf-8"),
139
+ );
140
+ } catch {
141
+ console.log(
142
+ " Warning: Could not read checksums from " +
143
+ latest +
144
+ ", treating as fresh install.",
145
+ );
146
+ return { files: {} };
147
+ }
148
+ }
149
+
150
+ // ── syncCodeforgeDirectory ───────────────────────────────────────
151
+ // Checksum-aware sync for .codeforge/ directory.
152
+ // Unmodified files get overwritten; modified files are preserved
153
+ // and new defaults are written as <file>.default.
154
+ function syncCodeforgeDirectory(src, dest) {
155
+ const stored = readChecksums(dest);
156
+ const stats = {
157
+ updated: 0,
158
+ preserved: 0,
159
+ added: 0,
160
+ preservedFiles: [],
161
+ defaultFiles: [],
162
+ };
163
+
164
+ function walk(srcDir, destDir, relativeBase) {
165
+ if (!fs.existsSync(destDir)) {
166
+ fs.mkdirSync(destDir, { recursive: true });
167
+ }
168
+
169
+ const entries = fs.readdirSync(srcDir, { withFileTypes: true });
170
+
171
+ for (const entry of entries) {
172
+ const srcPath = path.join(srcDir, entry.name);
173
+ const destPath = path.join(destDir, entry.name);
174
+ const relativePath = relativeBase
175
+ ? `${relativeBase}/${entry.name}`
176
+ : entry.name;
177
+
178
+ if (entry.isDirectory()) {
179
+ if (entry.name === ".checksums" || entry.name === ".markers") {
180
+ continue;
181
+ }
182
+ walk(srcPath, destPath, relativePath);
183
+ continue;
184
+ }
185
+
186
+ const storedHash = stored.files[relativePath];
187
+ const currentHash = fs.existsSync(destPath)
188
+ ? computeChecksum(destPath)
189
+ : null;
190
+
191
+ if (!storedHash) {
192
+ // First install or new file
193
+ if (currentHash === null) {
194
+ fs.copyFileSync(srcPath, destPath);
195
+ stats.added++;
196
+ } else {
197
+ // File exists but no stored hash — treat as user-created
198
+ fs.copyFileSync(srcPath, `${destPath}.default`);
199
+ stats.preserved++;
200
+ stats.preservedFiles.push(relativePath);
201
+ stats.defaultFiles.push(relativePath);
202
+ }
203
+ } else if (currentHash === storedHash) {
204
+ // File UNMODIFIED — overwrite with new version
205
+ fs.copyFileSync(srcPath, destPath);
206
+ stats.updated++;
207
+ } else {
208
+ // File USER-MODIFIED — keep user's file, write new as .default
209
+ fs.copyFileSync(srcPath, `${destPath}.default`);
210
+ stats.preserved++;
211
+ stats.preservedFiles.push(relativePath);
212
+ stats.defaultFiles.push(relativePath);
213
+ }
214
+ }
215
+ }
216
+
217
+ walk(src, dest, "");
218
+ return stats;
219
+ }
220
+
57
221
  // ── syncDirectory ────────────────────────────────────────────────
58
222
  // Selective overwrite: walks the package tree and copies files to dest.
59
223
  // - Framework files (scripts, features, plugins): always overwrite
@@ -80,7 +244,7 @@ function syncDirectory(src, dest, preserveSet) {
80
244
  const srcPath = path.join(srcDir, entry.name);
81
245
  const destPath = path.join(destDir, entry.name);
82
246
  const relativePath = relativeBase
83
- ? relativeBase + "/" + entry.name
247
+ ? `${relativeBase}/${entry.name}`
84
248
  : entry.name;
85
249
 
86
250
  if (entry.isDirectory()) {
@@ -90,7 +254,7 @@ function syncDirectory(src, dest, preserveSet) {
90
254
 
91
255
  // Special handling for devcontainer.json: overwrite + save .bak
92
256
  if (relativePath === "devcontainer.json" && fs.existsSync(destPath)) {
93
- fs.copyFileSync(destPath, destPath + ".bak");
257
+ fs.copyFileSync(destPath, `${destPath}.bak`);
94
258
  fs.copyFileSync(srcPath, destPath);
95
259
  stats.backedUp++;
96
260
  stats.updated++;
@@ -99,7 +263,7 @@ function syncDirectory(src, dest, preserveSet) {
99
263
 
100
264
  // Preserved files: skip overwrite, save package version as .codeforge-new
101
265
  if (preserveSet.has(relativePath) && fs.existsSync(destPath)) {
102
- fs.copyFileSync(srcPath, destPath + ".codeforge-new");
266
+ fs.copyFileSync(srcPath, `${destPath}.codeforge-new`);
103
267
  stats.preserved++;
104
268
  stats.preservedFiles.push(relativePath);
105
269
  continue;
@@ -123,20 +287,33 @@ function syncDirectory(src, dest, preserveSet) {
123
287
  // ── main ─────────────────────────────────────────────────────────
124
288
  function main() {
125
289
  const args = process.argv.slice(2);
290
+
291
+ // Subcommand: config apply
292
+ if (args[0] === "config" && args[1] === "apply") {
293
+ return configApply();
294
+ }
295
+
126
296
  const force = args.includes("--force") || args.includes("-f");
127
297
  const reset = args.includes("--reset");
128
298
 
129
299
  if (args.includes("--help") || args.includes("-h")) {
130
300
  console.log("Usage: codeforge [options]");
301
+ console.log(" codeforge config apply");
131
302
  console.log("");
132
303
  console.log("Options:");
133
304
  console.log(
134
- " --force, -f Update existing .devcontainer (preserves user config)",
305
+ " --force, -f Update existing .devcontainer and .codeforge (preserves user config)",
135
306
  );
136
307
  console.log(
137
- " --reset Remove all customizations and install fresh defaults",
308
+ " --reset Remove .devcontainer customizations and install fresh defaults",
309
+ );
310
+ console.log(" (.codeforge user modifications preserved)");
311
+ console.log(" --help, -h Show this help message");
312
+ console.log("");
313
+ console.log("Subcommands:");
314
+ console.log(
315
+ " config apply Deploy .codeforge/config/ files to ~/.claude/",
138
316
  );
139
- console.log(" --help, -h Show this help message");
140
317
  console.log("");
141
318
  console.log(
142
319
  "Without flags, installs only if .devcontainer does not exist.",
@@ -148,6 +325,11 @@ function main() {
148
325
  const packageDir = __dirname;
149
326
  const devcontainerSrc = path.join(packageDir, ".devcontainer");
150
327
  const devcontainerDest = path.join(currentDir, ".devcontainer");
328
+ const codeforgeSrc = path.join(packageDir, ".codeforge");
329
+ const codeforgeDest = path.join(currentDir, ".codeforge");
330
+ const packageVersion = JSON.parse(
331
+ fs.readFileSync(path.join(packageDir, "package.json"), "utf8"),
332
+ ).version;
151
333
 
152
334
  console.log("");
153
335
 
@@ -161,12 +343,44 @@ function main() {
161
343
 
162
344
  if (fs.existsSync(devcontainerDest)) {
163
345
  if (reset) {
164
- // Nuclear: delete everything and copy fresh
346
+ // Nuclear: delete .devcontainer and copy fresh
165
347
  console.log("Resetting .devcontainer to package defaults...");
166
348
  console.log("");
167
349
  fs.rmSync(devcontainerDest, { recursive: true, force: true });
168
350
  copyDirectory(devcontainerSrc, devcontainerDest);
169
- console.log(" Reset complete. All user customizations removed.");
351
+ console.log(
352
+ " Reset complete. All .devcontainer customizations removed.",
353
+ );
354
+
355
+ // .codeforge uses checksum-based preservation (not wipe)
356
+ if (fs.existsSync(codeforgeSrc)) {
357
+ if (fs.existsSync(codeforgeDest)) {
358
+ const codeforgeStats = syncCodeforgeDirectory(
359
+ codeforgeSrc,
360
+ codeforgeDest,
361
+ );
362
+ console.log(" .codeforge/ user modifications preserved.");
363
+ console.log(` Updated: ${codeforgeStats.updated} files`);
364
+ console.log(` Added: ${codeforgeStats.added} new files`);
365
+ console.log(
366
+ ` Preserved: ${codeforgeStats.preserved} user config files`,
367
+ );
368
+ if (codeforgeStats.defaultFiles.length > 0) {
369
+ console.log("");
370
+ console.log(
371
+ " Review .default files for new defaults you may want to merge:",
372
+ );
373
+ for (const f of codeforgeStats.defaultFiles) {
374
+ console.log(` ${f}.default`);
375
+ }
376
+ }
377
+ } else {
378
+ copyDirectory(codeforgeSrc, codeforgeDest);
379
+ }
380
+ const newChecksums = generateChecksums(codeforgeSrc);
381
+ writeChecksums(codeforgeDest, packageVersion, newChecksums);
382
+ }
383
+
170
384
  console.log("");
171
385
  printNextSteps();
172
386
  } else if (force) {
@@ -204,6 +418,34 @@ function main() {
204
418
  console.log("");
205
419
  }
206
420
 
421
+ // .codeforge sync with checksum-based preservation
422
+ if (fs.existsSync(codeforgeSrc)) {
423
+ const codeforgeStats = syncCodeforgeDirectory(
424
+ codeforgeSrc,
425
+ codeforgeDest,
426
+ );
427
+ const newChecksums = generateChecksums(codeforgeSrc);
428
+ writeChecksums(codeforgeDest, packageVersion, newChecksums);
429
+
430
+ console.log(" .codeforge/ update:");
431
+ console.log(` Updated: ${codeforgeStats.updated} files`);
432
+ console.log(` Added: ${codeforgeStats.added} new files`);
433
+ console.log(
434
+ ` Preserved: ${codeforgeStats.preserved} user config files`,
435
+ );
436
+
437
+ if (codeforgeStats.defaultFiles.length > 0) {
438
+ console.log("");
439
+ console.log(
440
+ " Review .default files for new defaults you may want to merge:",
441
+ );
442
+ for (const f of codeforgeStats.defaultFiles) {
443
+ console.log(` ${f}.default`);
444
+ }
445
+ }
446
+ console.log("");
447
+ }
448
+
207
449
  printNextSteps();
208
450
  } else {
209
451
  // No flags: error with guidance
@@ -221,6 +463,13 @@ function main() {
221
463
 
222
464
  try {
223
465
  copyDirectory(devcontainerSrc, devcontainerDest);
466
+
467
+ if (fs.existsSync(codeforgeSrc)) {
468
+ copyDirectory(codeforgeSrc, codeforgeDest);
469
+ const checksums = generateChecksums(codeforgeSrc);
470
+ writeChecksums(codeforgeDest, packageVersion, checksums);
471
+ }
472
+
224
473
  console.log(" CodeForge DevContainer configuration installed!");
225
474
  console.log("");
226
475
  printNextSteps();
@@ -232,13 +481,127 @@ function main() {
232
481
  }
233
482
  }
234
483
 
484
+ // ── configApply ──────────────────────────────────────────────────
485
+ // Deploys .codeforge/config/ files to ~/.claude/ using file-manifest.json.
486
+ function configApply() {
487
+ const codeforgeDir =
488
+ process.env.CODEFORGE_DIR || path.join(process.cwd(), ".codeforge");
489
+ const manifest = path.join(codeforgeDir, "file-manifest.json");
490
+
491
+ if (!fs.existsSync(manifest)) {
492
+ console.error("Error: file-manifest.json not found at " + manifest);
493
+ console.error("Are you in a CodeForge project directory?");
494
+ process.exit(1);
495
+ }
496
+
497
+ const entries = JSON.parse(fs.readFileSync(manifest, "utf-8"));
498
+ const claudeConfigDir =
499
+ process.env.CLAUDE_CONFIG_DIR ||
500
+ path.join(process.env.HOME || "/home/vscode", ".claude");
501
+ const workspaceRoot = process.env.WORKSPACE_ROOT || process.cwd();
502
+
503
+ function expandVars(val) {
504
+ return val
505
+ .replace(/\$\{CLAUDE_CONFIG_DIR\}/g, claudeConfigDir)
506
+ .replace(/\$\{WORKSPACE_ROOT\}/g, workspaceRoot)
507
+ .replace(/\$\{HOME\}/g, process.env.HOME || "/home/vscode");
508
+ }
509
+
510
+ console.log("");
511
+ console.log("Applying .codeforge/config/ to Claude configuration...");
512
+ console.log("");
513
+
514
+ let deployed = 0;
515
+ let skipped = 0;
516
+
517
+ const validOverwrite = ["always", "if-changed", "never"];
518
+
519
+ for (const entry of entries) {
520
+ if (entry.enabled === false) {
521
+ skipped++;
522
+ continue;
523
+ }
524
+
525
+ if (entry.overwrite && !validOverwrite.includes(entry.overwrite)) {
526
+ console.log(
527
+ ' Warning: Unknown overwrite value "' +
528
+ entry.overwrite +
529
+ '" for ' +
530
+ entry.src +
531
+ ', defaulting to "always"',
532
+ );
533
+ }
534
+
535
+ const codeforgeRoot = path.resolve(codeforgeDir);
536
+ const srcPath = path.resolve(codeforgeRoot, entry.src);
537
+ if (!srcPath.startsWith(codeforgeRoot + path.sep)) {
538
+ console.log(
539
+ " Skip: " + entry.src + " (source path escapes .codeforge/)",
540
+ );
541
+ skipped++;
542
+ continue;
543
+ }
544
+ if (!fs.existsSync(srcPath)) {
545
+ console.log(" Skip: " + entry.src + " (not found)");
546
+ skipped++;
547
+ continue;
548
+ }
549
+
550
+ const homeDir = path.resolve(process.env.HOME || "/home/vscode");
551
+ const allowedDestRoots = [
552
+ path.resolve(claudeConfigDir),
553
+ homeDir,
554
+ "/usr/local/share",
555
+ ];
556
+ const destDir = path.resolve(expandVars(entry.dest));
557
+ const destAllowed = allowedDestRoots.some(
558
+ (root) => destDir === root || destDir.startsWith(root + path.sep),
559
+ );
560
+ if (!destAllowed) {
561
+ console.log(
562
+ " Skip: " + entry.dest + " (destination outside allowed directories)",
563
+ );
564
+ skipped++;
565
+ continue;
566
+ }
567
+
568
+ const filename = entry.destFilename || path.basename(entry.src);
569
+ const destPath = path.join(destDir, filename);
570
+ fs.mkdirSync(destDir, { recursive: true });
571
+
572
+ if (entry.overwrite === "never" && fs.existsSync(destPath)) {
573
+ console.log(" Skip: " + filename + " (exists, overwrite=never)");
574
+ skipped++;
575
+ continue;
576
+ }
577
+
578
+ if (entry.overwrite === "if-changed" && fs.existsSync(destPath)) {
579
+ const srcHash = computeChecksum(srcPath);
580
+ const destHash = computeChecksum(destPath);
581
+ if (srcHash === destHash) {
582
+ skipped++;
583
+ continue;
584
+ }
585
+ }
586
+
587
+ fs.copyFileSync(srcPath, destPath);
588
+ console.log(" Deployed: " + entry.src + " → " + destPath);
589
+ deployed++;
590
+ }
591
+
592
+ console.log("");
593
+ console.log(
594
+ "Config apply complete: " + deployed + " deployed, " + skipped + " skipped",
595
+ );
596
+ }
597
+
235
598
  function printNextSteps() {
236
599
  console.log("Next steps:");
237
600
  console.log(" 1. Open this folder in VS Code");
238
601
  console.log(' 2. Select "Reopen in Container" from the command palette');
239
602
  console.log(" 3. Run: claude");
240
603
  console.log("");
241
- console.log("Documentation: .devcontainer/README.md");
604
+ console.log("Documentation: .devcontainer/README.md and .codeforge/");
242
605
  console.log("");
243
606
  }
244
607
 
@@ -255,4 +618,12 @@ if (require.main === module) {
255
618
  main();
256
619
  }
257
620
 
258
- module.exports = { copyDirectory, syncDirectory, loadPreserveList, main };
621
+ module.exports = {
622
+ copyDirectory,
623
+ syncDirectory,
624
+ syncCodeforgeDirectory,
625
+ loadPreserveList,
626
+ computeChecksum,
627
+ generateChecksums,
628
+ main,
629
+ };