@omnicoreos/planka-mcp 0.2.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.
Files changed (126) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/CONTRIBUTING.md +37 -0
  3. package/CREDITS.md +39 -0
  4. package/LICENSE +22 -0
  5. package/README.es.md +213 -0
  6. package/README.md +213 -0
  7. package/dist/client.d.ts +96 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +281 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/errors.d.ts +85 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/errors.js +226 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/identity.generated.d.ts +3 -0
  16. package/dist/identity.generated.d.ts.map +1 -0
  17. package/dist/identity.generated.js +4 -0
  18. package/dist/identity.generated.js.map +1 -0
  19. package/dist/index.d.ts +3 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +93 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/operations/attachments.d.ts +34 -0
  24. package/dist/operations/attachments.d.ts.map +1 -0
  25. package/dist/operations/attachments.js +89 -0
  26. package/dist/operations/attachments.js.map +1 -0
  27. package/dist/operations/board-id.d.ts +9 -0
  28. package/dist/operations/board-id.d.ts.map +1 -0
  29. package/dist/operations/board-id.js +86 -0
  30. package/dist/operations/board-id.js.map +1 -0
  31. package/dist/operations/boards.d.ts +143 -0
  32. package/dist/operations/boards.d.ts.map +1 -0
  33. package/dist/operations/boards.js +391 -0
  34. package/dist/operations/boards.js.map +1 -0
  35. package/dist/operations/cards.d.ts +36 -0
  36. package/dist/operations/cards.d.ts.map +1 -0
  37. package/dist/operations/cards.js +81 -0
  38. package/dist/operations/cards.js.map +1 -0
  39. package/dist/operations/comments.d.ts +28 -0
  40. package/dist/operations/comments.d.ts.map +1 -0
  41. package/dist/operations/comments.js +53 -0
  42. package/dist/operations/comments.js.map +1 -0
  43. package/dist/operations/labels.d.ts +60 -0
  44. package/dist/operations/labels.d.ts.map +1 -0
  45. package/dist/operations/labels.js +146 -0
  46. package/dist/operations/labels.js.map +1 -0
  47. package/dist/operations/lists.d.ts +15 -0
  48. package/dist/operations/lists.d.ts.map +1 -0
  49. package/dist/operations/lists.js +40 -0
  50. package/dist/operations/lists.js.map +1 -0
  51. package/dist/operations/projects.d.ts +24 -0
  52. package/dist/operations/projects.d.ts.map +1 -0
  53. package/dist/operations/projects.js +61 -0
  54. package/dist/operations/projects.js.map +1 -0
  55. package/dist/operations/tasks.d.ts +30 -0
  56. package/dist/operations/tasks.d.ts.map +1 -0
  57. package/dist/operations/tasks.js +111 -0
  58. package/dist/operations/tasks.js.map +1 -0
  59. package/dist/schemas/entities.d.ts +389 -0
  60. package/dist/schemas/entities.d.ts.map +1 -0
  61. package/dist/schemas/entities.js +202 -0
  62. package/dist/schemas/entities.js.map +1 -0
  63. package/dist/schemas/requests.d.ts +539 -0
  64. package/dist/schemas/requests.d.ts.map +1 -0
  65. package/dist/schemas/requests.js +187 -0
  66. package/dist/schemas/requests.js.map +1 -0
  67. package/dist/schemas/responses.d.ts +2626 -0
  68. package/dist/schemas/responses.d.ts.map +1 -0
  69. package/dist/schemas/responses.js +75 -0
  70. package/dist/schemas/responses.js.map +1 -0
  71. package/dist/tools/attachments.d.ts +270 -0
  72. package/dist/tools/attachments.d.ts.map +1 -0
  73. package/dist/tools/attachments.js +247 -0
  74. package/dist/tools/attachments.js.map +1 -0
  75. package/dist/tools/cards.d.ts +401 -0
  76. package/dist/tools/cards.d.ts.map +1 -0
  77. package/dist/tools/cards.js +368 -0
  78. package/dist/tools/cards.js.map +1 -0
  79. package/dist/tools/comments.d.ts +217 -0
  80. package/dist/tools/comments.d.ts.map +1 -0
  81. package/dist/tools/comments.js +208 -0
  82. package/dist/tools/comments.js.map +1 -0
  83. package/dist/tools/index.d.ts +1293 -0
  84. package/dist/tools/index.d.ts.map +1 -0
  85. package/dist/tools/index.js +62 -0
  86. package/dist/tools/index.js.map +1 -0
  87. package/dist/tools/labels.d.ts +214 -0
  88. package/dist/tools/labels.d.ts.map +1 -0
  89. package/dist/tools/labels.js +302 -0
  90. package/dist/tools/labels.js.map +1 -0
  91. package/dist/tools/lists.d.ts +117 -0
  92. package/dist/tools/lists.d.ts.map +1 -0
  93. package/dist/tools/lists.js +178 -0
  94. package/dist/tools/lists.js.map +1 -0
  95. package/dist/tools/navigation.d.ts +106 -0
  96. package/dist/tools/navigation.d.ts.map +1 -0
  97. package/dist/tools/navigation.js +151 -0
  98. package/dist/tools/navigation.js.map +1 -0
  99. package/dist/tools/queries.d.ts +380 -0
  100. package/dist/tools/queries.d.ts.map +1 -0
  101. package/dist/tools/queries.js +256 -0
  102. package/dist/tools/queries.js.map +1 -0
  103. package/dist/tools/tasks.d.ts +223 -0
  104. package/dist/tools/tasks.d.ts.map +1 -0
  105. package/dist/tools/tasks.js +169 -0
  106. package/dist/tools/tasks.js.map +1 -0
  107. package/docs/planka-2x-gotchas.md +192 -0
  108. package/docs/tools.md +659 -0
  109. package/docs/troubleshooting.md +138 -0
  110. package/package.json +71 -0
  111. package/project.identity.json +7 -0
  112. package/scripts/bootstrap-board.mjs +160 -0
  113. package/scripts/lib/planka-api.mjs +244 -0
  114. package/scripts/lib/workflow-template.mjs +26 -0
  115. package/scripts/setup.mjs +236 -0
  116. package/scripts/setup.sh +48 -0
  117. package/scripts/sync-identity.mjs +57 -0
  118. package/server.json +43 -0
  119. package/tests/smoke/planka-smoke.mjs +920 -0
  120. package/workflow/README.md +232 -0
  121. package/workflow/board-template.md +115 -0
  122. package/workflow/skills/planka-close-card/SKILL.md +81 -0
  123. package/workflow/skills/planka-orchestrator/SKILL.md +126 -0
  124. package/workflow/worktrees/README.md +104 -0
  125. package/workflow/worktrees/wt.conf.example +24 -0
  126. package/workflow/worktrees/wt.sh +345 -0
