@hailer/mcp 0.1.6 → 0.1.9

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 (137) hide show
  1. package/.claude/agents/agent-dmitri-activity-crud.md +3 -1
  2. package/.claude/agents/agent-giuseppe-app-builder.md +11 -12
  3. package/.claude/agents/agent-kenji-data-reader.md +5 -3
  4. package/.claude/hooks/sync-marketplace-agents.cjs +117 -56
  5. package/.claude/skills/hailer-app-builder/SKILL.md +506 -0
  6. package/.claude/skills/publish-hailer-app/SKILL.md +169 -0
  7. package/.claude/skills/tool-parameter-usage/SKILL.md +112 -0
  8. package/CHANGELOG.md +20 -0
  9. package/CLAUDE.md +37 -16
  10. package/REFACTOR_STATUS.md +127 -0
  11. package/dist/cli.js +0 -0
  12. package/dist/client/agents/base.d.ts +202 -0
  13. package/dist/client/agents/base.js +737 -0
  14. package/dist/client/agents/definitions.d.ts +53 -0
  15. package/dist/client/agents/definitions.js +178 -0
  16. package/dist/client/agents/orchestrator.d.ts +119 -0
  17. package/dist/client/agents/orchestrator.js +760 -0
  18. package/dist/client/agents/specialist.d.ts +86 -0
  19. package/dist/client/agents/specialist.js +340 -0
  20. package/dist/client/bot-manager.d.ts +44 -0
  21. package/dist/client/bot-manager.js +173 -0
  22. package/dist/client/chat-agent-daemon.d.ts +464 -0
  23. package/dist/client/chat-agent-daemon.js +1774 -0
  24. package/dist/client/daemon-factory.d.ts +106 -0
  25. package/dist/client/daemon-factory.js +301 -0
  26. package/dist/client/factory.d.ts +107 -0
  27. package/dist/client/factory.js +304 -0
  28. package/dist/client/index.d.ts +17 -0
  29. package/dist/client/index.js +38 -0
  30. package/dist/client/multi-bot-manager.d.ts +18 -0
  31. package/dist/client/multi-bot-manager.js +88 -1
  32. package/dist/client/orchestrator-daemon.d.ts +87 -0
  33. package/dist/client/orchestrator-daemon.js +444 -0
  34. package/dist/client/services/agent-registry.d.ts +108 -0
  35. package/dist/client/services/agent-registry.js +630 -0
  36. package/dist/client/services/conversation-manager.d.ts +50 -0
  37. package/dist/client/services/conversation-manager.js +136 -0
  38. package/dist/client/services/mcp-client.d.ts +48 -0
  39. package/dist/client/services/mcp-client.js +105 -0
  40. package/dist/client/services/message-classifier.d.ts +37 -0
  41. package/dist/client/services/message-classifier.js +187 -0
  42. package/dist/client/services/message-formatter.d.ts +84 -0
  43. package/dist/client/services/message-formatter.js +353 -0
  44. package/dist/client/services/session-logger.d.ts +106 -0
  45. package/dist/client/services/session-logger.js +446 -0
  46. package/dist/client/services/tool-executor.d.ts +41 -0
  47. package/dist/client/services/tool-executor.js +169 -0
  48. package/dist/client/services/workspace-schema-cache.d.ts +149 -0
  49. package/dist/client/services/workspace-schema-cache.js +732 -0
  50. package/dist/client/specialist-daemon.d.ts +77 -0
  51. package/dist/client/specialist-daemon.js +197 -0
  52. package/dist/client/specialists.d.ts +53 -0
  53. package/dist/client/specialists.js +178 -0
  54. package/dist/client/tool-schema-loader.d.ts +4 -3
  55. package/dist/client/tool-schema-loader.js +54 -8
  56. package/dist/client/types.d.ts +283 -55
  57. package/dist/client/types.js +113 -2
  58. package/dist/config.d.ts +1 -1
  59. package/dist/config.js +1 -1
  60. package/dist/core.d.ts +10 -2
  61. package/dist/core.js +43 -27
  62. package/dist/lib/logger.js +15 -3
  63. package/dist/mcp/UserContextCache.js +2 -2
  64. package/dist/mcp/hailer-clients.js +5 -5
  65. package/dist/mcp/signal-handler.js +27 -5
  66. package/dist/mcp/tools/activity.js +137 -65
  67. package/dist/mcp/tools/app-core.js +4 -140
  68. package/dist/mcp/tools/app-marketplace.js +15 -260
  69. package/dist/mcp/tools/app-member.js +2 -73
  70. package/dist/mcp/tools/app-scaffold.js +146 -87
  71. package/dist/mcp/tools/discussion.js +348 -73
  72. package/dist/mcp/tools/insight.js +74 -190
  73. package/dist/mcp/tools/workflow.js +20 -94
  74. package/dist/mcp/utils/hailer-api-client.d.ts +4 -2
  75. package/dist/mcp/utils/hailer-api-client.js +24 -10
  76. package/dist/mcp-server.d.ts +4 -0
  77. package/dist/mcp-server.js +24 -4
  78. package/dist/routes/agents.d.ts +44 -0
  79. package/dist/routes/agents.js +311 -0
  80. package/dist/services/agent-credential-store.d.ts +73 -0
  81. package/dist/services/agent-credential-store.js +212 -0
  82. package/lineup-manager/dist/assets/index-8ce6041d.css +1 -0
  83. package/lineup-manager/dist/assets/index-e168f265.js +600 -0
  84. package/lineup-manager/dist/index.html +15 -0
  85. package/lineup-manager/dist/manifest.json +17 -0
  86. package/lineup-manager/dist/vite.svg +1 -0
  87. package/package.json +1 -1
  88. package/dist/client/adaptive-documentation-bot.d.ts +0 -106
  89. package/dist/client/adaptive-documentation-bot.js +0 -464
  90. package/dist/client/adaptive-documentation-types.d.ts +0 -66
  91. package/dist/client/adaptive-documentation-types.js +0 -9
  92. package/dist/client/agent-activity-bot.d.ts +0 -51
  93. package/dist/client/agent-activity-bot.js +0 -166
  94. package/dist/client/agent-tracker.d.ts +0 -499
  95. package/dist/client/agent-tracker.js +0 -659
  96. package/dist/client/description-updater.d.ts +0 -56
  97. package/dist/client/description-updater.js +0 -259
  98. package/dist/client/log-parser.d.ts +0 -72
  99. package/dist/client/log-parser.js +0 -387
  100. package/dist/client/mcp-assistant.d.ts +0 -21
  101. package/dist/client/mcp-assistant.js +0 -58
  102. package/dist/client/mcp-client.d.ts +0 -50
  103. package/dist/client/mcp-client.js +0 -538
  104. package/dist/client/message-processor.d.ts +0 -35
  105. package/dist/client/message-processor.js +0 -357
  106. package/dist/client/providers/anthropic-provider.d.ts +0 -19
  107. package/dist/client/providers/anthropic-provider.js +0 -645
  108. package/dist/client/providers/assistant-provider.d.ts +0 -17
  109. package/dist/client/providers/assistant-provider.js +0 -51
  110. package/dist/client/providers/llm-provider.d.ts +0 -47
  111. package/dist/client/providers/llm-provider.js +0 -367
  112. package/dist/client/providers/openai-provider.d.ts +0 -23
  113. package/dist/client/providers/openai-provider.js +0 -630
  114. package/dist/client/simple-llm-caller.d.ts +0 -19
  115. package/dist/client/simple-llm-caller.js +0 -100
  116. package/dist/client/skill-generator.d.ts +0 -81
  117. package/dist/client/skill-generator.js +0 -386
  118. package/dist/client/test-adaptive-bot.d.ts +0 -9
  119. package/dist/client/test-adaptive-bot.js +0 -82
  120. package/dist/client/token-pricing.d.ts +0 -38
  121. package/dist/client/token-pricing.js +0 -127
  122. package/dist/client/token-tracker.d.ts +0 -232
  123. package/dist/client/token-tracker.js +0 -457
  124. package/dist/client/token-usage-bot.d.ts +0 -53
  125. package/dist/client/token-usage-bot.js +0 -153
  126. package/dist/client/tool-executor.d.ts +0 -69
  127. package/dist/client/tool-executor.js +0 -159
  128. package/dist/lib/materialize.d.ts +0 -3
  129. package/dist/lib/materialize.js +0 -101
  130. package/dist/lib/normalizedName.d.ts +0 -7
  131. package/dist/lib/normalizedName.js +0 -48
  132. package/dist/lib/terminal-prompt.d.ts +0 -9
  133. package/dist/lib/terminal-prompt.js +0 -108
  134. package/dist/mcp/tools/skill.d.ts +0 -10
  135. package/dist/mcp/tools/skill.js +0 -279
  136. package/dist/mcp/tools/workflow-template.d.ts +0 -19
  137. package/dist/mcp/tools/workflow-template.js +0 -822
