v1.14.0: Nuclear scope enforcement, plugin overhaul, config externalization#2
v1.14.0: Nuclear scope enforcement, plugin overhaul, config externalization#2AnExiledDev merged 2 commits intomainfrom
Conversation
…fig externalization Major security hardening, plugin architecture changes, and developer experience improvements: - Nuclear workspace scope enforcement: blacklist system, two-layer bash detection, fail-closed error handling, CWD context injection - Agent system: quality gate hooks (TaskCompleted, TeammateIdle), debug skill, permissionMode on all agents - Skill engine: 6 new matchers, team skill v0.2.0, all skills rewritten with USE WHEN guidance - Ticket workflow: migrated from slash commands to skill-based approach - Hook output schema: all hooks migrated to hookSpecificOutput wrapper - ccstatusline config externalized to .devcontainer/config/defaults/ via file-manifest deployment - Remote install docs added to all 11 plugin READMEs and features README - Astro/Starlight documentation site, GitHub Actions, logos - Development rules added to CLAUDE.md (changelog + docs update requirements) - Removed auto-formatter and auto-linter plugins (consolidated into auto-code-quality)
|
Important Review skippedToo many files! This PR contains 184 files, which is 34 over the limit of 150. ⛔ Files ignored due to path filters (10)
📒 Files selected for processing (184)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 11
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (21)
.devcontainer/features/ccms/install.sh (1)
56-64:⚠️ Potential issue | 🔴 CriticalAvoid predictable
/tmpcache paths to prevent binary injection.
/tmpis world-writable; with a fixed path, an attacker can pre-place${CACHE_DIR}/ccms, and this script (typically running as root) will copy it into/usr/local/bin. Use a uniquemktemp -d(and clean it up) or enforce strict ownership/permissions checks before trusting the cache.🔒 Safer per-run cache directory + cleanup
- CACHE_DIR="${TMPDIR:-/tmp}/ccms-build-cache" + CACHE_DIR="$(mktemp -d "${TMPDIR:-/tmp}/ccms-build-cache.XXXXXX")"cleanup() { - : + if [ -n "${CACHE_DIR:-}" ] && [ -d "${CACHE_DIR}" ]; then + rm -rf "${CACHE_DIR}" + fi }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/features/ccms/install.sh around lines 56 - 64, The current CACHE_DIR value (CACHE_DIR="${TMPDIR:-/tmp}/ccms") is predictable and allows TOCTOU/binary injection; change the install logic in .devcontainer/features/ccms/install.sh so you either (a) create a per-run secure temp dir with mktemp -d and use that instead of CACHE_DIR, cleaning it up after use, or (b) if reusing a cache path, validate ownership and mode before trusting the file (check that "${CACHE_DIR}/ccms" is owned by root or expected user and is not writable by others and is a regular file) before copying to /usr/local/bin/ccms; update the code paths that reference CACHE_DIR and the conditional that tests -x "${CACHE_DIR}/ccms" accordingly..devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/SKILL.md (1)
68-68:⚠️ Potential issue | 🟡 MinorOPTIONS success code should be
200, not204RFC 7231 §4.3.7 defines a successful OPTIONS response as
200 OK, with the response body orAllowheader communicating server capabilities.204implies no content, which conflicts with the spec-defined purpose of OPTIONS (returning capability information).🛠️ Proposed fix
-| **OPTIONS** | Describe capabilities | Yes | No | 204 | +| **OPTIONS** | Describe capabilities | Yes | No | 200 |🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/SKILL.md at line 68, Update the OPTIONS success status in the SKILL.md table row that starts with "**OPTIONS**" and description "Describe capabilities" from 204 to 200; edit the table cell containing "204" to "200" so the documented successful OPTIONS response matches RFC 7231 §4.3.7..devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/SKILL.md (1)
89-94:⚠️ Potential issue | 🟡 Minor"Never fix forward" is too absolute for database/data migrations.
This rule is correct for code changes but conflicts with real-world database migrations where the down-migration may be destructive (e.g., a
DROP COLUMNrollback would lose data). The Rollback Strategy section (Line 102) already acknowledges down-migrations, but it doesn't resolve the tension — an agent following both sections will receive contradictory guidance in exactly the scenarios where it matters most.Consider scoping the rule:
📝 Suggested wording
-4. Never fix forward by making changes intended for later steps. +4. Never fix forward by making changes intended for later steps. Exception: for + deployed database/data migrations where rollback would cause data loss, fix + forward with a corrective migration and document the deviation.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/SKILL.md around lines 89 - 94, Update the "Never fix forward" rule in SKILL.md so it is scoped for database/data migrations: change the absolute prohibition to a conditional guideline that allows forward-fixing only when a down-migration is destructive or unsafe (e.g., DROP COLUMN), and require explicit steps when doing so — document the decision, create backups/snapshots, obtain stakeholder/owner approval, run in a controlled maintenance window, and record the migration plan and post-checks; also add a cross-reference to the existing "Rollback Strategy" section to reconcile the guidance and make the expected decision flow deterministic for agents..devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/SKILL.md (1)
107-110:⚠️ Potential issue | 🟡 MinorMisleading comment:
git log --follow -pis notgit blameLine 109's comment
# Blame through renamesis inaccurate — the command isgit log, which produces commit/patch history, not per-line attribution.git blamehas no--followflag. Readers looking for blame-style output will be confused.📝 Proposed fix
-# Blame through renames -git log --follow -p -- path/to/file.py +# Show patch history of a file through renames +git log --follow -p -- path/to/file.py🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/SKILL.md around lines 107 - 110, The comment "# Blame through renames" is incorrect for the command shown; update the text around the command "git log --follow -p -- path/to/file.py" to say it shows commit/patch history through renames (not per-line blame), and remove or replace any instruction implying git blame supports --follow; if you want per-line attribution suggest using "git blame" separately or note that git blame does not have --follow and that rename/copy detection must be handled via heuristics (e.g., use "git blame -C -M" or use "git log --follow -p" for history).setup.js (1)
9-15:⚠️ Potential issue | 🟠 MajorAdd
config/defaults/ccstatusline-settings.jsontoDEFAULT_PRESERVE.The file is documented as user-customizable ("edit .devcontainer/config/defaults/ccstatusline-settings.json"), but currently lacks source-level protection during package updates. The file-manifest's
if-changedsemantics protect only the deployed destination (~/.config/ccstatusline/settings.json), not the source file in the package. During a--forceupdate, user edits to the source will be lost. Since other customizable files likesettings.json,main-system-prompt.md, andkeybindings.jsonare all protected in DEFAULT_PRESERVE,ccstatusline-settings.jsonshould be added alongside them.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@setup.js` around lines 9 - 15, DEFAULT_PRESERVE does not include "config/defaults/ccstatusline-settings.json", so package updates with --force can overwrite user-edited source defaults; update the DEFAULT_PRESERVE array constant to add "config/defaults/ccstatusline-settings.json" alongside the other preserved files (e.g., "config/defaults/settings.json", "config/defaults/main-system-prompt.md", "config/defaults/keybindings.json") so the source file is protected during updates..devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/SKILL.md (3)
365-376:⚠️ Potential issue | 🟡 Minor
forkSessionis absent from the Key Options Fields table.
forkSession: trueis introduced only in this example but is not listed in the options table at lines 93–110. Readers (and AI agents) treating that table as the canonical reference will have no knowledge of the option.📋 Suggested table addition
| `resume` | `string` | `undefined` | Session ID to resume | +| `forkSession` | `boolean` | `false` | Preserve context but diverge session history | | `settingSources` | `SettingSource[]` | `[]` | Which filesystem settings to load |🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/SKILL.md around lines 365 - 376, Add the missing forkSession option to the "Key Options Fields" table: document the new boolean option forkSession (default false) under the options object used by query, describe that forkSession: true creates a new session ID while inheriting conversation context from options.resume/sessionId (i.e., forks the conversation), and include expected type, default, and a short example reference to where it's used in the query(...) for clarity.
258-271:⚠️ Potential issue | 🟡 Minor"Read-Only Agent Pattern" should use
planmode, notbypassPermissions.The document's own permissions table (line 210) defines
'plan'as "Planning mode — read-only, no execution", which is the purpose-built, safe choice here. UsingbypassPermissions+allowDangerouslySkipPermissions: trueskips all permission checks and requires a dangerous opt-in flag — if theallowedToolslist ever drifts to include a write-capable tool, there is no safety net. Because AI agents consuming this skill will treat this example as the canonical read-only pattern, the weaker choice will propagate.🛡️ Proposed fix
-for await (const message of query({ - prompt: "Review this code for best practices", - options: { - allowedTools: ["Read", "Glob", "Grep"], - permissionMode: "bypassPermissions", - allowDangerouslySkipPermissions: true, - }, -})) { +for await (const message of query({ + prompt: "Review this code for best practices", + options: { + allowedTools: ["Read", "Glob", "Grep"], + permissionMode: "plan", + }, +})) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/SKILL.md around lines 258 - 271, The example using query(...) with options currently sets permissionMode to "bypassPermissions" and allowDangerouslySkipPermissions: true which is unsafe for a read-only pattern; update the options in the Read-Only Agent Pattern example to set permissionMode: "plan" (the read-only planning mode) and remove allowDangerouslySkipPermissions (and any bypass usage), keeping allowedTools: ["Read","Glob","Grep"] intact so the example enforces safe read-only behavior; locate the example by the query(...) call in the SKILL.md snippet.
439-451:⚠️ Potential issue | 🟡 MinorOverly narrow regex in
blockDestructivehook creates false security.
/rm\s+-rf\s+\//.test(cmd)requires a space +/immediately after the flags, blocking only absolute-path patterns likerm -rf /tmp/…. It silently passesrm -rf ./important-dir,rm -rf ~/data,rm -rf some-dir, and flag variants likerm -rfd …. ThecanUseToolexample earlier (line 234) correctly uses the broader/rm\s+-rf/.test(...). Readers copying the hook example for real protection will be left with a gap.🛡️ Proposed fix
- if (/rm\s+-rf\s+\//.test(cmd)) { + if (/rm\s+-rf/.test(cmd)) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/SKILL.md around lines 439 - 451, The current blockDestructive HookCallback uses a too-narrow regex (/rm\s+-rf\s+\//) to detect destructive rm invocations; update the detection in the blockDestructive hook (inspect the cmd variable inside blockDestructive) to use a broader pattern that matches rm followed by -rf (and flag permutations like -rfd) anywhere after the command (not requiring an immediate '/'), e.g., replace the regex check with one that looks for "rm" followed by whitespace and a flag token containing both r and f (or simply /rm\s+-?[^ ]*r[^ ]*f|rm\s+-?[^ ]*f[^ ]*r/ or the simpler /rm\s+-rf/ style used elsewhere), so it also catches forms like "rm -rf.", "rm -rfd", "rm -rf some-dir", and "rm -rf ~/path"..devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/SKILL.md (4)
288-300:⚠️ Potential issue | 🟡 MinorCloudflare D1 example references undeclared variables
now, anduserId.All three variables are used without being extracted from
requestor declared anywhere in scope. Since this SKILL.md drives AI code generation, incomplete examples risk propagating the same undefined-variable pattern into real codebases.📝 Suggested fix (illustrative extraction)
export default { async fetch(request, env) { + const url = new URL(request.url); + const email = url.searchParams.get("email"); + const { userId } = await request.json(); + const now = new Date().toISOString(); + const { results } = await env.DB.prepare( "SELECT * FROM users WHERE email = ?" ).bind(email).all();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/SKILL.md around lines 288 - 300, The example fetch handler uses undeclared variables (email, now, userId) causing runtime errors; update the fetch function to extract/compute these values before using them (e.g., parse email from request.body or request.query and compute now and userId from request or DB lookup) and then pass them into env.DB.prepare(...).bind(...).all() and into the batch statements so that email, now, and userId are defined in the scope of fetch when calling env.DB.prepare, env.DB.batch, and Response.json.
288-300:⚠️ Potential issue | 🟡 MinorD1 example references undeclared variables
now, anduserId.The Cloudflare D1 snippet uses three variables that are never extracted from
requestor declared anywhere, leaving the example non-functional as written. Since this SKILL.md drives AI code generation, incomplete examples risk propagating the same pattern.📝 Proposed fix (illustrative extraction)
export default { async fetch(request, env) { + const { searchParams } = new URL(request.url); + const email = searchParams.get("email"); + const { userId } = await request.json(); + const now = new Date().toISOString(); + const { results } = await env.DB.prepare( "SELECT * FROM users WHERE email = ?" ).bind(email).all(); // Batch multiple statements in one round-trip const batch = await env.DB.batch([ env.DB.prepare("INSERT INTO logs (action) VALUES (?)").bind("login"), env.DB.prepare("UPDATE users SET last_login = ? WHERE id = ?").bind(now, userId), ]); return Response.json(results); }, };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/SKILL.md around lines 288 - 300, The example in export default's async fetch uses undeclared variables email, now, and userId; fix by extracting/declaring them before use (e.g., parse request body or URL params to obtain email and userId, and compute now via Date.now() or new Date()), then pass those variables into env.DB.prepare(...).bind(...) and into the batch calls so env.DB.prepare, fetch, and batch references are valid and the example is functional.
28-28:⚠️ Potential issue | 🟡 MinorVersion baseline of 3.35 does not include
STRICTtables — that requires 3.37.
STRICTtable support was added in SQLite 3.37.0 (2021-11-27), two minor releases after 3.35. The body of the skill usesSTRICTon every schema example, so the stated minimum is understated by two releases and could cause confusion or silent failures for developers targeting 3.35/3.36.📝 Proposed fix
-Assume SQLite 3.35+ (2021) for all new code, which includes `RETURNING`, `INSERT ... ON CONFLICT`, and `STRICT` tables. +Assume SQLite 3.37+ (2021) for all new code, which includes `RETURNING`, `INSERT ... ON CONFLICT`, and `STRICT` tables.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/SKILL.md at line 28, The README incorrectly states the baseline as "SQLite 3.35+" while the examples use STRICT tables; update the version baseline to at least 3.37.0 (2021-11-27) in SKILL.md so the line "Assume SQLite 3.35+ ..." reads "Assume SQLite 3.37+ ..." (or explicitly call out that STRICT requires 3.37) and ensure any mentions of schema examples using the STRICT keyword are annotated to require 3.37+.
28-28:⚠️ Potential issue | 🟡 MinorIncorrect SQLite version baseline —
STRICTtables require 3.37, not 3.35.Line 28 claims the 3.35+ baseline "includes …
STRICTtables," butSTRICTtable support was introduced in SQLite 3.37.0 (2021-11-27), two releases after 3.35. Since the body of the skill relies heavily onSTRICT(every schema example uses it), the minimum version stated here understates the actual requirement.📝 Proposed fix
-Assume SQLite 3.35+ (2021) for all new code, which includes `RETURNING`, `INSERT ... ON CONFLICT`, and `STRICT` tables. +Assume SQLite 3.37+ (2021) for all new code, which includes `RETURNING`, `INSERT ... ON CONFLICT`, and `STRICT` tables.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/SKILL.md at line 28, Update the documented SQLite baseline version to accurately reflect the requirement for STRICT tables: change the sentence that reads "Assume SQLite 3.35+ (2021) for all new code, which includes `RETURNING`, `INSERT ... ON CONFLICT`, and `STRICT` tables." to state SQLite 3.37+ (2021-11-27) as the minimum, or otherwise call out that `STRICT` requires 3.37 while `RETURNING` and `INSERT ... ON CONFLICT` are available earlier; ensure the reference to `STRICT` in the SKILL.md content is consistent with the new minimum..devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py (1)
3-11:⚠️ Potential issue | 🟡 MinorUpdate docstring to match blocking output.
The description still mentionsadditionalContexteven though the hook now emits a block decision.Suggested fix
-On Stop, checks for uncommitted changes (staged + unstaged) and injects -an advisory reminder as additionalContext. Claude sees it and can -naturally ask the user if they want to commit. +On Stop, checks for uncommitted changes (staged + unstaged) and blocks +with a decision/reason message so Claude addresses them before finishing.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py around lines 3 - 11, Update the module docstring in commit-reminder.py to reflect that the stop hook emits a blocking JSON decision (including "decision" and "reason") instead of injecting "additionalContext"; mention that it reads hook input from stdin (JSON), writes a blocking decision JSON to stdout, and respects the stop_hook_active guard to avoid loops (referencing stop_hook_active). Ensure the docstring language around "On Stop" and the output format matches the current behavior (blocking decision/reason) and remove or replace references to "additionalContext"..devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/team/SKILL.md (1)
55-63:⚠️ Potential issue | 🟡 MinorImmediate Mode steps omit plan approval — agent will be surprised by it mid-execution.
The 7-step Immediate Mode checklist (lines 57–63) skips plan approval entirely. When
CLAUDE_CODE_PLAN_MODE_REQUIREDis true, an agent following these steps will encounter aplan_approval_requestwith no prior guidance in the section it's operating from. Section 5a is only discoverable in the Team Lifecycle section, which comes later in the document.Consider adding a note under step 4 (Spawn teammates) or step 6 (Coordinate) pointing to the Plan Approval sub-flow.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/team/SKILL.md around lines 55 - 63, The Immediate Mode checklist (steps using TeamCreate, TaskCreate, TaskUpdate, Task, TaskList, SendMessage, shutdown_request, TeamDelete) omits guidance about handling the plan_approval_request when CLAUDE_CODE_PLAN_MODE_REQUIRED is true; add a brief note under step 4 (Spawn teammates) or step 6 (Coordinate) that instructs agents to consult the Plan Approval sub-flow (referencing Section 5a / Team Lifecycle) before spawning or coordinating teammates and to await or trigger plan_approval_request handling as required by CLAUDE_CODE_PLAN_MODE_REQUIRED so the agent won’t be surprised mid-execution..devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/verify-tests-pass.py (1)
8-9:⚠️ Potential issue | 🟡 MinorModule docstring is stale after the stdout → stderr migration.
Line 8 still says
"Reads hook input from stdin (JSON). Returns JSON on stdout."but the script no longer writes anything to stdout — failures now go to stderr (plain text), and success produces no output at all.📝 Proposed fix
-Reads hook input from stdin (JSON). Returns JSON on stdout. -Non-blocking on detection failures: always exits 0 if no framework found. +Reads hook input from stdin (JSON). Signals failure via stderr (plain text). +Non-blocking on detection failures: always exits 0 if no framework found.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/verify-tests-pass.py around lines 8 - 9, Update the module docstring that currently reads "Reads hook input from stdin (JSON). Returns JSON on stdout." to reflect the current behavior: it reads JSON from stdin, produces no stdout on success, writes human-readable failure messages to stderr, and exits 0 when no framework is detected (non-blocking on detection failures); locate and edit the top-level module docstring string to replace the stale sentence with this accurate description..devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md (1)
82-95:⚠️ Potential issue | 🟠 MajorUpdate the hook lifecycle diagram and Plugin Structure section to include
advisory-test-runner.py.The
advisory-test-runner.pyscript exists in the plugin and is registered inhooks.jsonto run afterlint-file.pyon the Stop event, but both the hook lifecycle diagram (lines 82–95) and the Plugin Structure tree (lines 127–139) in the README are missing it.Hook lifecycle diagram should show
advisory-test-runner.pyas a third command in the Stop event afterlint-file.py.Plugin Structure should add
advisory-test-runner.pyto thescripts/list.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md around lines 82 - 95, Update the README's hook lifecycle diagram to include advisory-test-runner.py as the third command under the Stop event after lint-file.py (so the Stop event shows format-on-stop.py, lint-file.py, advisory-test-runner.py), and update the Plugin Structure tree to add advisory-test-runner.py to the scripts/ list; verify this matches the hooks.json registration for advisory-test-runner.py so the diagram and structure reflect the actual hook order and presence..devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py (1)
202-333:⚠️ Potential issue | 🔴 CriticalPrevent
~/ env-var path expansion from bypassing scope checks.Write targets and tool paths are resolved with
realpath()directly. In Bash,~/...and$HOME/...expand before execution, but your guard treats them as literals and may allow out-of-scope writes. This is a bypass of the scope guard.Proposed fix
+def resolve_path(raw_path: str, cwd: str) -> str: + """Expand ~ and env vars, then resolve against cwd.""" + expanded = os.path.expandvars(os.path.expanduser(raw_path)) + if not os.path.isabs(expanded): + expanded = os.path.join(cwd, expanded) + return os.path.realpath(expanded)- resolved = os.path.realpath(target.strip("'\"")) + resolved = resolve_path(target.strip("'\""), cwd)- resolved = os.path.realpath(path_str) + resolved = resolve_path(path_str, cwd)- resolved = os.path.realpath(target_path) + resolved = resolve_path(target_path, cwd)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py around lines 202 - 333, The guard currently calls os.path.realpath() directly and thus misses shell-style expansions (~/ and $VAR), allowing bypass; update check_bash_scope and main path handling to first run os.path.expanduser() and os.path.expandvars() on every path string before calling os.path.realpath(): apply this to entries in write_targets and workspace_paths in check_bash_scope (the resolved and resolved_targets calculations) and to target_path resolution in main (the resolved = ... assignment after get_target_path), ensuring all places that call realpath use expanded strings so ~ and env vars are correctly resolved prior to blacklist/scope/allowlist checks..devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py (1)
449-480:⚠️ Potential issue | 🟠 MajorHook event name should reflect the actual trigger.
If this script is still invoked for
SubagentStart, always emittinghookEventName: "UserPromptSubmit"can cause the hook output to be ignored or misrouted. Set the event name based on the input shape.Proposed fix
- output = { + event_name = "SubagentStart" if "subagent_type" in data else "UserPromptSubmit" + output = { "hookSpecificOutput": { - "hookEventName": "UserPromptSubmit", + "hookEventName": event_name, "additionalContext": ( f"MANDATORY — Skill activation required. The user's prompt matches: {skill_list}. " f"Before responding, evaluate each matched skill: is it relevant to this specific request? " f"For each relevant skill, activate it using the Skill tool NOW. " f"Skip any that are not relevant to the user's actual intent. " f"Do not proceed with implementation until relevant skills are loaded." ), } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py around lines 449 - 480, The code always sets hookEventName to "UserPromptSubmit" in main() which mislabels events when the script is invoked for other triggers (e.g., SubagentStart); change main() to derive the event name from the incoming payload (inspect keys such as "event", "hookEventName", "trigger", or a "type"/"name" field on the parsed data) and use that value for output["hookSpecificOutput"]["hookEventName"], falling back to "UserPromptSubmit" only if no recognizable event field exists; update the creation of output (the hookEventName assignment) so the emitted hook matches the actual input trigger..devcontainer/features/ccstatusline/install.sh (1)
205-223:⚠️ Potential issue | 🟡 MinorMake the jq pipeline failure path actually reachable under
set -euo pipefail.As written, a pipeline failure exits before the
$?check, so cleanup and the explicit error message may not run. Wrap the pipeline in anifblock to handle failure cleanly.Proposed fix
-jq '.statusLine //= {}' "${SETTINGS_FILE}" | \ -jq '.statusLine = { - type: "command", - command: "/usr/local/bin/ccstatusline-wrapper" -}' > "${SETTINGS_FILE}.tmp" - -# Atomic move (only if jq succeeded) -if [ $? -eq 0 ]; then +if jq '.statusLine //= {}' "${SETTINGS_FILE}" | \ + jq '.statusLine = { + type: "command", + command: "/usr/local/bin/ccstatusline-wrapper" + }' > "${SETTINGS_FILE}.tmp"; then mv "${SETTINGS_FILE}.tmp" "${SETTINGS_FILE}" if ! chown "${USERNAME}:${USERNAME}" "${SETTINGS_FILE}" 2>/dev/null; then echo "[ccstatusline] WARNING: Could not set ownership on ${SETTINGS_FILE}" fi echo "[ccstatusline] ✓ Configured in ${SETTINGS_FILE}" echo "[ccstatusline] Verify: cat ${SETTINGS_FILE} | jq '.statusLine'" else rm -f "${SETTINGS_FILE}.tmp" echo "[ccstatusline] ERROR: Configuration failed" exit 1 fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/features/ccstatusline/install.sh around lines 205 - 223, The jq pipeline that writes to "${SETTINGS_FILE}.tmp" can fail under set -euo pipefail before the subsequent $? check; change it to an explicit conditional: run the pipeline inside an if ... then ... else ... fi so success proceeds to mv, chown and success messages and failure runs rm -f "${SETTINGS_FILE}.tmp" and prints the ERROR message and exits non‑zero. Refer to the existing pipeline that uses jq '.statusLine //= {}' and jq '.statusLine = { type: "command", command: "/usr/local/bin/ccstatusline-wrapper" }' writing to "${SETTINGS_FILE}.tmp", and preserve the atomic mv of "${SETTINGS_FILE}.tmp" to "${SETTINGS_FILE}" and the chown/echo logic that references SETTINGS_FILE and USERNAME..devcontainer/docs/plugins.md (2)
172-175:⚠️ Potential issue | 🟠 Major
workspace-scope-guarddescription is stale — reads are now blocked, not warned.This description was written for the pre-v1.14.0 behavior. The v1.14.0 changelog explicitly states: "Reads (Read, Glob, Grep) now hard-blocked outside scope — upgraded from warning (exit 0) to block (exit 2)".
📝 Proposed fix
-**Purpose**: Enforces workspace scope by blocking writes outside the working directory and warning on out-of-scope reads. - -Runs as a PreToolUse hook on Write, Edit, and Read operations. Compares file paths against the current working directory and rejects modifications to files outside the project scope. Read operations outside scope produce a warning but are not blocked. Resolves symlinks and worktree paths correctly via `os.path.realpath()`. +**Purpose**: Nuclear workspace scope enforcement — blocks ALL operations (read, write, bash) outside the current project directory. Permanently blacklists `/workspaces/.devcontainer/`. + +Runs as a PreToolUse hook on Write, Edit, Read, Glob, Grep, and Bash operations. All out-of-scope operations are hard-blocked (exit 2). Includes two-layer Bash enforcement and CWD context injection across four hook events.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/docs/plugins.md around lines 172 - 175, Update the "workspace-scope-guard" plugin description to reflect current behavior: replace the claim that it "warn[s] on out-of-scope reads" with a statement that reads (Read, Glob, Grep) are now hard-blocked (exit 2) outside the workspace scope; reference the plugin name "workspace-scope-guard" and the changelog note about v1.14.0 when updating the Purpose text so it accurately states that both writes and reads are blocked outside the working directory.
46-49:⚠️ Potential issue | 🟡 MinorStale
auto-linterreference inPLUGIN_BLACKLISTexample.
auto-linterwas deleted in this PR (consolidated intoauto-code-quality). The example should reference a plugin that still exists.📝 Proposed fix
-PLUGIN_BLACKLIST="ticket-workflow,auto-linter" +PLUGIN_BLACKLIST="ticket-workflow,notify-hook"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.devcontainer/docs/plugins.md around lines 46 - 49, Update the example PLUGIN_BLACKLIST string so it no longer references the removed "auto-linter" plugin: locate the sample value PLUGIN_BLACKLIST="ticket-workflow,auto-linter" and replace "auto-linter" with the consolidated plugin name "auto-code-quality" (or another currently supported plugin), ensuring the example reflects existing plugins.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (10)
docs/package-lock.jsonis excluded by!**/package-lock.jsondocs/public/apple-touch-icon.pngis excluded by!**/*.pngdocs/public/favicon.pngis excluded by!**/*.pngdocs/public/favicon.svgis excluded by!**/*.svgdocs/public/og-image.pngis excluded by!**/*.pngdocs/src/assets/logo.pngis excluded by!**/*.pngdocs/src/assets/logo.svgis excluded by!**/*.svglogos/CodeForgeLogo.pngis excluded by!**/*.pnglogos/CodeForgeLogoTr.pngis excluded by!**/*.pnglogos/github-avatar.pngis excluded by!**/*.png
📒 Files selected for processing (184)
.devcontainer/CHANGELOG.md.devcontainer/CLAUDE.md.devcontainer/README.md.devcontainer/config/defaults/ccstatusline-settings.json.devcontainer/config/defaults/main-system-prompt.md.devcontainer/config/defaults/rules/spec-workflow.md.devcontainer/config/file-manifest.json.devcontainer/devcontainer.json.devcontainer/docs/optional-features.md.devcontainer/docs/plugins.md.devcontainer/features/README.md.devcontainer/features/biome/install.sh.devcontainer/features/ccms/install.sh.devcontainer/features/ccstatusline/install.sh.devcontainer/features/chromaterm/README.md.devcontainer/features/chromaterm/chromaterm.yml.devcontainer/features/chromaterm/devcontainer-feature.json.devcontainer/features/chromaterm/install.sh.devcontainer/features/claude-session-dashboard/README.md.devcontainer/features/claude-session-dashboard/devcontainer-feature.json.devcontainer/features/claude-session-dashboard/install.sh.devcontainer/features/kitty-terminfo/README.md.devcontainer/features/kitty-terminfo/devcontainer-feature.json.devcontainer/features/kitty-terminfo/install.sh.devcontainer/features/shellcheck/install.sh.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json.devcontainer/plugins/devs-marketplace/.gitignore.devcontainer/plugins/devs-marketplace/plugins/agent-system/.claude-plugin/plugin.json.devcontainer/plugins/devs-marketplace/plugins/agent-system/README.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/architect.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/bash-exec.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/claude-guide.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/debug-logs.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/dependency-analyst.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/doc-writer.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/explorer.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/generalist.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/git-archaeologist.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/migrator.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/perf-profiler.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/refactorer.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/researcher.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/security-auditor.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/spec-writer.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/statusline-config.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/test-writer.md.devcontainer/plugins/devs-marketplace/plugins/agent-system/hooks/hooks.json.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/inject-cwd.py.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/task-completed-check.py.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/teammate-idle-check.py.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/verify-no-regression.py.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/verify-tests-pass.py.devcontainer/plugins/devs-marketplace/plugins/agent-system/skills/debug/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/hooks/hooks.json.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/advisory-test-runner.py.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/.claude-plugin/plugin.json.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/README.md.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/hooks/hooks.json.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-on-stop.py.devcontainer/plugins/devs-marketplace/plugins/auto-linter/.claude-plugin/plugin.json.devcontainer/plugins/devs-marketplace/plugins/auto-linter/README.md.devcontainer/plugins/devs-marketplace/plugins/auto-linter/hooks/hooks.json.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/README.md.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/README.md.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py.devcontainer/plugins/devs-marketplace/plugins/notify-hook/README.md.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/README.md.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py.devcontainer/plugins/devs-marketplace/plugins/session-context/.claude-plugin/plugin.json.devcontainer/plugins/devs-marketplace/plugins/session-context/README.md.devcontainer/plugins/devs-marketplace/plugins/session-context/hooks/hooks.json.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/git-state-injector.py.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/todo-harvester.py.devcontainer/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json.devcontainer/plugins/devs-marketplace/plugins/skill-engine/README.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/hooks/hooks.json.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/api-design/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/ast-grep-patterns/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-agent-sdk/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/debugging/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/dependency-management/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker-py/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/docker/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/documentation-patterns/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/fastapi/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/git-forensics/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/migration-patterns/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/performance-profiling/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/pydantic-ai/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/refactoring-patterns/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/security-checklist/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/skill-building/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/sqlite/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/team/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/testing/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/.claude-plugin/plugin.json.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/README.md.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/hooks/hooks.json.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/scripts/spec-reminder.py.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-build/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-check/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-init/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-new/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-refine/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-review/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/spec-update/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/spec-workflow/skills/specification-writing/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/plugin.json.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/system-prompt.md.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/README.md.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/hooks/hooks.json.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/scripts/ticket-linker.py.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketcreate-pr/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketnew/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketreview-commit/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/skills/ticketwork/SKILL.md.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/hooks/hooks.json.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/inject-workspace-cwd.py.devcontainer/scripts/setup-aliases.sh.devcontainer/scripts/setup-config.sh.github/workflows/deploy-docs.yml.github/workflows/docs-ci.yml.github/workflows/publish-features.yml.github/workflows/release.yml.gitignore.npmignoreCLAUDE.mdREADME.mddocs/astro.config.mjsdocs/package.jsondocs/scripts/sync-changelog.mjsdocs/src/components/FeatureCard.astrodocs/src/components/Header.astrodocs/src/components/Hero.astrodocs/src/components/InstallCommand.astrodocs/src/content.config.tsdocs/src/content/docs/customization/configuration.mddocs/src/content/docs/customization/hooks.mddocs/src/content/docs/customization/index.mddocs/src/content/docs/customization/rules.mddocs/src/content/docs/customization/system-prompts.mddocs/src/content/docs/features/agents.mddocs/src/content/docs/features/code-intelligence.mddocs/src/content/docs/features/index.mddocs/src/content/docs/features/skills.mddocs/src/content/docs/features/tools.mddocs/src/content/docs/getting-started/first-session.mddocs/src/content/docs/getting-started/index.mddocs/src/content/docs/getting-started/installation.mddocs/src/content/docs/getting-started/requirements.mddocs/src/content/docs/index.mdxdocs/src/content/docs/plugins/agent-system.mddocs/src/content/docs/plugins/auto-code-quality.mddocs/src/content/docs/plugins/codeforge-lsp.mddocs/src/content/docs/plugins/dangerous-command-blocker.mddocs/src/content/docs/plugins/frontend-design.mddocs/src/content/docs/plugins/index.mddocs/src/content/docs/plugins/notify-hook.mddocs/src/content/docs/plugins/protected-files-guard.mddocs/src/content/docs/plugins/session-context.mddocs/src/content/docs/plugins/skill-engine.mddocs/src/content/docs/plugins/spec-workflow.mddocs/src/content/docs/plugins/ticket-workflow.mddocs/src/content/docs/plugins/workspace-scope-guard.mddocs/src/content/docs/reference/architecture.mddocs/src/content/docs/reference/changelog.mddocs/src/content/docs/reference/commands.mddocs/src/content/docs/reference/environment.mddocs/src/content/docs/reference/index.mddocs/src/styles/global.cssdocs/tsconfig.jsonpackage.jsonsetup.js
💤 Files with no reviewable changes (18)
- .devcontainer/docs/optional-features.md
- .devcontainer/plugins/devs-marketplace/plugins/session-context/.claude-plugin/plugin.json
- .devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/system-prompt.md
- .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/.claude-plugin/plugin.json
- .devcontainer/plugins/devs-marketplace/plugins/agent-system/.claude-plugin/plugin.json
- .devcontainer/plugins/devs-marketplace/plugins/auto-formatter/README.md
- .devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json
- .devcontainer/plugins/devs-marketplace/plugins/auto-formatter/hooks/hooks.json
- .devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py
- .devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-on-stop.py
- .devcontainer/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json
- .devcontainer/plugins/devs-marketplace/plugins/session-context/hooks/hooks.json
- .devcontainer/plugins/devs-marketplace/plugins/auto-linter/hooks/hooks.json
- .devcontainer/plugins/devs-marketplace/plugins/auto-linter/README.md
- .devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/hooks/hooks.json
- .devcontainer/plugins/devs-marketplace/plugins/spec-workflow/hooks/hooks.json
- .devcontainer/plugins/devs-marketplace/plugins/auto-linter/.claude-plugin/plugin.json
- .devcontainer/plugins/devs-marketplace/plugins/auto-formatter/.claude-plugin/plugin.json
| if [ -f /usr/local/share/nvm/nvm.sh ]; then | ||
| source /usr/local/share/nvm/nvm.sh | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
nvm.sh unbound variable set -u nounset bash compatibility issue
💡 Result:
When you source nvm.sh in a Bash session/script that has set -u / set -o nounset enabled, any reference inside nvm.sh to an unset variable can abort with unbound variable. A known example in nvm.sh was checking mirrors like NVM_IOJS_ORG_MIRROR using "$VAR" instead of a nounset-safe expansion; it was fixed by switching to ${VAR-} (treat unset as empty). [1]
Fix options
-
Upgrade nvm
If you’re on an older nvm release, update to a version that includes the nounset fix(es). (The fix pattern is shown in the referenced commit.) [1] -
Temporarily disable nounset just for sourcing
nvm.sh(common workaround)
set +u
# or: set +o nounset
. "$NVM_DIR/nvm.sh"
set -u(You can then re-enable nounset for the rest of your script.) This aligns with standard “default/guarded expansion” practices for nounset. [2]
- Patch locally (if you must stay pinned)
Change unsafe tests like:
if [ -z "$NVM_IOJS_ORG_MIRROR" ]; thento nounset-safe:
if [ -z "${NVM_IOJS_ORG_MIRROR-}" ]; thenThis is exactly the fix style used in nvm’s nounset-related change. [1]
Sources: [1] [2]
🏁 Script executed:
head -30 .devcontainer/features/biome/install.shRepository: AnExiledDev/CodeForge
Length of output: 772
source nvm.sh under set -u will abort the script with an unbound-variable error
set -euo pipefail at line 2 keeps nounset active for the entire script. NVM's nvm.sh references variables that may be unset during initialization (e.g., NVM_IOJS_ORG_MIRROR, internal intermediaries). With set -u in effect, the first such reference kills the script immediately with a terse shell error—before the npm validation at line 21 ever runs—making the failure hard to diagnose.
Suspend nounset around the source call:
Proposed fix
if [ -f /usr/local/share/nvm/nvm.sh ]; then
+ set +u
source /usr/local/share/nvm/nvm.sh
+ set -u
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if [ -f /usr/local/share/nvm/nvm.sh ]; then | |
| source /usr/local/share/nvm/nvm.sh | |
| fi | |
| if [ -f /usr/local/share/nvm/nvm.sh ]; then | |
| set +u | |
| source /usr/local/share/nvm/nvm.sh | |
| set -u | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.devcontainer/features/biome/install.sh around lines 16 - 18, The script
currently runs with set -euo pipefail (nounset enabled) which causes sourcing
/usr/local/share/nvm/nvm.sh to fail if nvm.sh references unset variables; fix by
temporarily disabling nounset around the source call: before sourcing, run set
+u, then source /usr/local/share/nvm/nvm.sh, then restore nounset with set -u
(keep errexit/pipefail active). Update the block that checks [ -f
/usr/local/share/nvm/nvm.sh ] to use set +u; source ...; set -u so the rest of
the script behavior remains unchanged.
| if [[ "${USERNAME}" == "auto" || "${USERNAME}" == "automatic" ]]; then | ||
| for u in vscode node codespace; do | ||
| if id -u "$u" >/dev/null 2>&1; then | ||
| USERNAME="$u" | ||
| break | ||
| fi | ||
| done | ||
| USERNAME="${USERNAME:-root}" | ||
| fi |
There was a problem hiding this comment.
Username auto-detection fails to fall back to root when no candidate user is found.
USERNAME="${USERNAME:-root}" only substitutes when the variable is unset or empty. After the loop, USERNAME is still "automatic" (non-empty), so the :-root expansion is silently skipped. Any subsequent sudo -u "automatic" call will fail with "unknown user".
This matches the canonical pattern used in Microsoft's devcontainer feature scripts: reset the variable to empty before the loop so the fallback works.
🐛 Proposed fix
if [[ "${USERNAME}" == "auto" || "${USERNAME}" == "automatic" ]]; then
+ USERNAME=""
for u in vscode node codespace; do
if id -u "$u" >/dev/null 2>&1; then
USERNAME="$u"
break
fi
done
USERNAME="${USERNAME:-root}"
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if [[ "${USERNAME}" == "auto" || "${USERNAME}" == "automatic" ]]; then | |
| for u in vscode node codespace; do | |
| if id -u "$u" >/dev/null 2>&1; then | |
| USERNAME="$u" | |
| break | |
| fi | |
| done | |
| USERNAME="${USERNAME:-root}" | |
| fi | |
| if [[ "${USERNAME}" == "auto" || "${USERNAME}" == "automatic" ]]; then | |
| USERNAME="" | |
| for u in vscode node codespace; do | |
| if id -u "$u" >/dev/null 2>&1; then | |
| USERNAME="$u" | |
| break | |
| fi | |
| done | |
| USERNAME="${USERNAME:-root}" | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.devcontainer/features/chromaterm/install.sh around lines 21 - 29, The
USERNAME auto-detection leaves USERNAME as the literal "auto"/"automatic" so the
fallback USERNAME="${USERNAME:-root}" never triggers; change the block so when
USERNAME is "auto" or "automatic" you first clear/reset USERNAME (e.g.,
USERNAME="") before iterating over candidates (the for u in vscode node
codespace loop) so that if no candidate is found the subsequent
USERNAME="${USERNAME:-root}" correctly falls back to root; update the
conditional branch containing the loop that sets USERNAME to ensure the variable
is emptyed prior to searching.
| if error: | ||
| json.dump({"error": error}, sys.stdout) | ||
| print(error, file=sys.stderr) | ||
| sys.exit(2) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, examine the file to confirm the docstring issue
head -n 620 ".devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py" | tail -n +1 | head -n 30Repository: AnExiledDev/CodeForge
Length of output: 1094
🏁 Script executed:
# Also check the error path around lines 603-605
sed -n '600,610p' ".devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py"Repository: AnExiledDev/CodeForge
Length of output: 241
🏁 Script executed:
# Search for Claude Code hook references in the codebase
rg -i "preToolUse|claude.*code.*hook|hookSpecificOutput" --max-count 20Repository: AnExiledDev/CodeForge
Length of output: 26012
🏁 Script executed:
# Search for how other hooks handle output after the migration
rg -i "hookSpecificOutput" -A 3 -B 3 --max-count 30Repository: AnExiledDev/CodeForge
Length of output: 14769
🏁 Script executed:
# Check for any hook documentation or integration code
fd -type f -name "*.py" -o -name "*.md" -o -name "*.json" | xargs rg -l "PreToolUse|preToolUse" 2>/dev/null | head -20Repository: AnExiledDev/CodeForge
Length of output: 1496
🏁 Script executed:
# Find and examine the hooks.json for agent-system to see how guard-readonly-bash is registered
find ".devcontainer/plugins/devs-marketplace/plugins/agent-system" -name "hooks.json" -exec cat -n {} +Repository: AnExiledDev/CodeForge
Length of output: 1349
🏁 Script executed:
# Check how other blocking PreToolUse hooks format their output (e.g., block-dangerous.py)
cat -n ".devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py" | head -n 100Repository: AnExiledDev/CodeForge
Length of output: 4216
🏁 Script executed:
# Search for guard-readonly-bash output handling to see if it should emit JSON
rg -A 10 -B 5 "sys.exit\(2\)" ".devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py"Repository: AnExiledDev/CodeForge
Length of output: 252
🏁 Script executed:
# Search for guard-readonly-bash registration across all hooks.json files
find . -name "hooks.json" -exec rg -l "guard-readonly-bash" {} +Repository: AnExiledDev/CodeForge
Length of output: 47
🏁 Script executed:
# Check if guard-readonly-bash is mentioned in agent-system plugin documentation
rg -i "guard-readonly-bash" ".devcontainer/plugins/devs-marketplace/plugins/agent-system/" -A 2Repository: AnExiledDev/CodeForge
Length of output: 4025
🏁 Script executed:
# Examine the complete structure of agent-system to see if it's registered elsewhere
find ".devcontainer/plugins/devs-marketplace/plugins/agent-system" -type f \( -name "*.json" -o -name "*.md" -o -name "*.py" \) | head -20Repository: AnExiledDev/CodeForge
Length of output: 1703
🏁 Script executed:
# Inspect one of the agent files to see exactly how guard-readonly-bash.py is invoked
sed -n '1,100p' ".devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/security-auditor.md"Repository: AnExiledDev/CodeForge
Length of output: 5238
🏁 Script executed:
# Look for hook output format documentation in the customization/hooks documentation
cat -n "docs/src/content/docs/customization/hooks.md" | head -n 150Repository: AnExiledDev/CodeForge
Length of output: 7273
🏁 Script executed:
# Check the exact definition of how PreToolUse hooks should output when blocking
rg -A 15 "PreToolUse.*only" "docs/src/content/docs/customization/hooks.md"Repository: AnExiledDev/CodeForge
Length of output: 629
🏁 Script executed:
# Check what block-dangerous.py outputs on the blocking path
sed -n '95,150p' ".devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py"Repository: AnExiledDev/CodeForge
Length of output: 1171
🏁 Script executed:
# Look for the complete hooks.md section on PreToolUse output format and JSON structure
sed -n '150,250p' "docs/src/content/docs/customization/hooks.md"Repository: AnExiledDev/CodeForge
Length of output: 2524
🏁 Script executed:
# Search for any recent changes to guard-readonly-bash.py to understand the intent of the stderr change
git log --oneline -20 -- ".devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py" 2>/dev/null || echo "Git log not available"Repository: AnExiledDev/CodeForge
Length of output: 161
Fix the docstring and implement the documented PreToolUse output contract.
Lines 15–17 incorrectly state "Returns JSON on stdout." The actual implementation outputs plain text to stderr. Additionally, the implementation diverges from the documented PreToolUse hook specification: blocking hooks (exit 2) should output JSON to stdout in the format {"decision": "block", "message": "..."}, not plain text to stderr. If Claude Code expects the documented format, the block reason may not reach the user.
📝 Corrected implementation
-Reads tool input from stdin (JSON). Returns JSON on stdout.
-Exit 0: Command is safe (allowed)
-Exit 2: Command would modify state (blocked)
+Reads tool input from stdin (JSON). Returns JSON to stdout on block.
+Exit 0: Command is safe (allowed)
+Exit 2: Command would modify state (blocked) — outputs JSON with block reasonAnd update the error path to emit JSON as documented:
if error:
- print(error, file=sys.stderr)
+ print(json.dumps({"decision": "block", "message": error}))
sys.exit(2)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
@.devcontainer/plugins/devs-marketplace/plugins/agent-system/scripts/guard-readonly-bash.py
around lines 603 - 605, Update the docstring around the PreToolUse hook to state
it writes JSON to stdout (not plain text to stderr), and change the error path
where 'error' is handled so it emits the documented JSON object to stdout like
{"decision":"block","message":"..."} instead of printing to stderr; ensure the
handler for the 'error' variable (the block case that currently calls
print(error, file=sys.stderr) and sys.exit(2)) writes the JSON string to stdout
and then exits with code 2 so the PreToolUse contract is satisfied.
| "spec-check": { | ||
| "phrases": [ | ||
| "check spec health", | ||
| "audit specs", | ||
| "which specs are stale", | ||
| "find missing specs", | ||
| "review spec quality", | ||
| "run spec-check", | ||
| "are my specs up to date", | ||
| ], | ||
| "terms": ["spec-check"], | ||
| }, | ||
| "spec-init": { | ||
| "phrases": [ | ||
| "initialize specs", | ||
| "specs directory", | ||
| "set up specs", | ||
| "bootstrap specs", | ||
| "start using specs", | ||
| "create spec directory", | ||
| "init specs", | ||
| "set up .specs", | ||
| ], | ||
| "terms": ["spec-init"], | ||
| }, | ||
| "spec-new": { | ||
| "phrases": [ | ||
| "create a spec", | ||
| "new spec", | ||
| "new feature spec", | ||
| "write a spec for", | ||
| "spec this feature", | ||
| "start a new spec", | ||
| "plan a feature", | ||
| "add a spec", | ||
| ], | ||
| "terms": ["spec-new"], | ||
| }, | ||
| "spec-refine": { | ||
| "phrases": [ | ||
| "refine the spec", | ||
| "review spec assumptions", | ||
| "validate spec decisions", | ||
| "approve the spec", | ||
| "walk me through the spec", | ||
| "check spec for assumptions", | ||
| "iterate on the spec", | ||
| ], | ||
| "terms": ["spec-refine"], | ||
| }, | ||
| "spec-update": { | ||
| "phrases": [ | ||
| "update the spec", | ||
| "mark spec as implemented", | ||
| "as-built update", | ||
| "finish the spec", | ||
| "close the spec", | ||
| "update spec status", | ||
| "sync spec with code", | ||
| ], | ||
| "terms": ["spec-update"], | ||
| }, | ||
| "team": { | ||
| "phrases": [ | ||
| "spawn a team", | ||
| "create a team", | ||
| "team of agents", | ||
| "use a swarm", | ||
| "work in parallel", | ||
| "coordinate multiple agents", | ||
| "split this across agents", | ||
| "team up", | ||
| ], | ||
| "terms": ["TeamCreate", "SendMessage"], | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check the structure of the skill-engine plugin to find skills directories
echo "=== Checking for skills directories under skill-engine ==="
fd --type d 'skills' .devcontainer/plugins/devs-marketplace/plugins/skill-engine
echo ""
echo "=== Listing skill folders (one level below skills directory) ==="
fd --type d --max-depth 3 . .devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills | head -50
echo ""
echo "=== Specifically searching for spec-* and team directories ==="
fd --type d '(spec-check|spec-init|spec-new|spec-refine|spec-update|team)' .devcontainer/plugins/devs-marketplace/plugins/skill-engineRepository: AnExiledDev/CodeForge
Length of output: 3991
🏁 Script executed:
#!/bin/bash
# Search for spec-* directories across all plugins
echo "=== Searching for spec-* directories across all plugins ==="
fd --type d '(spec-check|spec-init|spec-new|spec-refine|spec-update)' .devcontainer/plugins/devs-marketplace/plugins
echo ""
echo "=== List all plugins to understand structure ==="
fd --type d --max-depth 1 . .devcontainer/plugins/devs-marketplace/plugins | grep -v '^\.$' | sortRepository: AnExiledDev/CodeForge
Length of output: 1287
Move spec-* skill keywords to the correct plugin suggester.
The spec-check, spec-init, spec-new, spec-refine, and spec-update skills are defined in the spec-workflow plugin, not in skill-engine. Suggesting them from this plugin's suggester will cause runtime failures when the skill-engine tries to load them. Move these five entries to the spec-workflow plugin's suggester. The team skill is correctly placed.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
@.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py
around lines 343 - 417, The spec-related suggester entries "spec-check",
"spec-init", "spec-new", "spec-refine", and "spec-update" are defined here in
the skill-engine suggester but belong to the spec-workflow plugin; remove these
five entries from the skill-engine suggester and add the exact objects
(including their "phrases" and "terms" arrays) into the spec-workflow plugin's
suggester so the suggester for spec-workflow exposes those skills at runtime;
leave the "team" entry as-is in the skill-engine suggester and verify no other
references to "spec-*" remain in this file.
| description: >- | ||
| This skill should be used when the user asks to "run Claude Code in headless mode", | ||
| "use claude -p for automation", "parse stream-json output from Claude Code", | ||
| "use the Claude Agent SDK", "run Claude Code in CI/CD", | ||
| "configure MCP servers for headless Claude", | ||
| "set up Claude Code permissions for scripts", | ||
| "track Claude Code costs programmatically", or discusses Claude Code | ||
| print mode, non-interactive pipelines, SDK integration in TypeScript or Python, | ||
| structured output with json-schema, or session management for automation. | ||
| version: 0.1.0 | ||
| Guides non-interactive Claude Code usage via the -p flag, stream-json output | ||
| parsing, and CI/CD pipeline integration. USE WHEN the user asks to "run | ||
| Claude Code in headless mode", "use claude -p for automation", "parse | ||
| stream-json output", "run Claude Code in CI/CD", "track costs | ||
| programmatically", "set up permissions for scripts", or works with | ||
| --output-format stream-json, --permission-mode, --json-schema, --resume. | ||
| DO NOT USE for the TypeScript SDK API — use claude-agent-sdk instead. | ||
| version: 0.2.0 |
There was a problem hiding this comment.
Resolve the SDK guidance contradiction in this doc.
The description says “DO NOT USE for the TypeScript SDK API — use claude-agent-sdk instead,” but the “Agent SDK” section later still provides TypeScript usage with @anthropic-ai/claude-agent-sdk. This is internally inconsistent and will mislead users. Please align the description with the SDK section (or update/remove the SDK examples to match the new guidance).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
@.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/claude-code-headless/SKILL.md
around lines 3 - 11, The doc text currently contradicts itself by saying "DO NOT
USE for the TypeScript SDK API — use claude-agent-sdk instead" while the "Agent
SDK" section still includes TypeScript examples using
`@anthropic-ai/claude-agent-sdk`; update SKILL.md to be consistent by either
removing or replacing the "Agent SDK" TypeScript examples (and any references to
`@anthropic-ai/claude-agent-sdk`) to match the top-level directive, or conversely
change the description to allow TypeScript SDK usage; ensure the unique
identifiers "Agent SDK" section and the package name
"@anthropic-ai/claude-agent-sdk" are updated accordingly so the doc no longer
contradicts itself.
| Guides Svelte 5 development with runes, SvelteKit SPA configuration, and | ||
| component architecture. USE WHEN the user asks to "build a Svelte component", | ||
| "use Svelte 5 runes", "set up a SvelteKit SPA", "migrate from Svelte 4 | ||
| to 5", "manage state with $state", "add drag and drop to Svelte", or works | ||
| with $state, $derived, $effect, $props, @ai-sdk/svelte, svelte-dnd-action. | ||
| DO NOT USE for Svelte 4 legacy codebases or React/Vue development. |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check that the layercake reference file exists
find . -name "layercake.md" -type f 2>/dev/null | head -20
# Also check the svelte5 skill directory structure
fd -t f . '.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/' 2>/dev/null | head -30Repository: AnExiledDev/CodeForge
Length of output: 986
🏁 Script executed:
# Check how skill-suggester.py reads skill descriptions/keywords
find . -name "skill-suggester.py" -type f 2>/dev/null
# Also search for how skills are parsed/matched
rg -l 'description' --type py | grep -i skill | head -10Repository: AnExiledDev/CodeForge
Length of output: 240
🏁 Script executed:
wc -l .devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.pyRepository: AnExiledDev/CodeForge
Length of output: 155
🏁 Script executed:
cat -n .devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.pyRepository: AnExiledDev/CodeForge
Length of output: 17811
The skill-suggester.py has a hardcoded keyword list, not extracted from SKILL.md descriptions.
layercake is missing from the svelte5 skill activation keywords in skill-suggester.py (line 134), not in the SKILL.md description. The skill-suggester uses a hardcoded SKILLS dictionary with "phrases" and "terms"—it does not read from SKILL.md descriptions. Adding layercake to the SKILL.md description (lines 4–9) will not enable skill activation; the fix must be applied to skill-suggester.py instead.
✏️ Required fix location
In .devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py, line 134, add layercake to the terms list:
- "terms": ["sveltekit", "svelte", "svelte-dnd-action", "@ai-sdk/svelte"],
+ "terms": ["sveltekit", "svelte", "svelte-dnd-action", "@ai-sdk/svelte", "layercake"],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
@.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/svelte5/SKILL.md
around lines 4 - 9, The SKILL.md change is insufficient because
skill-suggester.py uses a hardcoded SKILLS dictionary; update the SKILLS entry
for the "svelte5" skill in
.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py
by adding "layercake" to that skill's "terms" list (the SKILLS dict -> "svelte5"
key -> "terms" array) so the skill activation recognizes the keyword; leave
SKILL.md as-is.
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: "Publish Features" | ||
| uses: devcontainers/action@v1 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, check the current state of the workflow file
cat -n .github/workflows/publish-features.yml | head -25Repository: AnExiledDev/CodeForge
Length of output: 802
🌐 Web query:
devcontainers/action@v1 latest release GitHub
💡 Result:
The latest release of devcontainers/action@v1 on GitHub is v1.4.3, published Oct 4, 2024. [1]
Recommended tag to use: devcontainers/action@v1 (major-version tag) or pin to devcontainers/action@v1.4.3. [1]
Sources: [1]
🌐 Web query:
actions/checkout@v4 latest stable release SHA GitHub
💡 Result:
Latest stable actions/checkout@v4 release is v4.3.1, which points to commit SHA:
34e114876b0b11c390a56381ad16ebd13914f8d5 (github.com)
Citations:
Pin action references to immutable SHAs to prevent supply-chain attacks.
Both actions/checkout@v4 and devcontainers/action@v1 use mutable floating major-version tags. This job holds packages: write, so a compromised or re-pointed tag could publish malicious images to GHCR using the workflow's GITHUB_TOKEN.
🔒 Proposed fix: pin to immutable SHAs
- - uses: actions/checkout@v4
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: "Publish Features"
- uses: devcontainers/action@v1
+ uses: devcontainers/action@HEAD # or pin to latest v1.4.3 release SHA when available📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - uses: actions/checkout@v4 | |
| - name: "Publish Features" | |
| uses: devcontainers/action@v1 | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| - name: "Publish Features" | |
| uses: devcontainers/action@HEAD # or pin to latest v1.4.3 release SHA when available |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/publish-features.yml around lines 16 - 19, Replace the
mutable action tags with pinned immutable SHAs: update the checkout usage
"actions/checkout@v4" and the devcontainer usage in the "Publish Features" job
"uses: devcontainers/action@v1" to their corresponding commit SHAs (e.g.,
actions/checkout@<sha> and devcontainers/action@<sha>); fetch the current secure
SHA from each action's repository (GitHub releases or the action's default
branch commit) and replace the version tags so the workflow uses fixed SHAs to
prevent supply-chain re-pointing while keeping the same inputs and behavior.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Fetch the latest release info from GitHub for both actions
curl -s https://api.github.com/repos/actions/checkout/releases/tags/v4.2.2 | jq -r '.target_commitish' > /tmp/checkout_sha.txt
curl -s https://api.github.com/repos/devcontainers/action/releases/tags/v1.3.1 | jq -r '.target_commitish' > /tmp/devcontainers_sha.txt
echo "=== actions/checkout@v4.2.2 commit SHA ==="
cat /tmp/checkout_sha.txt
echo "=== devcontainers/action@v1.3.1 commit SHA ==="
cat /tmp/devcontainers_sha.txtRepository: AnExiledDev/CodeForge
Length of output: 164
🌐 Web query:
actions/checkout v4.2.2 release GitHub commit SHA
💡 Result:
actions/checkout@v4.2.2 points to commit SHA:
11bd71901bbe5b1630ceea73d27597364c9af683 [1][2]
🌐 Web query:
devcontainers/action v1.3.1 release GitHub commit SHA
💡 Result:
devcontainers/action v1.3.1 tag points to commit SHA 79f4dee22a716245ebe89961ea56744a00741992. [1]
Sources:
[1] (github.com)
Citations:
Pin action references to immutable SHAs to prevent supply-chain attacks.
Both actions/checkout@v4 and devcontainers/action@v1 use mutable floating tags. This job holds packages: write, so a compromised or re-pointed tag could publish malicious images to GHCR using the workflow's GITHUB_TOKEN. Pin both actions to the full commit SHA of their latest releases:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: "Publish Features"
- uses: devcontainers/action@v1
+ uses: devcontainers/action@79f4dee22a716245ebe89961ea56744a00741992 # v1.3.1🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/publish-features.yml around lines 16 - 19, Replace the
mutable action refs in the workflow ("actions/checkout@v4" and
"devcontainers/action@v1") with their corresponding immutable commit SHAs (the
full 40‑char commit hashes from each action's latest release) so the job that
has packages: write cannot be hijacked by repointed tags; update the uses lines
to use the full SHA refs and document/update those SHAs whenever you
intentionally upgrade the action versions.
| - name: Publish to npm | ||
| run: npm publish | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | ||
|
|
||
| - name: Create git tag | ||
| run: | | ||
| VERSION="v${{ needs.check-version.outputs.version }}" | ||
| git tag "$VERSION" | ||
| git push origin "$VERSION" |
There was a problem hiding this comment.
Non-atomic release: npm publish succeeds but tag/release creation can fail, leaving inconsistent state.
If npm publish succeeds (line 48) but git tag or git push origin fails (e.g., the tag already exists from a prior manual run or a partially failed workflow), the package is live on npm with no corresponding GitHub release or tag. There's no rollback for the npm publish.
Consider either:
- Creating the tag before publishing to npm (tags are cheap to delete on failure), or
- Checking for an existing tag before creating one, and adding error handling.
Proposed fix: reorder to tag-first, publish-second
+ - name: Create git tag
+ run: |
+ VERSION="v${{ needs.check-version.outputs.version }}"
+ if git ls-remote --tags origin "$VERSION" | grep -q "$VERSION"; then
+ echo "Tag $VERSION already exists, skipping"
+ exit 1
+ fi
+ git tag "$VERSION"
+ git push origin "$VERSION"
+
- name: Run tests
run: npm test
- name: Publish to npm
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- - name: Create git tag
- run: |
- VERSION="v${{ needs.check-version.outputs.version }}"
- git tag "$VERSION"
- git push origin "$VERSION"
-🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release.yml around lines 47 - 56, The workflow is
non-atomic: "Publish to npm" runs npm publish before the "Create git tag" step
so a failed tag/push can leave a published package without a repo tag; move the
tag creation step (the shell that sets VERSION and runs git tag/git push) to run
before the npm publish step, and in that tag step add a pre-check for existing
tag (e.g., using git rev-parse --verify or git ls-remote --tags origin) to avoid
failing on duplicate tags and fail fast on other git errors; ensure the step
that creates the tag exits non-zero on unrecoverable errors so the publish step
never runs if tagging fails.
| ### Full-Access Agents | ||
|
|
||
| These agents can read and write files, run commands, and make changes to your codebase. | ||
|
|
||
| | Agent | Role | Model | Isolation | Skills | | ||
| |-------|------|-------|-----------|--------| | ||
| | **generalist** | General-purpose development tasks | Inherited | No | spec skills | | ||
| | **test-writer** | Test creation, coverage analysis, framework detection | Opus | Worktree | testing | | ||
| | **refactorer** | Behavior-preserving code transformations | Opus | Worktree | refactoring-patterns | | ||
| | **doc-writer** | Documentation writing and maintenance | Opus | Worktree | documentation-patterns | | ||
| | **migrator** | Code migration, framework upgrades | Opus | Worktree | migration-patterns | | ||
| | **spec-writer** | Specification authoring and refinement | Opus | No | specification-writing | | ||
| | **bash-exec** | Shell command execution and scripting | Sonnet | No | — | | ||
| | **statusline-config** | Statusline customization | Sonnet | No | — | |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's check the agent-system.md file around the mentioned lines
echo "=== Checking agent-system.md lines 126-139 ==="
sed -n '126,139p' docs/src/content/docs/plugins/agent-system.md
# Now let's check agents.md for spec-writer references
echo -e "\n=== Checking agents.md for spec-writer ==="
rg -n "spec-writer" docs/src/content/docs/features/agents.md -A 2 -B 2
# Find the actual agent definition file
echo -e "\n=== Looking for agent definition files ==="
find . -type f \( -name "*spec-writer*" -o -path "*/agents/*" \) | grep -v node_modules | head -20Repository: AnExiledDev/CodeForge
Length of output: 4088
🏁 Script executed:
# Let's search more broadly for the agents configuration
echo "=== Searching for permissionMode in agent definitions ==="
rg "spec-writer" -t md --type-add="yml:*.{yml,yaml}" --type-list | head -5
rg "permissionMode" -t md -t yml -i --max-count 20
# Also check if there's an agents directory or agent definitions
echo -e "\n=== Looking for agent definition structure ==="
fd "agent" --type f --extension json --extension yaml --extension md | grep -v node_modules | head -20Repository: AnExiledDev/CodeForge
Length of output: 883
🏁 Script executed:
# Check the full structure around spec-writer in both files
echo "=== Full context from agent-system.md ==="
sed -n '110,145p' docs/src/content/docs/plugins/agent-system.md
echo -e "\n=== Full context from agents.md ==="
sed -n '260,290p' docs/src/content/docs/features/agents.mdRepository: AnExiledDev/CodeForge
Length of output: 4684
🏁 Script executed:
cat ".devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/spec-writer.md"Repository: AnExiledDev/CodeForge
Length of output: 19908
Move spec-writer from Full-Access to Read-Only agents table
The spec-writer is incorrectly categorized as a Full-Access agent in this file. According to its agent definition (permissionMode: plan, tools: Read/Glob/Grep/WebSearch only), spec-writer is read-only—it creates specifications without modifying files. This matches the correct categorization in agents.md. Move spec-writer to the Read-Only Agents table.
Proposed fix
### Read-Only Agents
These agents investigate, analyze, and report — they never modify files.
| Agent | Role | Model | Skills |
|-------|------|-------|--------|
| **architect** | System design, implementation planning, trade-off analysis | Opus | api-design, spec skills |
| **explorer** | Fast codebase navigation, file discovery, pattern matching | Haiku | ast-grep-patterns |
| **researcher** | Deep investigation, information gathering, web research | Sonnet | documentation-patterns |
| **claude-guide** | Claude Code usage guidance and best practices | Haiku | claude-code-headless, claude-agent-sdk |
| **debug-logs** | Log analysis, error diagnosis, debugging | Sonnet | debugging |
| **dependency-analyst** | Dependency analysis, version conflicts, upgrade paths | Haiku | dependency-management |
| **git-archaeologist** | Git history analysis, blame, bisect, forensics | Haiku | git-forensics |
| **perf-profiler** | Performance profiling, bottleneck identification | Sonnet | performance-profiling |
| **security-auditor** | Security audit, vulnerability assessment, OWASP checks | Sonnet | security-checklist |
+| **spec-writer** | Specification authoring and refinement | Opus | specification-writing |
### Full-Access Agents
These agents can read and write files, run commands, and make changes to your codebase.
| Agent | Role | Model | Isolation | Skills |
|-------|------|-------|-----------|--------|
| **generalist** | General-purpose development tasks | Inherited | No | spec skills |
| **test-writer** | Test creation, coverage analysis, framework detection | Opus | Worktree | testing |
| **refactorer** | Behavior-preserving code transformations | Opus | Worktree | refactoring-patterns |
| **doc-writer** | Documentation writing and maintenance | Opus | Worktree | documentation-patterns |
| **migrator** | Code migration, framework upgrades | Opus | Worktree | migration-patterns |
-| **spec-writer** | Specification authoring and refinement | Opus | No | specification-writing |
| **bash-exec** | Shell command execution and scripting | Sonnet | No | — |
| **statusline-config** | Statusline customization | Sonnet | No | — |🧰 Tools
🪛 LanguageTool
[style] ~128-~128: Consider shortening or rephrasing this to strengthen your wording.
Context: ...read and write files, run commands, and make changes to your codebase. | Agent | Role | Model ...
(MAKE_CHANGES)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/src/content/docs/plugins/agent-system.md` around lines 126 - 139, The
spec-writer agent row is miscategorized under the "Full-Access Agents" table;
update the documentation by removing the spec-writer row from the Full-Access
Agents table and adding that same row to the "Read-Only Agents" table, ensuring
the entry reflects its permissionMode: plan and tools: Read/Glob/Grep/WebSearch
(i.e., read-only behavior) so the spec-writer row appears with the other
read-only agents.
| /* CodeForge Documentation — Ember/Charcoal Brand Theme */ | ||
|
|
||
| /* Cascade layer order — MUST be first */ | ||
| @layer base, starlight, theme, components, utilities; | ||
|
|
||
| /* Starlight + Tailwind compatibility */ | ||
| @import '@astrojs/starlight-tailwind'; | ||
|
|
||
| /* Tailwind v4 */ | ||
| @import 'tailwindcss/theme.css' layer(theme); | ||
| @import 'tailwindcss/utilities.css' layer(utilities); | ||
|
|
||
| /* Web font */ | ||
| @import '@fontsource-variable/plus-jakarta-sans'; | ||
|
|
||
| /* ============================================ | ||
| Tailwind v4 Theme Tokens | ||
| ============================================ */ | ||
| @theme { | ||
| /* Typography */ | ||
| --font-sans: 'Plus Jakarta Sans Variable', 'Plus Jakarta Sans', ui-sans-serif, system-ui, -apple-system, sans-serif; | ||
| --font-mono: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace; | ||
|
|
||
| /* Ember accent palette */ | ||
| --color-ember-50: #fff7ed; | ||
| --color-ember-100: #ffedd5; | ||
| --color-ember-200: #fed7aa; | ||
| --color-ember-300: #fdba74; | ||
| --color-ember-400: #fb923c; | ||
| --color-ember-500: #f97316; | ||
| --color-ember-600: #ea580c; | ||
| --color-ember-700: #c2410c; | ||
| --color-ember-800: #9a3412; | ||
| --color-ember-900: #7c2d12; | ||
| --color-ember-950: #431407; | ||
|
|
||
| /* Charcoal/stone palette */ | ||
| --color-charcoal-50: #fafaf9; | ||
| --color-charcoal-100: #f5f5f4; | ||
| --color-charcoal-200: #e7e5e4; | ||
| --color-charcoal-300: #d6d3d1; | ||
| --color-charcoal-400: #a8a29e; | ||
| --color-charcoal-500: #78716c; | ||
| --color-charcoal-600: #57534e; | ||
| --color-charcoal-700: #44403c; | ||
| --color-charcoal-800: #292524; | ||
| --color-charcoal-900: #1c1917; | ||
| --color-charcoal-950: #0c0a09; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect lint configs for Tailwind v4 support / custom syntax.
fd -a -H -t f '(^|/)\.stylelintrc(\.|$)|stylelint\.(config|rc)\.|(^|/)stylelint\.config\.(js|cjs|mjs|json)|(^|/)biome\.json$|(^|/)biome\.config\.(js|cjs|mjs|json)$' \
-E node_modules -E dist -E build
# Print relevant sections if found
for f in $(fd -a -H -t f '(^|/)\.stylelintrc(\.|$)|stylelint\.(config|rc)\.|(^|/)stylelint\.config\.(js|cjs|mjs|json)|(^|/)biome\.json$|(^|/)biome\.config\.(js|cjs|mjs|json)$' \
-E node_modules -E dist -E build); do
echo "----- $f -----"
sed -n '1,200p' "$f"
doneRepository: AnExiledDev/CodeForge
Length of output: 1911
🌐 Web query:
Biome 2.4.4 Tailwind v4 @theme at-rule support CSS linting
💡 Result:
Biome v2.4.4 can lint Tailwind CSS v4’s @theme (and other Tailwind v4 directives), but it’s opt-in: you must enable Tailwind-directive parsing in the CSS parser. (next.biomejs.dev)
Fix (Biome ≥ 2.3, including 2.4.4): add this to biome.json:
{
"css": {
"parser": {
"tailwindDirectives": true
}
}
}With that enabled, Biome recognizes Tailwind v4 directives like @theme, so lint/suspicious/noUnknownAtRules shouldn’t flag them as unknown anymore. (next.biomejs.dev)
If you don’t enable it, Biome will treat @theme as a non-standard at-rule and noUnknownAtRules will report it (the exact error many people hit in Tailwind v4 projects). (github.com)
Citations:
- 1: https://next.biomejs.dev/blog/biome-v2-3/?utm_source=openai
- 2: https://next.biomejs.dev/blog/biome-v2-3/?utm_source=openai
- 3: [lint] Biome doesn't support Tailwind v4 syntax vercel/next.js#82826
🏁 Script executed:
#!/bin/bash
# Check for CI/lint workflow files
fd -a -H -t f '(^|/)\.github/workflows' -E node_modules | head -20
# Check for any existing lint configs or ignore patterns
find . -maxdepth 3 -name '.stylelintignore' -o -name '.biomeignore' -o -name 'stylelint.ignore' 2>/dev/null
# Look for any Tailwind config that might affect linting
fd -a -H -t f '(tailwind\.config|tailwind\.preset)' -E node_modules -E dist -E build | head -10Repository: AnExiledDev/CodeForge
Length of output: 47
Add Tailwind v4 directive support to Biome CSS parser configuration.
Biome 2.4.4 supports Tailwind v4 directives like @theme, but this support is opt-in. The current biome.json is missing the required configuration, which means Biome's CSS linter will flag @theme as an unknown at-rule and fail linting.
Update biome.json to add:
biome.json CSS parser fix
{
"css": {
"linter": {
"enabled": true
},
"parser": {
"cssModules": true,
"tailwindDirectives": true
}
}
}🧰 Tools
🪛 Biome (2.4.4)
[error] 19-49: Tailwind-specific syntax is disabled.
(parse)
🪛 Stylelint (17.3.0)
[error] 19-19: Unexpected unknown at-rule "@theme" (scss/at-rule-no-unknown)
(scss/at-rule-no-unknown)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/src/styles/global.css` around lines 1 - 49, The Biome CSS parser is not
configured to accept Tailwind v4 directives like `@theme`, so add/update the "css"
section in biome.json to enable the linter and set parser.tailwindDirectives to
true (and keep cssModules as needed); specifically ensure biome.json contains
"css": { "linter": { "enabled": true }, "parser": { "cssModules": true,
"tailwindDirectives": true } } so Biome stops flagging `@theme` as an unknown
at-rule.
- chromaterm: username auto-detection fallback to root now works - biome: nvm.sh sourcing no longer aborts under set -u - setup.js: ccstatusline-settings.json added to DEFAULT_PRESERVE - docs: spec-writer moved to Read-Only agents table (matches definition) - guard-readonly-bash.py: docstring corrected (stderr, not JSON stdout) - git-forensics SKILL.md: misleading "blame" comment fixed
CodeRabbit Review — ResponseFixed (commit e68327f)
Dismissed (with justification)ccms predictable /tmp cache path (Critical)
skill-suggester spec-* skills "in wrong plugin" (Critical)
claude-code-headless SDK "contradiction" (Major)
OPTIONS success code 204 vs 200 (Minor)
migration-patterns "never fix forward" too absolute (Minor)
GitHub Actions SHA pinning (Major)
release.yml non-atomic publish ordering (Major)
biome.json for Tailwind v4 @theme (Major)
svelte5 add "layercake" to skill-suggester terms (Critical)
|
Summary
/workspaces/.devcontainer/, two-layer bash detection (20+ write-target patterns + workspace path scan), fail-closed error handling, CWD context injection via 4 hook eventspermissionModeon all 17 agentshookSpecificOutputwrapper; commit-reminder, spec-reminder, advisory-test-runner upgraded to blockingjq -nininstall.shtoconfig/defaults/ccstatusline-settings.json, deployed via file-manifest withif-changedsemantics.${HOME}variable expansion added tosetup-config.shTest plan
config/defaults/to~/.config/ccstatusline/settings.json/workspaces/.devcontainer/cd docs && npm run build)Summary by CodeRabbit
Release Notes
New Features
Changes
Documentation