@windyroad/architect 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # @windyroad/architect
2
+
3
+ **Architecture decision enforcement for Claude Code.** Ensures every code change is reviewed against your project's architecture decisions before it lands.
4
+
5
+ Part of [Windy Road Agent Plugins](../../README.md).
6
+
7
+ ## What It Does
8
+
9
+ The architect plugin prevents architectural drift by gating edits behind an architecture review. When you have a `docs/decisions/` directory, the plugin:
10
+
11
+ 1. **Detects** your architecture decisions on every prompt
12
+ 2. **Blocks** edits to project files until the architect agent has reviewed the proposed changes
13
+ 3. **Reviews** changes against your existing ADRs (Architecture Decision Records) and flags conflicts
14
+ 4. **Flags** when a new decision should be documented
15
+
16
+ No decisions directory yet? The plugin stays silent until you create one.
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ npx @windyroad/architect
22
+ ```
23
+
24
+ Restart Claude Code after installing.
25
+
26
+ ## Usage
27
+
28
+ Once installed, the plugin works automatically. You don't need to invoke it -- it intercepts edits and runs the review before allowing changes through.
29
+
30
+ **Create a new Architecture Decision Record:**
31
+
32
+ ```
33
+ /wr-architect:create-adr
34
+ ```
35
+
36
+ This walks you through creating an ADR in [MADR 4.0](https://adr.github.io/madr/) format. It examines your existing decisions, asks about the problem and options, and writes a properly formatted record to `docs/decisions/`.
37
+
38
+ ## How It Works
39
+
40
+ | Hook | Trigger | What it does |
41
+ |------|---------|-------------|
42
+ | `architect-detect.sh` | Every prompt | Checks for `docs/decisions/` and injects the review instruction |
43
+ | `architect-enforce-edit.sh` | Edit or Write | Blocks the edit if the architect hasn't reviewed yet |
44
+ | `architect-plan-enforce.sh` | ExitPlanMode | Ensures plans are reviewed before execution |
45
+ | `architect-mark-reviewed.sh` | Agent completes | Marks the review as done (TTL: 1800s) |
46
+ | `architect-refresh-hash.sh` | After edit | Refreshes the content hash so the next edit triggers a fresh review |
47
+
48
+ ## Agent
49
+
50
+ The `wr-architect:agent` reviews proposed changes against existing decisions in `docs/decisions/` and reports:
51
+
52
+ - Whether changes comply with or violate existing decisions
53
+ - Whether a new ADR should be created
54
+ - Whether existing decisions are stale and need reassessment
55
+
56
+ ## Updating and Uninstalling
57
+
58
+ ```bash
59
+ npx @windyroad/architect --update
60
+ npx @windyroad/architect --uninstall
61
+ ```
62
+
63
+ ## Licence
64
+
65
+ [MIT](../../LICENSE)
package/agents/agent.md CHANGED
@@ -10,7 +10,6 @@ tools:
10
10
  - Read
11
11
  - Glob
12
12
  - Grep
13
- - Bash
14
13
  model: inherit
15
14
  ---
16
15
 
@@ -107,20 +106,9 @@ Issue types:
107
106
  - **[Missing Supersession]**: A new decision should supersede an old one but doesn't
108
107
  - **[Confirmation Violation]**: New code violates a confirmation criterion of an existing decision
109
108
 
110
- ## Verdict File
111
-
112
- After completing your review, you MUST write a verdict file so the hook system knows the outcome:
113
-
114
- - After **PASS**: `echo "PASS" > /tmp/architect-verdict`
115
- - After **ISSUES FOUND**: `echo "FAIL" > /tmp/architect-verdict`
116
-
117
- Advisory items (staleness flags) do NOT count as FAIL. Only write FAIL when there are actionable issues (Decision Conflict, Undocumented Decision, Decision Format, Missing Supersession, Confirmation Violation).
118
-
119
- You MUST NOT use Bash for anything other than writing the verdict file.
120
-
121
109
  ## Constraints
122
110
 
123
- - You are read-only. You do not edit files (the only exception is writing the verdict file via Bash).
111
+ - You are read-only. You do not edit files.
124
112
  - You review all project files: source code, configuration, CI workflows, hook scripts, build scripts, and decision files. The only exclusions are stylesheets, images, lockfiles, and font files.
125
113
  - If the change is purely cosmetic (comments, formatting, whitespace), report PASS.
126
114
  - Do not block changes that are clearly within the scope of an existing accepted decision.
package/bin/install.mjs CHANGED
@@ -4,7 +4,7 @@ import { resolve, dirname } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
 
6
6
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
- const utils = await import(resolve(__dirname, "../../shared/install-utils.mjs"));
7
+ const utils = await import(resolve(__dirname, "../lib/install-utils.mjs"));
8
8
 
9
9
  const PLUGIN = "wr-architect";
10
10
  const DEPS = [];
@@ -20,6 +20,7 @@ Architecture decision enforcement for AI coding agents
20
20
  Options:
21
21
  --update Update this plugin and its skills
22
22
  --uninstall Remove this plugin
23
+ --scope Installation scope: project (default) or user
23
24
  --dry-run Show what would be done without executing
24
25
  --help, -h Show this help
25
26
  `);
