codeforge-dev 1.10.0 → 1.11.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 (45) hide show
  1. package/.devcontainer/CHANGELOG.md +69 -0
  2. package/.devcontainer/CLAUDE.md +15 -6
  3. package/.devcontainer/README.md +22 -11
  4. package/.devcontainer/config/defaults/main-system-prompt.md +104 -152
  5. package/.devcontainer/config/defaults/rules/session-search.md +66 -0
  6. package/.devcontainer/config/defaults/rules/spec-workflow.md +39 -12
  7. package/.devcontainer/config/defaults/settings.json +2 -1
  8. package/.devcontainer/config/defaults/writing-system-prompt.md +143 -0
  9. package/.devcontainer/config/file-manifest.json +12 -0
  10. package/.devcontainer/devcontainer.json +9 -2
  11. package/.devcontainer/features/ccms/README.md +50 -0
  12. package/.devcontainer/features/ccms/devcontainer-feature.json +21 -0
  13. package/.devcontainer/features/ccms/install.sh +105 -0
  14. package/.devcontainer/features/ccstatusline/install.sh +24 -2
  15. package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +8 -1
  16. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/architect.md +1 -0
  17. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/claude-guide.md +1 -1
  18. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/doc-writer.md +4 -4
  19. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/generalist.md +1 -0
  20. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/spec-writer.md +8 -8
  21. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/hooks/hooks.json +10 -0
  22. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/skill-suggester.cpython-314.pyc +0 -0
  23. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/git-state-injector.py +15 -4
  24. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/inject-cwd.py +37 -0
  25. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/skill-suggester.py +24 -0
  26. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/spec-reminder.py +3 -2
  27. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-build/SKILL.md +353 -0
  28. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-build/references/review-checklist.md +175 -0
  29. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-check/SKILL.md +15 -14
  30. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/SKILL.md +12 -11
  31. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/references/backlog-template.md +1 -1
  32. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/references/milestones-template.md +32 -0
  33. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-new/SKILL.md +17 -18
  34. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-new/references/template.md +12 -2
  35. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-review/SKILL.md +229 -0
  36. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-update/SKILL.md +6 -2
  37. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/SKILL.md +1 -1
  38. package/.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/.claude-plugin/plugin.json +38 -5
  39. package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/.claude-plugin/plugin.json +7 -0
  40. package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/hooks/hooks.json +17 -0
  41. package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/__pycache__/guard-workspace-scope.cpython-314.pyc +0 -0
  42. package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py +132 -0
  43. package/.devcontainer/scripts/setup-aliases.sh +68 -75
  44. package/package.json +1 -1
  45. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/references/roadmap-template.md +0 -33
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Enforce workspace scope for file operations.
4
+
5
+ Blocks write operations (Write, Edit, NotebookEdit) to files outside the
6
+ current working directory. Warns on read operations (Read, Glob, Grep)
7
+ outside the working directory. Allows unrestricted access when cwd is
8
+ /workspaces (the workspace root).
9
+
10
+ Exit code 2 blocks the operation with an error message.
11
+ Exit code 0 allows the operation to proceed (with optional warning context).
12
+ """
13
+
14
+ import json
15
+ import os
16
+ import sys
17
+
18
+ # Paths that are always allowed regardless of working directory
19
+ ALLOWED_PREFIXES = [
20
+ "/workspaces/.claude/",
21
+ "/workspaces/.tmp/",
22
+ "/workspaces/.devcontainer/",
23
+ "/tmp/",
24
+ "/home/",
25
+ ]
26
+
27
+ WRITE_TOOLS = {"Write", "Edit", "NotebookEdit"}
28
+ READ_TOOLS = {"Read", "Glob", "Grep"}
29
+
30
+ # Tool input field that contains the target path
31
+ PATH_FIELDS = {
32
+ "Read": "file_path",
33
+ "Write": "file_path",
34
+ "Edit": "file_path",
35
+ "NotebookEdit": "notebook_path",
36
+ "Glob": "path",
37
+ "Grep": "path",
38
+ }
39
+
40
+
41
+ def get_target_path(tool_name: str, tool_input: dict) -> str | None:
42
+ """Extract the target path from tool input.
43
+
44
+ Returns None if no path field is present or the field is empty,
45
+ which means the tool defaults to cwd (always in scope).
46
+ """
47
+ field = PATH_FIELDS.get(tool_name)
48
+ if not field:
49
+ return None
50
+ return tool_input.get(field) or None
51
+
52
+
53
+ def is_in_scope(resolved_path: str, cwd: str) -> bool:
54
+ """Check if resolved_path is within the working directory."""
55
+ cwd_prefix = cwd if cwd.endswith("/") else cwd + "/"
56
+ return resolved_path == cwd or resolved_path.startswith(cwd_prefix)
57
+
58
+
59
+ def is_allowlisted(resolved_path: str) -> bool:
60
+ """Check if resolved_path falls under an allowed prefix."""
61
+ return any(resolved_path.startswith(prefix) for prefix in ALLOWED_PREFIXES)
62
+
63
+
64
+ def main():
65
+ try:
66
+ input_data = json.load(sys.stdin)
67
+ tool_name = input_data.get("tool_name", "")
68
+ tool_input = input_data.get("tool_input", {})
69
+
70
+ cwd = os.getcwd()
71
+
72
+ # Unrestricted when working from the workspace root
73
+ if cwd == "/workspaces":
74
+ sys.exit(0)
75
+
76
+ target_path = get_target_path(tool_name, tool_input)
77
+
78
+ # No path specified — tool defaults to cwd, which is in scope
79
+ if target_path is None:
80
+ sys.exit(0)
81
+
82
+ resolved = os.path.realpath(target_path)
83
+
84
+ if is_in_scope(resolved, cwd):
85
+ sys.exit(0)
86
+
87
+ if is_allowlisted(resolved):
88
+ sys.exit(0)
89
+
90
+ # Out of scope
91
+ if tool_name in WRITE_TOOLS:
92
+ print(
93
+ json.dumps(
94
+ {
95
+ "error": (
96
+ f"Blocked: {tool_name} targets '{target_path}' which is "
97
+ f"outside the working directory ({cwd}). Move to that "
98
+ f"project's directory first or work from /workspaces."
99
+ )
100
+ }
101
+ )
102
+ )
103
+ sys.exit(2)
104
+
105
+ if tool_name in READ_TOOLS:
106
+ print(
107
+ json.dumps(
108
+ {
109
+ "additionalContext": (
110
+ f"Warning: {tool_name} targets '{target_path}' which is "
111
+ f"outside the working directory ({cwd}). This read is "
112
+ f"allowed but may indicate unintended cross-project access."
113
+ )
114
+ }
115
+ )
116
+ )
117
+ sys.exit(0)
118
+
119
+ # Unknown tool — allow by default
120
+ sys.exit(0)
121
+
122
+ except json.JSONDecodeError:
123
+ # Can't parse input — allow by default
124
+ sys.exit(0)
125
+ except Exception as e:
126
+ # Don't block on hook failure
127
+ print(f"Hook error: {e}", file=sys.stderr)
128
+ sys.exit(0)
129
+
130
+
131
+ if __name__ == "__main__":
132
+ main()
@@ -1,129 +1,122 @@
1
1
  #!/bin/bash
2
2
  # Setup cc/claude/ccraw aliases for claude with local system prompt support
3
+ #
4
+ # Idempotent: removes the entire managed block then re-writes it fresh.
5
+ # Safe to run on every container start via postStartCommand.
3
6
 
4
7
  CLAUDE_DIR="${CLAUDE_CONFIG_DIR:?CLAUDE_CONFIG_DIR not set}"
5
8
 
6
9
  echo "[setup-aliases] Configuring Claude aliases..."
7
10
 
8
- # Simple alias definitions (not functions functions don't behave reliably across shell contexts)
9
- # Aliases reference $_CLAUDE_BIN which is resolved at shell startup to prefer the native binary.
10
- ALIAS_CC='alias cc='"'"'CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 command "$_CLAUDE_BIN" --system-prompt-file "$CLAUDE_CONFIG_DIR/system-prompt.md" --permission-mode plan --allow-dangerously-skip-permissions'"'"''
11
- ALIAS_CLAUDE='alias claude='"'"'CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 command "$_CLAUDE_BIN" --system-prompt-file "$CLAUDE_CONFIG_DIR/system-prompt.md" --permission-mode plan --allow-dangerously-skip-permissions'"'"''
12
- ALIAS_CCRAW='alias ccraw='"'"'command "$_CLAUDE_BIN"'"'"''
11
+ # Resolve check-setup path once (used inside the block we write)
12
+ DEVCONTAINER_SCRIPTS="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
+
14
+ BLOCK_START='# === CodeForge Claude aliases START (managed by setup-aliases.sh do not edit) ==='
15
+ BLOCK_END='# === CodeForge Claude aliases END ==='
13
16
 
14
17
  for rc in ~/.bashrc ~/.zshrc; do
15
18
  if [ -f "$rc" ]; then
16
- # --- Backup before modifying ---
19
+ # --- 1. Backup before modifying ---
17
20
  cp "$rc" "${rc}.bak.$(date +%s)" 2>/dev/null || true
18
21
  # Clean old backups (keep last 3)
19
22
  ls -t "${rc}.bak."* 2>/dev/null | tail -n +4 | xargs rm -f 2>/dev/null || true
20
23
 
21
- # --- Cleanup old definitions ---
24
+ # --- 2. Remove existing managed block (if present) ---
25
+ sed -i '/# === CodeForge Claude aliases START/,/# === CodeForge Claude aliases END/d' "$rc"
22
26
 
23
- # Remove old cc alias
24
- if grep -q "alias cc=" "$rc" 2>/dev/null; then
25
- sed -i '/alias cc=/d' "$rc"
26
- echo "[setup-aliases] Removed old cc alias from $(basename $rc)"
27
- fi
28
- # Remove old cc function (single-line or multi-line)
27
+ # --- 3. Legacy cleanup (pre-marker formats only) ---
28
+ # These remove remnants from versions that predated the block-marker system.
29
+ # After step 2, anything matching these patterns is orphaned from old formats.
30
+
31
+ # Old function forms (pre-v1.10.0)
29
32
  if grep -q "^cc()" "$rc" 2>/dev/null; then
30
33
  sed -i '/^cc() {/,/^}/d' "$rc"
31
- echo "[setup-aliases] Removed old cc function from $(basename $rc)"
34
+ echo "[setup-aliases] Removed legacy cc() function from $(basename "$rc")"
32
35
  fi
33
- # Remove old _claude_with_config function
34
36
  if grep -q "^_claude_with_config()" "$rc" 2>/dev/null; then
35
37
  sed -i '/^_claude_with_config() {/,/^}/d' "$rc"
36
- echo "[setup-aliases] Removed old _claude_with_config function from $(basename $rc)"
38
+ echo "[setup-aliases] Removed legacy _claude_with_config() function from $(basename "$rc")"
37
39
  fi
38
- # Remove old claude function override
39
- if grep -q "^claude() {" "$rc" 2>/dev/null; then
40
+ if grep -q "^claude() { _claude_with_config" "$rc" 2>/dev/null; then
40
41
  sed -i '/^claude() { _claude_with_config/d' "$rc"
41
- echo "[setup-aliases] Removed old claude function from $(basename $rc)"
42
- fi
43
- # Remove old claude alias
44
- if grep -q "alias claude=" "$rc" 2>/dev/null; then
45
- sed -i '/alias claude=/d' "$rc"
42
+ echo "[setup-aliases] Removed legacy claude() function from $(basename "$rc")"
46
43
  fi
47
- # Remove old ccraw alias
48
- if grep -q "alias ccraw=" "$rc" 2>/dev/null; then
49
- sed -i '/alias ccraw=/d' "$rc"
50
- fi
51
- # Remove old specwright alias
52
44
  if grep -q "alias specwright=" "$rc" 2>/dev/null; then
53
45
  sed -i '/alias specwright=/d' "$rc"
46
+ echo "[setup-aliases] Removed legacy specwright alias from $(basename "$rc")"
54
47
  fi
55
- # Remove old cc-tools/check-setup functions
48
+
49
+ # Old alias/export form (v1.10.0 — no block markers)
50
+ sed -i '/# Claude Code environment and aliases/d' "$rc"
51
+ sed -i '/^export CLAUDE_CONFIG_DIR="/d' "$rc"
52
+ sed -i '/^export LANG=en_US\.UTF-8$/d' "$rc"
53
+ sed -i '/^export LC_ALL=en_US\.UTF-8$/d' "$rc"
54
+ # _CLAUDE_BIN if-block (4 patterns: if, elif, else, fi + assignments)
55
+ sed -i '/^if \[ -x "\$HOME\/\.local\/bin\/claude" \]/,/^fi$/d' "$rc"
56
+ sed -i '/^ _CLAUDE_BIN=/d' "$rc"
57
+ # Standalone aliases from old format
58
+ sed -i "/^alias cc='/d" "$rc"
59
+ sed -i "/^alias claude='/d" "$rc"
60
+ sed -i "/^alias ccraw='/d" "$rc"
61
+ sed -i "/^alias ccw='/d" "$rc"
62
+ sed -i '/^alias check-setup=/d' "$rc"
63
+ # cc-tools function from old format
56
64
  if grep -q "^cc-tools()" "$rc" 2>/dev/null; then
57
65
  sed -i '/^cc-tools() {/,/^}/d' "$rc"
58
66
  fi
59
- if grep -q "alias check-setup=" "$rc" 2>/dev/null; then
60
- sed -i '/alias check-setup=/d' "$rc"
61
- fi
62
- # --- Add environment and aliases (idempotent) ---
63
- # Guard: skip if aliases already present from a previous run
64
- if grep -q '# Claude Code environment and aliases' "$rc" 2>/dev/null; then
65
- echo "[setup-aliases] Aliases already present in $(basename $rc), skipping"
66
- continue
67
- fi
68
- echo "" >>"$rc"
69
- echo "# Claude Code environment and aliases (managed by setup-aliases.sh)" >>"$rc"
70
- # Export CLAUDE_CONFIG_DIR so it's available in all shells (not just VS Code remoteEnv)
71
- if ! grep -q 'export CLAUDE_CONFIG_DIR=' "$rc" 2>/dev/null; then
72
- echo "export CLAUDE_CONFIG_DIR=\"${CLAUDE_CONFIG_DIR}\"" >>"$rc"
73
- fi
74
- # Export UTF-8 locale so tmux renders Unicode correctly (docker exec doesn't inherit locale)
75
- if ! grep -q 'export LANG=en_US.UTF-8' "$rc" 2>/dev/null; then
76
- echo 'export LANG=en_US.UTF-8' >>"$rc"
77
- echo 'export LC_ALL=en_US.UTF-8' >>"$rc"
78
- fi
79
- # Prefer native binary over npm-installed version
80
- # 'claude install' puts the binary at ~/.local/bin/claude
81
- # Legacy manual installs used /usr/local/bin/claude — check both
82
- cat >>"$rc" <<'CLAUDEBIN_EOF'
83
- if [ -x "$HOME/.local/bin/claude" ]; then
84
- _CLAUDE_BIN="$HOME/.local/bin/claude"
67
+
68
+ # --- 5. Write fresh managed block ---
69
+ cat >>"$rc" <<BLOCK_EOF
70
+
71
+ ${BLOCK_START}
72
+ export CLAUDE_CONFIG_DIR="${CLAUDE_CONFIG_DIR}"
73
+ export LANG=en_US.UTF-8
74
+ export LC_ALL=en_US.UTF-8
75
+
76
+ # Prefer native binary over npm-installed version
77
+ if [ -x "\$HOME/.local/bin/claude" ]; then
78
+ _CLAUDE_BIN="\$HOME/.local/bin/claude"
85
79
  elif [ -x /usr/local/bin/claude ]; then
86
80
  _CLAUDE_BIN=/usr/local/bin/claude
87
81
  else
88
82
  _CLAUDE_BIN=claude
89
83
  fi
90
- CLAUDEBIN_EOF
91
- echo "$ALIAS_CC" >>"$rc"
92
- echo "$ALIAS_CLAUDE" >>"$rc"
93
- echo "$ALIAS_CCRAW" >>"$rc"
94
84
 
95
- # cc-tools: list all available CodeForge tools with version info
96
- cat >>"$rc" <<'CCTOOLS_EOF'
85
+ alias cc='CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 command "\$_CLAUDE_BIN" --system-prompt-file "\$CLAUDE_CONFIG_DIR/main-system-prompt.md" --permission-mode plan --allow-dangerously-skip-permissions'
86
+ alias claude='CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 command "\$_CLAUDE_BIN" --system-prompt-file "\$CLAUDE_CONFIG_DIR/main-system-prompt.md" --permission-mode plan --allow-dangerously-skip-permissions'
87
+ alias ccraw='command "\$_CLAUDE_BIN"'
88
+ alias ccw='CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 command "\$_CLAUDE_BIN" --system-prompt-file "\$CLAUDE_CONFIG_DIR/writing-system-prompt.md" --permission-mode plan --allow-dangerously-skip-permissions'
89
+
97
90
  cc-tools() {
98
91
  echo "CodeForge Available Tools"
99
92
  echo "━━━━━━━━━━━━━━━━━━━━━━━━"
100
93
  printf " %-20s %s\n" "COMMAND" "STATUS"
101
94
  echo " ────────────────────────────────────"
102
- for cmd in claude cc ccraw ccusage ccburn claude-monitor \
103
- ruff biome dprint shfmt shellcheck hadolint \
104
- ast-grep tree-sitter pyright typescript-language-server \
95
+ for cmd in claude cc ccw ccraw ccusage ccburn claude-monitor \\
96
+ ccms cargo ruff biome dprint shfmt shellcheck hadolint \\
97
+ ast-grep tree-sitter pyright typescript-language-server \\
105
98
  agent-browser gh docker git jq tmux bun go; do
106
- if command -v "$cmd" >/dev/null 2>&1; then
107
- ver=$("$cmd" --version 2>/dev/null | head -1 || echo "installed")
108
- printf " %-20s ✓ %s\n" "$cmd" "$ver"
99
+ if command -v "\$cmd" >/dev/null 2>&1; then
100
+ ver=\$("\$cmd" --version 2>/dev/null | head -1 || echo "installed")
101
+ printf " %-20s ✓ %s\n" "\$cmd" "\$ver"
109
102
  else
110
- printf " %-20s ✗ not found\n" "$cmd"
103
+ printf " %-20s ✗ not found\n" "\$cmd"
111
104
  fi
112
105
  done
113
106
  }
114
- CCTOOLS_EOF
115
107
 
116
- # check-setup: alias to the health check script
117
- DEVCONTAINER_SCRIPTS="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
118
- echo "alias check-setup='bash ${DEVCONTAINER_SCRIPTS}/check-setup.sh'" >>"$rc"
108
+ alias check-setup='bash ${DEVCONTAINER_SCRIPTS}/check-setup.sh'
109
+ ${BLOCK_END}
110
+ BLOCK_EOF
119
111
 
120
- echo "[setup-aliases] Added aliases to $(basename $rc)"
112
+ echo "[setup-aliases] Added aliases to $(basename "$rc")"
121
113
  fi
122
114
  done
123
115
 
124
116
  echo "[setup-aliases] Aliases configured:"
125
- echo " cc -> claude with \$CLAUDE_CONFIG_DIR/system-prompt.md"
126
- echo " claude -> claude with \$CLAUDE_CONFIG_DIR/system-prompt.md"
117
+ echo " cc -> claude with \$CLAUDE_CONFIG_DIR/main-system-prompt.md"
118
+ echo " claude -> claude with \$CLAUDE_CONFIG_DIR/main-system-prompt.md"
127
119
  echo " ccraw -> vanilla claude without any config"
120
+ echo " ccw -> claude with \$CLAUDE_CONFIG_DIR/writing-system-prompt.md"
128
121
  echo " cc-tools -> list all available CodeForge tools"
129
122
  echo " check-setup -> verify CodeForge setup health"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeforge-dev",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
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": {
@@ -1,33 +0,0 @@
1
- # Roadmap
2
-
3
- > Features live in the priority-graded backlog until pulled into a version.
4
- > Versions are scoped and spec'd when ready to build — not pre-assigned.
5
- > See `BACKLOG.md` for the feature backlog.
6
-
7
- ## How Versioning Works
8
-
9
- 1. **Backlog** — All desired features live in `BACKLOG.md`, graded by priority.
10
- 2. **Version scoping** — When ready to start a new version, pull features from the backlog.
11
- 3. **Spec first** — Each feature in a version gets a spec before implementation begins.
12
- 4. **Ship** — Version is done when all its specs are implemented and verified.
13
-
14
- Only the **next version** is defined in detail. Everything else is backlog.
15
-
16
- ## Released
17
-
18
- _None yet._
19
-
20
- ## Current
21
-
22
- ### v0.1.0 — [Name] 🔧
23
-
24
- - [ ] [Feature pulled from backlog]
25
- - [ ] [Feature pulled from backlog]
26
-
27
- ## Next
28
-
29
- > Scoped from `BACKLOG.md` when current version is complete.
30
-
31
- ## Out of Scope
32
-
33
- - [Items explicitly not planned]