@@ -0,0 +1,345 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ die() {
6
+ echo "wt: $*" >&2
7
+ exit 1
8
+ }
9
+
10
+ info() {
11
+ echo " $*"
12
+ }
13
+
14
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ INITIAL_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || true)"
16
+ [[ -n "$INITIAL_ROOT" ]] || die "not inside a Git repository"
17
+ COMMON_DIR="$(git -C "$INITIAL_ROOT" rev-parse --path-format=absolute --git-common-dir)"
18
+ REPO_ROOT="$(dirname "$COMMON_DIR")"
19
+
20
+ CONFIG_FILE="$REPO_ROOT/.agent-worktrees.conf"
21
+ if [[ -f "$CONFIG_FILE" ]]; then
22
+ # This file is repository configuration and executes as shell syntax. Review it
23
+ # before trusting a cloned repository, just like any other executable script.
24
+ # shellcheck disable=SC1090
25
+ source "$CONFIG_FILE"
26
+ fi
27
+
28
+ detect_base_branch() {
29
+ local remote current
30
+ remote="$(git -C "$REPO_ROOT" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || true)"
31
+ if [[ -n "$remote" ]]; then
32
+ echo "${remote#origin/}"
33
+ elif git -C "$REPO_ROOT" show-ref --verify --quiet refs/heads/main; then
34
+ echo "main"
35
+ elif git -C "$REPO_ROOT" show-ref --verify --quiet refs/heads/master; then
36
+ echo "master"
37
+ else
38
+ current="$(git -C "$REPO_ROOT" branch --show-current)"
39
+ [[ -n "$current" ]] || die "cannot determine a base branch"
40
+ echo "$current"
41
+ fi
42
+ }
43
+
44
+ WT_BASE_BRANCH="${WT_BASE_BRANCH:-$(detect_base_branch)}"
45
+ WT_ROOT="${WT_ROOT:-.worktrees}"
46
+ WT_BRANCH_PREFIX="${WT_BRANCH_PREFIX:-agent/}"
47
+ WT_MAX_WORKTREES="${WT_MAX_WORKTREES:-4}"
48
+ WT_PORT_START="${WT_PORT_START:-}"
49
+ WT_DEV_COMMAND="${WT_DEV_COMMAND:-}"
50
+ WT_INSTALL_COMMAND="${WT_INSTALL_COMMAND:-}"
51
+ WT_ENV_TEMPLATE="${WT_ENV_TEMPLATE:-}"
52
+ WT_ENV_TARGET="${WT_ENV_TARGET:-.env}"
53
+
54
+ [[ "$WT_ROOT" = /* ]] || WT_ROOT="$REPO_ROOT/$WT_ROOT"
55
+ [[ "$WT_MAX_WORKTREES" =~ ^[1-9][0-9]*$ ]] || die "WT_MAX_WORKTREES must be a positive integer"
56
+ if [[ -n "$WT_PORT_START" ]]; then
57
+ [[ "$WT_PORT_START" =~ ^[1-9][0-9]*$ ]] || die "WT_PORT_START must be a positive integer"
58
+ fi
59
+
60
+ validate_slug() {
61
+ local slug="$1"
62
+ [[ "$slug" =~ ^[a-z0-9][a-z0-9-]{1,40}$ ]] ||
63
+ die "invalid slug '$slug'; use 2-41 lowercase letters, numbers, or hyphens"
64
+ }
65
+
66
+ wt_path() {
67
+ echo "$WT_ROOT/$1"
68
+ }
69
+
70
+ meta_file() {
71
+ echo "$WT_ROOT/.metadata/$1"
72
+ }
73
+
74
+ read_meta() {
75
+ local slug="$1" key="$2" file
76
+ file="$(meta_file "$slug")"
77
+ [[ -f "$file" ]] || return 1
78
+ sed -n "s/^${key}=//p" "$file" | head -n 1
79
+ }
80
+
81
+ existing_slugs() {
82
+ local path
83
+ [[ -d "$WT_ROOT" ]] || return 0
84
+ for path in "$WT_ROOT"/*; do
85
+ [[ -d "$path" ]] || continue
86
+ basename "$path"
87
+ done | sort
88
+ }
89
+
90
+ port_in_use() {
91
+ local port="$1"
92
+ [[ -n "$port" ]] || return 1
93
+ if command -v lsof >/dev/null 2>&1; then
94
+ lsof -nP -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1
95
+ elif command -v ss >/dev/null 2>&1; then
96
+ ss -ltn "sport = :$port" 2>/dev/null | grep -q LISTEN
97
+ else
98
+ return 1
99
+ fi
100
+ }
101
+
102
+ pick_free_port() {
103
+ [[ -n "$WT_PORT_START" ]] || return 0
104
+ local offset candidate slug assigned reserved
105
+ for ((offset = 0; offset < WT_MAX_WORKTREES; offset += 1)); do
106
+ candidate=$((WT_PORT_START + offset))
107
+ reserved=0
108
+ while IFS= read -r slug; do
109
+ [[ -n "$slug" ]] || continue
110
+ assigned="$(read_meta "$slug" PORT || true)"
111
+ [[ "$assigned" == "$candidate" ]] && reserved=1
112
+ done < <(existing_slugs)
113
+ if [[ "$reserved" -eq 0 ]] && ! port_in_use "$candidate"; then
114
+ echo "$candidate"
115
+ return 0
116
+ fi
117
+ done
118
+ return 1
119
+ }
120
+
121
+ discover_dev_command() {
122
+ if [[ -n "$WT_DEV_COMMAND" ]]; then
123
+ echo "$WT_DEV_COMMAND"
124
+ return 0
125
+ fi
126
+ [[ -f "$REPO_ROOT/package.json" ]] ||
127
+ die "set WT_DEV_COMMAND; no package.json was found"
128
+ node -e '
129
+ const p = require(process.argv[1]);
130
+ process.exit(p.scripts && p.scripts.dev ? 0 : 1);
131
+ ' "$REPO_ROOT/package.json" || die "set WT_DEV_COMMAND; package.json has no dev script"
132
+
133
+ if [[ -f "$REPO_ROOT/pnpm-lock.yaml" ]]; then echo "pnpm run dev"
134
+ elif [[ -f "$REPO_ROOT/yarn.lock" ]]; then echo "yarn dev"
135
+ elif [[ -f "$REPO_ROOT/bun.lock" || -f "$REPO_ROOT/bun.lockb" ]]; then echo "bun run dev"
136
+ elif [[ -f "$REPO_ROOT/package-lock.json" ]]; then echo "npm run dev"
137
+ else die "set WT_DEV_COMMAND; no recognized package manager lockfile was found"
138
+ fi
139
+ }
140
+
141
+ cmd_new() {
142
+ local slug="${1:-}" path branch count port template target
143
+ validate_slug "$slug"
144
+ path="$(wt_path "$slug")"
145
+ branch="${WT_BRANCH_PREFIX}${slug}"
146
+ [[ ! -e "$path" ]] || die "worktree already exists: $path"
147
+ git -C "$REPO_ROOT" show-ref --verify --quiet "refs/heads/$branch" &&
148
+ die "branch already exists: $branch"
149
+
150
+ count="$(existing_slugs | sed '/^$/d' | wc -l | tr -d ' ')"
151
+ ((count < WT_MAX_WORKTREES)) || die "maximum of $WT_MAX_WORKTREES live worktrees reached"
152
+ port="$(pick_free_port)" || die "no configured worktree port is free"
153
+
154
+ mkdir -p "$WT_ROOT"
155
+ git -C "$REPO_ROOT" worktree add -b "$branch" "$path" "$WT_BASE_BRANCH"
156
+ mkdir -p "$WT_ROOT/.metadata"
157
+ {
158
+ echo "SLUG=$slug"
159
+ echo "BRANCH=$branch"
160
+ echo "PORT=$port"
161
+ } > "$(meta_file "$slug")"
162
+
163
+ if [[ -n "$WT_ENV_TEMPLATE" ]]; then
164
+ template="$WT_ENV_TEMPLATE"
165
+ [[ "$template" = /* ]] || template="$REPO_ROOT/$template"
166
+ [[ -f "$template" ]] || die "WT_ENV_TEMPLATE does not exist: $template"
167
+ target="$path/$WT_ENV_TARGET"
168
+ mkdir -p "$(dirname "$target")"
169
+ cp "$template" "$target"
170
+ chmod 600 "$target"
171
+ info "copied reviewed environment template to $WT_ENV_TARGET"
172
+ fi
173
+
174
+ if [[ -n "$WT_INSTALL_COMMAND" ]]; then
175
+ (cd "$path" && bash -lc "$WT_INSTALL_COMMAND")
176
+ info "dependency setup complete"
177
+ fi
178
+
179
+ info "worktree: $path"
180
+ info "branch: $branch"
181
+ [[ -n "$port" ]] && info "port: $port"
182
+ return 0
183
+ }
184
+
185
+ cmd_up() {
186
+ local slug="${1:-}" path port command
187
+ validate_slug "$slug"
188
+ path="$(wt_path "$slug")"
189
+ [[ -d "$path" ]] || die "unknown worktree: $slug"
190
+ port="$(read_meta "$slug" PORT || true)"
191
+ [[ -z "$port" ]] || ! port_in_use "$port" || die "port $port is already in use"
192
+ command="$(discover_dev_command)"
193
+ if [[ "$command" == *"{port}"* && -z "$port" ]]; then
194
+ die "WT_DEV_COMMAND uses {port}, but WT_PORT_START is not configured"
195
+ fi
196
+ command="${command//\{port\}/$port}"
197
+ info "starting $slug${port:+ on port $port}"
198
+ cd "$path"
199
+ if [[ -n "$port" ]]; then
200
+ PORT="$port" exec bash -lc "$command"
201
+ else
202
+ exec bash -lc "$command"
203
+ fi
204
+ }
205
+
206
+ cmd_list() {
207
+ local slugs slug path branch port dev dirty ahead
208
+ slugs="$(existing_slugs)"
209
+ [[ -n "$slugs" ]] || {
210
+ echo "No live worktrees."
211
+ return 0
212
+ }
213
+ printf '%-24s %-32s %-8s %-10s %s\n' WORKTREE BRANCH PORT DEV CHANGES
214
+ while IFS= read -r slug; do
215
+ [[ -n "$slug" ]] || continue
216
+ path="$(wt_path "$slug")"
217
+ branch="$(read_meta "$slug" BRANCH || git -C "$path" branch --show-current)"
218
+ port="$(read_meta "$slug" PORT || true)"
219
+ if [[ -n "$port" ]] && port_in_use "$port"; then dev="running"; else dev="stopped"; fi
220
+ dirty="$(git -C "$path" status --porcelain | sed '/^$/d' | wc -l | tr -d ' ')"
221
+ ahead="$(git -C "$path" rev-list --count "$WT_BASE_BRANCH..HEAD")"
222
+ printf '%-24s %-32s %-8s %-10s dirty=%s ahead=%s\n' \
223
+ "$slug" "$branch" "${port:--}" "$dev" "$dirty" "$ahead"
224
+ done <<< "$slugs"
225
+ }
226
+
227
+ cmd_doctor() {
228
+ local slugs tmp slug path shared file
229
+ slugs="$(existing_slugs)"
230
+ [[ -n "$slugs" ]] || {
231
+ echo "No live worktrees."
232
+ return 0
233
+ }
234
+ tmp="$(mktemp)"
235
+ trap 'rm -f "$tmp"' RETURN
236
+ while IFS= read -r slug; do
237
+ [[ -n "$slug" ]] || continue
238
+ path="$(wt_path "$slug")"
239
+ {
240
+ git -C "$path" diff --name-only "$WT_BASE_BRANCH...HEAD"
241
+ git -C "$path" diff --name-only
242
+ git -C "$path" ls-files --others --exclude-standard
243
+ } | sort -u | while IFS= read -r file; do
244
+ [[ -n "$file" ]] && printf '%s\t%s\n' "$slug" "$file"
245
+ done
246
+ done <<< "$slugs" > "$tmp"
247
+ shared="$(cut -f2- "$tmp" | sort | uniq -d)"
248
+ if [[ -z "$shared" ]]; then
249
+ echo "No file is modified by more than one worktree."
250
+ return 0
251
+ fi
252
+ echo "Files modified by multiple worktrees:"
253
+ while IFS= read -r file; do
254
+ [[ -n "$file" ]] || continue
255
+ echo " $file"
256
+ awk -F '\t' -v target="$file" '$2 == target { print " - " $1 }' "$tmp"
257
+ done <<< "$shared"
258
+ }
259
+
260
+ cmd_kill() {
261
+ local slug="${1:-}" force="${2:-}" path port dirty ahead branch
262
+ validate_slug "$slug"
263
+ [[ -z "$force" || "$force" == "--force" ]] || die "unknown kill option: $force"
264
+ path="$(wt_path "$slug")"
265
+ [[ -d "$path" ]] || die "unknown worktree: $slug"
266
+ port="$(read_meta "$slug" PORT || true)"
267
+ [[ -z "$port" ]] || ! port_in_use "$port" || die "development server is still using port $port"
268
+ dirty="$(git -C "$path" status --porcelain | sed '/^$/d' | wc -l | tr -d ' ')"
269
+ ahead="$(git -C "$path" rev-list --count "$WT_BASE_BRANCH..HEAD")"
270
+ if [[ "$force" != "--force" && ( "$dirty" -gt 0 || "$ahead" -gt 0 ) ]]; then
271
+ die "$slug has uncommitted or unmerged work; merge it or repeat kill with --force"
272
+ fi
273
+ branch="$(git -C "$path" branch --show-current)"
274
+ if [[ "$force" == "--force" ]]; then
275
+ git -C "$REPO_ROOT" worktree remove --force "$path"
276
+ else
277
+ git -C "$REPO_ROOT" worktree remove "$path"
278
+ fi
279
+ if [[ -n "$branch" && "$branch" != "$WT_BASE_BRANCH" ]]; then
280
+ git -C "$REPO_ROOT" branch -d "$branch" >/dev/null 2>&1 ||
281
+ info "branch preserved because it is not merged: $branch"
282
+ fi
283
+ rm -f "$(meta_file "$slug")"
284
+ info "closed $slug"
285
+ }
286
+
287
+ cmd_merge() {
288
+ local slug="${1:-}" path port dirty ahead branch current lock
289
+ validate_slug "$slug"
290
+ path="$(wt_path "$slug")"
291
+ [[ -d "$path" ]] || die "unknown worktree: $slug"
292
+ mkdir -p "$WT_ROOT"
293
+ lock="$WT_ROOT/.merge.lock"
294
+ mkdir "$lock" 2>/dev/null || die "another merge is in progress"
295
+ MERGE_LOCK_DIR="$lock"
296
+ trap 'rmdir "$MERGE_LOCK_DIR" 2>/dev/null || true' EXIT INT TERM
297
+
298
+ port="$(read_meta "$slug" PORT || true)"
299
+ [[ -z "$port" ]] || ! port_in_use "$port" || die "development server is still using port $port"
300
+ dirty="$(git -C "$path" status --porcelain | sed '/^$/d' | wc -l | tr -d ' ')"
301
+ [[ "$dirty" -eq 0 ]] || die "worker tree has $dirty uncommitted change(s)"
302
+ ahead="$(git -C "$path" rev-list --count "$WT_BASE_BRANCH..HEAD")"
303
+ ((ahead > 0)) || die "worker branch has no commits ahead of $WT_BASE_BRANCH"
304
+ dirty="$(git -C "$REPO_ROOT" status --porcelain | sed '/^$/d' | wc -l | tr -d ' ')"
305
+ [[ "$dirty" -eq 0 ]] || die "base tree has $dirty uncommitted change(s)"
306
+ current="$(git -C "$REPO_ROOT" branch --show-current)"
307
+ [[ "$current" == "$WT_BASE_BRANCH" ]] || die "base tree must have $WT_BASE_BRANCH checked out"
308
+ branch="$(git -C "$path" branch --show-current)"
309
+
310
+ if ! git -C "$path" merge "$WT_BASE_BRANCH" -m "merge: refresh $slug before integration"; then
311
+ git -C "$path" merge --abort >/dev/null 2>&1 || true
312
+ die "conflict while bringing $WT_BASE_BRANCH into $slug; resolve it in the worktree"
313
+ fi
314
+ git -C "$REPO_ROOT" merge --no-ff "$branch" -m "merge($slug): integrate worktree"
315
+ cmd_kill "$slug"
316
+ rmdir "$MERGE_LOCK_DIR" 2>/dev/null || true
317
+ trap - EXIT INT TERM
318
+ info "merged locally into $WT_BASE_BRANCH; nothing was pushed"
319
+ }
320
+
321
+ usage() {
322
+ cat <<'EOF'
323
+ wt.sh — conservative Git worktree helper
324
+
325
+ wt.sh new <slug> create a branch and worktree
326
+ wt.sh up <slug> start the configured/discovered dev command
327
+ wt.sh list show live worktrees and Git state
328
+ wt.sh doctor report overlapping changed files
329
+ wt.sh merge <slug> merge --no-ff locally and close the worktree
330
+ wt.sh kill <slug> [--force] close without merging
331
+
332
+ Configuration: .agent-worktrees.conf in the main repository root.
333
+ EOF
334
+ }
335
+
336
+ case "${1:-}" in
337
+ new) shift; cmd_new "$@" ;;
338
+ up) shift; cmd_up "$@" ;;
339
+ list) shift; cmd_list "$@" ;;
340
+ doctor) shift; cmd_doctor "$@" ;;
341
+ merge) shift; cmd_merge "$@" ;;
342
+ kill) shift; cmd_kill "$@" ;;
343
+ -h|--help|help) usage ;;
344
+ *) usage; exit 1 ;;
345
+ esac