@@ -38,5 +39,5 @@ if (flags.uninstall) {
38
39
  } else if (flags.update) {
39
40
  utils.updatePackage(PLUGIN);
40
41
  } else {
41
- utils.installPackage(PLUGIN, { deps: DEPS });
42
+ utils.installPackage(PLUGIN, { deps: DEPS, scope: flags.scope });
42
43
  }
@@ -20,6 +20,17 @@ if [ -z "$FILE_PATH" ]; then
20
20
  exit 0
21
21
  fi
22
22
 
23
+ # P004: Only gate files inside the project root. Absolute paths outside
24
+ # $PWD (e.g., ~/.claude/channels/*) are not project files.
25
+ case "$FILE_PATH" in
26
+ /*)
27
+ case "$FILE_PATH" in
28
+ "$PWD"/*) ;;
29
+ *) exit 0 ;;
30
+ esac
31
+ ;;
32
+ esac
33
+
23
34
  # Only gate if the project has architecture decisions
24
35
  if [ ! -d "docs/decisions" ]; then
25
36
  exit 0
@@ -53,6 +64,17 @@ case "$FILE_PATH" in
53
64
  exit 0 ;;
54
65
  */docs/problems/*.md|docs/problems/*.md)
55
66
  exit 0 ;;
67
+ # Peer-plugin policy files — governed by their own plugin's enforce hook, not architect (P009)
68
+ */docs/JOBS_TO_BE_DONE.md|docs/JOBS_TO_BE_DONE.md)
69
+ exit 0 ;;
70
+ */docs/PRODUCT_DISCOVERY.md|docs/PRODUCT_DISCOVERY.md)
71
+ exit 0 ;;
72
+ */docs/jtbd/*|docs/jtbd/*)
73
+ exit 0 ;;
74
+ */docs/VOICE-AND-TONE.md|docs/VOICE-AND-TONE.md)
75
+ exit 0 ;;
76
+ */docs/STYLE-GUIDE.md|docs/STYLE-GUIDE.md)
77
+ exit 0 ;;
56
78
  esac
57
79
 
58
80
  # Check gate
@@ -1,17 +1,19 @@
1
1
  #!/bin/bash
2
2
  # Architecture - PostToolUse hook for Agent tool
3
3
  # Creates a session marker when architect has been consulted.
4
+ # Parses verdict from agent output text (session-safe, no temp files).
4
5
  # This marker unlocks the architect-enforce-edit.sh PreToolUse block.
5
- # Mirrors: voice-tone-mark-reviewed.sh
6
6
 
7
- # Source shared portable helpers (_mtime, _hashcmd)
8
7
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
9
8
  source "$SCRIPT_DIR/lib/gate-helpers.sh"
10
9
 
11
- INPUT=$(cat)
10
+ _parse_input
12
11
 
13
- SUBAGENT=$(echo "$INPUT" | jq -r '.tool_input.subagent_type // empty') || true
14
- SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty') || true
12
+ TOOL_NAME=$(_get_tool_name)
13
+ [ "$TOOL_NAME" = "Agent" ] || exit 0
14
+
15
+ SUBAGENT=$(_get_subagent_type)
16
+ SESSION_ID=$(_get_session_id)
15
17
 
16
18
  if [ -z "$SESSION_ID" ]; then
17
19
  exit 0