@@ -25,7 +25,9 @@ Load `hailer-api` for field type reference.
25
25
  2. **STRING for activitylink/dropdown** - Never arrays.
26
26
  3. **Timestamps for dates** - Unix ms, not strings.
27
27
  4. **Orchestrator provides IDs** - I don't fetch schema.
28
- 5. **JSON ONLY** - Output closing brace, then STOP. Zero prose after JSON.
28
+ 5. **BULK: `_id` not `activityId`** - In activities array, use `_id` key.
29
+ 6. **OMIT unused params** - Don't pass empty `[]` or `""`, just omit.
30
+ 7. **JSON ONLY** - Output closing brace, then STOP. Zero prose after JSON.
29
31
  </rules>
30
32
 
31
33
  <field-types>
@@ -14,27 +14,26 @@ Orchestrator MUST provide: Workflow ID(s), Phase ID(s), Field IDs + types.
14
14
  If missing: STOP and request.
15
15
  </pre-flight>
16
16
 
17
- <skills>
18
- Load `hailer-app-builder` for full templates and patterns.
19
- </skills>
20
-
21
17
  <execution>
18
+ **STEP 0 - MANDATORY:** Read `.claude/skills/hailer-app-builder/SKILL.md` FIRST. This contains critical patterns. Do NOT skip.
19
+
22
20
  1. Enable: node .claude/hooks/app-edit-guard.cjs --agent-on
