codeforge-dev 1.10.0 → 1.12.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.
- package/.devcontainer/.env +7 -1
- package/.devcontainer/.gitignore +1 -0
- package/.devcontainer/CHANGELOG.md +138 -0
- package/.devcontainer/CLAUDE.md +87 -8
- package/.devcontainer/README.md +55 -18
- package/.devcontainer/config/defaults/main-system-prompt.md +132 -152
- package/.devcontainer/config/defaults/rules/session-search.md +66 -0
- package/.devcontainer/config/defaults/rules/spec-workflow.md +39 -12
- package/.devcontainer/config/defaults/settings.json +2 -1
- package/.devcontainer/config/defaults/writing-system-prompt.md +185 -0
- package/.devcontainer/config/file-manifest.json +12 -0
- package/.devcontainer/connect-external-terminal.ps1 +1 -1
- package/.devcontainer/devcontainer.json +40 -10
- package/.devcontainer/docs/configuration-reference.md +3 -0
- package/.devcontainer/docs/plugins.md +9 -2
- package/.devcontainer/docs/troubleshooting.md +2 -2
- package/.devcontainer/features/README.md +8 -9
- package/.devcontainer/features/agent-browser/devcontainer-feature.json +21 -21
- package/.devcontainer/features/agent-browser/install.sh +0 -7
- package/.devcontainer/features/ast-grep/devcontainer-feature.json +22 -22
- package/.devcontainer/features/biome/devcontainer-feature.json +12 -14
- package/.devcontainer/features/ccms/README.md +50 -0
- package/.devcontainer/features/ccms/devcontainer-feature.json +21 -0
- package/.devcontainer/features/ccms/install.sh +122 -0
- package/.devcontainer/features/ccstatusline/install.sh +24 -2
- package/.devcontainer/features/lsp-servers/devcontainer-feature.json +43 -43
- package/.devcontainer/features/mcp-qdrant/poststart-hook.sh +2 -1
- package/.devcontainer/features/ruff/devcontainer-feature.json +17 -19
- package/.devcontainer/features/tmux/install.sh +2 -2
- package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +8 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/README.md +81 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/README.md +92 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/README.md +250 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/architect.md +1 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/claude-guide.md +2 -2
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/debug-logs.md +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/dependency-analyst.md +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/doc-writer.md +4 -4
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/explorer.md +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/generalist.md +2 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/git-archaeologist.md +2 -2
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/researcher.md +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/security-auditor.md +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/spec-writer.md +8 -8
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/hooks/hooks.json +10 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/skill-suggester.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/git-state-injector.py +15 -4
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/inject-cwd.py +37 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/skill-suggester.py +24 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/spec-reminder.py +3 -2
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-build/SKILL.md +353 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-build/references/review-checklist.md +175 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-check/SKILL.md +15 -14
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/SKILL.md +12 -11
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/references/backlog-template.md +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/references/milestones-template.md +32 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-new/SKILL.md +17 -18
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-new/references/template.md +12 -2
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-review/SKILL.md +229 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-update/SKILL.md +6 -2
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/SKILL.md +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/.claude-plugin/plugin.json +38 -5
- package/.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/README.md +41 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/README.md +72 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py +73 -47
- package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/README.md +42 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/README.md +86 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/hooks/hooks.json +25 -15
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py +122 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +3 -3
- package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/README.md +96 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md +94 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/hooks/hooks.json +17 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/__pycache__/guard-workspace-scope.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py +132 -0
- package/.devcontainer/scripts/check-setup.sh +1 -1
- package/.devcontainer/scripts/setup-aliases.sh +68 -75
- package/.devcontainer/scripts/setup-projects.sh +23 -16
- package/.devcontainer/scripts/setup.sh +48 -5
- package/README.md +17 -8
- package/package.json +1 -2
- package/.devcontainer/features/mcp-reasoner/README.md +0 -177
- package/.devcontainer/features/mcp-reasoner/devcontainer-feature.json +0 -25
- package/.devcontainer/features/mcp-reasoner/install.sh +0 -184
- package/.devcontainer/features/mcp-reasoner/poststart-hook.sh +0 -67
- package/.devcontainer/features/splitrail/README.md +0 -140
- package/.devcontainer/features/splitrail/devcontainer-feature.json +0 -39
- package/.devcontainer/features/splitrail/install.sh +0 -136
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/references/roadmap-template.md +0 -33
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Enforce workspace scope for file operations",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"PreToolUse": [
|
|
5
|
+
{
|
|
6
|
+
"matcher": "Read|Write|Edit|NotebookEdit|Glob|Grep",
|
|
7
|
+
"hooks": [
|
|
8
|
+
{
|
|
9
|
+
"type": "command",
|
|
10
|
+
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/guard-workspace-scope.py",
|
|
11
|
+
"timeout": 5
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
Binary file
|
|
@@ -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/vscode/",
|
|
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()
|
|
@@ -34,7 +34,7 @@ warn_check() {
|
|
|
34
34
|
echo ""
|
|
35
35
|
echo "Core:"
|
|
36
36
|
check "Claude Code installed" "command -v claude"
|
|
37
|
-
warn_check "Claude native binary" "[ -x /usr/local/bin/claude ]"
|
|
37
|
+
warn_check "Claude native binary" "[ -x ~/.local/bin/claude ] || [ -x /usr/local/bin/claude ]"
|
|
38
38
|
check "cc alias configured" "grep -q 'alias cc=' ~/.bashrc 2>/dev/null || grep -q 'alias cc=' ~/.zshrc 2>/dev/null"
|
|
39
39
|
check "Config directory exists" "[ -d '${CLAUDE_CONFIG_DIR:-/workspaces/.claude}' ]"
|
|
40
40
|
check "Settings file exists" "[ -f '${CLAUDE_CONFIG_DIR:-/workspaces/.claude}/settings.json' ]"
|
|
@@ -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
|
-
#
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
# ---
|
|
24
|
+
# --- 2. Remove existing managed block (if present) ---
|
|
25
|
+
sed -i '/# === CodeForge Claude aliases START/,/# === CodeForge Claude aliases END/d' "$rc"
|
|
22
26
|
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
|
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
|
|
38
|
+
echo "[setup-aliases] Removed legacy _claude_with_config() function from $(basename "$rc")"
|
|
37
39
|
fi
|
|
38
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
96
|
-
|
|
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 "
|
|
107
|
-
ver
|
|
108
|
-
printf " %-20s ✓ %s\n" "
|
|
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" "
|
|
103
|
+
printf " %-20s ✗ not found\n" "\$cmd"
|
|
111
104
|
fi
|
|
112
105
|
done
|
|
113
106
|
}
|
|
114
|
-
CCTOOLS_EOF
|
|
115
107
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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"
|
|
@@ -29,7 +29,7 @@ is_excluded() {
|
|
|
29
29
|
|
|
30
30
|
has_project_markers() {
|
|
31
31
|
local dir="$1"
|
|
32
|
-
[ -d "$dir/.git" ] || [ -f "$dir/package.json" ] || [ -f "$dir/pyproject.toml" ] ||
|
|
32
|
+
[ -d "$dir/.git" ] || [ -f "$dir/.git" ] || [ -f "$dir/package.json" ] || [ -f "$dir/pyproject.toml" ] ||
|
|
33
33
|
[ -f "$dir/Cargo.toml" ] || [ -f "$dir/go.mod" ] || [ -f "$dir/deno.json" ] ||
|
|
34
34
|
[ -f "$dir/Makefile" ] || [ -f "$dir/CLAUDE.md" ]
|
|
35
35
|
}
|
|
@@ -38,7 +38,11 @@ detect_tags() {
|
|
|
38
38
|
local dir="$1"
|
|
39
39
|
local tags=()
|
|
40
40
|
|
|
41
|
-
[ -
|
|
41
|
+
if [ -f "$dir/.git" ] && grep -q "gitdir:" "$dir/.git" 2>/dev/null; then
|
|
42
|
+
tags+=("git" "worktree")
|
|
43
|
+
elif [ -d "$dir/.git" ]; then
|
|
44
|
+
tags+=("git")
|
|
45
|
+
fi
|
|
42
46
|
[ -f "$dir/package.json" ] && tags+=("node")
|
|
43
47
|
[ -f "$dir/pyproject.toml" ] && tags+=("python")
|
|
44
48
|
[ -f "$dir/Cargo.toml" ] && tags+=("rust")
|
|
@@ -95,6 +99,19 @@ scan_and_update() {
|
|
|
95
99
|
is_excluded "$subname" && continue
|
|
96
100
|
new_projects=$(register_project "$new_projects" "$subname" "$subdir")
|
|
97
101
|
done
|
|
102
|
+
|
|
103
|
+
# Depth 3: .worktrees/ is hidden (not matched by */) — scan explicitly
|
|
104
|
+
local wtcontainer="${dir%/}/.worktrees"
|
|
105
|
+
if [ -d "$wtcontainer" ]; then
|
|
106
|
+
for wtdir in "${wtcontainer%/}"/*/; do
|
|
107
|
+
[ -d "$wtdir" ] || continue
|
|
108
|
+
local wtname
|
|
109
|
+
wtname=$(basename "$wtdir")
|
|
110
|
+
if has_project_markers "$wtdir"; then
|
|
111
|
+
new_projects=$(register_project "$new_projects" "$wtname" "$wtdir")
|
|
112
|
+
fi
|
|
113
|
+
done
|
|
114
|
+
fi
|
|
98
115
|
fi
|
|
99
116
|
done
|
|
100
117
|
|
|
@@ -158,20 +175,10 @@ start_watcher() {
|
|
|
158
175
|
stop_watcher
|
|
159
176
|
fi
|
|
160
177
|
|
|
161
|
-
# Check if inotifywait is available
|
|
178
|
+
# Check if inotifywait is available (installed by tmux feature at build time)
|
|
162
179
|
if ! command -v inotifywait &>/dev/null; then
|
|
163
|
-
echo "$LOG_PREFIX
|
|
164
|
-
|
|
165
|
-
sudo apt-get update -qq && sudo apt-get install -y -qq inotify-tools >/dev/null 2>&1
|
|
166
|
-
else
|
|
167
|
-
apt-get update -qq && apt-get install -y -qq inotify-tools >/dev/null 2>&1
|
|
168
|
-
fi
|
|
169
|
-
|
|
170
|
-
if ! command -v inotifywait &>/dev/null; then
|
|
171
|
-
echo "$LOG_PREFIX WARNING: Could not install inotify-tools, watcher disabled"
|
|
172
|
-
return 1
|
|
173
|
-
fi
|
|
174
|
-
echo "$LOG_PREFIX inotify-tools installed"
|
|
180
|
+
echo "$LOG_PREFIX WARNING: inotify-tools not installed, watcher disabled"
|
|
181
|
+
return 1
|
|
175
182
|
fi
|
|
176
183
|
|
|
177
184
|
# Fork background watcher in its own process group for clean shutdown
|
|
@@ -181,7 +188,7 @@ start_watcher() {
|
|
|
181
188
|
# -r watches subdirectories (catches events inside container dirs like projects/)
|
|
182
189
|
# --exclude filters noisy dirs that generate frequent irrelevant events
|
|
183
190
|
inotifywait -m -r -q -e create,delete,moved_to,moved_from \
|
|
184
|
-
--exclude '(node_modules|\.git
|
|
191
|
+
--exclude '(node_modules|\.git|\.tmp|__pycache__|\.venv)' \
|
|
185
192
|
--format '%w%f %e' "$WORKSPACE_ROOT" 2>/dev/null |
|
|
186
193
|
while read -r _path event; do
|
|
187
194
|
# Small delay to let filesystem settle (e.g., move operations)
|
|
@@ -22,8 +22,9 @@ fi
|
|
|
22
22
|
: "${SETUP_UPDATE_CLAUDE:=true}"
|
|
23
23
|
: "${SETUP_PROJECTS:=true}"
|
|
24
24
|
: "${SETUP_TERMINAL:=true}"
|
|
25
|
+
: "${SETUP_POSTSTART:=true}"
|
|
25
26
|
|
|
26
|
-
export CLAUDE_CONFIG_DIR CONFIG_SOURCE_DIR SETUP_CONFIG SETUP_ALIASES SETUP_AUTH SETUP_PLUGINS SETUP_UPDATE_CLAUDE SETUP_PROJECTS SETUP_TERMINAL
|
|
27
|
+
export CLAUDE_CONFIG_DIR CONFIG_SOURCE_DIR SETUP_CONFIG SETUP_ALIASES SETUP_AUTH SETUP_PLUGINS SETUP_UPDATE_CLAUDE SETUP_PROJECTS SETUP_TERMINAL SETUP_POSTSTART
|
|
27
28
|
|
|
28
29
|
SETUP_START=$(date +%s)
|
|
29
30
|
SETUP_RESULTS=()
|
|
@@ -42,12 +43,16 @@ run_script() {
|
|
|
42
43
|
if [ "$enabled" = "true" ]; then
|
|
43
44
|
if [ -f "$script" ]; then
|
|
44
45
|
printf " %-30s" "$name..."
|
|
45
|
-
|
|
46
|
+
local output
|
|
47
|
+
if output=$(bash "$script" 2>&1); then
|
|
46
48
|
echo "done"
|
|
47
49
|
SETUP_RESULTS+=("$name:ok")
|
|
48
50
|
else
|
|
49
|
-
|
|
51
|
+
local exit_code=$?
|
|
52
|
+
echo "FAILED (exit $exit_code)"
|
|
50
53
|
SETUP_RESULTS+=("$name:failed")
|
|
54
|
+
# Show output on failure for diagnostics
|
|
55
|
+
echo "$output" | sed 's/^/ /'
|
|
51
56
|
fi
|
|
52
57
|
else
|
|
53
58
|
echo " $name... not found, skipping"
|
|
@@ -59,6 +64,30 @@ run_script() {
|
|
|
59
64
|
fi
|
|
60
65
|
}
|
|
61
66
|
|
|
67
|
+
run_poststart_hooks() {
|
|
68
|
+
local hook_dir="/usr/local/devcontainer-poststart.d"
|
|
69
|
+
if [ ! -d "$hook_dir" ]; then
|
|
70
|
+
return 0
|
|
71
|
+
fi
|
|
72
|
+
local count=0
|
|
73
|
+
for hook in "$hook_dir"/*.sh; do
|
|
74
|
+
[ -f "$hook" ] || continue
|
|
75
|
+
[ -x "$hook" ] || continue
|
|
76
|
+
local name
|
|
77
|
+
name="$(basename "$hook")"
|
|
78
|
+
printf " %-30s" "$name..."
|
|
79
|
+
if bash "$hook" 2>&1; then
|
|
80
|
+
echo "done"
|
|
81
|
+
count=$((count + 1))
|
|
82
|
+
else
|
|
83
|
+
echo "FAILED (exit $?)"
|
|
84
|
+
fi
|
|
85
|
+
done
|
|
86
|
+
if [ $count -gt 0 ]; then
|
|
87
|
+
SETUP_RESULTS+=("poststart-hooks:ok ($count)")
|
|
88
|
+
fi
|
|
89
|
+
}
|
|
90
|
+
|
|
62
91
|
run_script "$SCRIPT_DIR/setup-symlink-claude.sh" "true"
|
|
63
92
|
run_script "$SCRIPT_DIR/setup-auth.sh" "$SETUP_AUTH"
|
|
64
93
|
run_script "$SCRIPT_DIR/setup-config.sh" "$SETUP_CONFIG"
|
|
@@ -66,7 +95,20 @@ run_script "$SCRIPT_DIR/setup-aliases.sh" "$SETUP_ALIASES"
|
|
|
66
95
|
run_script "$SCRIPT_DIR/setup-plugins.sh" "$SETUP_PLUGINS"
|
|
67
96
|
run_script "$SCRIPT_DIR/setup-projects.sh" "$SETUP_PROJECTS"
|
|
68
97
|
run_script "$SCRIPT_DIR/setup-terminal.sh" "$SETUP_TERMINAL"
|
|
69
|
-
|
|
98
|
+
|
|
99
|
+
# Background the update to avoid blocking container start
|
|
100
|
+
if [ "$SETUP_UPDATE_CLAUDE" = "true" ] && [ -f "$SCRIPT_DIR/setup-update-claude.sh" ]; then
|
|
101
|
+
bash "$SCRIPT_DIR/setup-update-claude.sh" &>/dev/null &
|
|
102
|
+
disown
|
|
103
|
+
SETUP_RESULTS+=("setup-update-claude:background")
|
|
104
|
+
else
|
|
105
|
+
SETUP_RESULTS+=("setup-update-claude:disabled")
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Run post-start hooks
|
|
109
|
+
if [ "$SETUP_POSTSTART" = "true" ]; then
|
|
110
|
+
run_poststart_hooks
|
|
111
|
+
fi
|
|
70
112
|
|
|
71
113
|
echo ""
|
|
72
114
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
@@ -77,10 +119,11 @@ for result in "${SETUP_RESULTS[@]}"; do
|
|
|
77
119
|
name="${result%%:*}"
|
|
78
120
|
status="${result##*:}"
|
|
79
121
|
case "$status" in
|
|
80
|
-
ok)
|
|
122
|
+
ok*) printf " ✓ %s\n" "$name" ;;
|
|
81
123
|
failed) printf " ✗ %s (FAILED)\n" "$name"; FAILURES=$((FAILURES + 1)) ;;
|
|
82
124
|
disabled) printf " - %s (disabled)\n" "$name" ;;
|
|
83
125
|
missing) printf " ? %s (not found)\n" "$name" ;;
|
|
126
|
+
background) printf " ⇢ %s (background)\n" "$name" ;;
|
|
84
127
|
esac
|
|
85
128
|
done
|
|
86
129
|
ELAPSED=$(( $(date +%s) - SETUP_START ))
|
package/README.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
4
4
|
[](https://www.npmjs.com/package/codeforge-dev)
|
|
5
|
+
[](.devcontainer/CHANGELOG.md)
|
|
6
|
+
[](https://github.com/AnExiledDev/CodeForge/commits)
|
|
7
|
+
[](https://www.npmjs.com/package/codeforge-dev)
|
|
8
|
+
[](https://nodejs.org/)
|
|
9
|
+
[](https://github.com/AnExiledDev/CodeForge/issues)
|
|
5
10
|
|
|
6
11
|
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.
|
|
7
12
|
|
|
@@ -43,15 +48,15 @@ npx codeforge-dev@1.2.3
|
|
|
43
48
|
|
|
44
49
|
### Languages & Runtimes
|
|
45
50
|
|
|
46
|
-
Python 3.14, Node.js LTS, TypeScript, Go
|
|
51
|
+
Python 3.14, Node.js LTS, TypeScript, Rust, Bun, Go (optional)
|
|
47
52
|
|
|
48
53
|
### Package Managers
|
|
49
54
|
|
|
50
|
-
`uv`, `npm`, `pip` / `pipx`
|
|
55
|
+
`uv`, `npm`, `bun`, `pip` / `pipx`
|
|
51
56
|
|
|
52
57
|
### Development Tools
|
|
53
58
|
|
|
54
|
-
`gh` (GitHub CLI), `docker`, `git`, `jq`, `curl`, `tmux`, `biome`, `agent-browser`
|
|
59
|
+
`gh` (GitHub CLI), `docker`, `git`, `jq`, `curl`, `tmux`, `biome`, `ruff`, `ccms`, `agent-browser`
|
|
55
60
|
|
|
56
61
|
### Code Intelligence
|
|
57
62
|
|
|
@@ -59,15 +64,15 @@ tree-sitter (JS/TS/Python), ast-grep, Pyright, TypeScript LSP
|
|
|
59
64
|
|
|
60
65
|
### Claude Code Tools
|
|
61
66
|
|
|
62
|
-
`claude`, `cc` (wrapper), `ccusage`, `ccburn`, `ccstatusline`, `claude-monitor`
|
|
67
|
+
`claude`, `cc` (wrapper), `ccw` (writing mode wrapper), `ccusage`, `ccburn`, `ccstatusline`, `claude-monitor`
|
|
63
68
|
|
|
64
|
-
### Custom Features (
|
|
69
|
+
### Custom Features (18)
|
|
65
70
|
|
|
66
|
-
tmux, agent-browser, claude-monitor, ccusage, ccburn, ccstatusline, ast-grep, tree-sitter, lsp-servers, biome, notify-hook, mcp-qdrant
|
|
71
|
+
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
|
|
67
72
|
|
|
68
|
-
### Agents (17) & Skills (
|
|
73
|
+
### Agents (17) & Skills (28)
|
|
69
74
|
|
|
70
|
-
The `code-directive` plugin includes 17 specialized agents (architect, explorer, test-writer, security-auditor, etc.) and
|
|
75
|
+
The `code-directive` plugin includes 17 specialized agents (architect, explorer, test-writer, security-auditor, etc.) and 28 domain-specific coding reference skills (fastapi, svelte5, docker, testing, spec-workflow, security-checklist, etc.).
|
|
71
76
|
|
|
72
77
|
## Quick Start
|
|
73
78
|
|
|
@@ -95,6 +100,10 @@ npm test
|
|
|
95
100
|
npm publish
|
|
96
101
|
```
|
|
97
102
|
|
|
103
|
+
## Changelog
|
|
104
|
+
|
|
105
|
+
See [CHANGELOG.md](.devcontainer/CHANGELOG.md) for release history. Current version: **1.12.0** (2026-02-18).
|
|
106
|
+
|
|
98
107
|
## Further Reading
|
|
99
108
|
|
|
100
109
|
- [Full Usage Guide](.devcontainer/README.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeforge-dev",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.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": {
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
"vscode",
|
|
21
21
|
"container",
|
|
22
22
|
"qdrant",
|
|
23
|
-
"reasoner",
|
|
24
23
|
"ai-development"
|
|
25
24
|
],
|
|
26
25
|
"author": "AnExiledDev",
|