@@ -19,25 +21,24 @@ fi
19
21
 
20
22
  case "$SUBAGENT" in
21
23
  *architect*)
22
- # Check verdict file from architect agent
23
- VERDICT_FILE="/tmp/architect-verdict"
24
+ # Parse verdict from agent output text (no temp file needed)
25
+ AGENT_OUTPUT=$(_get_tool_output)
24
26
  VERDICT=""
25
- if [ -f "$VERDICT_FILE" ]; then
26
- VERDICT=$(cat "$VERDICT_FILE")
27
- rm -f "$VERDICT_FILE"
27
+ if echo "$AGENT_OUTPUT" | grep -q "Architecture Review: PASS"; then
28
+ VERDICT="PASS"
29
+ elif echo "$AGENT_OUTPUT" | grep -q "ISSUES FOUND"; then
30
+ VERDICT="FAIL"
28
31
  fi
29
32
 
30
33
  case "$VERDICT" in
31
34
  PASS)
32
- # Architect explicitly passed, create marker
33
35
  touch "/tmp/architect-reviewed-${SESSION_ID}"
34
36
  ;;
35
37
  FAIL)
36
- # Architect found issues, do NOT create marker
38
+ # Do NOT create marker review found issues
37
39
  ;;
38
40
  *)
39
- # No verdict file (agent error or old agent version)
40
- # Allow with warning to avoid permanent lockout
41
+ # Could not parse verdict allow with marker to avoid lockout
41
42
  touch "/tmp/architect-reviewed-${SESSION_ID}"
42
43
  ;;
43
44
  esac
@@ -52,6 +53,8 @@ case "$SUBAGENT" in
52
53
  echo "$HASH" > "/tmp/architect-reviewed-${SESSION_ID}.hash"
53
54
  fi
54
55
 
56
+ # Plan review marker
57
+ touch "/tmp/architect-plan-reviewed-${SESSION_ID}"
55
58
  ;;
56
59
  esac
57
60
 
package/hooks/hooks.json CHANGED
@@ -10,9 +10,6 @@
10
10
  "PostToolUse": [
11
11
  { "matcher": "Agent", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-mark-reviewed.sh" }] },
12
12
  { "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-refresh-hash.sh" }] }
13
- ],
14
- "Stop": [
15
- { "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-reset-marker.sh" }] }
16
13
  ]
17
14
  }
18
15
  }
