orchestrix 16.1.2 → 16.1.4
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/lib/embedded/start-orchestrix.sh +9 -2
- package/lib/embedded/yuri.md +193 -0
- package/lib/license.js +36 -4
- package/package.json +1 -1
|
@@ -45,11 +45,18 @@ if [ -z "$REPO_ID" ]; then
|
|
|
45
45
|
fi
|
|
46
46
|
|
|
47
47
|
# Generate dynamic session name and log file
|
|
48
|
-
|
|
48
|
+
# If ORCHESTRIX_SESSION is pre-set (e.g., by Yuri remote orchestration), use it.
|
|
49
|
+
# Otherwise, default to orchestrix-{REPO_ID}.
|
|
50
|
+
if [ -n "$ORCHESTRIX_SESSION" ]; then
|
|
51
|
+
SESSION_NAME="$ORCHESTRIX_SESSION"
|
|
52
|
+
echo "🏷️ Using pre-set session name: $SESSION_NAME"
|
|
53
|
+
else
|
|
54
|
+
SESSION_NAME="orchestrix-${REPO_ID}"
|
|
55
|
+
echo "🏷️ Repository ID: $REPO_ID"
|
|
56
|
+
fi
|
|
49
57
|
# IMPORTANT: Must match handoff-detector.sh pattern: /tmp/${SESSION_NAME}-handoff.log
|
|
50
58
|
LOG_FILE="/tmp/${SESSION_NAME}-handoff.log"
|
|
51
59
|
|
|
52
|
-
echo "🏷️ Repository ID: $REPO_ID"
|
|
53
60
|
echo "📺 tmux Session: $SESSION_NAME"
|
|
54
61
|
echo "📝 Log file: $LOG_FILE"
|
|
55
62
|
|
package/lib/embedded/yuri.md
CHANGED
|
@@ -37,6 +37,52 @@ HANDOFF → Auto-routing between agents
|
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
40
|
+
## Environment Detection (Blueprint vs Standalone)
|
|
41
|
+
|
|
42
|
+
> On startup, detect which mode you're running in. This determines how project creation and session management work.
|
|
43
|
+
|
|
44
|
+
### Detection
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Check if running inside a youlidao.ai blueprint workspace
|
|
48
|
+
test -f .youlidao/blueprint.json
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
| Condition | Mode | Workspace Path | Session Naming |
|
|
52
|
+
|-----------|------|----------------|----------------|
|
|
53
|
+
| `.youlidao/blueprint.json` exists | **Blueprint** | Current directory (already set up) | `op-{blueprint-name}` |
|
|
54
|
+
| `.youlidao/blueprint.json` missing | **Standalone** | Create `~/Codes/{name}/` | `op-{project-name}` |
|
|
55
|
+
|
|
56
|
+
### Blueprint Mode Behavior
|
|
57
|
+
|
|
58
|
+
When running inside a blueprint workspace (e.g., activated by Gateway in a blueprint tmux session):
|
|
59
|
+
|
|
60
|
+
1. **Skip project creation steps** — directory, git init, Orchestrix install all done by Gateway
|
|
61
|
+
2. **Read project context from `.youlidao/blueprint.json`**:
|
|
62
|
+
```bash
|
|
63
|
+
cat .youlidao/blueprint.json
|
|
64
|
+
# → { "blueprintId": "...", "blueprintName": "...", "createdAt": "..." }
|
|
65
|
+
```
|
|
66
|
+
3. **Initialize `.yuri/` memory** in the existing workspace (if not already present):
|
|
67
|
+
- Create `.yuri/identity.yaml` from blueprint metadata
|
|
68
|
+
- Create `.yuri/focus.yaml`, `.yuri/state/`, `.yuri/timeline/`
|
|
69
|
+
- Register in `~/.yuri/portfolio/registry.yaml`
|
|
70
|
+
4. **Use current directory as `WORK_DIR`** for all op-session operations
|
|
71
|
+
5. **Derive `OP_SESSION` name** from `blueprintName`:
|
|
72
|
+
```bash
|
|
73
|
+
OP_SESSION="op-$(cat .youlidao/blueprint.json | jq -r '.blueprintName')"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Standalone Mode Behavior
|
|
77
|
+
|
|
78
|
+
When NOT in a blueprint workspace (standard Orchestrix usage):
|
|
79
|
+
|
|
80
|
+
1. Follow the full `*create` flow (collect name, create `~/Codes/{name}/`, install Orchestrix)
|
|
81
|
+
2. Use `~/Codes/{name}/` as `WORK_DIR`
|
|
82
|
+
3. Session name: `op-{project-dir-name}`
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
40
86
|
## tmux Protocol (Mandatory 3-Step Pattern)
|
|
41
87
|
|
|
42
88
|
> **When sending any content to Claude Code via tmux, strictly follow three steps. Violating this causes content to get stuck in the input box.**
|
|
@@ -76,6 +122,153 @@ tmux send-keys -t $WIN Enter
|
|
|
76
122
|
|
|
77
123
|
---
|
|
78
124
|
|
|
125
|
+
## Remote Orchestration Mode (op-session)
|
|
126
|
+
|
|
127
|
+
> When Yuri runs as a **resident coordinator** (e.g., in a blueprint session), agents work in a **separate tmux session** named `op-{project-name}`. Yuri manages the full lifecycle of this session remotely.
|
|
128
|
+
|
|
129
|
+
### Architecture
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
Yuri's window (resident, never cleared)
|
|
133
|
+
│
|
|
134
|
+
│ tmux send-keys / capture-pane
|
|
135
|
+
↓
|
|
136
|
+
op-{project-name} ← separate tmux session
|
|
137
|
+
├── Phase A: 1 window "planning", sequential agent switching
|
|
138
|
+
└── Phase B: 4 windows (start-orchestrix.sh), HANDOFF automation
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Key principle**: Yuri **never leaves its own window**. All agent operations happen in the `op-{name}` session via tmux commands.
|
|
142
|
+
|
|
143
|
+
### Phase A: Remote Planning Pipeline
|
|
144
|
+
|
|
145
|
+
#### Step 1: Create op-session
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Determine session name and workspace path
|
|
149
|
+
# Blueprint mode: read from .youlidao/blueprint.json
|
|
150
|
+
# Standalone mode: use project directory name
|
|
151
|
+
if [ -f .youlidao/blueprint.json ]; then
|
|
152
|
+
BP_NAME=$(cat .youlidao/blueprint.json | jq -r '.blueprintName')
|
|
153
|
+
OP_SESSION="op-${BP_NAME}"
|
|
154
|
+
WORK_DIR="$(pwd)"
|
|
155
|
+
else
|
|
156
|
+
OP_SESSION="op-{project-name}"
|
|
157
|
+
WORK_DIR="$HOME/Codes/{project-name}"
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
# Create session with planning window
|
|
161
|
+
tmux new-session -d -s "$OP_SESSION" -n "planning" -c "$WORK_DIR"
|
|
162
|
+
|
|
163
|
+
# Start Claude Code in planning window
|
|
164
|
+
tmux send-keys -t "$OP_SESSION:planning" "cc"
|
|
165
|
+
sleep 1
|
|
166
|
+
tmux send-keys -t "$OP_SESSION:planning" Enter
|
|
167
|
+
|
|
168
|
+
# Wait for CC ready
|
|
169
|
+
sleep 12
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### Step 2: Run planning agents sequentially
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Activate first agent
|
|
176
|
+
tmux send-keys -t "$OP_SESSION:planning" "/o analyst"
|
|
177
|
+
sleep 1
|
|
178
|
+
tmux send-keys -t "$OP_SESSION:planning" Enter
|
|
179
|
+
sleep 12 # Wait for agent load via MCP
|
|
180
|
+
|
|
181
|
+
# Send command
|
|
182
|
+
tmux send-keys -t "$OP_SESSION:planning" "*create-doc project-brief"
|
|
183
|
+
sleep 1
|
|
184
|
+
tmux send-keys -t "$OP_SESSION:planning" Enter
|
|
185
|
+
|
|
186
|
+
# Monitor completion (use detection methods from table above)
|
|
187
|
+
# Poll output until completion detected:
|
|
188
|
+
tmux capture-pane -t "$OP_SESSION:planning" -p | tail -20
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### Step 3: Switch to next agent (repeat for each)
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Clear previous agent
|
|
195
|
+
tmux send-keys -t "$OP_SESSION:planning" "/clear"
|
|
196
|
+
sleep 1
|
|
197
|
+
tmux send-keys -t "$OP_SESSION:planning" Enter
|
|
198
|
+
sleep 2
|
|
199
|
+
|
|
200
|
+
# Activate next agent
|
|
201
|
+
tmux send-keys -t "$OP_SESSION:planning" "/o pm"
|
|
202
|
+
sleep 1
|
|
203
|
+
tmux send-keys -t "$OP_SESSION:planning" Enter
|
|
204
|
+
sleep 12
|
|
205
|
+
|
|
206
|
+
# Send command
|
|
207
|
+
tmux send-keys -t "$OP_SESSION:planning" "*create-doc prd"
|
|
208
|
+
sleep 1
|
|
209
|
+
tmux send-keys -t "$OP_SESSION:planning" Enter
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Planning agent sequence**: `analyst` → `pm` → `ux-expert` (optional) → `architect` → `po`
|
|
213
|
+
|
|
214
|
+
#### Step 4: Report progress
|
|
215
|
+
|
|
216
|
+
After each agent completes, output structured progress to your own window (visible to the user):
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
📋 Planning Progress [2/5]
|
|
220
|
+
✅ Analyst — project-brief.md generated
|
|
221
|
+
▶️ PM — generating PRD...
|
|
222
|
+
⏳ UX Expert
|
|
223
|
+
⏳ Architect
|
|
224
|
+
⏳ PO
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Phase A → B Transition
|
|
228
|
+
|
|
229
|
+
When PO completes (step 5):
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
# Kill planning session
|
|
233
|
+
tmux kill-session -t "$OP_SESSION"
|
|
234
|
+
|
|
235
|
+
# Launch dev session with same op-{name}
|
|
236
|
+
# start-orchestrix.sh reads ORCHESTRIX_SESSION and uses it as the session name
|
|
237
|
+
# instead of its default "orchestrix-{repo-id}" naming.
|
|
238
|
+
ORCHESTRIX_SESSION="$OP_SESSION" bash "$WORK_DIR/.orchestrix-core/scripts/start-orchestrix.sh"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Important**: Pass `ORCHESTRIX_SESSION` inline (not `export`) to avoid polluting Yuri's own shell environment. The script creates a new `op-{name}` session with 4 dev agent windows.
|
|
242
|
+
|
|
243
|
+
### Phase B: Remote Development Monitoring
|
|
244
|
+
|
|
245
|
+
After `start-orchestrix.sh` launches, HANDOFF automation runs autonomously. Yuri monitors:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
# Watch HANDOFF log
|
|
249
|
+
tail -f /tmp/${OP_SESSION}-handoff.log
|
|
250
|
+
|
|
251
|
+
# Check specific agent output
|
|
252
|
+
tmux capture-pane -t "$OP_SESSION:1" -p | tail -20 # SM
|
|
253
|
+
tmux capture-pane -t "$OP_SESSION:2" -p | tail -20 # Dev
|
|
254
|
+
|
|
255
|
+
# Check story completion
|
|
256
|
+
ls "$WORK_DIR/docs/stories/"
|
|
257
|
+
git -C "$WORK_DIR" log --oneline -5
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Session Lifecycle
|
|
261
|
+
|
|
262
|
+
| Event | Action |
|
|
263
|
+
|-------|--------|
|
|
264
|
+
| User says "start planning" | Create `op-{name}`, begin agent sequence |
|
|
265
|
+
| Planning complete | Kill `op-{name}`, launch `start-orchestrix.sh` |
|
|
266
|
+
| Development complete | Report to user, ready for Phase C |
|
|
267
|
+
| User says "stop" / "cancel" | `tmux kill-session -t "$OP_SESSION"` |
|
|
268
|
+
| Reconnect / resume | Check `tmux has-session -t "$OP_SESSION"` to detect existing session |
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
79
272
|
## Full Workflow Overview
|
|
80
273
|
|
|
81
274
|
```
|
package/lib/license.js
CHANGED
|
@@ -2,14 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
5
6
|
const ui = require('./ui');
|
|
6
7
|
|
|
8
|
+
// License key format: orch_{tier}_{identifier}
|
|
9
|
+
// Examples: orch_live_xxx, orch_trial_xxx, ORCH-TEAM-xxx
|
|
10
|
+
const KEY_PATTERN = /^(orch_[a-z]+_[\w]+|ORCH-[A-Z]+-[\w-]+)$/;
|
|
11
|
+
|
|
12
|
+
function isValidKeyFormat(key) {
|
|
13
|
+
return KEY_PATTERN.test(key);
|
|
14
|
+
}
|
|
15
|
+
|
|
7
16
|
/**
|
|
8
17
|
* Resolve license key from multiple sources (priority order):
|
|
9
18
|
* 1. --key CLI flag
|
|
10
19
|
* 2. ORCHESTRIX_LICENSE_KEY env var
|
|
11
|
-
* 3. .
|
|
12
|
-
* 4.
|
|
20
|
+
* 3. .orchestrix-key hidden file (project dir → home dir)
|
|
21
|
+
* 4. .env.local file in cwd
|
|
22
|
+
* 5. Interactive prompt (saves to .orchestrix-key for next time)
|
|
13
23
|
*/
|
|
14
24
|
async function resolveKey(flags) {
|
|
15
25
|
// 1. CLI flag
|
|
@@ -23,7 +33,23 @@ async function resolveKey(flags) {
|
|
|
23
33
|
return process.env.ORCHESTRIX_LICENSE_KEY;
|
|
24
34
|
}
|
|
25
35
|
|
|
26
|
-
// 3. .
|
|
36
|
+
// 3. .orchestrix-key hidden file (project dir first, then home dir)
|
|
37
|
+
const keyLocations = [
|
|
38
|
+
path.join(process.cwd(), '.orchestrix-key'),
|
|
39
|
+
path.join(os.homedir(), '.orchestrix-key'),
|
|
40
|
+
];
|
|
41
|
+
for (const keyPath of keyLocations) {
|
|
42
|
+
if (fs.existsSync(keyPath)) {
|
|
43
|
+
const raw = fs.readFileSync(keyPath, 'utf-8').trim();
|
|
44
|
+
if (raw && isValidKeyFormat(raw)) {
|
|
45
|
+
const rel = keyPath.startsWith(process.cwd()) ? '.orchestrix-key' : '~/.orchestrix-key';
|
|
46
|
+
ui.info(`Using license key from ${rel}`);
|
|
47
|
+
return raw;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 4. .env.local file
|
|
27
53
|
const envLocalPath = path.join(process.cwd(), '.env.local');
|
|
28
54
|
if (fs.existsSync(envLocalPath)) {
|
|
29
55
|
const content = fs.readFileSync(envLocalPath, 'utf-8');
|
|
@@ -34,11 +60,17 @@ async function resolveKey(flags) {
|
|
|
34
60
|
}
|
|
35
61
|
}
|
|
36
62
|
|
|
37
|
-
//
|
|
63
|
+
// 5. Interactive prompt
|
|
38
64
|
const key = await ui.prompt('Enter your Orchestrix license key');
|
|
39
65
|
if (!key) {
|
|
40
66
|
throw new Error('License key is required. Use --key <KEY> or set ORCHESTRIX_LICENSE_KEY env var.');
|
|
41
67
|
}
|
|
68
|
+
|
|
69
|
+
// Save to ~/.orchestrix-key for future use
|
|
70
|
+
const globalKeyPath = path.join(os.homedir(), '.orchestrix-key');
|
|
71
|
+
fs.writeFileSync(globalKeyPath, key + '\n', { mode: 0o600 });
|
|
72
|
+
ui.success(`License key saved to ~/.orchestrix-key`);
|
|
73
|
+
|
|
42
74
|
return key;
|
|
43
75
|
}
|
|
44
76
|
|