@uniswap/ai-toolkit-nx-claude 0.5.23 → 0.5.24-next.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.
- package/dist/content/commands/agnostic/CLAUDE.md +2 -1
- package/dist/content/commands/agnostic/git-worktree-orchestrator.md +248 -11
- package/dist/generators/addons/CLAUDE.md +35 -14
- package/dist/generators/addons/generator.cjs +46 -53
- package/dist/generators/addons/schema.json +19 -1
- package/dist/generators/init/CLAUDE.md +1 -1
- package/dist/generators/init/generator.cjs +77 -167
- package/dist/index.cjs +46 -53
- package/dist/packages/ai-toolkit-nx-claude/src/generators/addons/addon-registry.d.ts.map +1 -1
- package/dist/packages/ai-toolkit-nx-claude/src/generators/addons/claude-mcp-installer.d.ts +2 -0
- package/dist/packages/ai-toolkit-nx-claude/src/generators/addons/claude-mcp-installer.d.ts.map +1 -1
- package/dist/packages/ai-toolkit-nx-claude/src/generators/addons/generator.d.ts.map +1 -1
- package/dist/packages/ai-toolkit-nx-claude/src/generators/init/generator.d.ts.map +1 -1
- package/dist/packages/ai-toolkit-nx-claude/src/utils/auto-update-utils.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -35,7 +35,7 @@ Language-agnostic slash command definitions for Claude Code. Each markdown file
|
|
|
35
35
|
- `work-through-pr-comments.md` - Methodically work through PR comments
|
|
36
36
|
- `generate-commit-message.md` - Generate structured commit messages
|
|
37
37
|
- `split-stack.md` - Split Graphite PR stacks
|
|
38
|
-
- `git-worktree-orchestrator.md` - Create and manage git worktrees with spec-workflow and Linear integration
|
|
38
|
+
- `git-worktree-orchestrator.md` - Create and manage git worktrees with spec-workflow, Graphite, setup scripts, and Linear integration
|
|
39
39
|
|
|
40
40
|
### Documentation
|
|
41
41
|
|
|
@@ -118,6 +118,7 @@ Commands that manage larger processes:
|
|
|
118
118
|
- `/implement-spec` - Spec task implementation
|
|
119
119
|
- `/execute-plan` - Plan execution
|
|
120
120
|
- `/work-through-pr-comments` - Comment resolution workflow
|
|
121
|
+
- `/git-worktree-orchestrator` - Worktree creation with Graphite, setup scripts, and Linear automation
|
|
121
122
|
|
|
122
123
|
## Development
|
|
123
124
|
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: git-worktree-orchestrator
|
|
3
|
-
description: Create and manage a git worktree based on the current directory and a branch name, with optional spec-workflow setup and Linear task automation.
|
|
4
|
-
argument-hint: <branch-name> [linear-task-id]
|
|
3
|
+
description: Create and manage a git worktree based on the current directory and a branch name, with optional spec-workflow setup, Graphite integration, and Linear task automation.
|
|
4
|
+
argument-hint: <branch-name> [--graphite [--trunk <branch>]] [--setup <script-or-command>] [linear-task-id]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Git Worktree Orchestrator
|
|
8
8
|
|
|
9
9
|
Create and manage a git worktree based on the current directory and a branch name. After creating the worktree, it copies the .spec-workflow directory, adds the spec-workflow MCP, and can optionally commit/push, open a PR, and clean up the worktree. It also copies other files from the root git worktree into the newly created worktree.
|
|
10
10
|
|
|
11
|
+
Supports Graphite integration to track the new branch on top of a trunk branch, and can run a setup script immediately after creating the worktree.
|
|
12
|
+
|
|
11
13
|
Optionally accepts a Linear task ID to automatically spawn a new Claude Code instance that will complete the task autonomously.
|
|
12
14
|
|
|
13
15
|
## Usage
|
|
@@ -16,13 +18,28 @@ Optionally accepts a Linear task ID to automatically spawn a new Claude Code ins
|
|
|
16
18
|
# Create worktree for a new branch
|
|
17
19
|
/git-worktree-orchestrator add-new-color
|
|
18
20
|
|
|
19
|
-
# Create worktree
|
|
20
|
-
/git-worktree-orchestrator feature/
|
|
21
|
+
# Create worktree with Graphite tracking (will prompt for trunk branch)
|
|
22
|
+
/git-worktree-orchestrator feature/new-ui --graphite
|
|
23
|
+
|
|
24
|
+
# Create worktree with Graphite tracking on specific trunk
|
|
25
|
+
/git-worktree-orchestrator feature/new-ui --graphite --trunk main
|
|
26
|
+
|
|
27
|
+
# Create worktree and run a setup script
|
|
28
|
+
/git-worktree-orchestrator feature/setup-test --setup "./scripts/setup-dev.sh"
|
|
29
|
+
|
|
30
|
+
# Create worktree and run an inline setup command
|
|
31
|
+
/git-worktree-orchestrator feature/npm-setup --setup "npm install && npm run build"
|
|
32
|
+
|
|
33
|
+
# Full example with all options
|
|
34
|
+
/git-worktree-orchestrator feature/auth-system --graphite --trunk main --setup "npm ci" DEV-1234
|
|
21
35
|
```
|
|
22
36
|
|
|
23
37
|
## Arguments
|
|
24
38
|
|
|
25
39
|
- **branch** (required): Branch name to create/use for the worktree
|
|
40
|
+
- **--graphite, -g** (optional): Enable Graphite integration to track the branch
|
|
41
|
+
- **--trunk <branch>** (optional): Trunk branch name for Graphite tracking (defaults to prompting user, or 'main' if not specified)
|
|
42
|
+
- **--setup <script-or-command>** (optional): Setup script file path or inline command to run after creating the worktree
|
|
26
43
|
- **linear_task_id** (optional): Linear task ID (e.g., DEV-1234) to complete autonomously in the new worktree
|
|
27
44
|
|
|
28
45
|
## Prerequisites
|
|
@@ -30,6 +47,7 @@ Optionally accepts a Linear task ID to automatically spawn a new Claude Code ins
|
|
|
30
47
|
- git (2.5+ for worktree support)
|
|
31
48
|
- claude CLI (for MCP registration and spawning new instances)
|
|
32
49
|
- gh (optional, for PR creation)
|
|
50
|
+
- gt (optional, for Graphite integration)
|
|
33
51
|
|
|
34
52
|
## Workflow Steps
|
|
35
53
|
|
|
@@ -64,7 +82,36 @@ Registers the spec-workflow MCP with Claude for the new worktree directory:
|
|
|
64
82
|
claude mcp add spec-workflow "npx @uniswap/spec-workflow-mcp@latest" "$NEW_DIR"
|
|
65
83
|
```
|
|
66
84
|
|
|
67
|
-
### Step 6: Optional
|
|
85
|
+
### Step 6: Graphite Integration (Optional)
|
|
86
|
+
|
|
87
|
+
If Graphite is enabled (via `--graphite` flag or user prompt):
|
|
88
|
+
|
|
89
|
+
1. Prompts for trunk branch if not provided via `--trunk`
|
|
90
|
+
2. Runs `gt track` in the new worktree to track the branch on top of the trunk branch
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
gt track --trunk "$TRUNK_BRANCH"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Step 7: Setup Script Execution (Optional)
|
|
97
|
+
|
|
98
|
+
If a setup script is provided (via `--setup` flag or user prompt):
|
|
99
|
+
|
|
100
|
+
1. Detects whether the input is a file path or inline command
|
|
101
|
+
2. If file: validates existence and executes it
|
|
102
|
+
3. If inline command: executes directly in the worktree directory
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# File execution
|
|
106
|
+
if [[ -f "$SETUP_SCRIPT" ]]; then
|
|
107
|
+
bash "$SETUP_SCRIPT"
|
|
108
|
+
else
|
|
109
|
+
# Inline command execution
|
|
110
|
+
eval "$SETUP_SCRIPT"
|
|
111
|
+
fi
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Step 8: Optional Operations
|
|
68
115
|
|
|
69
116
|
Interactive prompts for:
|
|
70
117
|
|
|
@@ -73,7 +120,7 @@ Interactive prompts for:
|
|
|
73
120
|
- **Create PR**: Create a pull request via `gh` or provide URL for manual creation
|
|
74
121
|
- **Cleanup**: Remove the worktree after work is complete
|
|
75
122
|
|
|
76
|
-
### Step
|
|
123
|
+
### Step 9: Linear Task Automation
|
|
77
124
|
|
|
78
125
|
If a Linear task ID is provided, optionally spawns a new Claude Code instance to:
|
|
79
126
|
|
|
@@ -91,13 +138,68 @@ If a Linear task ID is provided, optionally spawns a new Claude Code instance to
|
|
|
91
138
|
#!/usr/bin/env bash
|
|
92
139
|
set -euo pipefail
|
|
93
140
|
|
|
94
|
-
#
|
|
95
|
-
BRANCH="
|
|
96
|
-
LINEAR_TASK_ID="
|
|
141
|
+
# Initialize variables
|
|
142
|
+
BRANCH=""
|
|
143
|
+
LINEAR_TASK_ID=""
|
|
144
|
+
USE_GRAPHITE=""
|
|
145
|
+
TRUNK_BRANCH=""
|
|
146
|
+
SETUP_SCRIPT=""
|
|
147
|
+
|
|
148
|
+
# Parse arguments
|
|
149
|
+
while [[ $# -gt 0 ]]; do
|
|
150
|
+
case "$1" in
|
|
151
|
+
--graphite|-g)
|
|
152
|
+
USE_GRAPHITE="true"
|
|
153
|
+
shift
|
|
154
|
+
;;
|
|
155
|
+
--trunk)
|
|
156
|
+
if [[ -n "${2:-}" && ! "$2" =~ ^- ]]; then
|
|
157
|
+
TRUNK_BRANCH="$2"
|
|
158
|
+
shift 2
|
|
159
|
+
else
|
|
160
|
+
echo "Error: --trunk requires a branch name argument"
|
|
161
|
+
exit 1
|
|
162
|
+
fi
|
|
163
|
+
;;
|
|
164
|
+
--setup)
|
|
165
|
+
if [[ -n "${2:-}" && ! "$2" =~ ^- ]]; then
|
|
166
|
+
SETUP_SCRIPT="$2"
|
|
167
|
+
shift 2
|
|
168
|
+
else
|
|
169
|
+
echo "Error: --setup requires a script path or command argument"
|
|
170
|
+
exit 1
|
|
171
|
+
fi
|
|
172
|
+
;;
|
|
173
|
+
-*)
|
|
174
|
+
echo "Error: Unknown option: $1"
|
|
175
|
+
echo "Usage: <command> <branch-name> [--graphite| -g] [--trunk <branch>] [--setup <script-or-command>] [linear-task-id]"
|
|
176
|
+
exit 1
|
|
177
|
+
;;
|
|
178
|
+
*)
|
|
179
|
+
# Positional arguments: first is branch, second is linear_task_id
|
|
180
|
+
if [[ -z "$BRANCH" ]]; then
|
|
181
|
+
BRANCH="$1"
|
|
182
|
+
elif [[ -z "$LINEAR_TASK_ID" ]]; then
|
|
183
|
+
LINEAR_TASK_ID="$1"
|
|
184
|
+
else
|
|
185
|
+
echo "Error: Unexpected argument: $1"
|
|
186
|
+
exit 1
|
|
187
|
+
fi
|
|
188
|
+
shift
|
|
189
|
+
;;
|
|
190
|
+
esac
|
|
191
|
+
done
|
|
192
|
+
|
|
193
|
+
# Also check for env-provided variables as fallback
|
|
194
|
+
BRANCH="${BRANCH:-${branch:-}}"
|
|
195
|
+
LINEAR_TASK_ID="${LINEAR_TASK_ID:-${linear_task_id:-}}"
|
|
196
|
+
USE_GRAPHITE="${USE_GRAPHITE:-${use_graphite:-}}"
|
|
197
|
+
TRUNK_BRANCH="${TRUNK_BRANCH:-${trunk_branch:-}}"
|
|
198
|
+
SETUP_SCRIPT="${SETUP_SCRIPT:-${setup_script:-}}"
|
|
97
199
|
|
|
98
200
|
if [[ -z "${BRANCH:-}" ]]; then
|
|
99
201
|
echo "Error: branch name is required."
|
|
100
|
-
echo "Usage: <command> <branch-name> [linear-task-id]"
|
|
202
|
+
echo "Usage: <command> <branch-name> [--graphite | -g] [--trunk <branch>] [--setup <script-or-command>] [linear-task-id]"
|
|
101
203
|
exit 1
|
|
102
204
|
fi
|
|
103
205
|
|
|
@@ -205,6 +307,98 @@ else
|
|
|
205
307
|
echo " claude mcp add spec-workflow npx @uniswap/spec-workflow-mcp@latest \"$NEW_DIR\""
|
|
206
308
|
fi
|
|
207
309
|
|
|
310
|
+
# Prompt for Graphite usage if not specified
|
|
311
|
+
if [[ -z "${USE_GRAPHITE:-}" ]]; then
|
|
312
|
+
echo ""
|
|
313
|
+
read -r -p "Use Graphite to track this branch? (y/N): " GRAPHITE_RESP || true
|
|
314
|
+
if [[ "$GRAPHITE_RESP" =~ ^[Yy]$ ]]; then
|
|
315
|
+
USE_GRAPHITE="true"
|
|
316
|
+
fi
|
|
317
|
+
fi
|
|
318
|
+
|
|
319
|
+
# Graphite integration
|
|
320
|
+
if [[ "${USE_GRAPHITE:-}" == "true" ]]; then
|
|
321
|
+
echo ""
|
|
322
|
+
echo "============================================"
|
|
323
|
+
echo "Graphite Integration"
|
|
324
|
+
echo "============================================"
|
|
325
|
+
|
|
326
|
+
if ! command -v gt >/dev/null 2>&1; then
|
|
327
|
+
echo "Warning: 'gt' (Graphite CLI) not found. Skipping Graphite setup."
|
|
328
|
+
echo "Install it from: https://graphite.dev/docs/installing-the-cli"
|
|
329
|
+
else
|
|
330
|
+
# Prompt for trunk branch if not provided
|
|
331
|
+
if [[ -z "${TRUNK_BRANCH:-}" ]]; then
|
|
332
|
+
DEFAULT_TRUNK="$(git -C "$NEW_DIR" remote show origin 2>/dev/null | sed -n 's/.*HEAD branch: //p')" || DEFAULT_TRUNK=""
|
|
333
|
+
DEFAULT_TRUNK="${DEFAULT_TRUNK:-main}"
|
|
334
|
+
read -r -p "Enter trunk branch name [$DEFAULT_TRUNK]: " TRUNK_INPUT || true
|
|
335
|
+
TRUNK_BRANCH="${TRUNK_INPUT:-$DEFAULT_TRUNK}"
|
|
336
|
+
fi
|
|
337
|
+
|
|
338
|
+
echo "Tracking branch '$BRANCH' on top of trunk '$TRUNK_BRANCH'..."
|
|
339
|
+
if ! gt -C "$NEW_DIR" track --trunk "$TRUNK_BRANCH"; then
|
|
340
|
+
echo "Warning: Failed to track branch with Graphite. You can run this manually:"
|
|
341
|
+
echo " cd \"$NEW_DIR\" && gt track --trunk \"$TRUNK_BRANCH\""
|
|
342
|
+
else
|
|
343
|
+
echo "Successfully tracked branch with Graphite."
|
|
344
|
+
fi
|
|
345
|
+
fi
|
|
346
|
+
fi
|
|
347
|
+
|
|
348
|
+
# Prompt for setup script if not provided
|
|
349
|
+
if [[ -z "${SETUP_SCRIPT:-}" ]]; then
|
|
350
|
+
echo ""
|
|
351
|
+
read -r -p "Run a setup script/command in the new worktree? (y/N): " SETUP_RESP || true
|
|
352
|
+
if [[ "$SETUP_RESP" =~ ^[Yy]$ ]]; then
|
|
353
|
+
read -r -p "Enter script path or inline command: " SETUP_SCRIPT || true
|
|
354
|
+
fi
|
|
355
|
+
fi
|
|
356
|
+
|
|
357
|
+
# Execute setup script if provided
|
|
358
|
+
if [[ -n "${SETUP_SCRIPT:-}" ]]; then
|
|
359
|
+
echo ""
|
|
360
|
+
echo "============================================"
|
|
361
|
+
echo "Running Setup Script"
|
|
362
|
+
echo "============================================"
|
|
363
|
+
echo "Executing: $SETUP_SCRIPT"
|
|
364
|
+
echo ""
|
|
365
|
+
|
|
366
|
+
# Change to the new worktree directory for script execution
|
|
367
|
+
pushd "$NEW_DIR" >/dev/null
|
|
368
|
+
|
|
369
|
+
# Check if it's a file path (absolute or relative to original directory)
|
|
370
|
+
SCRIPT_PATH=""
|
|
371
|
+
if [[ -f "$SETUP_SCRIPT" ]]; then
|
|
372
|
+
SCRIPT_PATH="$SETUP_SCRIPT"
|
|
373
|
+
elif [[ -f "$CWD/$SETUP_SCRIPT" ]]; then
|
|
374
|
+
SCRIPT_PATH="$CWD/$SETUP_SCRIPT"
|
|
375
|
+
elif [[ -f "$REPO_ROOT/$SETUP_SCRIPT" ]]; then
|
|
376
|
+
SCRIPT_PATH="$REPO_ROOT/$SETUP_SCRIPT"
|
|
377
|
+
fi
|
|
378
|
+
|
|
379
|
+
if [[ -n "$SCRIPT_PATH" ]]; then
|
|
380
|
+
echo "Running script file: $SCRIPT_PATH"
|
|
381
|
+
if [[ -x "$SCRIPT_PATH" ]]; then
|
|
382
|
+
"$SCRIPT_PATH"
|
|
383
|
+
else
|
|
384
|
+
bash "$SCRIPT_PATH"
|
|
385
|
+
fi
|
|
386
|
+
else
|
|
387
|
+
# Treat as inline command
|
|
388
|
+
echo "Running inline command..."
|
|
389
|
+
eval "$SETUP_SCRIPT"
|
|
390
|
+
fi
|
|
391
|
+
|
|
392
|
+
SETUP_EXIT_CODE=$?
|
|
393
|
+
popd >/dev/null
|
|
394
|
+
|
|
395
|
+
if [[ $SETUP_EXIT_CODE -eq 0 ]]; then
|
|
396
|
+
echo "Setup script completed successfully."
|
|
397
|
+
else
|
|
398
|
+
echo "Warning: Setup script exited with code $SETUP_EXIT_CODE"
|
|
399
|
+
fi
|
|
400
|
+
fi
|
|
401
|
+
|
|
208
402
|
# Prompt for Linear task ID if not provided
|
|
209
403
|
if [[ -z "${LINEAR_TASK_ID:-}" ]]; then
|
|
210
404
|
echo ""
|
|
@@ -361,7 +555,8 @@ The command creates worktrees in a sibling directory pattern:
|
|
|
361
555
|
- **Spec Workflow**: Symlinks `.spec-workflow` from the main repo to enable spec-workflow MCP functionality
|
|
362
556
|
- **Claude Settings**: Copies local Claude settings to maintain consistent behavior
|
|
363
557
|
- **Linear Integration**: Supports autonomous task completion when provided with a Linear task ID
|
|
364
|
-
- **Graphite
|
|
558
|
+
- **Graphite Integration**: Built-in support for `gt track` to manage branch stacks; tracks new branches on specified trunk
|
|
559
|
+
- **Setup Scripts**: Runs custom setup scripts or commands (e.g., `npm install`, `./scripts/setup.sh`) after worktree creation
|
|
365
560
|
|
|
366
561
|
## Merging Work Back
|
|
367
562
|
|
|
@@ -407,4 +602,46 @@ claude mcp add spec-workflow "npx @uniswap/spec-workflow-mcp@latest" "/path/to/w
|
|
|
407
602
|
|
|
408
603
|
**Solution:** Install Claude Code from <https://claude.ai/download>
|
|
409
604
|
|
|
605
|
+
### "gt (Graphite CLI) not found"
|
|
606
|
+
|
|
607
|
+
**Solution:** Install the Graphite CLI:
|
|
608
|
+
|
|
609
|
+
```bash
|
|
610
|
+
npm install -g @withgraphite/graphite-cli
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
Or follow the installation guide at <https://graphite.dev/docs/installing-the-cli>
|
|
614
|
+
|
|
615
|
+
### "Failed to track branch with Graphite"
|
|
616
|
+
|
|
617
|
+
**Solution:** Ensure you're in a Graphite-enabled repository and the trunk branch exists:
|
|
618
|
+
|
|
619
|
+
```bash
|
|
620
|
+
cd /path/to/worktree
|
|
621
|
+
gt init # If not already initialized
|
|
622
|
+
gt track --trunk main
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
### "Setup script failed"
|
|
626
|
+
|
|
627
|
+
**Solution:** Check that your script:
|
|
628
|
+
|
|
629
|
+
1. Exists at the specified path (relative to CWD or repo root)
|
|
630
|
+
2. Has proper execute permissions (`chmod +x script.sh`)
|
|
631
|
+
3. Returns exit code 0 on success
|
|
632
|
+
|
|
633
|
+
You can test the script manually:
|
|
634
|
+
|
|
635
|
+
```bash
|
|
636
|
+
cd /path/to/worktree
|
|
637
|
+
./your-script.sh # or: bash your-script.sh
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### "Setup command not found"
|
|
641
|
+
|
|
642
|
+
**Solution:** Inline commands are executed with `eval` in the worktree directory. Ensure:
|
|
643
|
+
|
|
644
|
+
1. The command is available in your PATH
|
|
645
|
+
2. You're using proper shell syntax for chained commands (`&&` for sequential, `;` for independent)
|
|
646
|
+
|
|
410
647
|
Arguments: $ARGUMENTS
|
|
@@ -23,14 +23,19 @@ npx nx generate @uniswap/ai-toolkit-nx-claude:addons \
|
|
|
23
23
|
|
|
24
24
|
### Installation Control
|
|
25
25
|
|
|
26
|
-
- `
|
|
26
|
+
- `selectionMode` - Selection mode for which addons to install:
|
|
27
27
|
- `all` - Install all available addons
|
|
28
28
|
- `specific` - Choose specific addons to install
|
|
29
|
-
- `
|
|
29
|
+
- `addons` - Specific addons to install (when `selectionMode=specific`)
|
|
30
|
+
- `installationType` - Installation location for MCP servers:
|
|
31
|
+
- `global` - Install to `~/.claude` (available in all projects)
|
|
32
|
+
- `local` - Install to `./.claude` (project-specific configuration)
|
|
30
33
|
|
|
31
34
|
### Integration Options
|
|
32
35
|
|
|
33
|
-
- `installMode` (hidden) - Set to "default" or "custom" when called from init generator to
|
|
36
|
+
- `installMode` (hidden) - Set to "default" or "custom" when called from init generator to control prompting behavior:
|
|
37
|
+
- When `installMode === 'default'`: Skips all prompts, uses `installationType` from parent or defaults to 'global'
|
|
38
|
+
- When undefined (standalone execution): Shows full interactive prompts including `installationType` selection
|
|
34
39
|
|
|
35
40
|
## Available Addons
|
|
36
41
|
|
|
@@ -95,10 +100,10 @@ Registered in `addon-registry.ts`:
|
|
|
95
100
|
|
|
96
101
|
## Generator Flow
|
|
97
102
|
|
|
98
|
-
1. **Mode Selection** (if
|
|
103
|
+
1. **Mode Selection** (if running standalone):
|
|
99
104
|
|
|
100
|
-
- All addons
|
|
101
|
-
-
|
|
105
|
+
- Selection mode: All addons vs Specific addons
|
|
106
|
+
- Installation location: Global (`~/.claude`) vs Local (`./.claude`)
|
|
102
107
|
|
|
103
108
|
2. **Addon Selection** (if specific mode):
|
|
104
109
|
|
|
@@ -110,7 +115,9 @@ Registered in `addon-registry.ts`:
|
|
|
110
115
|
|
|
111
116
|
- For each selected addon:
|
|
112
117
|
- Run addon's setup function
|
|
113
|
-
-
|
|
118
|
+
- Install MCP server using `claude mcp add` with appropriate scope:
|
|
119
|
+
- `--scope user` for global installation
|
|
120
|
+
- `--scope project` for local installation
|
|
114
121
|
- Create necessary files/directories
|
|
115
122
|
- Install dependencies if needed
|
|
116
123
|
|
|
@@ -190,21 +197,35 @@ npx nx generate @uniswap/ai-toolkit-nx-claude:addons \
|
|
|
190
197
|
|
|
191
198
|
## Integration with Init Generator
|
|
192
199
|
|
|
193
|
-
The addons generator supports the `installMode` pattern:
|
|
200
|
+
The addons generator supports the `installMode` pattern for programmatic invocation:
|
|
194
201
|
|
|
195
202
|
```typescript
|
|
196
203
|
// From init generator
|
|
197
204
|
await addonsGenerator(tree, {
|
|
198
|
-
installMode:
|
|
199
|
-
|
|
205
|
+
installMode: 'default', // or 'custom'
|
|
206
|
+
selectionMode: 'all', // or 'specific'
|
|
207
|
+
installationType: normalizedOptions.installationType, // 'global' or 'local'
|
|
208
|
+
force: false,
|
|
209
|
+
skipVerification: false,
|
|
210
|
+
dashboardMode: 'always',
|
|
211
|
+
port: 0,
|
|
200
212
|
});
|
|
201
213
|
```
|
|
202
214
|
|
|
203
|
-
|
|
215
|
+
**Behavior by installMode**:
|
|
204
216
|
|
|
205
|
-
-
|
|
206
|
-
|
|
207
|
-
-
|
|
217
|
+
- `installMode === 'default'`:
|
|
218
|
+
|
|
219
|
+
- Skips all interactive prompts
|
|
220
|
+
- Uses `installationType` from init generator (or defaults to 'global')
|
|
221
|
+
- Uses provided `selectionMode` and other options
|
|
222
|
+
- No user interaction required
|
|
223
|
+
|
|
224
|
+
- `installMode` undefined (standalone execution):
|
|
225
|
+
- Shows `selectionMode` prompt (all vs specific)
|
|
226
|
+
- Shows `installationType` prompt (global vs local)
|
|
227
|
+
- Shows addon selection for 'specific' mode
|
|
228
|
+
- Full interactive experience
|
|
208
229
|
|
|
209
230
|
## Configuration Updates
|
|
210
231
|
|
|
@@ -127,9 +127,7 @@ async function validateAddonRequirements(addonId) {
|
|
|
127
127
|
if (addon.requirements?.node) {
|
|
128
128
|
const nodeVersion = process.version;
|
|
129
129
|
if (!nodeVersion.match(/v1[89]\.\d+\.\d+/) && !nodeVersion.match(/v2\d+\.\d+\.\d+/)) {
|
|
130
|
-
errors.push(
|
|
131
|
-
`Node.js ${addon.requirements.node} required, found ${nodeVersion}`
|
|
132
|
-
);
|
|
130
|
+
errors.push(`Node.js ${addon.requirements.node} required, found ${nodeVersion}`);
|
|
133
131
|
}
|
|
134
132
|
}
|
|
135
133
|
if (addon.requirements?.commands) {
|
|
@@ -211,7 +209,7 @@ var init_addon_registry = __esm({
|
|
|
211
209
|
mcp: {
|
|
212
210
|
serverName: "slack",
|
|
213
211
|
command: "npx",
|
|
214
|
-
args: ["-y", "@zencoderai/slack-mcp-server", "
|
|
212
|
+
args: ["-y", "-p", "@zencoderai/slack-mcp-server", "slack-mcp"],
|
|
215
213
|
env: {
|
|
216
214
|
SLACK_BOT_TOKEN: "PROMPT_TO_INSERT_SLACK_BOT_TOKEN",
|
|
217
215
|
SLACK_TEAM_ID: "TKZBCKUJJ"
|
|
@@ -233,13 +231,13 @@ var init_addon_registry = __esm({
|
|
|
233
231
|
{
|
|
234
232
|
id: "linear-mcp",
|
|
235
233
|
name: "Linear MCP",
|
|
236
|
-
description: "MCP server for Linear issue tracking (
|
|
234
|
+
description: "MCP server for Linear issue tracking (HTTP)",
|
|
237
235
|
type: "mcp-server",
|
|
238
236
|
packageName: "linear",
|
|
239
237
|
mcp: {
|
|
240
238
|
serverName: "linear",
|
|
241
|
-
|
|
242
|
-
|
|
239
|
+
transport: "http",
|
|
240
|
+
url: "https://mcp.linear.app/mcp"
|
|
243
241
|
}
|
|
244
242
|
},
|
|
245
243
|
{
|
|
@@ -429,7 +427,25 @@ var require_schema = __commonJS({
|
|
|
429
427
|
type: "string",
|
|
430
428
|
description: "Installation mode from parent generator (default or custom)",
|
|
431
429
|
enum: ["default", "custom"],
|
|
432
|
-
hidden: true
|
|
430
|
+
hidden: true,
|
|
431
|
+
"x-skip-prompt": true
|
|
432
|
+
},
|
|
433
|
+
installationType: {
|
|
434
|
+
type: "string",
|
|
435
|
+
description: "Installation location for MCP servers",
|
|
436
|
+
enum: ["global", "local"],
|
|
437
|
+
"prompt-message": "\u{1F4CD} Where should MCP servers be installed?",
|
|
438
|
+
"prompt-type": "list",
|
|
439
|
+
"prompt-items": [
|
|
440
|
+
{
|
|
441
|
+
value: "global",
|
|
442
|
+
label: "Global (~/.claude) - Available in all projects"
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
value: "local",
|
|
446
|
+
label: "Local (./.claude) - Project-specific configuration"
|
|
447
|
+
}
|
|
448
|
+
]
|
|
433
449
|
}
|
|
434
450
|
},
|
|
435
451
|
required: []
|
|
@@ -838,7 +854,7 @@ init_addon_registry();
|
|
|
838
854
|
var import_child_process = require("child_process");
|
|
839
855
|
init_addon_registry();
|
|
840
856
|
async function installMcpServer(options) {
|
|
841
|
-
const { addon, additionalArgs = [], dryRun = false } = options;
|
|
857
|
+
const { addon, additionalArgs = [], dryRun = false, installationType = "global" } = options;
|
|
842
858
|
try {
|
|
843
859
|
(0, import_child_process.execSync)("claude --version", { stdio: "ignore" });
|
|
844
860
|
} catch {
|
|
@@ -849,7 +865,8 @@ async function installMcpServer(options) {
|
|
|
849
865
|
};
|
|
850
866
|
}
|
|
851
867
|
const serverName = addon.mcp?.serverName;
|
|
852
|
-
|
|
868
|
+
const scope = installationType === "local" ? "project" : "user";
|
|
869
|
+
let command = `claude mcp add ${serverName} --scope ${scope}`;
|
|
853
870
|
if (addon.mcp?.env && Object.keys(addon.mcp.env).length > 0) {
|
|
854
871
|
for (const [key, value] of Object.entries(addon.mcp.env)) {
|
|
855
872
|
command += ` --env ${key}=${value}`;
|
|
@@ -892,10 +909,8 @@ async function installMcpServer(options) {
|
|
|
892
909
|
while (attempts < maxAttempts) {
|
|
893
910
|
attempts++;
|
|
894
911
|
try {
|
|
895
|
-
console.log(
|
|
896
|
-
|
|
897
|
-
\u{1F527} Installing MCP server (attempt ${attempts}/${maxAttempts})...`
|
|
898
|
-
);
|
|
912
|
+
console.log(`
|
|
913
|
+
\u{1F527} Installing MCP server (attempt ${attempts}/${maxAttempts})...`);
|
|
899
914
|
const output = (0, import_child_process.execSync)(command, {
|
|
900
915
|
encoding: "utf-8",
|
|
901
916
|
stdio: "pipe"
|
|
@@ -1260,6 +1275,7 @@ async function generator(tree, schema) {
|
|
|
1260
1275
|
port: schema.port || 0,
|
|
1261
1276
|
dry: schema.dry || false,
|
|
1262
1277
|
installMode: "default",
|
|
1278
|
+
installationType: schema.installationType || "global",
|
|
1263
1279
|
dryRun: isDryRun
|
|
1264
1280
|
};
|
|
1265
1281
|
} else {
|
|
@@ -1297,10 +1313,8 @@ async function installSelectedAddons(tree, options) {
|
|
|
1297
1313
|
const results = [];
|
|
1298
1314
|
for (let i = 0; i < selectedAddons.length; i++) {
|
|
1299
1315
|
const addon = selectedAddons[i];
|
|
1300
|
-
console.log(
|
|
1301
|
-
|
|
1302
|
-
[${i + 1}/${selectedAddons.length}] Installing: ${addon.name}`
|
|
1303
|
-
);
|
|
1316
|
+
console.log(`
|
|
1317
|
+
[${i + 1}/${selectedAddons.length}] Installing: ${addon.name}`);
|
|
1304
1318
|
console.log(` ${addon.description}`);
|
|
1305
1319
|
try {
|
|
1306
1320
|
if (!options.force && !options.dryRun) {
|
|
@@ -1341,23 +1355,17 @@ async function installSelectedAddons(tree, options) {
|
|
|
1341
1355
|
});
|
|
1342
1356
|
options.projectPath = projectPath;
|
|
1343
1357
|
if (options.dryRun) {
|
|
1344
|
-
console.log(
|
|
1345
|
-
|
|
1346
|
-
\u{1F4C1} [DRY-RUN] Would set up project configuration at: ${projectPath}`
|
|
1347
|
-
);
|
|
1358
|
+
console.log(`
|
|
1359
|
+
\u{1F4C1} [DRY-RUN] Would set up project configuration at: ${projectPath}`);
|
|
1348
1360
|
}
|
|
1349
1361
|
await installProjectSetup(addon, options);
|
|
1350
1362
|
} else if (options.dryRun) {
|
|
1351
|
-
console.log(
|
|
1352
|
-
"\n\u{1F4C1} [DRY-RUN] Skipping project configuration (user chose not to set up)"
|
|
1353
|
-
);
|
|
1363
|
+
console.log("\n\u{1F4C1} [DRY-RUN] Skipping project configuration (user chose not to set up)");
|
|
1354
1364
|
}
|
|
1355
1365
|
}
|
|
1356
1366
|
results.push({ addon, success: true });
|
|
1357
1367
|
} catch (error) {
|
|
1358
|
-
console.error(
|
|
1359
|
-
` \u274C Failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1360
|
-
);
|
|
1368
|
+
console.error(` \u274C Failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1361
1369
|
results.push({
|
|
1362
1370
|
addon,
|
|
1363
1371
|
success: false,
|
|
@@ -1374,9 +1382,7 @@ async function installSelectedAddons(tree, options) {
|
|
|
1374
1382
|
if (failed.length > 0) {
|
|
1375
1383
|
console.log(`
|
|
1376
1384
|
\u274C Failed to install: ${failed.length}`);
|
|
1377
|
-
failed.forEach(
|
|
1378
|
-
(r) => console.log(` \u2022 ${r.addon.name} - ${r.error || "Unknown error"}`)
|
|
1379
|
-
);
|
|
1385
|
+
failed.forEach((r) => console.log(` \u2022 ${r.addon.name} - ${r.error || "Unknown error"}`));
|
|
1380
1386
|
}
|
|
1381
1387
|
if (options.dryRun) {
|
|
1382
1388
|
console.log("\n\u2728 Dry-run complete! No changes were made.\n");
|
|
@@ -1422,9 +1428,7 @@ async function installAllAddons(tree, options) {
|
|
|
1422
1428
|
await installMcpAddon(addon, options);
|
|
1423
1429
|
results.push({ addon, success: true });
|
|
1424
1430
|
} catch (error) {
|
|
1425
|
-
console.error(
|
|
1426
|
-
` \u274C Failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1427
|
-
);
|
|
1431
|
+
console.error(` \u274C Failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1428
1432
|
results.push({
|
|
1429
1433
|
addon,
|
|
1430
1434
|
success: false,
|
|
@@ -1441,9 +1445,7 @@ async function installAllAddons(tree, options) {
|
|
|
1441
1445
|
if (failed.length > 0) {
|
|
1442
1446
|
console.log(`
|
|
1443
1447
|
\u274C Failed to install: ${failed.length}`);
|
|
1444
|
-
failed.forEach(
|
|
1445
|
-
(r) => console.log(` \u2022 ${r.addon.name} - ${r.error || "Unknown error"}`)
|
|
1446
|
-
);
|
|
1448
|
+
failed.forEach((r) => console.log(` \u2022 ${r.addon.name} - ${r.error || "Unknown error"}`));
|
|
1447
1449
|
}
|
|
1448
1450
|
if (options.dryRun) {
|
|
1449
1451
|
console.log("\n\u2728 Dry-run complete! No changes were made.\n");
|
|
@@ -1482,7 +1484,8 @@ async function installMcpAddon(addon, options) {
|
|
|
1482
1484
|
const installResult = await installMcpServer({
|
|
1483
1485
|
addon,
|
|
1484
1486
|
additionalArgs,
|
|
1485
|
-
dryRun: options.dryRun
|
|
1487
|
+
dryRun: options.dryRun,
|
|
1488
|
+
installationType: options.installationType || "global"
|
|
1486
1489
|
});
|
|
1487
1490
|
if (!installResult.success) {
|
|
1488
1491
|
throw new Error(installResult.error || installResult.message);
|
|
@@ -1524,9 +1527,7 @@ function showGeneralMcpInstructions(installedAddons) {
|
|
|
1524
1527
|
const hasSlack = installedAddons.some((addon) => addon.id === "slack-mcp");
|
|
1525
1528
|
const hasGithub = installedAddons.some((addon) => addon.id === "github-mcp");
|
|
1526
1529
|
const hasPulumi = installedAddons.some((addon) => addon.id === "pulumi-mcp");
|
|
1527
|
-
const hasAws = installedAddons.some(
|
|
1528
|
-
(addon) => addon.id === "aws-log-analyzer-mcp"
|
|
1529
|
-
);
|
|
1530
|
+
const hasAws = installedAddons.some((addon) => addon.id === "aws-log-analyzer-mcp");
|
|
1530
1531
|
if (hasSlack || hasGithub || hasAws || hasPulumi) {
|
|
1531
1532
|
console.log("\u{1F4CB} Specific Authentication Instructions:\n");
|
|
1532
1533
|
if (hasSlack) {
|
|
@@ -1542,18 +1543,12 @@ function showGeneralMcpInstructions(installedAddons) {
|
|
|
1542
1543
|
console.log("\u{1F510} GitHub MCP:");
|
|
1543
1544
|
console.log(" You can obtain your GitHub Personal Access Token using:");
|
|
1544
1545
|
console.log(" $ gh auth token");
|
|
1545
|
-
console.log(
|
|
1546
|
-
" (Requires GitHub CLI to be installed and authenticated)\n"
|
|
1547
|
-
);
|
|
1546
|
+
console.log(" (Requires GitHub CLI to be installed and authenticated)\n");
|
|
1548
1547
|
}
|
|
1549
1548
|
if (hasAws) {
|
|
1550
1549
|
console.log("\u{1F510} AWS Log Analyzer MCP:");
|
|
1551
|
-
console.log(
|
|
1552
|
-
|
|
1553
|
-
);
|
|
1554
|
-
console.log(
|
|
1555
|
-
" \u{1F4D6} Documentation: https://github.com/awslabs/Log-Analyzer-with-MCP\n"
|
|
1556
|
-
);
|
|
1550
|
+
console.log(" Requires AWS credentials with CloudWatchLogsReadOnlyAccess");
|
|
1551
|
+
console.log(" \u{1F4D6} Documentation: https://github.com/awslabs/Log-Analyzer-with-MCP\n");
|
|
1557
1552
|
}
|
|
1558
1553
|
if (hasPulumi) {
|
|
1559
1554
|
console.log("\u{1F510} Pulumi MCP:");
|
|
@@ -1563,9 +1558,7 @@ function showGeneralMcpInstructions(installedAddons) {
|
|
|
1563
1558
|
console.log(
|
|
1564
1559
|
" \u{1F4D6} Documentation: https://www.pulumi.com/docs/iac/guides/ai-integration/mcp-server/"
|
|
1565
1560
|
);
|
|
1566
|
-
console.log(
|
|
1567
|
-
" Create your PAT at: https://app.pulumi.com/account/tokens\n"
|
|
1568
|
-
);
|
|
1561
|
+
console.log(" Create your PAT at: https://app.pulumi.com/account/tokens\n");
|
|
1569
1562
|
}
|
|
1570
1563
|
}
|
|
1571
1564
|
}
|