瀏覽代碼

feat(sdd): add sdd-workspace helper for a self-ignoring artifact dir

Jesse Vincent 2 月之前
父節點
當前提交
207a12b203
共有 2 個文件被更改,包括 104 次插入0 次删除
  1. 22 0
      skills/subagent-driven-development/scripts/sdd-workspace
  2. 82 0
      tests/claude-code/test-sdd-workspace.sh

+ 22 - 0
skills/subagent-driven-development/scripts/sdd-workspace

@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+# Resolve and ensure the working-tree directory SDD uses for its short-lived
+# artifacts: task briefs, implementer reports, review packages, and the
+# progress ledger. Print the directory's absolute path.
+#
+# The workspace lives in the working tree (not under .git/) because Claude Code
+# treats .git/ as a protected path and denies agent writes there — which blocks
+# an implementer subagent from writing its report file. A self-ignoring
+# .gitignore keeps the workspace out of `git status` and out of accidental
+# commits without modifying any tracked file.
+#
+# Single source of truth for the workspace location, so task-brief and
+# review-package cannot drift to different directories.
+#
+# Usage: sdd-workspace
+set -euo pipefail
+
+root=$(git rev-parse --show-toplevel)
+dir="$root/.superpowers/sdd"
+mkdir -p "$dir"
+printf '*\n' > "$dir/.gitignore"
+cd "$dir" && pwd

+ 82 - 0
tests/claude-code/test-sdd-workspace.sh

@@ -0,0 +1,82 @@
+#!/usr/bin/env bash
+# Tests for the SDD workspace: scripts/sdd-workspace resolves a self-ignoring
+# working-tree directory for SDD artifacts, and the SDD scripts write into it.
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
+SDD_SCRIPTS="$REPO_ROOT/skills/subagent-driven-development/scripts"
+
+FAILURES=0
+TEST_ROOT=""
+
+pass() { echo "  [PASS] $1"; }
+fail() {
+    echo "  [FAIL] $1"
+    FAILURES=$((FAILURES + 1))
+}
+
+cleanup() {
+    if [[ -n "$TEST_ROOT" && -d "$TEST_ROOT" ]]; then
+        rm -rf "$TEST_ROOT"
+    fi
+}
+
+main() {
+    echo "=== Test: sdd-workspace ==="
+
+    TEST_ROOT="$(mktemp -d)"
+    trap cleanup EXIT
+
+    # Resolve repo to its physical path so string comparisons match the
+    # helper's output (git rev-parse --show-toplevel resolves symlinks; on
+    # macOS mktemp lives under /var -> /private/var).
+    git init -q -b main "$TEST_ROOT/repo"
+    local repo
+    repo="$(cd "$TEST_ROOT/repo" && git rev-parse --show-toplevel)"
+
+    local dir
+    dir="$(cd "$repo" && "$SDD_SCRIPTS/sdd-workspace")"
+
+    if [[ "$dir" == "$repo/.superpowers/sdd" ]]; then
+        pass "prints <repo-root>/.superpowers/sdd"
+    else
+        fail "prints <repo-root>/.superpowers/sdd"
+        echo "    got: $dir"
+    fi
+
+    if [[ -f "$repo/.superpowers/sdd/.gitignore" && "$(cat "$repo/.superpowers/sdd/.gitignore")" == "*" ]]; then
+        pass "self-ignoring .gitignore created with '*'"
+    else
+        fail "self-ignoring .gitignore created with '*'"
+    fi
+
+    printf 'x\n' > "$repo/.superpowers/sdd/artifact.md"
+    local status
+    status="$(cd "$repo" && git status --porcelain)"
+    if [[ -z "$status" ]]; then
+        pass "workspace invisible to git status"
+    else
+        fail "workspace invisible to git status"
+        echo "    status: $status"
+    fi
+
+    ( cd "$repo" && git add -A )
+    local staged
+    staged="$(cd "$repo" && git diff --cached --name-only)"
+    if [[ -z "$staged" ]]; then
+        pass "git add -A does not stage the workspace"
+    else
+        fail "git add -A does not stage the workspace"
+        echo "    staged: $staged"
+    fi
+
+    echo ""
+    if [[ "$FAILURES" -ne 0 ]]; then
+        echo "FAILED: $FAILURES assertion(s)."
+        exit 1
+    fi
+    echo "PASS"
+}
+
+main "$@"