tylor-mcp 1.0.0 β†’ 1.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.
Files changed (59) hide show
  1. package/.npmrc.publish +1 -0
  2. package/README.md +165 -146
  3. package/bin/tylor.js +36 -2
  4. package/package.json +5 -2
  5. package/pytest.ini +2 -2
  6. package/scripts/dev-sync.js +113 -0
  7. package/server/config.py +8 -13
  8. package/server/storage/dynamo.py +33 -0
  9. package/server/storage/json_store.py +49 -2
  10. package/server/tools/agents.py +296 -11
  11. package/server/tools/executor.py +83 -12
  12. package/server/tools/harness.py +238 -31
  13. package/server/tools/help.py +21 -1
  14. package/server/tools/hooks.py +19 -6
  15. package/server/tools/registry.py +50 -3
  16. package/server/tools/security.py +303 -0
  17. package/server/tools/skill_installer.py +34 -1
  18. package/server/tools/summarizer.py +3 -1
  19. package/server/tools/tylor.py +11 -10
  20. package/server/ui_server.py +160 -11
  21. package/server/validate.py +29 -15
  22. package/skills/ecc-data/SKILL.md +32 -0
  23. package/skills/ecc-diagrams/SKILL.md +31 -0
  24. package/skills/ecc-pipeline/SKILL.md +31 -0
  25. package/skills/ecc-presentation/SKILL.md +31 -0
  26. package/skills/ecc-web/SKILL.md +31 -0
  27. package/skills/help-agent101/SKILL.md +1 -0
  28. package/skills/open-threads-ui/SKILL.md +33 -0
  29. package/skills/tylor-run/SKILL.md +61 -0
  30. package/ui/index.html +760 -889
  31. package/server/storage/tests/__init__.py +0 -0
  32. package/server/storage/tests/test_dynamo.py +0 -452
  33. package/server/storage/tests/test_json_store.py +0 -226
  34. package/server/storage/tests/test_opensearch.py +0 -270
  35. package/server/storage/tests/test_s3.py +0 -125
  36. package/server/tests/__init__.py +0 -0
  37. package/server/tests/test_install.py +0 -606
  38. package/server/tests/test_isolation.py +0 -90
  39. package/server/tests/test_ui_server.py +0 -385
  40. package/server/tests/test_ui_shader_background.py +0 -52
  41. package/server/tests/test_ui_story_6_3.py +0 -105
  42. package/server/tools/tests/__init__.py +0 -0
  43. package/server/tools/tests/test_agents.py +0 -246
  44. package/server/tools/tests/test_code_index.py +0 -108
  45. package/server/tools/tests/test_ecc_tools.py +0 -51
  46. package/server/tools/tests/test_executor.py +0 -584
  47. package/server/tools/tests/test_help_agent101.py +0 -149
  48. package/server/tools/tests/test_hooks.py +0 -124
  49. package/server/tools/tests/test_kill_thread.py +0 -125
  50. package/server/tools/tests/test_new_thread_list_threads.py +0 -293
  51. package/server/tools/tests/test_personas.py +0 -52
  52. package/server/tools/tests/test_recall_memory.py +0 -55
  53. package/server/tools/tests/test_registry_client.py +0 -308
  54. package/server/tools/tests/test_router.py +0 -263
  55. package/server/tools/tests/test_skill_installer.py +0 -174
  56. package/server/tools/tests/test_switch_thread.py +0 -163
  57. package/server/tools/tests/test_thread_command_skills.py +0 -54
  58. package/server/tools/tests/test_thread_resolver.py +0 -165
  59. package/server/tools/tests/test_tier1_schema.py +0 -296