23
21
  2. Scaffold: scaffold_hailer_app({ projectName, template: "react-ts-style" })
24
22
  3. Create: src/types/index.ts, src/utils/fields.ts, src/constants/fields.ts
25
- 4. Modify: src/App.tsx (NEVER main.tsx)
23
+ 4. Modify: src/App.tsx (NEVER main.tsx) - FOLLOW SKILL PATTERNS EXACTLY
26
24
  5. BUILD LOOP: npm run build → fix → repeat until pass
27
25
  6. Disable: node .claude/hooks/app-edit-guard.cjs --agent-off
28
26
  </execution>
29
27
 
30
28
  <rules>
31
- 1. **NEVER FABRICATE** - Must call tools.
32
- 2. **Import**: `import useHailer from './hailer/use-hailer'` (local, default!)
33
- 3. **useEffect dep**: `[inside]` NEVER `[hailer]` (infinite loop)
34
- 4. **Hooks at TOP**: Before any early returns.
35
- 5. **Fields optional**: `fields?: Record<string, { value: unknown }>`
36
- 6. **Theme**: `useColorModeValue('white', 'gray.700')` - no fake tokens.
37
- 7. **JSON ONLY** - Output closing brace, then STOP. Zero prose after JSON.
29
+ 1. **READ SKILL FIRST** - Use Read tool on `.claude/skills/hailer-app-builder/SKILL.md` before ANY code. No exceptions.
30
+ 2. **NEVER FABRICATE** - Must call tools.
31
+ 3. **Import**: `import useHailer from './hailer/use-hailer'` (local, default!)
32
+ 4. **useEffect dep**: `[inside]` ONLY. NEVER include `hailer` or `config` (causes infinite loops)
33
+ 5. **Hooks at TOP**: Before any early returns.
34
+ 6. **Fields optional**: `fields?: Record<string, { value: unknown }>`
35
+ 7. **Theme**: `useColorModeValue('white', 'gray.700')` - no fake tokens.
36
+ 8. **JSON ONLY** - Output closing brace, then STOP. Zero prose after JSON.
38
37
  </rules>
39
38
 
40
39
  <sdk-api>
@@ -2,7 +2,7 @@
2
2
  name: agent-kenji-data-reader
3
3
  description: LOCAL-FIRST data retrieval for SDK v0.8.4 - reads workspace/ before API. Knows about workflows, fields, phases, templates, functions, teams, groups, and insights.\n\n<example>\nuser: "What fields does Tasks have?"\nassistant: {"status":"success","result":{"fields":["taskName","project","dueDate"]},"source":"local","summary":"Read fields.ts"}\n</example>
4
4
  model: haiku
