intent-hub 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.turbo/cache/019f5ae385027cb1-meta.json +1 -0
  3. package/.turbo/cache/019f5ae385027cb1.tar.zst +0 -0
  4. package/.turbo/cache/040af6112a552a64-meta.json +1 -0
  5. package/.turbo/cache/040af6112a552a64.tar.zst +0 -0
  6. package/.turbo/cache/11195eac3ca5c6ce-meta.json +1 -0
  7. package/.turbo/cache/11195eac3ca5c6ce.tar.zst +0 -0
  8. package/.turbo/cache/13d11166efdf11cf-meta.json +1 -0
  9. package/.turbo/cache/13d11166efdf11cf.tar.zst +0 -0
  10. package/.turbo/cache/19af1af3b136706c-meta.json +1 -0
  11. package/.turbo/cache/19af1af3b136706c.tar.zst +0 -0
  12. package/.turbo/cache/1d33efac91c05b50-meta.json +1 -0
  13. package/.turbo/cache/1d33efac91c05b50.tar.zst +0 -0
  14. package/.turbo/cache/200b85a612af2d13-meta.json +1 -0
  15. package/.turbo/cache/200b85a612af2d13.tar.zst +0 -0
  16. package/.turbo/cache/210308c9ea929858-meta.json +1 -0
  17. package/.turbo/cache/210308c9ea929858.tar.zst +0 -0
  18. package/.turbo/cache/38df8e44c617835e-meta.json +1 -0
  19. package/.turbo/cache/38df8e44c617835e.tar.zst +0 -0
  20. package/.turbo/cache/3e449de5ef60a7a0-meta.json +1 -0
  21. package/.turbo/cache/3e449de5ef60a7a0.tar.zst +0 -0
  22. package/.turbo/cache/51ff024a97c2b4f5-meta.json +1 -0
  23. package/.turbo/cache/51ff024a97c2b4f5.tar.zst +0 -0
  24. package/.turbo/cache/54bc756eeebb377a-meta.json +1 -0
  25. package/.turbo/cache/54bc756eeebb377a.tar.zst +0 -0
  26. package/.turbo/cache/5ed6a840acafc873-meta.json +1 -0
  27. package/.turbo/cache/5ed6a840acafc873.tar.zst +0 -0
  28. package/.turbo/cache/6702dc24e5ca3c2e-meta.json +1 -0
  29. package/.turbo/cache/6702dc24e5ca3c2e.tar.zst +0 -0
  30. package/.turbo/cache/725c72cf71ea854f-meta.json +1 -0
  31. package/.turbo/cache/725c72cf71ea854f.tar.zst +0 -0
  32. package/.turbo/cache/7344ca28d348037a-meta.json +1 -0
  33. package/.turbo/cache/7344ca28d348037a.tar.zst +0 -0
  34. package/.turbo/cache/748fb444cdc0b78c-meta.json +1 -0
  35. package/.turbo/cache/748fb444cdc0b78c.tar.zst +0 -0
  36. package/.turbo/cache/789677c36fe7fb98-meta.json +1 -0
  37. package/.turbo/cache/789677c36fe7fb98.tar.zst +0 -0
  38. package/.turbo/cache/89ff6c6f38dd4a18-meta.json +1 -0
  39. package/.turbo/cache/89ff6c6f38dd4a18.tar.zst +0 -0
  40. package/.turbo/cache/8dbc92d00de0c92e-meta.json +1 -0
  41. package/.turbo/cache/8dbc92d00de0c92e.tar.zst +0 -0
  42. package/.turbo/cache/8eb03f40082b9441-meta.json +1 -0
  43. package/.turbo/cache/8eb03f40082b9441.tar.zst +0 -0
  44. package/.turbo/cache/9157134d4b916017-meta.json +1 -0
  45. package/.turbo/cache/9157134d4b916017.tar.zst +0 -0
  46. package/.turbo/cache/94219ffd32b48e93-meta.json +1 -0
  47. package/.turbo/cache/94219ffd32b48e93.tar.zst +0 -0
  48. package/.turbo/cache/95c1d160b4fa84eb-meta.json +1 -0
  49. package/.turbo/cache/95c1d160b4fa84eb.tar.zst +0 -0
  50. package/.turbo/cache/998833ea02dfb225-meta.json +1 -0
  51. package/.turbo/cache/998833ea02dfb225.tar.zst +0 -0
  52. package/.turbo/cache/a5974ef6ade3eb90-meta.json +1 -0
  53. package/.turbo/cache/a5974ef6ade3eb90.tar.zst +0 -0
  54. package/.turbo/cache/aab811809257decb-meta.json +1 -0
  55. package/.turbo/cache/aab811809257decb.tar.zst +0 -0
  56. package/.turbo/cache/ab2f82a54da854fd-meta.json +1 -0
  57. package/.turbo/cache/ab2f82a54da854fd.tar.zst +0 -0
  58. package/.turbo/cache/abbf4d95d62a7303-meta.json +1 -0
  59. package/.turbo/cache/abbf4d95d62a7303.tar.zst +0 -0
  60. package/.turbo/cache/af4441f519f9ce50-meta.json +1 -0
  61. package/.turbo/cache/af4441f519f9ce50.tar.zst +0 -0
  62. package/.turbo/cache/b9b85aaaf03d00a6-meta.json +1 -0
  63. package/.turbo/cache/b9b85aaaf03d00a6.tar.zst +0 -0
  64. package/.turbo/cache/cd58ee8721bbfed7-meta.json +1 -0
  65. package/.turbo/cache/cd58ee8721bbfed7.tar.zst +0 -0
  66. package/.turbo/cache/d285e48b8afa30b5-meta.json +1 -0
  67. package/.turbo/cache/d285e48b8afa30b5.tar.zst +0 -0
  68. package/.turbo/cache/d33e90229142acce-meta.json +1 -0
  69. package/.turbo/cache/d33e90229142acce.tar.zst +0 -0
  70. package/.turbo/cache/d57839a0d3b04540-meta.json +1 -0
  71. package/.turbo/cache/d57839a0d3b04540.tar.zst +0 -0
  72. package/.turbo/cache/d8554ef2c8b6e5eb-meta.json +1 -0
  73. package/.turbo/cache/d8554ef2c8b6e5eb.tar.zst +0 -0
  74. package/.turbo/cache/dc7375b51290e102-meta.json +1 -0
  75. package/.turbo/cache/dc7375b51290e102.tar.zst +0 -0
  76. package/.turbo/cache/e5310fe547fdbf0a-meta.json +1 -0
  77. package/.turbo/cache/e5310fe547fdbf0a.tar.zst +0 -0
  78. package/.turbo/cache/f12bb5f2f188758d-meta.json +1 -0
  79. package/.turbo/cache/f12bb5f2f188758d.tar.zst +0 -0
  80. package/.turbo/cache/f2db5af0c0b4d23f-meta.json +1 -0
  81. package/.turbo/cache/f2db5af0c0b4d23f.tar.zst +0 -0
  82. package/.turbo/cache/f8935ade01a88cd7-meta.json +1 -0
  83. package/.turbo/cache/f8935ade01a88cd7.tar.zst +0 -0
  84. package/.turbo/cache/f982b8dd966f823a-meta.json +1 -0
  85. package/.turbo/cache/f982b8dd966f823a.tar.zst +0 -0
  86. package/.turbo/cache/f9d4036dd350ba1a-meta.json +1 -0
  87. package/.turbo/cache/f9d4036dd350ba1a.tar.zst +0 -0
  88. package/README.md +661 -0
  89. package/README_ko.md +577 -0
  90. package/bun.lock +135 -0
  91. package/package.json +26 -0
  92. package/packages/agent/.turbo/turbo-build.log +5 -0
  93. package/packages/agent/.turbo/turbo-typecheck.log +1 -0
  94. package/packages/agent/dist/connection/hub-client.d.ts +33 -0
  95. package/packages/agent/dist/connection/hub-client.d.ts.map +1 -0
  96. package/packages/agent/dist/connection/index.d.ts +2 -0
  97. package/packages/agent/dist/connection/index.d.ts.map +1 -0
  98. package/packages/agent/dist/hooks/index.d.ts +3 -0
  99. package/packages/agent/dist/hooks/index.d.ts.map +1 -0
  100. package/packages/agent/dist/hooks/intent-hub-hooks.d.ts +47 -0
  101. package/packages/agent/dist/hooks/intent-hub-hooks.d.ts.map +1 -0
  102. package/packages/agent/dist/index.d.ts +6 -0
  103. package/packages/agent/dist/index.d.ts.map +1 -0
  104. package/packages/agent/dist/index.js +3315 -0
  105. package/packages/agent/dist/plugin/index.d.ts +3 -0
  106. package/packages/agent/dist/plugin/index.d.ts.map +1 -0
  107. package/packages/agent/dist/plugin/intent-hub-plugin.d.ts +54 -0
  108. package/packages/agent/dist/plugin/intent-hub-plugin.d.ts.map +1 -0
  109. package/packages/agent/package.json +32 -0
  110. package/packages/agent/src/connection/hub-client.ts +152 -0
  111. package/packages/agent/src/connection/index.ts +1 -0
  112. package/packages/agent/src/hooks/index.ts +2 -0
  113. package/packages/agent/src/hooks/intent-hub-hooks.ts +245 -0
  114. package/packages/agent/src/index.ts +5 -0
  115. package/packages/agent/src/plugin/index.ts +2 -0
  116. package/packages/agent/src/plugin/intent-hub-plugin.ts +153 -0
  117. package/packages/agent/tsconfig.json +9 -0
  118. package/packages/hub/.turbo/turbo-build.log +6 -0
  119. package/packages/hub/.turbo/turbo-typecheck.log +1 -0
  120. package/packages/hub/dist/api/dashboard.d.ts +17 -0
  121. package/packages/hub/dist/api/dashboard.d.ts.map +1 -0
  122. package/packages/hub/dist/cli.d.ts +3 -0
  123. package/packages/hub/dist/cli.d.ts.map +1 -0
  124. package/packages/hub/dist/cli.js +7719 -0
  125. package/packages/hub/dist/core/conflict-detector.d.ts +36 -0
  126. package/packages/hub/dist/core/conflict-detector.d.ts.map +1 -0
  127. package/packages/hub/dist/core/index.d.ts +7 -0
  128. package/packages/hub/dist/core/index.d.ts.map +1 -0
  129. package/packages/hub/dist/core/intent-analyzer.d.ts +8 -0
  130. package/packages/hub/dist/core/intent-analyzer.d.ts.map +1 -0
  131. package/packages/hub/dist/core/lock-manager.d.ts +13 -0
  132. package/packages/hub/dist/core/lock-manager.d.ts.map +1 -0
  133. package/packages/hub/dist/core/orchestrator.d.ts +46 -0
  134. package/packages/hub/dist/core/orchestrator.d.ts.map +1 -0
  135. package/packages/hub/dist/index.d.ts +9 -0
  136. package/packages/hub/dist/index.d.ts.map +1 -0
  137. package/packages/hub/dist/index.js +4686 -0
  138. package/packages/hub/dist/llm/index.d.ts +7 -0
  139. package/packages/hub/dist/llm/index.d.ts.map +1 -0
  140. package/packages/hub/dist/llm/negotiation-engine.d.ts +40 -0
  141. package/packages/hub/dist/llm/negotiation-engine.d.ts.map +1 -0
  142. package/packages/hub/dist/llm/provider.d.ts +46 -0
  143. package/packages/hub/dist/llm/provider.d.ts.map +1 -0
  144. package/packages/hub/dist/llm/smart-analyzer.d.ts +20 -0
  145. package/packages/hub/dist/llm/smart-analyzer.d.ts.map +1 -0
  146. package/packages/hub/dist/server/hub-server.d.ts +35 -0
  147. package/packages/hub/dist/server/hub-server.d.ts.map +1 -0
  148. package/packages/hub/dist/server/index.d.ts +5 -0
  149. package/packages/hub/dist/server/index.d.ts.map +1 -0
  150. package/packages/hub/dist/server/message-handler.d.ts +18 -0
  151. package/packages/hub/dist/server/message-handler.d.ts.map +1 -0
  152. package/packages/hub/dist/server/smart-hub-server.d.ts +43 -0
  153. package/packages/hub/dist/server/smart-hub-server.d.ts.map +1 -0
  154. package/packages/hub/dist/state/index.d.ts +2 -0
  155. package/packages/hub/dist/state/index.d.ts.map +1 -0
  156. package/packages/hub/dist/state/session-manager.d.ts +19 -0
  157. package/packages/hub/dist/state/session-manager.d.ts.map +1 -0
  158. package/packages/hub/dist/tunnel/index.d.ts +14 -0
  159. package/packages/hub/dist/tunnel/index.d.ts.map +1 -0
  160. package/packages/hub/package.json +54 -0
  161. package/packages/hub/src/api/dashboard.ts +261 -0
  162. package/packages/hub/src/cli.ts +193 -0
  163. package/packages/hub/src/core/conflict-detector.ts +138 -0
  164. package/packages/hub/src/core/index.ts +6 -0
  165. package/packages/hub/src/core/intent-analyzer.ts +112 -0
  166. package/packages/hub/src/core/lock-manager.ts +95 -0
  167. package/packages/hub/src/core/orchestrator.ts +255 -0
  168. package/packages/hub/src/index.ts +8 -0
  169. package/packages/hub/src/llm/index.ts +17 -0
  170. package/packages/hub/src/llm/negotiation-engine.ts +297 -0
  171. package/packages/hub/src/llm/provider.ts +175 -0
  172. package/packages/hub/src/llm/smart-analyzer.ts +169 -0
  173. package/packages/hub/src/server/hub-server.ts +219 -0
  174. package/packages/hub/src/server/index.ts +4 -0
  175. package/packages/hub/src/server/message-handler.ts +111 -0
  176. package/packages/hub/src/server/smart-hub-server.ts +374 -0
  177. package/packages/hub/src/state/index.ts +1 -0
  178. package/packages/hub/src/state/session-manager.ts +59 -0
  179. package/packages/hub/src/tunnel/index.ts +153 -0
  180. package/packages/hub/tsconfig.json +9 -0
  181. package/packages/shared/.turbo/turbo-build.log +5 -0
  182. package/packages/shared/.turbo/turbo-typecheck.log +1 -0
  183. package/packages/shared/dist/index.d.ts +3 -0
  184. package/packages/shared/dist/index.d.ts.map +1 -0
  185. package/packages/shared/dist/index.js +50 -0
  186. package/packages/shared/dist/types/domain.d.ts +50 -0
  187. package/packages/shared/dist/types/domain.d.ts.map +1 -0
  188. package/packages/shared/dist/types/index.d.ts +4 -0
  189. package/packages/shared/dist/types/index.d.ts.map +1 -0
  190. package/packages/shared/dist/types/intent.d.ts +24 -0
  191. package/packages/shared/dist/types/intent.d.ts.map +1 -0
  192. package/packages/shared/dist/types/message.d.ts +151 -0
  193. package/packages/shared/dist/types/message.d.ts.map +1 -0
  194. package/packages/shared/dist/utils/id.d.ts +6 -0
  195. package/packages/shared/dist/utils/id.d.ts.map +1 -0
  196. package/packages/shared/dist/utils/index.d.ts +3 -0
  197. package/packages/shared/dist/utils/index.d.ts.map +1 -0
  198. package/packages/shared/dist/utils/message.d.ts +5 -0
  199. package/packages/shared/dist/utils/message.d.ts.map +1 -0
  200. package/packages/shared/package.json +33 -0
  201. package/packages/shared/src/index.ts +2 -0
  202. package/packages/shared/src/types/domain.ts +57 -0
  203. package/packages/shared/src/types/index.ts +3 -0
  204. package/packages/shared/src/types/intent.ts +34 -0
  205. package/packages/shared/src/types/message.ts +188 -0
  206. package/packages/shared/src/utils/id.ts +21 -0
  207. package/packages/shared/src/utils/index.ts +2 -0
  208. package/packages/shared/src/utils/message.ts +30 -0
  209. package/packages/shared/tsconfig.json +9 -0
  210. package/scripts/test-e2e.ts +194 -0
  211. package/scripts/test-mvp2.ts +167 -0
  212. package/scripts/test-mvp3.ts +405 -0
  213. package/tsconfig.json +19 -0
  214. package/turbo.json +22 -0
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { HubServer } from './server/hub-server.js';
4
+ import { SmartHubServer } from './server/smart-hub-server.js';
5
+ import { createProvider } from './llm/provider.js';
6
+ import { DashboardApi } from './api/dashboard.js';
7
+ import { createTunnel, printShareInfo, type TunnelResult } from './tunnel/index.js';
8
+
9
+ const DEFAULT_PORT = 9876;
10
+ const DEFAULT_DASHBOARD_PORT = 9877;
11
+
12
+ interface ParsedArgs {
13
+ port: number;
14
+ host: string;
15
+ mode: 'simple' | 'smart';
16
+ llmProvider: 'openai' | 'anthropic' | 'mock';
17
+ apiKey?: string;
18
+ autoNegotiate: boolean;
19
+ dashboard: boolean;
20
+ dashboardPort: number;
21
+ share: boolean;
22
+ tunnelProvider: 'localtunnel' | 'cloudflared' | 'auto';
23
+ }
24
+
25
+ function parseArgs(): ParsedArgs {
26
+ const args = process.argv.slice(2);
27
+
28
+ const getArg = (name: string): string | undefined => {
29
+ const arg = args.find(a => a.startsWith(`--${name}=`));
30
+ return arg?.split('=')[1];
31
+ };
32
+
33
+ const hasFlag = (name: string): boolean => {
34
+ return args.includes(`--${name}`);
35
+ };
36
+
37
+ return {
38
+ port: parseInt(getArg('port') ?? String(DEFAULT_PORT), 10),
39
+ host: getArg('host') ?? '0.0.0.0',
40
+ mode: (getArg('mode') as 'simple' | 'smart') ?? 'smart',
41
+ llmProvider: (getArg('llm') as 'openai' | 'anthropic' | 'mock') ?? 'mock',
42
+ apiKey: getArg('api-key') ?? process.env['OPENAI_API_KEY'] ?? process.env['ANTHROPIC_API_KEY'],
43
+ autoNegotiate: !hasFlag('no-negotiate'),
44
+ dashboard: hasFlag('dashboard'),
45
+ dashboardPort: parseInt(getArg('dashboard-port') ?? String(DEFAULT_DASHBOARD_PORT), 10),
46
+ share: hasFlag('share'),
47
+ tunnelProvider: (getArg('tunnel') as 'localtunnel' | 'cloudflared' | 'auto') ?? 'auto',
48
+ };
49
+ }
50
+
51
+ function printBanner(config: ParsedArgs, shareUrl?: string) {
52
+ console.log(`
53
+ ╔═══════════════════════════════════════════════════════════════╗
54
+ ║ ║
55
+ ║ ██╗███╗ ██╗████████╗███████╗███╗ ██╗████████╗ ║
56
+ ║ ██║████╗ ██║╚══██╔══╝██╔════╝████╗ ██║╚══██╔══╝ ║
57
+ ║ ██║██╔██╗ ██║ ██║ █████╗ ██╔██╗ ██║ ██║ ║
58
+ ║ ██║██║╚██╗██║ ██║ ██╔══╝ ██║╚██╗██║ ██║ ║
59
+ ║ ██║██║ ╚████║ ██║ ███████╗██║ ╚████║ ██║ ║
60
+ ║ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ║
61
+ ║ HUB ║
62
+ ║ ║
63
+ ║ Team-based LLM Coordination Platform ║
64
+ ║ ║
65
+ ╚═══════════════════════════════════════════════════════════════╝
66
+
67
+ Mode: ${config.mode}
68
+ LLM Provider: ${config.llmProvider}
69
+ Auto-Negotiate: ${config.autoNegotiate}
70
+ WebSocket: ws://${config.host}:${config.port}
71
+ Dashboard: ${config.dashboard ? `http://localhost:${config.dashboardPort}` : 'disabled (use --dashboard to enable)'}
72
+ Share Mode: ${config.share ? 'enabled' : 'disabled (use --share to enable)'}
73
+ `);
74
+ }
75
+
76
+ function printHelp() {
77
+ console.log(`
78
+ Intent Hub - Team-based LLM Coordination Platform
79
+
80
+ Usage:
81
+ intent-hub [options]
82
+
83
+ Options:
84
+ --port=<number> WebSocket port (default: 9876)
85
+ --host=<string> Host to bind to (default: 0.0.0.0)
86
+ --mode=<mode> Server mode: simple | smart (default: smart)
87
+ --llm=<provider> LLM provider: openai | anthropic | mock (default: mock)
88
+ --api-key=<key> API key for LLM provider (or use env vars)
89
+ --no-negotiate Disable auto-negotiation
90
+ --dashboard Enable web dashboard
91
+ --dashboard-port=<number> Dashboard HTTP port (default: 9877)
92
+ --share Enable Live Share mode (creates public URL)
93
+ --tunnel=<provider> Tunnel provider: auto | localtunnel | cloudflared (default: auto)
94
+ --help Show this help message
95
+
96
+ Live Share Mode (--share):
97
+ Creates a public URL that team members can use to connect.
98
+ No network configuration needed - works through NAT/firewalls.
99
+
100
+ Example:
101
+ intent-hub --share --llm=openai
102
+ # Outputs: wss://intent-hub-abc123.loca.lt
103
+ # Share this URL with your team!
104
+
105
+ Environment Variables:
106
+ OPENAI_API_KEY OpenAI API key
107
+ ANTHROPIC_API_KEY Anthropic API key
108
+
109
+ Examples:
110
+ intent-hub --share # Quick team session
111
+ intent-hub --share --llm=openai --dashboard # Full featured
112
+ intent-hub --port=9876 --mode=smart --llm=mock # Local testing
113
+ `);
114
+ }
115
+
116
+ async function main() {
117
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
118
+ printHelp();
119
+ process.exit(0);
120
+ }
121
+
122
+ const config = parseArgs();
123
+
124
+ let tunnel: TunnelResult | null = null;
125
+
126
+ let server: HubServer | SmartHubServer;
127
+ let dashboard: DashboardApi | null = null;
128
+
129
+ if (config.mode === 'smart') {
130
+ const provider = createProvider(config.llmProvider, config.apiKey);
131
+
132
+ const smartServer = new SmartHubServer({
133
+ port: config.port,
134
+ host: config.host,
135
+ llmProvider: provider,
136
+ orchestratorConfig: {
137
+ useLLMAnalysis: config.llmProvider !== 'mock',
138
+ autoNegotiate: config.autoNegotiate,
139
+ conflictThreshold: 'medium',
140
+ },
141
+ });
142
+
143
+ server = smartServer;
144
+
145
+ if (config.dashboard) {
146
+ dashboard = new DashboardApi({
147
+ port: config.dashboardPort,
148
+ hubServer: smartServer,
149
+ });
150
+ }
151
+ } else {
152
+ server = new HubServer({ port: config.port, host: config.host });
153
+
154
+ if (config.dashboard) {
155
+ console.log('[Dashboard] Warning: Dashboard requires smart mode. Use --mode=smart to enable.');
156
+ }
157
+ }
158
+
159
+ server.start();
160
+ dashboard?.start();
161
+
162
+ printBanner(config);
163
+
164
+ if (config.share) {
165
+ console.log('[Share] Creating tunnel...');
166
+ try {
167
+ tunnel = await createTunnel({
168
+ port: config.port,
169
+ provider: config.tunnelProvider,
170
+ });
171
+ printShareInfo(tunnel);
172
+ } catch (err) {
173
+ console.error('[Share] Failed to create tunnel:', err);
174
+ console.log('[Share] Falling back to local mode. Team members need network access to connect.');
175
+ }
176
+ }
177
+
178
+ const shutdown = async () => {
179
+ console.log('\n[Hub] Shutting down...');
180
+ if (tunnel) {
181
+ console.log('[Share] Closing tunnel...');
182
+ await tunnel.close();
183
+ }
184
+ dashboard?.stop();
185
+ server.stop();
186
+ process.exit(0);
187
+ };
188
+
189
+ process.on('SIGINT', shutdown);
190
+ process.on('SIGTERM', shutdown);
191
+ }
192
+
193
+ main();
@@ -0,0 +1,138 @@
1
+ import type { SmartIntentAnalysis, SmartIntentAnalyzer } from '../llm/smart-analyzer.js';
2
+ import type { Intent } from '@anthropic-for-korea/intent-hub-shared';
3
+
4
+ export interface ActiveIntent {
5
+ intentId: string;
6
+ sessionId: string;
7
+ userId: string;
8
+ username: string;
9
+ prompt: string;
10
+ analysis: SmartIntentAnalysis;
11
+ status: 'active' | 'waiting' | 'completed';
12
+ startedAt: number;
13
+ }
14
+
15
+ export interface ConflictInfo {
16
+ intentId1: string;
17
+ intentId2: string;
18
+ userId1: string;
19
+ userId2: string;
20
+ username1: string;
21
+ username2: string;
22
+ overlapDomains: string[];
23
+ overlapFiles: string[];
24
+ severity: 'low' | 'medium' | 'high';
25
+ reason: string;
26
+ }
27
+
28
+ export class ConflictDetector {
29
+ private activeIntents: Map<string, ActiveIntent> = new Map();
30
+
31
+ constructor(private analyzer: SmartIntentAnalyzer) {}
32
+
33
+ addIntent(intent: ActiveIntent): void {
34
+ this.activeIntents.set(intent.intentId, intent);
35
+ }
36
+
37
+ removeIntent(intentId: string): void {
38
+ this.activeIntents.delete(intentId);
39
+ }
40
+
41
+ getActiveIntents(): ActiveIntent[] {
42
+ return Array.from(this.activeIntents.values()).filter(i => i.status === 'active');
43
+ }
44
+
45
+ async detectConflicts(newIntent: ActiveIntent): Promise<ConflictInfo[]> {
46
+ const conflicts: ConflictInfo[] = [];
47
+ const activeIntents = this.getActiveIntents();
48
+
49
+ for (const existing of activeIntents) {
50
+ if (existing.userId === newIntent.userId) continue;
51
+
52
+ const overlap = await this.analyzer.checkOverlap(
53
+ existing.analysis,
54
+ newIntent.analysis
55
+ );
56
+
57
+ if (overlap.hasOverlap && overlap.severity !== 'none') {
58
+ conflicts.push({
59
+ intentId1: existing.intentId,
60
+ intentId2: newIntent.intentId,
61
+ userId1: existing.userId,
62
+ userId2: newIntent.userId,
63
+ username1: existing.username,
64
+ username2: newIntent.username,
65
+ overlapDomains: overlap.overlapDomains,
66
+ overlapFiles: overlap.overlapFiles,
67
+ severity: overlap.severity as 'low' | 'medium' | 'high',
68
+ reason: overlap.reason,
69
+ });
70
+ }
71
+ }
72
+
73
+ return conflicts;
74
+ }
75
+
76
+ quickConflictCheck(newIntent: ActiveIntent): ConflictInfo[] {
77
+ const conflicts: ConflictInfo[] = [];
78
+ const activeIntents = this.getActiveIntents();
79
+
80
+ for (const existing of activeIntents) {
81
+ if (existing.userId === newIntent.userId) continue;
82
+
83
+ const domainMatch = existing.analysis.domain === newIntent.analysis.domain;
84
+ const subdomainMatch = existing.analysis.subdomain &&
85
+ newIntent.analysis.subdomain &&
86
+ existing.analysis.subdomain === newIntent.analysis.subdomain;
87
+
88
+ const fileOverlap = existing.analysis.affectedFiles.filter(
89
+ f => newIntent.analysis.affectedFiles.includes(f)
90
+ );
91
+
92
+ const tagOverlap = existing.analysis.semanticTags.filter(
93
+ t => newIntent.analysis.semanticTags.includes(t)
94
+ );
95
+
96
+ let severity: 'low' | 'medium' | 'high' = 'low';
97
+ let hasConflict = false;
98
+
99
+ if (subdomainMatch || fileOverlap.length > 0) {
100
+ severity = 'high';
101
+ hasConflict = true;
102
+ } else if (domainMatch) {
103
+ severity = 'medium';
104
+ hasConflict = true;
105
+ } else if (tagOverlap.length >= 3) {
106
+ severity = 'low';
107
+ hasConflict = true;
108
+ }
109
+
110
+ if (hasConflict) {
111
+ conflicts.push({
112
+ intentId1: existing.intentId,
113
+ intentId2: newIntent.intentId,
114
+ userId1: existing.userId,
115
+ userId2: newIntent.userId,
116
+ username1: existing.username,
117
+ username2: newIntent.username,
118
+ overlapDomains: domainMatch ? [existing.analysis.domain] : [],
119
+ overlapFiles: fileOverlap,
120
+ severity,
121
+ reason: subdomainMatch ? 'Same subdomain' :
122
+ fileOverlap.length > 0 ? 'Shared files' :
123
+ domainMatch ? 'Same domain' : 'Related tags',
124
+ });
125
+ }
126
+ }
127
+
128
+ return conflicts;
129
+ }
130
+
131
+ getIntentsByDomain(domain: string): ActiveIntent[] {
132
+ return this.getActiveIntents().filter(i => i.analysis.domain === domain);
133
+ }
134
+
135
+ getIntentByUser(userId: string): ActiveIntent | undefined {
136
+ return this.getActiveIntents().find(i => i.userId === userId);
137
+ }
138
+ }
@@ -0,0 +1,6 @@
1
+ export { IntentAnalyzer } from './intent-analyzer.js';
2
+ export { LockManager } from './lock-manager.js';
3
+ export { ConflictDetector } from './conflict-detector.js';
4
+ export type { ActiveIntent, ConflictInfo } from './conflict-detector.js';
5
+ export { Orchestrator } from './orchestrator.js';
6
+ export type { OrchestratorConfig, IntentRequest, IntentResponse } from './orchestrator.js';
@@ -0,0 +1,112 @@
1
+ import type { IntentAnalysis } from '@anthropic-for-korea/intent-hub-shared';
2
+
3
+ const DOMAIN_KEYWORDS: Record<string, string[]> = {
4
+ auth: ['login', 'logout', 'auth', 'authentication', 'oauth', 'session', 'token', 'jwt', 'password', '로그인', '인증'],
5
+ user: ['user', 'profile', 'account', 'member', '사용자', '회원', '프로필'],
6
+ payment: ['payment', 'pay', 'billing', 'invoice', 'checkout', 'cart', '결제', '장바구니'],
7
+ database: ['database', 'db', 'migration', 'schema', 'model', 'entity', '데이터베이스'],
8
+ api: ['api', 'endpoint', 'route', 'controller', 'rest', 'graphql'],
9
+ ui: ['ui', 'component', 'button', 'form', 'modal', 'layout', 'style', 'css', '버튼', '컴포넌트'],
10
+ test: ['test', 'spec', 'jest', 'vitest', 'e2e', 'unit', '테스트'],
11
+ config: ['config', 'setting', 'env', 'environment', '설정'],
12
+ docs: ['doc', 'readme', 'documentation', '문서'],
13
+ };
14
+
15
+ const SUBDOMAIN_KEYWORDS: Record<string, Record<string, string[]>> = {
16
+ auth: {
17
+ oauth: ['oauth', 'google', 'github', 'facebook', 'social'],
18
+ session: ['session', 'cookie'],
19
+ jwt: ['jwt', 'token', 'refresh'],
20
+ },
21
+ payment: {
22
+ checkout: ['checkout', 'order'],
23
+ subscription: ['subscription', 'recurring'],
24
+ },
25
+ };
26
+
27
+ export class IntentAnalyzer {
28
+ analyze(prompt: string): IntentAnalysis {
29
+ const lowerPrompt = prompt.toLowerCase();
30
+
31
+ let domain = 'general';
32
+ let maxScore = 0;
33
+
34
+ for (const [d, keywords] of Object.entries(DOMAIN_KEYWORDS)) {
35
+ const score = keywords.filter(kw => lowerPrompt.includes(kw)).length;
36
+ if (score > maxScore) {
37
+ maxScore = score;
38
+ domain = d;
39
+ }
40
+ }
41
+
42
+ let subdomain: string | undefined;
43
+ const subdomains = SUBDOMAIN_KEYWORDS[domain];
44
+ if (subdomains) {
45
+ let subMaxScore = 0;
46
+ for (const [sd, keywords] of Object.entries(subdomains)) {
47
+ const score = keywords.filter(kw => lowerPrompt.includes(kw)).length;
48
+ if (score > subMaxScore) {
49
+ subMaxScore = score;
50
+ subdomain = sd;
51
+ }
52
+ }
53
+ }
54
+
55
+ const affectedFiles = this.extractAffectedFiles(prompt);
56
+ const semanticTags = this.extractSemanticTags(lowerPrompt);
57
+ const estimatedScope = this.estimateScope(prompt);
58
+
59
+ return {
60
+ domain,
61
+ subdomain,
62
+ affectedFiles,
63
+ semanticTags,
64
+ estimatedScope,
65
+ dependencies: [],
66
+ };
67
+ }
68
+
69
+ private extractAffectedFiles(prompt: string): string[] {
70
+ const filePatterns = [
71
+ /[\w\-\/]+\.(ts|tsx|js|jsx|json|css|scss|html|md)/gi,
72
+ /src\/[\w\-\/]+/gi,
73
+ /pages\/[\w\-\/]+/gi,
74
+ /components\/[\w\-\/]+/gi,
75
+ ];
76
+
77
+ const files: Set<string> = new Set();
78
+ for (const pattern of filePatterns) {
79
+ const matches = prompt.match(pattern);
80
+ if (matches) {
81
+ for (const match of matches) {
82
+ files.add(match);
83
+ }
84
+ }
85
+ }
86
+
87
+ return Array.from(files);
88
+ }
89
+
90
+ private extractSemanticTags(lowerPrompt: string): string[] {
91
+ const tags: string[] = [];
92
+
93
+ for (const [domain, keywords] of Object.entries(DOMAIN_KEYWORDS)) {
94
+ for (const kw of keywords) {
95
+ if (lowerPrompt.includes(kw)) {
96
+ tags.push(kw);
97
+ }
98
+ }
99
+ }
100
+
101
+ return [...new Set(tags)].slice(0, 10);
102
+ }
103
+
104
+ private estimateScope(prompt: string): 'small' | 'medium' | 'large' {
105
+ const length = prompt.length;
106
+ const fileCount = (prompt.match(/\.(ts|tsx|js|jsx|json|css)/gi) ?? []).length;
107
+
108
+ if (length < 100 && fileCount <= 1) return 'small';
109
+ if (length < 300 && fileCount <= 3) return 'medium';
110
+ return 'large';
111
+ }
112
+ }
@@ -0,0 +1,95 @@
1
+ import type { DomainLock, LockStatus } from '@anthropic-for-korea/intent-hub-shared';
2
+
3
+ export class LockManager {
4
+ private locks: Map<string, DomainLock> = new Map();
5
+
6
+ acquireLock(
7
+ domainId: string,
8
+ intentId: string,
9
+ userId: string,
10
+ username: string,
11
+ expiresInMs?: number
12
+ ): DomainLock | null {
13
+ const existing = this.locks.get(domainId);
14
+ if (existing && existing.status === 'active' && existing.userId !== userId) {
15
+ return null;
16
+ }
17
+
18
+ const lock: DomainLock = {
19
+ domainId,
20
+ intentId,
21
+ userId,
22
+ username,
23
+ acquiredAt: Date.now(),
24
+ expiresAt: expiresInMs ? Date.now() + expiresInMs : undefined,
25
+ status: 'active',
26
+ };
27
+
28
+ this.locks.set(domainId, lock);
29
+ return lock;
30
+ }
31
+
32
+ releaseLock(domainId: string, userId: string): boolean {
33
+ const lock = this.locks.get(domainId);
34
+ if (!lock || lock.userId !== userId) {
35
+ return false;
36
+ }
37
+
38
+ lock.status = 'released';
39
+ this.locks.delete(domainId);
40
+ return true;
41
+ }
42
+
43
+ releaseAllForUser(userId: string): number {
44
+ let count = 0;
45
+ for (const [domainId, lock] of this.locks) {
46
+ if (lock.userId === userId) {
47
+ this.locks.delete(domainId);
48
+ count++;
49
+ }
50
+ }
51
+ return count;
52
+ }
53
+
54
+ getLockForDomain(domainId: string): DomainLock | undefined {
55
+ const lock = this.locks.get(domainId);
56
+ if (lock && lock.status === 'active') {
57
+ if (lock.expiresAt && Date.now() > lock.expiresAt) {
58
+ this.locks.delete(domainId);
59
+ return undefined;
60
+ }
61
+ return lock;
62
+ }
63
+ return undefined;
64
+ }
65
+
66
+ getActiveLocks(): DomainLock[] {
67
+ const now = Date.now();
68
+ const active: DomainLock[] = [];
69
+
70
+ for (const [domainId, lock] of this.locks) {
71
+ if (lock.status === 'active') {
72
+ if (lock.expiresAt && now > lock.expiresAt) {
73
+ this.locks.delete(domainId);
74
+ } else {
75
+ active.push(lock);
76
+ }
77
+ }
78
+ }
79
+
80
+ return active;
81
+ }
82
+
83
+ getLocksForUser(userId: string): DomainLock[] {
84
+ return this.getActiveLocks().filter(lock => lock.userId === userId);
85
+ }
86
+
87
+ hasConflict(domainId: string, userId: string): boolean {
88
+ const lock = this.getLockForDomain(domainId);
89
+ return lock !== undefined && lock.userId !== userId;
90
+ }
91
+
92
+ findOverlappingDomains(domains: string[], userId: string): string[] {
93
+ return domains.filter(d => this.hasConflict(d, userId));
94
+ }
95
+ }