package/.npmrc.publish ADDED
@@ -0,0 +1 @@
1
+ //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}
package/README.md CHANGED
@@ -1,146 +1,165 @@
1
- <div align="center">
2
- <img src="assets/tylor_logo.png" alt="Tylor Logo" width="150">
3
- <h1>Tylor</h1>
4
- <p><strong>The Tailor to Your Threads</strong></p>
5
- <p><em>Give Claude Code persistent memory, laser-focused context, and an autonomous team of specialists.</em></p>
6
-
7
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
8
- [![Platform: Windows | macOS | Linux | WSL](https://img.shields.io/badge/Platform-Cross--Platform-success)](#)
9
- [![Claude Code](https://img.shields.io/badge/Integration-Claude_Code-orange)](#)
10
- [![GitHub Copilot](https://img.shields.io/badge/Integration-GitHub_Copilot-blue)](#)
11
- [![Antigravity](https://img.shields.io/badge/Integration-Antigravity-blueviolet)](#)
12
- </div>
13
-
14
- ---
15
-
16
- Tylor transforms your Claude Code experience from a single-shot terminal interaction into a **persistent, intelligent workspace**.
17
-
18
- Every time you open Claude Code, you normally start from zero. Tylor fixes that. It organizes your work into **threads**β€”isolated, named workspaces that survive restarts and reboots. It remembers every decision, every line of code, and every discussion, so you never have to repeat yourself.
19
-
20
- **No database. No cloud account. No configuration. Just install and go.**
21
-
22
- ---
23
-
24
- ## 🎨 How It Works
25
-
26
- <div align="center">
27
- <img src="assets/tylor_threads_concept.png" alt="Tylor Threads Architecture" width="800">
28
- <p><em>Tylor weaves parallel, persistent memory threads and orchestrates specialist sub-agents.</em></p>
29
- </div>
30
-
31
- ---
32
-
33
- ## ✨ Features
34
-
35
- ### 🧠 Persistent Memory
36
- Tylor completely eliminates the "context reset." Shut down your computer, close your terminal, and come back a week laterβ€”Claude will pick up exactly where you left off.
37
-
38
- ### πŸ—‚οΈ Context Isolation (Threads)
39
- Work in parallel without context bleed. Discuss frontend components in a `Frontend` thread and database schemas in a `Backend` thread. By isolating context, token usage stays low, and Claude's focus stays incredibly sharp.
40
-
41
- ### πŸ€– Intelligent Orchestration
42
- You don't need to micromanage. Claude acts as the orchestrator. If you ask it to review architecture, it will dynamically load its `cto` persona. If you ask it to write a PRD, it natively invokes the `bmad` skill framework to get the job done.
43
-
44
- ### πŸ”Œ Infinite Extensibility (Lazy-Loading)
45
- Tylor is built on a production-hardened ADK-pattern harness. You can register hundreds of domain-specific ECC skills (like `ecc/web`, `ecc/data`) via the `/add-skill` command. Tylor **lazy-loads** only the tools required for the current prompt, giving you massive capability scaling without ever blowing up Claude's token context window.
46
-
47
- ### πŸ—οΈ Autonomous AFK Sandboxing
48
- Declare a sandbox for your thread and let Claude work autonomously. Assign large, complex tasks and let Claude execute them while you step away from the keyboard.
49
-
50
- ### πŸ“Š Visual Dashboard
51
- Monitor your entire workspace through a beautiful, locally hosted web UI. Track active threads, review past conversations, and watch autonomous agent progress in real-time.
52
-
53
- ---
54
-
55
- ## πŸš€ Installation
56
-
57
- Tylor installs seamlessly into your Claude Code, Claude Desktop, GitHub Copilot, Antigravity, or VSCode Claude extension environment. Requires Python 3.8+.
58
-
59
- ### ⚑ Option 1: The One-Line Installer (Recommended)
60
-
61
- If you have Node.js installed, you can configure Tylor instantly across all your clients without manually cloning the repository. Simply run:
62
-
63
- ```bash
64
- npx tylor-mcp
65
- ```
66
-
67
- ### πŸ’» Option 2: Manual Git Clone
68
-
69
- **macOS / Linux / WSL:**
70
- ```bash
71
- git clone https://github.com/GunjanGrunge/tylor ~/.claude/plugins/GunjanGrunge/tylor
72
- python3 ~/.claude/plugins/GunjanGrunge/tylor/install.py
73
- ```
74
-
75
- **Windows:**
76
- ```powershell
77
- git clone https://github.com/GunjanGrunge/tylor %USERPROFILE%\.claude\plugins\GunjanGrunge\tylor
78
- python %USERPROFILE%\.claude\plugins\GunjanGrunge\tylor\install.py
79
- ```
80
-
81
- ### Step 3: Verify
82
-
83
- 1. Restart your Claude, GitHub Copilot, or Antigravity client completely (close the terminal/app and reopen it).
84
- 2. Type `/help-agent101` in your prompt (or use Copilot Chat / `/mcp show`).
85
- 3. If you see the capability index, Tylor is fully operational!
86
-
87
- ---
88
-
89
- ## πŸ•ΉοΈ Quick Start
90
-
91
- Creating your first persistent workflow is incredibly simple:
92
-
93
- ```text
94
- /new-thread Authentication ← Create a persistent workspace
95
- /run we need to implement JWT based authentication
96
-
97
- /new-thread Dashboard UI ← Create an isolated UI thread
98
- /run build a react dashboard with a sidebar
99
-
100
- /switch-thread Authentication ← Instantly switch context back to Auth
101
- /run add refresh token logic
102
-
103
- /list-threads ← View your workspace status
104
- /open-threads-ui ← Launch the visual dashboard
105
- ```
106
-
107
- ---
108
-
109
- ## πŸ› οΈ Command Reference
110
-
111
- Tylor exposes a suite of powerful commands directly within Claude:
112
-
113
- | Command | Description |
114
- |---|---|
115
- | `/new-thread <name>` | Create a named thread and seamlessly switch future work into it. |
116
- | `/switch-thread <name>` | Switch context to an existing thread (fuzzy matching supported). |
117
- | `/list-threads` | Show all available threads alongside their status and activity. |
118
- | `/kill-thread <name>` | Close a thread and dispatch asynchronous summarization. |
119
- | `/recall` | Search through the deep semantic memory of your active thread. |
120
- | `/add-skill` | Install a new skill package dynamically. |
121
- | `/open-threads-ui` | Open the live, local thread visualizer UI in your browser. |
122
- | `/set-sandbox <path>` | Declare specific filesystem roots for secure, autonomous execution. |
123
- | `/afk-status` | Get real-time progress reports on current autonomous background tasks. |
124
-
125
- > **Pro Tip:** You can also use shorthand aliases like `CT <name>` to create a thread or `SwThread <name>` to switch.
126
-
127
- ---
128
-
129
- ## 🎭 Sub-Agents & Personas
130
-
131
- Tylor comes pre-equipped with specialist sub-agents. Claude will **automatically invoke** these personas based on the nature of your queryβ€”no manual intervention required.
132
-
133
- * **`cto`**: System architecture, tradeoffs, platform strategy, and engineering standards.
134
- * **`code_agent`**: Senior software engineer laser-focused on shipping robust code and tests.
135
- * **`analyst`**: Market research, data synthesis, and technical decision support.
136
- * **`ceo`**: Product strategy, roadmap prioritization, and stakeholder framing.
137
-
138
- ---
139
-
140
- ## πŸ“„ License
141
-
142
- This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
143
-
144
- <div align="center">
145
- <p><em>Tylor β€” Tailoring the future of AI development.</em></p>
146
- </div>
1
+ <div align="center">
2
+ <img src="assets/tylor_logo.png" alt="Tylor Logo" width="150">
3
+ <h1>Tylor</h1>
4
+ <p><strong>The Tailor to Your Threads</strong></p>
5
+ <p><em>Give Claude Code persistent memory, laser-focused context, and an autonomous team of specialists.</em></p>
6
+
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
8
+ [![Platform: Windows | macOS | Linux | WSL](https://img.shields.io/badge/Platform-Cross--Platform-success)](#)
9
+ [![Claude Code](https://img.shields.io/badge/Integration-Claude_Code-orange)](#)
10
+ [![GitHub Copilot](https://img.shields.io/badge/Integration-GitHub_Copilot-blue)](#)
11
+ [![Antigravity](https://img.shields.io/badge/Integration-Antigravity-blueviolet)](#)
12
+ </div>
13
+
14
+ ---
15
+
16
+ Tylor transforms your Claude Code experience from a single-shot terminal interaction into a **persistent, intelligent workspace**.
17
+
18
+ Every time you open Claude Code, you normally start from zero. Tylor fixes that. It organizes your work into **threads**β€”isolated, named workspaces that survive restarts and reboots. It remembers every decision, every line of code, and every discussion, so you never have to repeat yourself.
19
+
20
+ **No database. No cloud account. No configuration. Just install and go.**
21
+
22
+ ---
23
+
24
+ ## 🎨 How It Works
25
+
26
+ <div align="center">
27
+ <img src="assets/tylor_threads_concept.png" alt="Tylor Threads Architecture" width="800">
28
+ <p><em>Tylor weaves parallel, persistent memory threads and orchestrates specialist sub-agents.</em></p>
29
+ </div>
30
+
31
+ ---
32
+
33
+ ## ✨ Features
34
+
35
+ ### 🧠 Persistent Memory
36
+ Tylor completely eliminates the "context reset." Shut down your computer, close your terminal, and come back a week laterβ€”Claude will pick up exactly where you left off.
37
+
38
+ ### πŸ—‚οΈ Context Isolation (Threads)
39
+ Work in parallel without context bleed. Discuss frontend components in a `Frontend` thread and database schemas in a `Backend` thread. By isolating context, token usage stays low, and Claude's focus stays incredibly sharp.
40
+
41
+ ### πŸ€– Intelligent Orchestration
42
+ You don't need to micromanage. Claude acts as the orchestrator. If you ask it to review architecture, it will dynamically load its `cto` persona. If you ask it to write a PRD, it natively invokes the `bmad` skill framework to get the job done.
43
+
44
+ ### πŸ”Œ Infinite Extensibility (Lazy-Loading)
45
+ Tylor is built on a production-hardened ADK-pattern harness. You can register hundreds of domain-specific ECC skills (like `ecc/web`, `ecc/data`) via the `/add-skill` command. Tylor **lazy-loads** only the tools required for the current prompt, giving you massive capability scaling without ever blowing up Claude's token context window.
46
+
47
+ ### πŸ—οΈ Autonomous AFK Sandboxing
48
+ Declare a sandbox for your thread and let Claude work autonomously. Assign large, complex tasks and let Claude execute them while you step away from the keyboard.
49
+
50
+ ### πŸ“Š Visual Dashboard
51
+ Monitor your entire workspace through a beautiful, locally hosted web UI. Track active threads, review past conversations, and watch autonomous agent progress in real-time.
52
+
53
+ ---
54
+
55
+ ## πŸš€ Installation
56
+
57
+ Tylor installs seamlessly into your Claude Code, Claude Desktop, GitHub Copilot, Antigravity, or VSCode Claude extension environment. Requires Python 3.8+.
58
+
59
+ ### ⚑ Option 1: The One-Line Installer (Recommended)
60
+
61
+ If you have Node.js installed, you can configure Tylor instantly across all your clients without manually cloning the repository. Simply run:
62
+
63
+ ```bash
64
+ npx tylor-mcp
65
+ ```
66
+
67
+ ### πŸ’» Option 2: Manual Git Clone
68
+
69
+ **macOS / Linux / WSL:**
70
+ ```bash
71
+ git clone https://github.com/GunjanGrunge/tylor ~/.claude/plugins/GunjanGrunge/tylor
72
+ python3 ~/.claude/plugins/GunjanGrunge/tylor/install.py
73
+ ```
74
+
75
+ **Windows:**
76
+ ```powershell
77
+ git clone https://github.com/GunjanGrunge/tylor %USERPROFILE%\.claude\plugins\GunjanGrunge\tylor
78
+ python %USERPROFILE%\.claude\plugins\GunjanGrunge\tylor\install.py
79
+ ```
80
+
81
+ ### Step 3: Verify
82
+
83
+ 1. Restart your Claude, GitHub Copilot, or Antigravity client completely (close the terminal/app and reopen it).
84
+ 2. Type `/help-agent101` in your prompt (or use Copilot Chat / `/mcp show`).
85
+ 3. If you see the capability index, Tylor is fully operational!
86
+
87
+ ---
88
+
89
+ ## πŸ•ΉοΈ Quick Start
90
+
91
+ Creating your first persistent workflow is incredibly simple:
92
+
93
+ ```text
94
+ /new-thread Authentication ← Create a persistent workspace
95
+ /run we need to implement JWT based authentication
96
+
97
+ /new-thread Dashboard UI ← Create an isolated UI thread
98
+ /run build a react dashboard with a sidebar
99
+
100
+ /switch-thread Authentication ← Instantly switch context back to Auth
101
+ /run add refresh token logic
102
+
103
+ /list-threads ← View your workspace status
104
+ /open-threads-ui ← Launch the visual dashboard
105
+ ```
106
+
107
+ ---
108
+
109
+ ## πŸ› οΈ Command Reference
110
+
111
+ Tylor exposes a suite of powerful commands directly within Claude:
112
+
113
+ | Command | Description |
114
+ |---|---|
115
+ | `/new-thread <name>` | Create a named thread and seamlessly switch future work into it. |
116
+ | `/switch-thread <name>` | Switch context to an existing thread (fuzzy matching supported). |
117
+ | `/list-threads` | Show all available threads alongside their status and activity. |
118
+ | `/kill-thread <name>` | Close a thread and dispatch asynchronous summarization. |
119
+ | `/recall` | Search through the deep semantic memory of your active thread. |
120
+ | `/add-skill` | Install a new skill package dynamically. |
121
+ | `/open-threads-ui` | Open the live, local thread visualizer UI in your browser. |
122
+ | `/set-sandbox <path>` | Declare specific filesystem roots for secure, autonomous execution. |
123
+ | `/afk-status` | Get real-time progress reports on current autonomous background tasks. |
124
+
125
+ > **Pro Tip:** You can also use shorthand aliases like `CT <name>` to create a thread or `SwThread <name>` to switch.
126
+
127
+ ---
128
+
129
+ ## πŸ”’ Bumblebee Security Gate
130
+
131
+ Tylor now includes a default, plugin-wide security gate powered by Bumblebee. When a risky command is detectedβ€”especially package installs, extension installs, skill/package additions, or MCP config changesβ€”Tylor will initiate a read-only Bumblebee scan before the command runs.
132
+
133
+ - Enabled by default for any command pattern that looks like `pip install`, `npm install`, editor/extension installs, or skill/config setup.
134
+ - If Bumblebee is missing, Tylor will flag the command and surface clear guidance instead of executing it blindly.
135
+ - If Bumblebee detects risk, execution is blocked and the user sees actionable alternatives.
136
+
137
+ Suggested responses from the gate include:
138
+
139
+ - Install Bumblebee or set `BUMBLEBEE_PATH` if the CLI is not found.
140
+ - Run `bumblebee scan --json` manually before retrying.
141
+ - Disable the gate temporarily with `BUMBLEBEE_ENABLED=false` only if you understand the risk.
142
+ - Review package metadata, MCP config changes, and AI tool integrations before proceeding.
143
+
144
+ This layer applies across the plugin, regardless of which thread or persona is active.
145
+
146
+ ---
147
+
148
+ ## 🎭 Sub-Agents & Personas
149
+
150
+ Tylor comes pre-equipped with specialist sub-agents. Claude will **automatically invoke** these personas based on the nature of your queryβ€”no manual intervention required.
151
+
152
+ * **`cto`**: System architecture, tradeoffs, platform strategy, and engineering standards.
153
+ * **`code_agent`**: Senior software engineer laser-focused on shipping robust code and tests.
154
+ * **`analyst`**: Market research, data synthesis, and technical decision support.
155
+ * **`ceo`**: Product strategy, roadmap prioritization, and stakeholder framing.
156
+
157
+ ---
158
+
159
+ ## πŸ“„ License
160
+
161
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
162
+
163
+ <div align="center">
164
+ <p><em>Tylor β€” Tailoring the future of AI development.</em></p>
165
+ </div>
package/bin/tylor.js CHANGED
@@ -1,7 +1,41 @@
1
1
  #!/usr/bin/env node
2
- const { spawnSync } = require('child_process');
2
+ const { spawnSync, execSync } = require('child_process');
3
3
  const path = require('path');
4
+ const https = require('https');
5
+ const fs = require('fs');
4
6
 
7
+ // ── Update check ──────────────────────────────────────────────────────────────
8
+ function checkForUpdate() {
9
+ try {
10
+ const pkgPath = path.join(__dirname, '..', 'package.json');
11
+ const localPkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
12
+ const localVersion = localPkg.version;
13
+ const pkgName = localPkg.name;
14
+
15
+ const url = `https://registry.npmjs.org/${pkgName}/latest`;
16
+ const req = https.get(url, { timeout: 3000 }, (res) => {
17
+ let data = '';
18
+ res.on('data', chunk => data += chunk);
19
+ res.on('end', () => {
20
+ try {
21
+ const latest = JSON.parse(data).version;
22
+ if (latest && latest !== localVersion) {
23
+ console.log('');
24
+ console.log(` ⚠️ Update available: ${localVersion} β†’ ${latest}`);
25
+ console.log(` Run: npm update -g ${pkgName} (or npx ${pkgName}@latest)`);
26
+ console.log('');
27
+ }
28
+ } catch (_) { /* silent */ }
29
+ });
30
+ });
31
+ req.on('error', () => { /* silent β€” offline or registry down */ });
32
+ req.on('timeout', () => { req.destroy(); });
33
+ } catch (_) { /* silent */ }
34
+ }
35
+
36
+ checkForUpdate();
37
+
38
+ // ── Installer ─────────────────────────────────────────────────────────────────
5
39
  const installPy = path.join(__dirname, '..', 'install.py');
6
40
  const args = process.argv.slice(2);
7
41
 
@@ -13,7 +47,7 @@ let result = spawnSync('python', [installPy, ...args], { stdio: 'inherit' });
13
47
  // Fallback to `python3` if `python` fails (standard on macOS/Linux)
14
48
  if (result.error || result.status !== 0) {
15
49
  result = spawnSync('python3', [installPy, ...args], { stdio: 'inherit' });
16
-
50
+
17
51
  if (result.error) {
18
52
  console.error("❌ Failed to launch the Tylor installer. Please ensure Python 3.8+ is installed on your system.");
19
53
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tylor-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Give Claude Code persistent memory, laser-focused context, and an autonomous team of specialists.",
5
5
  "main": "server/main.py",
6
6
  "bin": {
@@ -20,5 +20,8 @@
20
20
  "github-copilot"
21
21
  ],
22
22
  "author": "Gunjan Grunge",
23
- "license": "MIT"
23
+ "license": "MIT",
24
+ "scripts": {
25
+ "dev:sync": "node scripts/dev-sync.js"
26
+ }
24
27
  }
package/pytest.ini CHANGED
@@ -1,2 +1,2 @@
1
- [pytest]
2
- asyncio_mode = auto
1
+ [pytest]
2
+ asyncio_mode = auto
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * dev-sync.js β€” sync dev changes to the live installed server.
4
+ *
5
+ * Reads PYTHONPATH from ~/.claude/settings.json (written there by install.py),
6
+ * so it always targets the correct installed location regardless of npm version
7
+ * or _npx cache hash. No hardcoding.
8
+ *
9
+ * Usage:
10
+ * npm run dev:sync
11
+ */
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const os = require('os');
16
+
17
+ // ── Find the installed server path from settings.json ────────────────────────
18
+
19
+ function findInstalledPath() {
20
+ const candidates = [
21
+ path.join(os.homedir(), '.claude', 'settings.json'),
22
+ path.join(os.homedir(), 'AppData', 'Roaming', 'Claude', 'settings.json'),
23
+ ];
24
+
25
+ for (const candidate of candidates) {
26
+ if (!fs.existsSync(candidate)) continue;
27
+ try {
28
+ const settings = JSON.parse(fs.readFileSync(candidate, 'utf8'));
29
+ const pythonpath =
30
+ settings?.mcpServers?.agent101?.env?.PYTHONPATH;
31
+ if (pythonpath && fs.existsSync(pythonpath)) {
32
+ return pythonpath;
33
+ }
34
+ } catch (_) { /* malformed β€” skip */ }
35
+ }
36
+ return null;
37
+ }
38
+
39
+ // ── Files and directories to sync ────────────────────────────────────────────
40
+
41
+ const DEV_ROOT = path.join(__dirname, '..');
42
+
43
+ const SYNC_FILES = [
44
+ 'server/tools/harness.py',
45
+ 'server/tools/security.py',
46
+ 'server/tools/executor.py',
47
+ 'server/tools/registry.py',
48
+ ];
49
+
50
+ const SYNC_DIRS = [
51
+ 'skills',
52
+ ];
53
+
54
+ // ── Helpers ───────────────────────────────────────────────────────────────────
55
+
56
+ function copyFile(src, dst) {
57
+ fs.mkdirSync(path.dirname(dst), { recursive: true });
58
+ fs.copyFileSync(src, dst);
59
+ }
60
+
61
+ function syncDir(srcDir, dstDir) {
62
+ if (!fs.existsSync(srcDir)) return 0;
63
+ let count = 0;
64
+ for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
65
+ const srcPath = path.join(srcDir, entry.name);
66
+ const dstPath = path.join(dstDir, entry.name);
67
+ if (entry.isDirectory()) {
68
+ count += syncDir(srcPath, dstPath);
69
+ } else {
70
+ copyFile(srcPath, dstPath);
71
+ count++;
72
+ }
73
+ }
74
+ return count;
75
+ }
76
+
77
+ // ── Main ──────────────────────────────────────────────────────────────────────
78
+
79
+ const installed = findInstalledPath();
80
+
81
+ if (!installed) {
82
+ console.error('❌ Could not find installed server path in ~/.claude/settings.json');
83
+ console.error(' Run: npx tylor-mcp to install first.');
84
+ process.exit(1);
85
+ }
86
+
87
+ console.log(`🎯 Target: ${installed}\n`);
88
+
89
+ let total = 0;
90
+
91
+ for (const rel of SYNC_FILES) {
92
+ const src = path.join(DEV_ROOT, rel);
93
+ const dst = path.join(installed, rel);
94
+ if (!fs.existsSync(src)) {
95
+ console.warn(`⚠️ skip (not found): ${rel}`);
96
+ continue;
97
+ }
98
+ copyFile(src, dst);
99
+ console.log(` βœ“ ${rel}`);
100
+ total++;
101
+ }
102
+
103
+ for (const rel of SYNC_DIRS) {
104
+ const src = path.join(DEV_ROOT, rel);
105
+ const dst = path.join(installed, rel);
106
+ const n = syncDir(src, dst);
107
+ if (n > 0) {
108
+ console.log(` βœ“ ${rel}/ (${n} files)`);
109
+ total += n;
110
+ }
111
+ }
112
+
113
+ console.log(`\nβœ… ${total} file(s) synced. Restart Claude Code to pick up changes.`);
package/server/config.py CHANGED
@@ -1,8 +1,8 @@
1
1
  """
2
2
  server/config.py β€” Agent101 server configuration.
3
- Reads AWS profile, Bedrock region, and Platform on AWS key.
3
+ Reads local storage settings and optional cloud provider settings.
4
4
  Resolution order: env var β†’ ~/.agent101/config.json β†’ .env file β†’ defaults.
5
- Warns on missing optional config; never crashes on startup.
5
+ Missing cloud settings are normal in local-first mode; startup never crashes.
6
6
  """
7
7
  from __future__ import annotations
8
8
  import json
@@ -57,6 +57,7 @@ def _load() -> dict:
57
57
  cfg = {
58
58
  "aws_profile": _get("AWS_PROFILE"),
59
59
  "aws_access_key_id": _get("AWS_ACCESS_KEY_ID"),
60
+ "storage_mode": _get("AGENT101_STORAGE_MODE", default="local"),
60
61
  "bedrock_region": _get("BEDROCK_REGION", default="us-east-1"),
61
62
  "bedrock_opus_model": _get(
62
63
  "BEDROCK_OPUS_MODEL",
@@ -69,19 +70,13 @@ def _load() -> dict:
69
70
  "s3_bucket": _get("S3_BUCKET"),
70
71
  "opensearch_host": _get("OPENSEARCH_HOST"),
71
72
  "opensearch_port": _get("OPENSEARCH_PORT", default="9200"),
73
+ "bumblebee_enabled": str(_get("BUMBLEBEE_ENABLED", default="true")).strip().lower() in {"1", "true", "yes", "on"},
74
+ "bumblebee_path": _get("BUMBLEBEE_PATH"),
72
75
  }
73
76
 
74
- # Warn on optional keys that affect runtime features
75
- if not cfg["platform_key"]:
76
- logger.warning(
77
- "ANTHROPIC_PLATFORM_AWS_API_KEY/ANTHROPIC_AWS_API_KEY not set β€” "
78
- "token overflow fallback to Claude Platform on AWS is disabled"
79
- )
80
- if not cfg["opensearch_host"]:
81
- logger.warning(
82
- "OPENSEARCH_HOST not set β€” "
83
- "semantic memory recall (recall_memory) will be unavailable until configured"
84
- )
77
+ if cfg["storage_mode"] not in {"local", "aws"}:
78
+ logger.warning("Unknown AGENT101_STORAGE_MODE=%s; falling back to local mode", cfg["storage_mode"])
79
+ cfg["storage_mode"] = "local"
85
80
 
86
81
  return cfg
87
82
 
@@ -187,6 +187,29 @@ class DynamoClient:
187
187
  attributes["Task"] = task
188
188
  return self.put_item(sk=sk, attributes=attributes)
189
189
 
190
+ def put_agent_event(
191
+ self,
192
+ thread_id: str,
193
+ agent_id: str,
194
+ event_type: str,
195
+ content: str,
196
+ persona: str | None = None,
197
+ ) -> dict:
198
+ """Persist a streamed sub-agent event for live UI replay."""
199
+ self._validate_agent_id(agent_id)
200
+ sk = f"THREAD#{thread_id}#AGENT#{agent_id}#EVENT#{_unique_event_suffix()}"
201
+ self._assert_thread_isolation(thread_id, sk)
202
+ attributes = {
203
+ "ThreadId": thread_id,
204
+ "AgentId": agent_id,
205
+ "Type": "agent_event",
206
+ "EventType": event_type,
207
+ "Content": content,
208
+ }
209
+ if persona:
210
+ attributes["Persona"] = persona
211
+ return self.put_item(sk=sk, attributes=attributes)
212
+
190
213
  def put_agent_handoff(
191
214
  self,
192
215
  thread_id: str,
@@ -230,6 +253,16 @@ class DynamoClient:
230
253
  items = self.query_thread(thread_id, f"THREAD#{thread_id}#AGENT#")
231
254
  return [item for item in items if item.get("SK", "").endswith("#STATE")]
232
255
 
256
+ def query_agent_events(self, thread_id: str, agent_id: str | None = None) -> list:
257
+ """Return streamed sub-agent events for one thread, optionally one agent."""
258
+ prefix = f"THREAD#{thread_id}#AGENT#"
259
+ if agent_id:
260
+ self._validate_agent_id(agent_id)
261
+ prefix = f"{prefix}{agent_id}#EVENT#"
262
+ items = self.query_thread(thread_id, prefix)
263
+ events = [item for item in items if "#EVENT#" in item.get("SK", "")]
264
+ return sorted(events, key=lambda item: item.get("SK", ""))
265
+
233
266
  def get_thread_meta(self, thread_id: str) -> dict | None:
234
267
  """Return thread META item for the given thread_id."""
235
268
  return self.get_item(thread_id, f"THREAD#{thread_id}#META")