5
- tools: Read, Glob, mcp__hailer__list_workflows_minimal, mcp__hailer__count_activities, mcp__hailer__list_activities, mcp__hailer__list_workflow_phases
5
+ tools: Read, Glob, mcp__hailer__list_workflows_minimal, mcp__hailer__count_activities, mcp__hailer__list_activities, mcp__hailer__list_workflow_phases, mcp__hailer__get_workflow_schema
6
6
  ---
7
7
 
8
8
  <identity>
@@ -28,7 +28,8 @@ Load `json-only-output` to avoid prose after JSON responses.
28
28
  <rules>
29
29
  1. **NEVER FABRICATE** - Must call tools.
30
30
  2. **LOCAL FIRST** - Check workspace/ before API.
31
- 3. **JSON ONLY** - Output closing brace, then STOP. Zero prose after JSON.
31
+ 3. **VERIFY FIELD IDS** - If local files missing/stale, use get_workflow_schema to confirm field IDs before passing to other agents.
32
+ 4. **JSON ONLY** - Output closing brace, then STOP. Zero prose after JSON.
32
33
  </rules>
33
34
 
34
35
  <local-paths>
@@ -48,7 +49,7 @@ workspace/[Workflow]_[id]/main.test.ts → function field tests
48
49
  </local-paths>
49
50
 
50
51
  <decision-tree>
51
- Field schema? → Read workspace/[workflow]/fields.ts
52
+ Field schema? → Read workspace/[workflow]/fields.ts → IF MISSING: get_workflow_schema (API)
52
53
  Phase names? → Read workspace/[workflow]/phases.ts
53
54
  Workflow list? → Read workspace/workflows.ts
54
55
  Workflow settings? → Read workspace/[workflow]/main.ts
@@ -63,6 +64,7 @@ Workflow counts? → list_workflows_minimal (API)
63
64
  Phase IDs for API? → list_workflow_phases (API)
64
65
  Activity data? → list_activities (API)
65
66
  Activity counts? → count_activities (API)
67
+ Field IDs (no local)? → get_workflow_schema (API)
66
68
  </decision-tree>
67
69
 
68
70
  <protocol>
@@ -1,82 +1,98 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * Sync Marketplace Agents Hook
4
- * Runs once per session on first prompt, scans ENABLED plugins,
5
- * updates CLAUDE.md with available marketplace agents.
4
+ * Watches installed_plugins.json for changes and updates CLAUDE.md
5
+ * with available marketplace agents when plugins are installed/uninstalled.
6
6
  */
7
7
 
8
8
  const fs = require('fs');
9
9
  const path = require('path');
10
10
  const os = require('os');
11
+ const crypto = require('crypto');
11
12
 
12
- // Session marker - unique per Claude Code restart (uses parent PID)
13
- const SESSION_MARKER = path.join(os.tmpdir(), `claude-agent-sync-${process.ppid}`);
14
13
  const PLUGINS_DIR = path.join(os.homedir(), '.claude', 'plugins', 'marketplaces');
15
- const CLAUDE_MD = path.join(process.env.CLAUDE_PROJECT_DIR || process.cwd(), 'CLAUDE.md');
16
- const PROJECT_SETTINGS = path.join(process.env.CLAUDE_PROJECT_DIR || process.cwd(), '.claude', 'settings.json');
14
+ const INSTALLED_PLUGINS = path.join(os.homedir(), '.claude', 'plugins', 'installed_plugins.json');
15
+ const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
16
+ const CLAUDE_MD = path.join(PROJECT_DIR, 'CLAUDE.md');
17
+ const PROJECT_SETTINGS = path.join(PROJECT_DIR, '.claude', 'settings.json');
17
18
  const USER_SETTINGS = path.join(os.homedir(), '.claude', 'settings.json');
18
19
 
20
+ // Store sync state in user's home, keyed by project path hash
21
+ const SYNC_STATE_DIR = path.join(os.homedir(), '.claude', 'sync-state');
22
+ const PROJECT_HASH = crypto.createHash('md5').update(PROJECT_DIR).digest('hex').slice(0, 12);
23
+ const SYNC_STATE = path.join(SYNC_STATE_DIR, `${PROJECT_HASH}.state`);
24
+
19
25
  /**
20
- * Check if already synced this session
26
+ * Get hash of installed_plugins.json content
21
27
  */
