switchman-dev 0.1.3 → 0.1.5
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 +93 -495
- package/examples/README.md +18 -0
- package/examples/demo.sh +124 -0
- package/examples/setup.sh +13 -6
- package/examples/taskapi/.cursor/mcp.json +8 -0
- package/examples/taskapi/.mcp.json +8 -0
- package/examples/taskapi/src/middleware/auth.js +4 -0
- package/examples/taskapi/src/middleware/validate.js +4 -0
- package/examples/taskapi/src/routes/tasks.js +4 -0
- package/examples/taskapi/src/server.js +4 -0
- package/examples/walkthrough.sh +16 -9
- package/package.json +3 -3
- package/src/cli/index.js +1331 -274
- package/src/core/db.js +252 -2
- package/src/core/git.js +74 -1
- package/src/core/ignore.js +2 -0
- package/src/core/mcp.js +39 -10
- package/src/core/outcome.js +48 -11
- package/src/core/policy.js +49 -0
- package/src/core/queue.js +225 -0
package/examples/demo.sh
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# examples/demo.sh
|
|
3
|
+
#
|
|
4
|
+
# Short, recordable Switchman demo for terminal capture.
|
|
5
|
+
# Run AFTER setup.sh:
|
|
6
|
+
# bash examples/demo.sh
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
|
+
TASKAPI_DIR="$SCRIPT_DIR/taskapi"
|
|
13
|
+
WT_RATE="$SCRIPT_DIR/worktrees/agent-rate-limiting"
|
|
14
|
+
WT_VALID="$SCRIPT_DIR/worktrees/agent-validation"
|
|
15
|
+
|
|
16
|
+
if [ -f "$REPO_ROOT/src/cli/index.js" ]; then
|
|
17
|
+
SWITCHMAN=(node "$REPO_ROOT/src/cli/index.js")
|
|
18
|
+
else
|
|
19
|
+
SWITCHMAN=(switchman)
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
CYAN='\033[0;36m'
|
|
23
|
+
GREEN='\033[0;32m'
|
|
24
|
+
YELLOW='\033[1;33m'
|
|
25
|
+
BOLD='\033[1m'
|
|
26
|
+
RESET='\033[0m'
|
|
27
|
+
|
|
28
|
+
step() { echo ""; echo -e "${BOLD}── $1 ──────────────────────────────────────${RESET}"; }
|
|
29
|
+
info() { echo -e "${YELLOW}→${RESET} $1"; }
|
|
30
|
+
ok() { echo -e "${GREEN}✓${RESET} $1"; }
|
|
31
|
+
|
|
32
|
+
json_field() {
|
|
33
|
+
local payload="$1"
|
|
34
|
+
local expression="$2"
|
|
35
|
+
node -e "const data = JSON.parse(process.argv[1]); console.log(${expression});" "$payload"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
ensure_setup() {
|
|
39
|
+
if [ ! -d "$TASKAPI_DIR/.switchman" ]; then
|
|
40
|
+
echo "Switchman example is not set up yet."
|
|
41
|
+
echo "Run: bash examples/setup.sh"
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
append_demo_change() {
|
|
47
|
+
local file_path="$1"
|
|
48
|
+
local message="$2"
|
|
49
|
+
printf "\n// %s\n" "$message" >> "$file_path"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
git_commit_files() {
|
|
53
|
+
local repo_dir="$1"
|
|
54
|
+
local message="$2"
|
|
55
|
+
shift 2
|
|
56
|
+
if [ "$#" -gt 0 ]; then
|
|
57
|
+
git -C "$repo_dir" add "$@"
|
|
58
|
+
git -C "$repo_dir" commit -m "$message" -q
|
|
59
|
+
fi
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
ensure_setup
|
|
63
|
+
cd "$TASKAPI_DIR"
|
|
64
|
+
|
|
65
|
+
MAIN_BRANCH="$(git branch --show-current)"
|
|
66
|
+
|
|
67
|
+
echo ""
|
|
68
|
+
echo -e "${BOLD}━━━ Switchman Demo ━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
|
|
69
|
+
echo ""
|
|
70
|
+
echo "This is the short version: two agents, one blocked overlap, safe landing, clean gate."
|
|
71
|
+
|
|
72
|
+
step "1. Repo dashboard"
|
|
73
|
+
"${SWITCHMAN[@]}" status
|
|
74
|
+
|
|
75
|
+
step "2. Agent 1 starts work and locks files"
|
|
76
|
+
TASK1="$("${SWITCHMAN[@]}" lease next --json --worktree agent-rate-limiting --agent cursor)"
|
|
77
|
+
TASK1_ID="$(json_field "$TASK1" "data.task.id")"
|
|
78
|
+
"${SWITCHMAN[@]}" claim "$TASK1_ID" agent-rate-limiting src/middleware/auth.js src/server.js
|
|
79
|
+
append_demo_change "$WT_RATE/src/middleware/auth.js" "demo: rate-limiting agent touched auth middleware"
|
|
80
|
+
append_demo_change "$WT_RATE/src/server.js" "demo: rate-limiting agent touched server"
|
|
81
|
+
git_commit_files "$WT_RATE" "Demo: rate-limiting change" src/middleware/auth.js src/server.js
|
|
82
|
+
ok "Agent 1 has its own files and a committed branch"
|
|
83
|
+
|
|
84
|
+
step "3. Agent 2 tries to overlap and gets blocked"
|
|
85
|
+
TASK2="$("${SWITCHMAN[@]}" lease next --json --worktree agent-validation --agent cursor)"
|
|
86
|
+
TASK2_ID="$(json_field "$TASK2" "data.task.id")"
|
|
87
|
+
"${SWITCHMAN[@]}" claim "$TASK2_ID" agent-validation src/middleware/auth.js src/middleware/validate.js src/routes/tasks.js || true
|
|
88
|
+
info "Switchman blocks the overlapping claim before merge-time pain."
|
|
89
|
+
|
|
90
|
+
step "4. Agent 2 switches to safe files"
|
|
91
|
+
"${SWITCHMAN[@]}" claim "$TASK2_ID" agent-validation src/middleware/validate.js src/routes/tasks.js
|
|
92
|
+
append_demo_change "$WT_VALID/src/middleware/validate.js" "demo: validation agent touched validation middleware"
|
|
93
|
+
append_demo_change "$WT_VALID/src/routes/tasks.js" "demo: validation agent touched tasks route"
|
|
94
|
+
git_commit_files "$WT_VALID" "Demo: validation change" src/middleware/validate.js src/routes/tasks.js
|
|
95
|
+
ok "Agent 2 adapts instead of colliding"
|
|
96
|
+
|
|
97
|
+
step "5. Finish work and watch the repo stay readable"
|
|
98
|
+
"${SWITCHMAN[@]}" task done "$TASK1_ID"
|
|
99
|
+
"${SWITCHMAN[@]}" task done "$TASK2_ID"
|
|
100
|
+
"${SWITCHMAN[@]}" status --watch --max-cycles 1
|
|
101
|
+
|
|
102
|
+
step "6. Queue both finished branches for safe landing"
|
|
103
|
+
"${SWITCHMAN[@]}" queue add --worktree agent-rate-limiting --target "$MAIN_BRANCH"
|
|
104
|
+
"${SWITCHMAN[@]}" queue add --worktree agent-validation --target "$MAIN_BRANCH"
|
|
105
|
+
"${SWITCHMAN[@]}" queue status
|
|
106
|
+
|
|
107
|
+
step "7. Land the work safely"
|
|
108
|
+
"${SWITCHMAN[@]}" queue run --max-items 2 --target "$MAIN_BRANCH"
|
|
109
|
+
|
|
110
|
+
step "8. Final safety check"
|
|
111
|
+
"${SWITCHMAN[@]}" gate ci
|
|
112
|
+
|
|
113
|
+
echo ""
|
|
114
|
+
echo -e "${GREEN}✓ Demo complete.${RESET}"
|
|
115
|
+
echo ""
|
|
116
|
+
echo "What just happened:"
|
|
117
|
+
echo " • agents took different tasks"
|
|
118
|
+
echo " • Switchman blocked an overlapping file claim early"
|
|
119
|
+
echo " • finished branches landed through the queue"
|
|
120
|
+
echo " • the repo safety gate passed"
|
|
121
|
+
echo ""
|
|
122
|
+
echo "Reset and run again:"
|
|
123
|
+
echo " bash examples/teardown.sh && bash examples/setup.sh"
|
|
124
|
+
echo ""
|
package/examples/setup.sh
CHANGED
|
@@ -15,9 +15,16 @@
|
|
|
15
15
|
set -e
|
|
16
16
|
|
|
17
17
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
18
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
18
19
|
TASKAPI_DIR="$SCRIPT_DIR/taskapi"
|
|
19
20
|
WORKTREES_DIR="$SCRIPT_DIR/worktrees"
|
|
20
21
|
|
|
22
|
+
if [ -f "$REPO_ROOT/src/cli/index.js" ]; then
|
|
23
|
+
SWITCHMAN=(node "$REPO_ROOT/src/cli/index.js")
|
|
24
|
+
else
|
|
25
|
+
SWITCHMAN=(switchman)
|
|
26
|
+
fi
|
|
27
|
+
|
|
21
28
|
echo ""
|
|
22
29
|
echo "━━━ Switchman Example Setup ━━━━━━━━━━━━━━━━━━"
|
|
23
30
|
echo ""
|
|
@@ -68,26 +75,26 @@ git worktree list
|
|
|
68
75
|
|
|
69
76
|
echo ""
|
|
70
77
|
echo "→ Initialising Switchman in taskapi..."
|
|
71
|
-
|
|
78
|
+
"${SWITCHMAN[@]}" init
|
|
72
79
|
|
|
73
80
|
# ── Step 4: Seed tasks ────────────────────────────────────────────────────────
|
|
74
81
|
|
|
75
82
|
echo ""
|
|
76
83
|
echo "→ Seeding 4 parallel tasks..."
|
|
77
84
|
|
|
78
|
-
|
|
85
|
+
"${SWITCHMAN[@]}" task add "Add rate limiting to all API routes" \
|
|
79
86
|
--priority 8 \
|
|
80
87
|
--description "Token bucket: 100 req/min per API key. Return 429 with Retry-After header."
|
|
81
88
|
|
|
82
|
-
|
|
89
|
+
"${SWITCHMAN[@]}" task add "Add input validation to POST /tasks and PATCH /tasks/:id" \
|
|
83
90
|
--priority 7 \
|
|
84
91
|
--description "Validate title length, status enum, priority enum. Return 400 with descriptive errors."
|
|
85
92
|
|
|
86
|
-
|
|
93
|
+
"${SWITCHMAN[@]}" task add "Write tests for the auth middleware" \
|
|
87
94
|
--priority 6 \
|
|
88
95
|
--description "Test requireAuth and requireAdmin: valid key, missing key, bad key, wrong role."
|
|
89
96
|
|
|
90
|
-
|
|
97
|
+
"${SWITCHMAN[@]}" task add "Add pagination to GET /tasks" \
|
|
91
98
|
--priority 5 \
|
|
92
99
|
--description "Add ?page=1&limit=20. Return { tasks, count, page, totalPages }."
|
|
93
100
|
|
|
@@ -96,7 +103,7 @@ echo "━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
96
103
|
echo "✓ Setup complete."
|
|
97
104
|
echo ""
|
|
98
105
|
echo "Tasks ready:"
|
|
99
|
-
|
|
106
|
+
"${SWITCHMAN[@]}" task list
|
|
100
107
|
echo ""
|
|
101
108
|
echo "→ Next: bash examples/walkthrough.sh"
|
|
102
109
|
echo ""
|
|
@@ -131,3 +131,7 @@ module.exports = {
|
|
|
131
131
|
// Export primitives so routes can build ad-hoc schemas
|
|
132
132
|
validators: { required, isString, isNumber, isEmail, oneOf, maxLength, minLength, compose },
|
|
133
133
|
};
|
|
134
|
+
|
|
135
|
+
// demo: validation agent touched validation middleware
|
|
136
|
+
|
|
137
|
+
// demo: validation agent touched validation middleware
|
package/examples/walkthrough.sh
CHANGED
|
@@ -10,11 +10,18 @@
|
|
|
10
10
|
set -e
|
|
11
11
|
|
|
12
12
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
13
14
|
TASKAPI_DIR="$SCRIPT_DIR/taskapi"
|
|
14
15
|
WT_RATE="$SCRIPT_DIR/worktrees/agent-rate-limiting"
|
|
15
16
|
WT_VALID="$SCRIPT_DIR/worktrees/agent-validation"
|
|
16
17
|
WT_TESTS="$SCRIPT_DIR/worktrees/agent-tests"
|
|
17
18
|
|
|
19
|
+
if [ -f "$REPO_ROOT/src/cli/index.js" ]; then
|
|
20
|
+
SWITCHMAN=(node "$REPO_ROOT/src/cli/index.js")
|
|
21
|
+
else
|
|
22
|
+
SWITCHMAN=(switchman)
|
|
23
|
+
fi
|
|
24
|
+
|
|
18
25
|
# Colours
|
|
19
26
|
CYAN='\033[0;36m'
|
|
20
27
|
GREEN='\033[0;32m'
|
|
@@ -44,7 +51,7 @@ read -r
|
|
|
44
51
|
step "1. Starting state"
|
|
45
52
|
info "4 tasks waiting in the queue, 3 worktrees ready"
|
|
46
53
|
echo ""
|
|
47
|
-
|
|
54
|
+
"${SWITCHMAN[@]}" status
|
|
48
55
|
|
|
49
56
|
read -r
|
|
50
57
|
|
|
@@ -54,7 +61,7 @@ step "2. Agent 1 picks up the highest-priority task"
|
|
|
54
61
|
agent "agent-rate-limiting" "calling: switchman lease next --json"
|
|
55
62
|
echo ""
|
|
56
63
|
|
|
57
|
-
TASK1=$(
|
|
64
|
+
TASK1=$("${SWITCHMAN[@]}" lease next --json --worktree agent-rate-limiting --agent claude-code 2>/dev/null || echo "null")
|
|
58
65
|
TASK1_ID=$(echo "$TASK1" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['task']['id'])" 2>/dev/null || echo "")
|
|
59
66
|
|
|
60
67
|
if [ -z "$TASK1_ID" ]; then
|
|
@@ -73,7 +80,7 @@ step "3. Agent 1 claims the files it needs"
|
|
|
73
80
|
agent "agent-rate-limiting" "I'll be editing the middleware and server files"
|
|
74
81
|
echo ""
|
|
75
82
|
|
|
76
|
-
|
|
83
|
+
"${SWITCHMAN[@]}" claim "$TASK1_ID" agent-rate-limiting \
|
|
77
84
|
src/middleware/auth.js \
|
|
78
85
|
src/server.js
|
|
79
86
|
|
|
@@ -87,7 +94,7 @@ step "4. Agent 2 picks up the next task"
|
|
|
87
94
|
agent "agent-validation" "calling: switchman lease next --json"
|
|
88
95
|
echo ""
|
|
89
96
|
|
|
90
|
-
TASK2=$(
|
|
97
|
+
TASK2=$("${SWITCHMAN[@]}" lease next --json --worktree agent-validation --agent claude-code 2>/dev/null || echo "null")
|
|
91
98
|
TASK2_ID=$(echo "$TASK2" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['task']['id'])" 2>/dev/null || echo "")
|
|
92
99
|
ok "Task + lease assigned to agent-validation"
|
|
93
100
|
|
|
@@ -100,7 +107,7 @@ agent "agent-validation" "Input validation also touches auth.js..."
|
|
|
100
107
|
echo ""
|
|
101
108
|
|
|
102
109
|
# This should warn about the conflict
|
|
103
|
-
|
|
110
|
+
"${SWITCHMAN[@]}" claim "$TASK2_ID" agent-validation \
|
|
104
111
|
src/middleware/auth.js \
|
|
105
112
|
src/middleware/validate.js \
|
|
106
113
|
src/routes/tasks.js || true
|
|
@@ -117,7 +124,7 @@ step "6. Agent 2 claims only the files that aren't taken"
|
|
|
117
124
|
agent "agent-validation" "Claiming only validate.js and routes/tasks.js instead"
|
|
118
125
|
echo ""
|
|
119
126
|
|
|
120
|
-
|
|
127
|
+
"${SWITCHMAN[@]}" claim "$TASK2_ID" agent-validation \
|
|
121
128
|
src/middleware/validate.js \
|
|
122
129
|
src/routes/tasks.js
|
|
123
130
|
|
|
@@ -131,7 +138,7 @@ step "7. Full conflict scan across all worktrees"
|
|
|
131
138
|
info "This is what you'd run before any merge"
|
|
132
139
|
echo ""
|
|
133
140
|
|
|
134
|
-
|
|
141
|
+
"${SWITCHMAN[@]}" scan
|
|
135
142
|
|
|
136
143
|
read -r
|
|
137
144
|
|
|
@@ -141,7 +148,7 @@ step "8. Agent 1 finishes — marks task done and releases files"
|
|
|
141
148
|
agent "agent-rate-limiting" "Rate limiting implemented and committed."
|
|
142
149
|
echo ""
|
|
143
150
|
|
|
144
|
-
|
|
151
|
+
"${SWITCHMAN[@]}" task done "$TASK1_ID"
|
|
145
152
|
ok "Task done. src/middleware/auth.js and src/server.js are now free."
|
|
146
153
|
|
|
147
154
|
read -r
|
|
@@ -149,7 +156,7 @@ read -r
|
|
|
149
156
|
# ── Step 9: Final status ──────────────────────────────────────────────────────
|
|
150
157
|
|
|
151
158
|
step "9. Final status"
|
|
152
|
-
|
|
159
|
+
"${SWITCHMAN[@]}" status
|
|
153
160
|
|
|
154
161
|
echo ""
|
|
155
162
|
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchman-dev",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "Project manager for AI coding assistants running safely on one codebase",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"scripts": {
|
|
16
16
|
"start": "node src/cli/index.js",
|
|
17
17
|
"mcp": "node src/mcp/server.js",
|
|
18
|
-
"test": "node tests/test.js"
|
|
18
|
+
"test": "NODE_NO_WARNINGS=1 node tests/test.js"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@modelcontextprotocol/sdk": "^1.27.1",
|