@@ -12,7 +12,7 @@ source "$_ARCHITECT_GATE_DIR/gate-helpers.sh"
12
12
  check_architect_gate() {
13
13
  local SESSION_ID="$1"
14
14
  local MARKER="/tmp/architect-reviewed-${SESSION_ID}"
15
- local TTL_SECONDS="${ARCHITECT_TTL:-600}"
15
+ local TTL_SECONDS="${ARCHITECT_TTL:-1800}"
16
16
 
17
17
  if [ -n "$SESSION_ID" ] && [ -f "$MARKER" ]; then
18
18
  local NOW=$(date +%s)
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # Tests for architect-enforce-edit.sh — verifies peer-plugin policy files are
4
+ # exempt from the architect gate (P009). Each plugin governs its own policy
5
+ # docs via its own enforce hook; the architect should not re-gate them.
6
+ #
7
+ # All tests are functional (P011): they execute the hook with mock JSON
8
+ # input and assert on exit status + BLOCKED output. Source-grep assertions
9
+ # were removed because they over-specified the implementation and gave
10
+ # false confidence (a literal string can appear in source without the
11
+ # matching case branch actually short-circuiting).
12
+
13
+ setup() {
14
+ SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
15
+ HOOK="$SCRIPT_DIR/architect-enforce-edit.sh"
16
+ ORIG_DIR="$PWD"
17
+ TEST_DIR=$(mktemp -d)
18
+ cd "$TEST_DIR"
19
+ # Engage the gate: architect-enforce only runs when docs/decisions/ exists.
20
+ mkdir -p docs/decisions
21
+ }
22
+
23
+ teardown() {
24
+ cd "$ORIG_DIR"
25
+ rm -rf "$TEST_DIR"
26
+ }
27
+
28
+ # Helper: run the hook with a mock JSON input for a given file path.
29
+ # Claude Code passes absolute file_path values, so tests use $PWD-prefixed
30
+ # paths to match real shape (after the P004 root check).
31
+ run_hook_with_file() {
32
+ local file_path="$1"
33
+ local json="{\"tool_input\":{\"file_path\":\"${file_path}\"},\"session_id\":\"test-session-$$\"}"
34
+ echo "$json" | bash "$HOOK"
35
+ }
36
+
37
+ assert_path_allowed() {
38
+ local file_path="$1"
39
+ run run_hook_with_file "$file_path"
40
+ [ "$status" -eq 0 ]
41
+ [[ "$output" != *"BLOCKED"* ]]
42
+ }
43
+
44
+ @test "architect: exempts JTBD policy file (P009)" {
45
+ assert_path_allowed "$PWD/docs/JOBS_TO_BE_DONE.md"
46
+ }
47
+
48
+ @test "architect: exempts JTBD directory file (P009)" {
49
+ assert_path_allowed "$PWD/docs/jtbd/solo-developer/persona.md"
50
+ }
51
+
52
+ @test "architect: exempts PRODUCT_DISCOVERY.md (P009)" {
53
+ assert_path_allowed "$PWD/docs/PRODUCT_DISCOVERY.md"
54
+ }
55
+
56
+ @test "architect: exempts voice-tone policy file (P009)" {
57
+ assert_path_allowed "$PWD/docs/VOICE-AND-TONE.md"
58
+ }
59
+
60
+ @test "architect: exempts style-guide policy file (P009)" {
61
+ assert_path_allowed "$PWD/docs/STYLE-GUIDE.md"
62
+ }
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # Tests for architect-gate.sh (TTL, drift, marker)
4
+
5
+ setup() {
6
+ SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
7
+ source "$SCRIPT_DIR/lib/architect-gate.sh"
8
+ TEST_SESSION="test-$$-$BATS_TEST_NUMBER"
9
+ }
10
+
11
+ teardown() {
12
+ rm -f "/tmp/architect-reviewed-${TEST_SESSION}"
13
+ rm -f "/tmp/architect-reviewed-${TEST_SESSION}.hash"
14
+ }
15
+
16
+ @test "gate denies when no marker exists" {
17
+ run check_architect_gate "$TEST_SESSION"
18
+ [ "$status" -ne 0 ]
19
+ }
20
+
21
+ @test "gate allows when marker exists and is fresh" {
22
+ touch "/tmp/architect-reviewed-${TEST_SESSION}"
23
+ run check_architect_gate "$TEST_SESSION"
24
+ [ "$status" -eq 0 ]
25
+ }
26
+
27
+ @test "gate denies when marker is expired" {
28
+ touch "/tmp/architect-reviewed-${TEST_SESSION}"
29
+ # Set TTL to 0 to force expiry
30
+ ARCHITECT_TTL=0 run check_architect_gate "$TEST_SESSION"
31
+ [ "$status" -ne 0 ]
32
+ }
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # Tests for architect verdict parsing from output text
4
+
5
+ @test "grep matches 'Architecture Review: PASS'" {
6
+ output="Some preamble text\n\n**Architecture Review: PASS**\n\nNo conflicts."
7
+ echo -e "$output" | grep -q "Architecture Review: PASS"
8
+ }
9
+
10
+ @test "grep matches 'ISSUES FOUND'" {
11
+ output="**Architecture Review: ISSUES FOUND**\n\n1. [Decision Conflict]"
12
+ echo -e "$output" | grep -q "ISSUES FOUND"
13
+ }
14
+
15
+ @test "grep does NOT match unrelated text" {
16
+ output="The review is complete. Everything looks good."
17
+ ! echo -e "$output" | grep -q "Architecture Review: PASS"
18
+ }
19
+
20
+ @test "subagent pattern matches wr-architect:agent" {
21
+ SUBAGENT="wr-architect:agent"
22
+ case "$SUBAGENT" in
23
+ *architect*) true ;;
24
+ *) false ;;
25
+ esac
26
+ }
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # P001 / ADR-009: Stop-hook marker reset removed. Marker lifecycle is
4
+ # governed by TTL + drift detection. This test asserts the Stop hook
5
+ # entry is absent from the plugin's hooks.json.
6
+
7
+ setup() {
8
+ PLUGIN_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/../.." && pwd)"
9
+ }
10
+
11
+ @test "architect: hooks.json has no Stop hook entry (ADR-009)" {
12
+ ! grep -q '"Stop"' "$PLUGIN_DIR/hooks/hooks.json"
13
+ }
14
+
15
+ @test "architect: architect-reset-marker.sh has been removed" {
16
+ [ ! -f "$PLUGIN_DIR/hooks/architect-reset-marker.sh" ]
17
+ }
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # Tests for architect-enforce-edit.sh project-root check (P004).
4
+ # Verifies that absolute paths outside $PWD are exempted.
5
+
6
+ setup() {
7
+ SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
8
+ HOOK="$SCRIPT_DIR/architect-enforce-edit.sh"
9
+ ORIG_DIR="$PWD"
10
+ }
11
+
12
+ teardown() {
13
+ cd "$ORIG_DIR"
14
+ }
15
+
16
+ run_hook_with_file() {
17
+ local file_path="$1"
18
+ local json="{\"tool_input\":{\"file_path\":\"${file_path}\"},\"session_id\":\"test-session-$$\"}"
19
+ echo "$json" | bash "$HOOK"
20
+ }
21
+
22
+ @test "project-root: absolute path outside project exits 0" {
23
+ run run_hook_with_file "/Users/other/somewhere/file.json"
24
+ [ "$status" -eq 0 ]
25
+ [[ "$output" != *"BLOCKED"* ]]
26
+ }
27
+
28
+ @test "project-root: home-dir config path exits 0" {
29
+ run run_hook_with_file "/Users/somebody/.claude/channels/discord/access.json"
30
+ [ "$status" -eq 0 ]
31
+ [[ "$output" != *"BLOCKED"* ]]
32
+ }
33
+
34
+ @test "project-root: absolute path inside \$PWD is still gated" {
35
+ # Use a temp dir as PWD with docs/decisions to trigger the gate
36
+ TEST_DIR=$(mktemp -d)
37
+ mkdir -p "$TEST_DIR/docs/decisions"
38
+ echo "# ADR" > "$TEST_DIR/docs/decisions/001-test.proposed.md"
39
+ cd "$TEST_DIR"
40
+ run run_hook_with_file "$TEST_DIR/src/index.ts"
41
+ [ "$status" -eq 0 ]
42
+ [[ "$output" == *"BLOCKED"* ]]
43
+ rm -rf "$TEST_DIR"
44
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Shared install utilities for @windyroad/* packages.
3
+ * Used by both per-plugin installers and the meta-installer.
4
+ */
5
+
6
+ import { execSync } from "node:child_process";
7
+
8
+ const MARKETPLACE_REPO = "windyroad/agent-plugins";
9
+ const MARKETPLACE_NAME = "windyroad";
10
+
11
+ let _dryRun = false;
12
+
13
+ export { MARKETPLACE_REPO, MARKETPLACE_NAME };
14
+
15
+ export function setDryRun(value) {
16
+ _dryRun = value;
17
+ }
18
+
19
+ export function isDryRun() {
20
+ return _dryRun;
21
+ }
22
+
23
+ export function run(cmd, label) {
24
+ console.log(` ${label}...`);
25
+ if (_dryRun) {
26
+ console.log(` [dry-run] ${cmd}`);
27
+ return true;
28
+ }
29
+ try {
30
+ execSync(cmd, { stdio: "inherit" });
31
+ return true;
32
+ } catch {
33
+ console.error(` FAILED: ${label}`);
34
+ return false;
35
+ }
36
+ }
37
+
38
+ export function checkPrerequisites() {
39
+ if (_dryRun) return;
40
+
41
+ try {
42
+ execSync("claude --version", { stdio: "pipe" });
43
+ } catch {
44
+ console.error(
45
+ "Error: 'claude' CLI not found. Install Claude Code first:\n https://docs.anthropic.com/en/docs/claude-code\n"
46
+ );
47
+ process.exit(1);
48
+ }
49
+ }
50
+
51
+ export function addMarketplace() {
52
+ return run(
53
+ `claude plugin marketplace add ${MARKETPLACE_REPO}`,
54
+ `Marketplace: ${MARKETPLACE_NAME}`
55
+ );
56
+ }
57
+
58
+ export function installPlugin(pluginName, { scope = "project" } = {}) {
59
+ return run(
60
+ `claude plugin install ${pluginName}@${MARKETPLACE_NAME} --scope ${scope}`,
61
+ pluginName
62
+ );
63
+ }
64
+
65
+ export function updatePlugin(pluginName) {
66
+ return run(`claude plugin update ${pluginName}`, pluginName);
67
+ }
68
+
69
+ export function uninstallPlugin(pluginName) {
70
+ return run(`claude plugin uninstall ${pluginName}`, `Removing ${pluginName}`);
71
+ }
72
+
73
+ /**
74
+ * Install a single package: marketplace add + plugin install.
75
+ */
76
+ export function installPackage(pluginName, { deps = [], scope = "project" } = {}) {
77
+ console.log(`\nInstalling @windyroad/${pluginName.replace("wr-", "")} (${scope} scope)...\n`);
78
+
79
+ addMarketplace();
80
+ installPlugin(pluginName, { scope });
81
+
82
+ if (deps.length > 0) {
83
+ console.log(`\nNote: This plugin works best with:`);
84
+ for (const dep of deps) {
85
+ console.log(` - @windyroad/${dep.replace("wr-", "")} (npx @windyroad/${dep.replace("wr-", "")})`);
86
+ }
87
+ }
88
+
89
+ console.log(
90
+ `\nDone! Restart Claude Code to activate.\n`
91
+ );
92
+ }
93
+
94
+ /**
95
+ * Update a single package.
96
+ */
97
+ export function updatePackage(pluginName) {
98
+ console.log(`\nUpdating @windyroad/${pluginName.replace("wr-", "")}...\n`);
99
+
100
+ run(
101
+ `claude plugin marketplace update ${MARKETPLACE_NAME}`,
102
+ "Updating marketplace"
103
+ );
104
+ updatePlugin(pluginName);
105
+
106
+ console.log("\nDone! Restart Claude Code to apply updates.\n");
107
+ }
108
+
109
+ /**
110
+ * Uninstall a single package.
111
+ */
112
+ export function uninstallPackage(pluginName) {
113
+ console.log(`\nUninstalling @windyroad/${pluginName.replace("wr-", "")}...\n`);
114
+
115
+ uninstallPlugin(pluginName);
116
+
117
+ console.log("\nDone. Restart Claude Code to apply changes.\n");
118
+ }
119
+
120
+ /**
121
+ * Parse standard flags used by all per-plugin installers.
122
+ */
123
+ export function parseStandardArgs(argv) {
124
+ const args = argv.slice(2);
125
+ const flags = {
126
+ help: args.includes("--help") || args.includes("-h"),
127
+ uninstall: args.includes("--uninstall"),
128
+ update: args.includes("--update"),
129
+ dryRun: args.includes("--dry-run"),
130
+ scope: "project",
131
+ };
132
+ const scopeIdx = args.indexOf("--scope");
133
+ if (scopeIdx !== -1 && args[scopeIdx + 1]) {
134
+ const val = args[scopeIdx + 1];
135
+ if (["project", "user", "local"].includes(val)) {
136
+ flags.scope = val;
137
+ } else {
138
+ console.error("--scope requires: project, user, or local");
139
+ process.exit(1);
140
+ }
141
+ }
142
+ return flags;
143
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/architect",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Architecture decision enforcement for AI coding agents",
5
5
  "bin": {
6
6
  "windyroad-architect": "./bin/install.mjs"
@@ -23,6 +23,7 @@
23
23
  "agents/",
24
24
  "hooks/",
25
25
  "skills/",
26
- ".claude-plugin/"
26
+ ".claude-plugin/",
27
+ "lib/"
27
28
  ]
28
29
  }
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: wr:adr
2
+ name: wr-architect:create-adr
3
3
  description: Create a new Architecture Decision Record (MADR 4.0) in docs/decisions/. Examines existing decisions, asks about the problem and options, and writes a properly formatted ADR.
4
4
  allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
5
5
  ---
@@ -0,0 +1,91 @@
1
+ ---
2
+ name: wr-architect:review-design
3
+ description: On-demand architecture compliance review. Checks staged changes and recent commits against existing ADRs in docs/decisions/. Use before editing architecture-bearing files or before a release.
4
+ allowed-tools: Read, Glob, Grep, Bash, AskUserQuestion, Skill
5
+ ---
6
+
7
+ # Architecture Compliance Review Skill
8
+
9
+ Run an architecture compliance review on demand — outside the pre-tool-use hook gate. Reviews staged changes and recent commits against the project's ADRs in `docs/decisions/`.
10
+
11
+ This skill is **read-only**. It does not commit, push, or modify files.
12
+
13
+ ## When to use
14
+
15
+ - Pre-flight before a release or client handover: confirm no ADR violations crept in
16
+ - After a large refactor: verify the new structure still complies with decisions
17
+ - When proposing a structural change: get a review before editing architecture-bearing files
18
+ - Any time the hook gate is not convenient: e.g., planning mode, exploratory spikes
19
+
20
+ ## Steps
21
+
22
+ ### 1. Parse arguments
23
+
24
+ Read `$ARGUMENTS` for an explicit review scope (e.g., "review my changes to the auth module", "check the new API routes", "pre-release review"). If a scope is provided, use it. If empty, proceed to auto-detection.
25
+
26
+ ### 2. Auto-detect context
27
+
28
+ Run the following to establish what needs reviewing:
29
+
30
+ ```bash
31
+ # Staged changes
32
+ git diff --cached --stat
33
+
34
+ # Recent commits not yet pushed
35
+ git log origin/$(git rev-parse --abbrev-ref HEAD)..HEAD --oneline 2>/dev/null || git log HEAD -5 --oneline
36
+
37
+ # Changed files
38
+ git diff --cached --name-only
39
+ git diff --name-only HEAD
40
+ ```
41
+
42
+ Summarise:
43
+ - Files staged or recently committed
44
+ - Whether the changes are architectural (source code, config, schema, tooling) vs purely documentary
45
+
46
+ ### 3. Resolve ambiguity
47
+
48
+ If there are no staged changes and no recent unpushed commits, use `AskUserQuestion` to ask:
49
+
50
+ > "I don't see any staged or unpushed changes. What would you like me to review?
51
+ > (a) A specific set of files — please name them
52
+ > (b) All changes since the last tag
53
+ > (c) A planned change you'd like to describe
54
+ > (d) Cancel"
55
+
56
+ Do not ask if there is an obvious set of changed files.
57
+
58
+ ### 4. Construct the assessment prompt
59
+
60
+ Build a self-contained prompt for the architect subagent that includes:
61
+ - The list of changed/staged files
62
+ - The git diff summary (stat output)
63
+ - Any explicit scope from the user
64
+ - The request: "Review these proposed changes against the project's ADRs. Flag any violations, gaps that need a new ADR, or compliance questions."
65
+
66
+ ### 5. Delegate to wr-architect:agent
67
+
68
+ Invoke the architect subagent via the `Skill` tool:
69
+
70
+ ```
71
+ subagent_type: wr-architect:agent
72
+ prompt: <constructed review prompt from step 4>
73
+ ```
74
+
75
+ Wait for the subagent to complete.
76
+
77
+ ### 6. Present results
78
+
79
+ Present the full compliance report to the user. The architect subagent will report:
80
+ - PASS: no violations found
81
+ - FLAGGED: specific violations or compliance questions with ADR references
82
+ - NEW ADR NEEDED: decisions that should be recorded before proceeding
83
+
84
+ If violations are flagged, use `AskUserQuestion` to ask how the user wants to proceed:
85
+ - (a) Address the violations before continuing
86
+ - (b) Proceed with a documented exception
87
+ - (c) Draft a new or amended ADR to legitimise the approach
88
+
89
+ Do not make the decision unilaterally — per ADR-013 Rule 1, architectural risk decisions are the user's.
90
+
91
+ $ARGUMENTS
@@ -1,15 +0,0 @@
1
- #!/bin/bash
2
- # Architecture - Stop hook
3
- # Removes the architect session marker so the next turn requires a fresh review.
4
- # Mirrors: voice-tone-reset-marker.sh
5
-
6
- INPUT=$(cat)
7
-
8
- SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty') || true
9
-
10
- if [ -n "$SESSION_ID" ]; then
11
- rm -f "/tmp/architect-reviewed-${SESSION_ID}"
12
- rm -f "/tmp/architect-reviewed-${SESSION_ID}.hash"
13
- fi
14
-
15
- exit 0