22
- function alreadySynced() {
23
- return fs.existsSync(SESSION_MARKER);
28
+ function getInstalledPluginsHash() {
29
+ if (!fs.existsSync(INSTALLED_PLUGINS)) {
30
+ return 'empty';
31
+ }
32
+ const content = fs.readFileSync(INSTALLED_PLUGINS, 'utf-8');
33
+ return crypto.createHash('md5').update(content).digest('hex');
24
34
  }
25
35
 
26
36
  /**
27
- * Mark as synced
37
+ * Check if plugins have changed since last sync
28
38
  */
29
- function markSynced() {
30
- fs.writeFileSync(SESSION_MARKER, new Date().toISOString());
39
+ function pluginsChanged() {
40
+ const currentHash = getInstalledPluginsHash();
41
+
42
+ if (!fs.existsSync(SYNC_STATE)) {
43
+ return true; // First run
44
+ }
45
+
46
+ const lastHash = fs.readFileSync(SYNC_STATE, 'utf-8').trim();
47
+ return currentHash !== lastHash;
31
48
  }
32
49
 
33
50
  /**
34
- * Get enabled plugins from settings.json files
51
+ * Save current sync state
52
+ */
53
+ function saveSyncState() {
54
+ const currentHash = getInstalledPluginsHash();
55
+ // Ensure sync state directory exists
56
+ if (!fs.existsSync(SYNC_STATE_DIR)) {
57
+ fs.mkdirSync(SYNC_STATE_DIR, { recursive: true });
58
+ }
59
+ fs.writeFileSync(SYNC_STATE, currentHash);
60
+ }
61
+
62
+ /**
63
+ * Get installed plugins from installed_plugins.json
35
64
  * Returns Set of "plugin@marketplace" strings
36
65
  */
37
- function getEnabledPlugins() {
38
- const enabled = new Set();
39
-
40
- // Check project settings
41
- if (fs.existsSync(PROJECT_SETTINGS)) {
42
- try {
43
- const settings = JSON.parse(fs.readFileSync(PROJECT_SETTINGS, 'utf-8'));
44
- if (settings.enabledPlugins) {
45
- for (const [key, value] of Object.entries(settings.enabledPlugins)) {
46
- if (value === true) {
47
- enabled.add(key); // format: "plugin@marketplace"
48
- }
49
- }
50
- }
51
- } catch (e) { /* ignore parse errors */ }
66
+ function getInstalledPlugins() {
67
+ const plugins = new Set();
68
+
69
+ if (!fs.existsSync(INSTALLED_PLUGINS)) {
70
+ return plugins;
52
71
  }
53
72
 
54
- // Check user settings
55
- if (fs.existsSync(USER_SETTINGS)) {
56
- try {
57
- const settings = JSON.parse(fs.readFileSync(USER_SETTINGS, 'utf-8'));
58
- if (settings.enabledPlugins) {
59
- for (const [key, value] of Object.entries(settings.enabledPlugins)) {
60
- if (value === true) {
61
- enabled.add(key);
62
- }
63
- }
73
+ try {
74
+ const data = JSON.parse(fs.readFileSync(INSTALLED_PLUGINS, 'utf-8'));
75
+ if (data.plugins) {
76
+ for (const pluginKey of Object.keys(data.plugins)) {
77
+ plugins.add(pluginKey);
64
78
  }
65
- } catch (e) { /* ignore parse errors */ }
79
+ }
80
+ } catch (e) {
81
+ // Ignore parse errors
66
82
  }
67
83
 
68
- return enabled;
84
+ return plugins;
69
85
  }
70
86
 
71
87
  /**
72
- * Scan marketplace plugins for agents (only enabled plugins)
73
- * Supports both flat and nested structures:
88
+ * Scan marketplace plugins for agents (all discovered plugins)
89
+ * Supports three structures:
74
90
  * - Flat: {marketplace}/agents/ (when plugin name == marketplace name)
91
+ * - Root-level: {marketplace}/{plugin}/agents/ (plugin dirs at marketplace root)
75
92
  * - Nested: {marketplace}/plugins/{plugin}/agents/
76
93
  */
77
- function scanMarketplaceAgents() {
94
+ function scanMarketplaceAgents(enabledPlugins) {
78
95
  const agents = [];
79
- const enabledPlugins = getEnabledPlugins();
80
96
 
81
97
  if (!fs.existsSync(PLUGINS_DIR)) {
82
98
  return agents;
@@ -86,6 +102,7 @@ function scanMarketplaceAgents() {
86
102
 
87
103
  for (const marketplace of marketplaces) {
88
104
  const marketplacePath = path.join(PLUGINS_DIR, marketplace);
105
+ if (!fs.statSync(marketplacePath).isDirectory()) continue;
89
106
 
90
107
  // Try flat structure first: {marketplace}/agents/
91
108
  const flatAgentsPath = path.join(marketplacePath, 'agents');
@@ -113,6 +130,45 @@ function scanMarketplaceAgents() {
113
130
  }
114
131
  }
115
132
 
133
+ // Try root-level plugins: {marketplace}/{plugin}/agents/
134
+ // Each subdirectory with .claude-plugin is a plugin
135
+ const marketplaceContents = fs.readdirSync(marketplacePath);
136
+ for (const item of marketplaceContents) {
137
+ if (item === '.claude-plugin' || item === '.git' || item === 'plugins') continue;
138
+
139
+ const itemPath = path.join(marketplacePath, item);
140
+ if (!fs.statSync(itemPath).isDirectory()) continue;
141
+
142
+ const pluginJsonPath = path.join(itemPath, '.claude-plugin', 'plugin.json');
143
+
144
+ // Check if this is a plugin (has .claude-plugin/plugin.json)
145
+ if (fs.existsSync(pluginJsonPath)) {
146
+ const pluginKey = `${item}@${marketplace}`;
147
+ if (!enabledPlugins.has(pluginKey)) continue;
148
+
149
+ const agentsPath = path.join(itemPath, 'agents');
150
+ if (!fs.existsSync(agentsPath)) continue;
151
+
152
+ const agentFiles = fs.readdirSync(agentsPath).filter(f => f.endsWith('.md'));
153
+
154
+ for (const agentFile of agentFiles) {
155
+ const agentName = path.basename(agentFile, '.md');
156
+ const fullPath = path.join(agentsPath, agentFile);
157
+ const content = fs.readFileSync(fullPath, 'utf-8');
158
+ const frontmatter = parseFrontmatter(content);
159
+
160
+ agents.push({
161
+ marketplace,
162
+ plugin: item,
163
+ name: agentName,
164
+ fullName: `${item}:${agentName}`,
165
+ description: frontmatter.description || '',
166
+ model: frontmatter.model || 'sonnet'
167
+ });
168
+ }
169
+ }
170
+ }
171
+
116
172
  // Try nested structure: {marketplace}/plugins/{plugin}/agents/
117
173
  const pluginsPath = path.join(marketplacePath, 'plugins');
118
174
  if (fs.existsSync(pluginsPath)) {
@@ -124,7 +180,10 @@ function scanMarketplaceAgents() {
124
180
  continue;
125
181
  }
126
182
 
127
- const agentsPath = path.join(pluginsPath, plugin, 'agents');
183
+ const pluginPath = path.join(pluginsPath, plugin);
184
+ if (!fs.statSync(pluginPath).isDirectory()) continue;
185
+
186
+ const agentsPath = path.join(pluginPath, 'agents');
128
187
  if (!fs.existsSync(agentsPath)) continue;
129
188
 
130
189
  const agentFiles = fs.readdirSync(agentsPath).filter(f => f.endsWith('.md'));
@@ -224,24 +283,26 @@ function updateClaudeMd(agents) {
224
283
  * Main
225
284
  */
226
285
  function main() {
227
- // Skip if already synced this session
228
- if (alreadySynced()) {
286
+ // Skip if installed_plugins.json hasn't changed
287
+ if (!pluginsChanged()) {
229
288
  process.exit(0);
230
289
  }
231
290
 
232
291
  try {
233
- const agents = scanMarketplaceAgents();
292
+ // Get actually installed plugins from installed_plugins.json
293
+ const installedPlugins = getInstalledPlugins();
294
+
295
+ // Scan agents only from installed plugins
296
+ const agents = scanMarketplaceAgents(installedPlugins);
234
297
  const result = updateClaudeMd(agents);
235
298
 
236
- // Mark as synced
237
- markSynced();
299
+ // Save sync state
300
+ saveSyncState();
238
301
 
239
- // Output info if agents found
240
- if (agents.length > 0) {
241
- console.error(`[sync-agents] Found ${agents.length} marketplace agents`);
242
- if (result.updated) {
243
- console.error(`[sync-agents] Updated CLAUDE.md`);
244
- }
302
+ // Output info
303
+ console.error(`[sync-agents] Detected plugin changes, found ${agents.length} marketplace agents`);
304
+ if (result.updated) {
305
+ console.error(`[sync-agents] Updated CLAUDE.md`);
245
306
  }
246
307
 
247
308
  process.exit(0);