@windyroad/jtbd 0.1.1

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.
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "wr-jtbd",
3
+ "version": "0.1.0",
4
+ "description": "Jobs-to-be-done enforcement for Claude Code"
5
+ }
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: agent
3
+ description: Jobs To Be Done reviewer. Use before editing any user-facing UI files.
4
+ Reads docs/JOBS_TO_BE_DONE.md and PRODUCT_DISCOVERY.md and reviews proposed changes
5
+ against documented jobs, persona constraints, and screen mappings. Reports alignment
6
+ or gaps.
7
+ tools:
8
+ - Read
9
+ - Glob
10
+ - Grep
11
+ - Bash
12
+ model: inherit
13
+ ---
14
+
15
+ You are the JTBD Lead. You review proposed UI changes against the project's Jobs To Be Done documentation and persona definitions before any user-facing code is edited. You are a reviewer, not an editor.
16
+
17
+ ## Your Role
18
+
19
+ 1. Read `docs/jtbd/README.md` for the index of all personas and jobs
20
+ 2. Read the relevant persona and job files matching the area being edited
21
+ 3. Read the file(s) being edited to understand what user flow is changing
22
+ 4. Review proposed changes against every documented job and the persona
23
+ 5. Report: PASS if aligned, or list specific misalignments and gaps
24
+
25
+ ## What You Check
26
+
27
+ All review criteria come from the JTBD documentation. Read the docs first and apply them. Typical checks include:
28
+
29
+ ### Job Alignment
30
+ - Does the change serve a documented job? Match the change to a specific job ID
31
+ - If the change introduces a new user flow not covered by any job, flag it as a job gap
32
+ - If the change contradicts the intent of a documented job, flag it as a misalignment
33
+
34
+ ### Persona Fit
35
+ - Read the persona definitions from the JTBD docs
36
+ - Check the change against the primary persona's context constraints as documented
37
+ - Flag changes that conflict with documented persona needs
38
+
39
+ ### Screen Mapping
40
+ - Is the file being edited mapped to a specific job in the Job-to-Screen Mapping table?
41
+ - If adding a new route or page, does it have a corresponding job documented?
42
+ - Are `// @jtbd` annotations present and correct?
43
+
44
+ ### API / Action Alignment
45
+ - If the change involves API interactions, do the actions align with the job's expected flow?
46
+ - Are new actions documented in the relevant job's action list?
47
+
48
+ ## How to Report
49
+
50
+ If the change aligns with documented jobs:
51
+ > **JTBD Review: PASS**
52
+ > Change serves job: `[job-id]` — [brief alignment summary]
53
+ > Persona fit: confirmed — [which constraints were checked]
54
+
55
+ If there are misalignments or gaps:
56
+
57
+ > **JTBD Review: ISSUES FOUND**
58
+ >
59
+ > 1. **[Job Gap / Persona Mismatch / Missing Annotation]**
60
+ > - **File**: `path`, Line ~N
61
+ > - **Issue**: What is misaligned
62
+ > - **Job**: Which job is affected (or "no matching job")
63
+ > - **Fix**: Suggested resolution (update JTBD doc, adjust UI, add annotation)
64
+ >
65
+ > 2. ...
66
+
67
+ ## Guide Gap Detection
68
+
69
+ If the code introduces a user flow, screen, or interaction not covered by the JTBD docs, flag this as a job gap:
70
+
71
+ > **JTBD Review: JOB UPDATE NEEDED**
72
+ >
73
+ > The code introduces [flow/screen/interaction] which is not covered by any documented job.
74
+ > Recommended addition to JTBD docs: [specific job definition to add]
75
+
76
+ If the code serves a user type not described by the existing persona:
77
+
78
+ > **JTBD Review: PERSONA UPDATE NEEDED**
79
+ >
80
+ > The code serves [user type/context] which is not described by the current persona.
81
+ > Recommended update to persona docs: [specific persona attributes to add]
82
+
83
+ These are FAIL verdicts — the JTBD documentation must be updated before the code can proceed.
84
+
85
+ ## Verdict
86
+
87
+ After completing your review, write your verdict to `/tmp/jtbd-verdict`:
88
+ - `printf 'PASS' > /tmp/jtbd-verdict` — change aligns with documented jobs and persona
89
+ - `printf 'FAIL' > /tmp/jtbd-verdict` — misalignment, job gap, or persona gap detected
90
+
91
+ ## Constraints
92
+
93
+ - You are read-only. You do not edit files (except writing the verdict file).
94
+ - You review user-facing UI files.
95
+ - If the change is purely structural with no user-visible impact (CSS refactor, types, imports), report PASS.
96
+ - Do not review accessibility (that is accessibility-lead's job).
97
+ - Do not review styling (that is style-guide-lead's job).
98
+ - Do not review copy/tone (that is voice-and-tone-lead's job).
99
+ - Focus on: does this change serve a real user job, and does it fit the persona?
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { resolve, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ const utils = await import(resolve(__dirname, "../lib/install-utils.mjs"));
8
+
9
+ const PLUGIN = "wr-jtbd";
10
+ const DEPS = [];
11
+
12
+ const flags = utils.parseStandardArgs(process.argv);
13
+
14
+ if (flags.help) {
15
+ console.log(`
16
+ Usage: npx @windyroad/jtbd [options]
17
+
18
+ Jobs-to-be-done enforcement for UI changes
19
+
20
+ Options:
21
+ --update Update this plugin and its skills
22
+ --uninstall Remove this plugin
23
+ --dry-run Show what would be done without executing
24
+ --help, -h Show this help
25
+ `);
26
+ process.exit(0);
27
+ }
28
+
29
+ if (flags.dryRun) {
30
+ utils.setDryRun(true);
31
+ console.log("[dry-run mode — no commands will be executed]\n");
32
+ }
33
+
34
+ utils.checkPrerequisites();
35
+
36
+ if (flags.uninstall) {
37
+ utils.uninstallPackage(PLUGIN);
38
+ } else if (flags.update) {
39
+ utils.updatePackage(PLUGIN);
40
+ } else {
41
+ utils.installPackage(PLUGIN, { deps: DEPS });
42
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "hooks": {
3
+ "UserPromptSubmit": [
4
+ { "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/jtbd-eval.sh" }] }
5
+ ],
6
+ "PreToolUse": [
7
+ { "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/jtbd-enforce-edit.sh" }] }
8
+ ],
9
+ "PostToolUse": [
10
+ { "matcher": "Agent", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/jtbd-mark-reviewed.sh" }] }
11
+ ],
12
+ "Stop": [
13
+ { "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/jtbd-reset-marker.sh" }] }
14
+ ]
15
+ }
16
+ }
@@ -0,0 +1,58 @@
1
+ #!/bin/bash
2
+ # JTBD - PreToolUse enforcement hook
3
+ # BLOCKS Edit/Write to UI files until jtbd-lead is consulted.
4
+ # Uses shared review-gate.sh for TTL, drift detection, and fail-closed.
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
7
+ source "$SCRIPT_DIR/lib/review-gate.sh"
8
+
9
+ INPUT=$(cat)
10
+
11
+ FILE_PATH=$(echo "$INPUT" | python3 -c "
12
+ import sys, json
13
+ try:
14
+ data = json.load(sys.stdin)
15
+ print(data.get('tool_input', {}).get('file_path', ''))
16
+ except:
17
+ print('')
18
+ " 2>/dev/null || echo "")
19
+
20
+ SESSION_ID=$(echo "$INPUT" | python3 -c "
21
+ import sys, json
22
+ try:
23
+ data = json.load(sys.stdin)
24
+ print(data.get('session_id', ''))
25
+ except:
26
+ print('')
27
+ " 2>/dev/null || echo "")
28
+
29
+ if [ -z "$SESSION_ID" ]; then
30
+ review_gate_parse_error
31
+ exit 0
32
+ fi
33
+
34
+ if [ -z "$FILE_PATH" ]; then
35
+ exit 0
36
+ fi
37
+
38
+ # Gate UI component and route files
39
+ case "$FILE_PATH" in
40
+ *.html|*.jsx|*.tsx|*.vue|*.svelte|*.ejs|*.hbs) ;;
41
+ *) exit 0 ;;
42
+ esac
43
+
44
+ BASENAME=$(basename "$FILE_PATH")
45
+
46
+ # If no JTBD doc exists, block and direct to create skill
47
+ if [ ! -f "docs/JOBS_TO_BE_DONE.md" ]; then
48
+ review_gate_deny "BLOCKED: Cannot edit '${BASENAME}' because docs/JOBS_TO_BE_DONE.md does not exist. Run /wr-jtbd:create to generate a JTBD document for this project, then delegate to wr-jtbd:agent for review."
49
+ exit 0
50
+ fi
51
+
52
+ # Check gate with TTL + drift detection
53
+ if check_review_gate "$SESSION_ID" "jtbd" "docs/JOBS_TO_BE_DONE.md"; then
54
+ exit 0
55
+ fi
56
+
57
+ review_gate_deny "BLOCKED: Cannot edit '${BASENAME}' without JTBD review. You MUST first delegate to wr-jtbd:agent using the Agent tool (subagent_type: 'wr-jtbd:agent'). ${REVIEW_GATE_REASON}"
58
+ exit 0
@@ -0,0 +1,35 @@
1
+ #!/bin/bash
2
+ # JTBD - UserPromptSubmit hook
3
+ # Detects JOBS_TO_BE_DONE.md in the project and injects delegation instruction.
4
+ # If the file doesn't exist, instructs Claude to create it via the agent.
5
+
6
+ if [ -f "docs/JOBS_TO_BE_DONE.md" ]; then
7
+ cat <<'HOOK_OUTPUT'
8
+ INSTRUCTION: MANDATORY JTBD CHECK. YOU MUST FOLLOW THIS.
9
+ DETECTED: docs/JOBS_TO_BE_DONE.md exists in this project.
10
+
11
+ This is a NON-OPTIONAL instruction. You MUST use the jtbd-lead agent
12
+ before editing any user-facing UI file (.html, .jsx, .tsx, .vue, .svelte, .ejs, .hbs).
13
+ This is proactive. Do not wait for the user to ask.
14
+
15
+ REQUIRED ACTIONS:
16
+ 1. Use the Agent tool to delegate to wr-jtbd:agent
17
+ (subagent_type: "wr-jtbd:agent")
18
+ 2. The jtbd-lead will review proposed changes against docs/JOBS_TO_BE_DONE.md
19
+ and PRODUCT_DISCOVERY.md persona definitions
20
+ 3. Do NOT write or edit UI code without jtbd-lead review FIRST
21
+ 4. Do NOT skip this step even if you think you can handle it yourself
22
+
23
+ SCOPE: User-facing files (.html, .jsx, .tsx, .vue, .svelte, .ejs, .hbs).
24
+ Does NOT apply to: .css files, .ts/.js backend files, config files.
25
+ HOOK_OUTPUT
26
+ else
27
+ # Check if this is a web project (has UI files)
28
+ if ls src/**/*.tsx src/**/*.jsx src/**/*.html 2>/dev/null | head -1 | grep -q .; then
29
+ cat <<'HOOK_OUTPUT'
30
+ NOTE: This project has UI files but no docs/JOBS_TO_BE_DONE.md.
31
+ If the user's task involves editing UI files, the edit will be blocked
32
+ until a JTBD document exists. Run /wr-jtbd:create to generate one.
33
+ HOOK_OUTPUT
34
+ fi
35
+ fi
@@ -0,0 +1,50 @@
1
+ #!/bin/bash
2
+ # JTBD - PostToolUse hook for Agent tool
3
+ # Creates session markers when jtbd-lead has been consulted with PASS verdict.
4
+ # Handles both edit review and plan review verdicts.
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
7
+ source "$SCRIPT_DIR/lib/review-gate.sh"
8
+
9
+ INPUT=$(cat)
10
+
11
+ SUBAGENT=$(echo "$INPUT" | jq -r '.tool_input.subagent_type // empty') || true
12
+ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty') || true
13
+
14
+ if [ -z "$SESSION_ID" ]; then
15
+ exit 0
16
+ fi
17
+
18
+ case "$SUBAGENT" in
19
+ *jtbd-lead*|*wr-jtbd*)
20
+ # Check for edit review verdict
21
+ VERDICT_FILE="/tmp/jtbd-verdict"
22
+ VERDICT=""
23
+ if [ -f "$VERDICT_FILE" ]; then
24
+ VERDICT=$(cat "$VERDICT_FILE")
25
+ rm -f "$VERDICT_FILE"
26
+ fi
27
+
28
+ case "$VERDICT" in
29
+ PASS)
30
+ touch "/tmp/jtbd-reviewed-${SESSION_ID}"
31
+ store_review_hash "$SESSION_ID" "jtbd" "docs/jtbd"
32
+ ;;
33
+ FAIL)
34
+ # Do NOT create marker — review found issues
35
+ ;;
36
+ *)
37
+ # No verdict file — backward compat, allow with marker
38
+ touch "/tmp/jtbd-reviewed-${SESSION_ID}"
39
+ store_review_hash "$SESSION_ID" "jtbd" "docs/jtbd"
40
+ ;;
41
+ esac
42
+
43
+ # Plan review: agent completion = reviewed.
44
+ # The main agent must actually run the review agent to reach this hook.
45
+ # No verdict file needed — PostToolUse:Agent is the unforgeable signal.
46
+ touch "/tmp/jtbd-plan-reviewed-${SESSION_ID}"
47
+ ;;
48
+ esac
49
+
50
+ exit 0
@@ -0,0 +1,16 @@
1
+ #!/bin/bash
2
+ # Stop hook: Clears JTBD review session markers.
3
+
4
+ INPUT=$(cat)
5
+
6
+ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty') || true
7
+
8
+ if [ -n "$SESSION_ID" ]; then
9
+ rm -f "/tmp/jtbd-reviewed-${SESSION_ID}" \
10
+ "/tmp/jtbd-reviewed-${SESSION_ID}.hash" \
11
+ "/tmp/jtbd-verdict" \
12
+ "/tmp/jtbd-plan-reviewed-${SESSION_ID}" \
13
+ "/tmp/jtbd-plan-verdict"
14
+ fi
15
+
16
+ exit 0
@@ -0,0 +1,102 @@
1
+ #!/bin/bash
2
+ # Shared gate logic for review enforcement hooks (a11y, voice-tone, style-guide).
3
+ # Sourced by *-enforce-edit.sh hooks and review-plan-enforce.sh.
4
+ # Provides: check_review_gate, review_gate_deny, review_gate_parse_error
5
+
6
+ # Source shared portable helpers (_mtime, _hashcmd)
7
+ _REVIEW_GATE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ source "$_REVIEW_GATE_DIR/gate-helpers.sh"
9
+
10
+ # Check review gate marker. Returns 0 if marker is valid (allow), 1 if invalid (deny).
11
+ # Sets REVIEW_GATE_REASON on failure.
12
+ # Usage: check_review_gate "$SESSION_ID" "style-guide" "docs/STYLE-GUIDE.md"
13
+ check_review_gate() {
14
+ local SESSION_ID="$1"
15
+ local SYSTEM="$2" # e.g., "a11y", "voice-tone", "style-guide"
16
+ local POLICY_FILE="$3" # e.g., "docs/STYLE-GUIDE.md"
17
+ local MARKER="/tmp/${SYSTEM}-reviewed-${SESSION_ID}"
18
+ local HASH_FILE="/tmp/${SYSTEM}-reviewed-${SESSION_ID}.hash"
19
+ local TTL_SECONDS="${REVIEW_TTL:-600}"
20
+
21
+ # 1. Marker must exist
22
+ if [ ! -f "$MARKER" ]; then
23
+ REVIEW_GATE_REASON="No ${SYSTEM} review marker found. The ${SYSTEM} agent must review first."
24
+ return 1
25
+ fi
26
+
27
+ # 2. TTL check — marker mtime must be within TTL
28
+ local NOW=$(date +%s)
29
+ local MARKER_TIME=$(_mtime "$MARKER")
30
+ local AGE=$(( NOW - MARKER_TIME ))
31
+ if [ "$AGE" -ge "$TTL_SECONDS" ]; then
32
+ rm -f "$MARKER" "$HASH_FILE"
33
+ REVIEW_GATE_REASON="${SYSTEM} review expired (${AGE}s old, TTL ${TTL_SECONDS}s). Re-run the ${SYSTEM} agent."
34
+ return 1
35
+ fi
36
+
37
+ # 3. Drift detection — policy file hash must match
38
+ if [ -f "$HASH_FILE" ] && [ -n "$POLICY_FILE" ]; then
39
+ local STORED_HASH=$(cat "$HASH_FILE")
40
+ local CURRENT_HASH=""
41
+ if [ -f "$POLICY_FILE" ]; then
42
+ CURRENT_HASH=$(cat "$POLICY_FILE" | _hashcmd | cut -d' ' -f1)
43
+ elif [ -d "$POLICY_FILE" ]; then
44
+ # Directory (e.g., docs/decisions/) — hash all .md files
45
+ CURRENT_HASH=$(find "$POLICY_FILE" -name '*.md' -not -name 'README.md' -print0 | sort -z | xargs -0 cat 2>/dev/null | _hashcmd | cut -d' ' -f1)
46
+ else
47
+ CURRENT_HASH="missing"
48
+ fi
49
+ if [ "$STORED_HASH" != "$CURRENT_HASH" ]; then
50
+ rm -f "$MARKER" "$HASH_FILE"
51
+ REVIEW_GATE_REASON="${SYSTEM} policy file changed since last review. Re-run the ${SYSTEM} agent."
52
+ return 1
53
+ fi
54
+ fi
55
+
56
+ # Slide TTL window forward
57
+ touch "$MARKER"
58
+ return 0
59
+ }
60
+
61
+ # Store policy file hash after a successful review.
62
+ # Usage: store_review_hash "$SESSION_ID" "style-guide" "docs/STYLE-GUIDE.md"
63
+ store_review_hash() {
64
+ local SESSION_ID="$1"
65
+ local SYSTEM="$2"
66
+ local POLICY_FILE="$3"
67
+ local HASH_FILE="/tmp/${SYSTEM}-reviewed-${SESSION_ID}.hash"
68
+
69
+ if [ -n "$POLICY_FILE" ]; then
70
+ local HASH=""
71
+ if [ -f "$POLICY_FILE" ]; then
72
+ HASH=$(cat "$POLICY_FILE" | _hashcmd | cut -d' ' -f1)
73
+ elif [ -d "$POLICY_FILE" ]; then
74
+ HASH=$(find "$POLICY_FILE" -name '*.md' -not -name 'README.md' -print0 | sort -z | xargs -0 cat 2>/dev/null | _hashcmd | cut -d' ' -f1)
75
+ else
76
+ HASH="missing"
77
+ fi
78
+ echo "$HASH" > "$HASH_FILE"
79
+ fi
80
+ }
81
+
82
+ # Emit fail-closed deny JSON for PreToolUse hooks.
83
+ review_gate_deny() {
84
+ local REASON="$1"
85
+ cat <<EOF
86
+ {
87
+ "hookSpecificOutput": {
88
+ "hookEventName": "PreToolUse",
89
+ "permissionDecision": "deny",
90
+ "permissionDecisionReason": "$REASON"
91
+ }
92
+ }
93
+ EOF
94
+ }
95
+
96
+ # Emit fail-closed deny JSON for parse failures.
97
+ review_gate_parse_error() {
98
+ cat <<'EOF'
99
+ { "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny",
100
+ "permissionDecisionReason": "BLOCKED: Could not parse hook input. Gate is fail-closed." } }
101
+ EOF
102
+ }
@@ -0,0 +1,163 @@
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
+ try {
51
+ execSync("npx --version", { stdio: "pipe" });
52
+ } catch {
53
+ console.error(
54
+ "Error: 'npx' not found. Install Node.js first:\n https://nodejs.org\n"
55
+ );
56
+ process.exit(1);
57
+ }
58
+ }
59
+
60
+ export function addMarketplace() {
61
+ return run(
62
+ `claude plugin marketplace add ${MARKETPLACE_REPO}`,
63
+ `Marketplace: ${MARKETPLACE_NAME}`
64
+ );
65
+ }
66
+
67
+ export function installPlugin(pluginName) {
68
+ return run(
69
+ `claude plugin install ${pluginName}@${MARKETPLACE_NAME}`,
70
+ pluginName
71
+ );
72
+ }
73
+
74
+ export function updatePlugin(pluginName) {
75
+ return run(`claude plugin update ${pluginName}`, pluginName);
76
+ }
77
+
78
+ export function uninstallPlugin(pluginName) {
79
+ return run(`claude plugin uninstall ${pluginName}`, `Removing ${pluginName}`);
80
+ }
81
+
82
+ export function installSkills() {
83
+ return run(
84
+ `npx -y skills add --yes --all ${MARKETPLACE_REPO}`,
85
+ "Skills (via skills package)"
86
+ );
87
+ }
88
+
89
+ export function updateSkills() {
90
+ return run("npx -y skills update", "Skills update");
91
+ }
92
+
93
+ export function removeSkills() {
94
+ return run(
95
+ `npx -y skills remove --yes --all ${MARKETPLACE_REPO}`,
96
+ "Removing skills"
97
+ );
98
+ }
99
+
100
+ /**
101
+ * Install a single package: marketplace add + plugin install + skills.
102
+ */
103
+ export function installPackage(pluginName, { deps = [] } = {}) {
104
+ console.log(`\nInstalling @windyroad/${pluginName.replace("wr-", "")}...\n`);
105
+
106
+ addMarketplace();
107
+ installPlugin(pluginName);
108
+ installSkills();
109
+
110
+ if (deps.length > 0) {
111
+ console.log(`\nNote: This plugin works best with:`);
112
+ for (const dep of deps) {
113
+ console.log(` - @windyroad/${dep.replace("wr-", "")} (npx @windyroad/${dep.replace("wr-", "")})`);
114
+ }
115
+ }
116
+
117
+ console.log(
118
+ `\nDone! Restart Claude Code to activate.\n`
119
+ );
120
+ }
121
+
122
+ /**
123
+ * Update a single package.
124
+ */
125
+ export function updatePackage(pluginName) {
126
+ console.log(`\nUpdating @windyroad/${pluginName.replace("wr-", "")}...\n`);
127
+
128
+ run(
129
+ `claude plugin marketplace update ${MARKETPLACE_NAME}`,
130
+ "Updating marketplace"
131
+ );
132
+ updatePlugin(pluginName);
133
+ updateSkills();
134
+
135
+ console.log("\nDone! Restart Claude Code to apply updates.\n");
136
+ }
137
+
138
+ /**
139
+ * Uninstall a single package.
140
+ */
141
+ export function uninstallPackage(pluginName) {
142
+ console.log(`\nUninstalling @windyroad/${pluginName.replace("wr-", "")}...\n`);
143
+
144
+ uninstallPlugin(pluginName);
145
+
146
+ console.log("\nDone. Restart Claude Code to apply changes.\n");
147
+ console.log("Note: Skills are shared across packages. Run");
148
+ console.log(" npx @windyroad/agent-plugins --uninstall");
149
+ console.log("to remove all skills.\n");
150
+ }
151
+
152
+ /**
153
+ * Parse standard flags used by all per-plugin installers.
154
+ */
155
+ export function parseStandardArgs(argv) {
156
+ const args = argv.slice(2);
157
+ return {
158
+ help: args.includes("--help") || args.includes("-h"),
159
+ uninstall: args.includes("--uninstall"),
160
+ update: args.includes("--update"),
161
+ dryRun: args.includes("--dry-run"),
162
+ };
163
+ }
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@windyroad/jtbd",
3
+ "version": "0.1.1",
4
+ "description": "Jobs-to-be-done enforcement for UI changes",
5
+ "bin": {
6
+ "windyroad-jtbd": "./bin/install.mjs"
7
+ },
8
+ "type": "module",
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/windyroad/agent-plugins.git",
13
+ "directory": "packages/jtbd"
14
+ },
15
+ "keywords": [
16
+ "claude-code",
17
+ "claude-code-plugin",
18
+ "ai-agent",
19
+ "ai-coding"
20
+ ],
21
+ "files": [
22
+ "bin/",
23
+ "agents/",
24
+ "hooks/",
25
+ "skills/",
26
+ ".claude-plugin/",
27
+ "lib/"
28
+ ]
29
+ }
@@ -0,0 +1,96 @@
1
+ ---
2
+ name: wr:jtbd
3
+ description: Create or update the project's docs/JOBS_TO_BE_DONE.md by examining existing features and asking the user about user jobs, personas, and desired outcomes.
4
+ allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
5
+ ---
6
+
7
+ # Jobs To Be Done Document Generator
8
+
9
+ Create or update `docs/JOBS_TO_BE_DONE.md` tailored to this project's users and their goals. The jtbd-lead agent reads this file to review UI changes against user jobs.
10
+
11
+ ## What belongs in docs/JOBS_TO_BE_DONE.md
12
+
13
+ - **User personas**: Who uses this product and what characterises them
14
+ - **Jobs**: What users are trying to accomplish (functional, emotional, social)
15
+ - **Job stories**: "When [situation], I want to [motivation], so I can [expected outcome]"
16
+ - **Desired outcomes**: Measurable results users want from each job
17
+ - **Current solutions**: How users currently accomplish these jobs (competitors, workarounds)
18
+ - **Last reviewed date**: When the document was last reviewed or updated
19
+
20
+ ## Steps
21
+
22
+ ### 1. Discover project context
23
+
24
+ Examine the project to understand what it does and who uses it.
25
+
26
+ **Find the product definition** by scanning for:
27
+ - README.md and documentation
28
+ - Landing page or marketing content
29
+ - Product discovery documents (PRODUCT_DISCOVERY.md, personas, user research)
30
+ - Route/page structure (reveals user workflows)
31
+ - Feature flags or configuration (reveals capabilities)
32
+
33
+ **Discover user workflows**:
34
+ - Map the main user-facing pages/screens and their purpose
35
+ - Identify the core user journey (what do users do from start to finish?)
36
+ - Look for onboarding flows, dashboards, settings, or admin areas
37
+ - Check for different user roles (admin, member, viewer, etc.)
38
+
39
+ **Discover existing JTBD artefacts**:
40
+ - User stories in issues or project boards
41
+ - Persona documents
42
+ - User research notes or interview transcripts
43
+ - Analytics configuration (what events are tracked?)
44
+
45
+ ### 2. Check for existing document
46
+
47
+ If `docs/JOBS_TO_BE_DONE.md` already exists, read it. Identify:
48
+ - Whether jobs still match the current feature set
49
+ - Whether personas still reflect the actual user base
50
+ - Whether the last reviewed date is stale (> 2 weeks)
51
+
52
+ ### 3. Draft the JTBD document
53
+
54
+ Based on project discovery, draft sections covering:
55
+
56
+ **Personas** (2-4):
57
+ For each persona, describe: who they are, what characterises them, what they care about, and what frustrates them. Ground these in the actual product, not generic archetypes.
58
+
59
+ **Jobs** (3-8):
60
+ For each job:
61
+ - A job statement: "Help [persona] [accomplish goal] when [situation]"
62
+ - Whether it's functional, emotional, or social
63
+ - Priority (must-have, important, nice-to-have)
64
+
65
+ **Job Stories** (1-2 per job):
66
+ "When [situation], I want to [motivation], so I can [expected outcome]"
67
+
68
+ **Desired Outcomes** (per job):
69
+ What does success look like? How would the user measure it?
70
+
71
+ **Current Solutions**:
72
+ How do users currently accomplish these jobs without (or with competitors to) this product?
73
+
74
+ ### 4. Confirm with the user
75
+
76
+ You MUST use the AskUserQuestion tool to collect user confirmation.
77
+
78
+ Present:
79
+ 1. The drafted personas and ask if they're accurate
80
+ 2. The jobs identified and ask if they cover the core value proposition
81
+ 3. The job stories and ask if the situations and motivations ring true
82
+ 4. Whether any user segments or jobs are missing
83
+
84
+ ### 5. Write docs/JOBS_TO_BE_DONE.md
85
+
86
+ Write the document including:
87
+ - A header with "Last reviewed" date (today's date)
88
+ - All sections from step 3, refined based on user feedback from step 4
89
+ - A note that the wr-jtbd:agent reads this file to review UI changes against user jobs
90
+
91
+ If updating rather than creating:
92
+ - Preserve existing content the user hasn't asked to change
93
+ - Show the user a diff of what changed
94
+ - Update the "Last reviewed" date
95
+
96
+ $ARGUMENTS