@tangle-network/starter-foundry 0.5.3 → 0.5.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/README.md +49 -0
- package/corpus/ideasai-prompts.json +119 -58
- package/dist/cli.js +28 -2
- package/dist/cli.js.map +1 -1
- package/dist/lib/agent-context.js +34 -22
- package/dist/lib/agent-context.js.map +1 -1
- package/dist/lib/build-plan.d.ts +18 -0
- package/dist/lib/build-plan.js +72 -0
- package/dist/lib/build-plan.js.map +1 -1
- package/dist/lib/buildout-traces.d.ts +113 -0
- package/dist/lib/buildout-traces.js +83 -0
- package/dist/lib/buildout-traces.js.map +1 -0
- package/dist/lib/capability-inferrer.d.ts +40 -0
- package/dist/lib/capability-inferrer.js +82 -0
- package/dist/lib/capability-inferrer.js.map +1 -0
- package/dist/lib/context-pack.d.ts +6 -1
- package/dist/lib/context-pack.js +9 -3
- package/dist/lib/context-pack.js.map +1 -1
- package/dist/lib/keywords.js +71 -18
- package/dist/lib/keywords.js.map +1 -1
- package/dist/lib/llm.d.ts +9 -0
- package/dist/lib/llm.js +70 -0
- package/dist/lib/llm.js.map +1 -0
- package/dist/lib/planner/contracts.d.ts +11 -0
- package/dist/lib/planner/contracts.js +119 -0
- package/dist/lib/planner/contracts.js.map +1 -0
- package/dist/lib/planner/detectors.d.ts +14 -0
- package/dist/lib/planner/detectors.js +195 -0
- package/dist/lib/planner/detectors.js.map +1 -0
- package/dist/lib/planner/helpers.d.ts +2 -0
- package/dist/lib/planner/helpers.js +135 -0
- package/dist/lib/planner/helpers.js.map +1 -0
- package/dist/lib/planner/implicit-caps.d.ts +1 -0
- package/dist/lib/planner/implicit-caps.js +91 -0
- package/dist/lib/planner/implicit-caps.js.map +1 -0
- package/dist/lib/planner/projects.d.ts +13 -0
- package/dist/lib/planner/projects.js +181 -0
- package/dist/lib/planner/projects.js.map +1 -0
- package/dist/lib/planner/signals.d.ts +17 -0
- package/dist/lib/planner/signals.js +324 -0
- package/dist/lib/planner/signals.js.map +1 -0
- package/dist/lib/product-brief.d.ts +25 -0
- package/dist/lib/product-brief.js +129 -0
- package/dist/lib/product-brief.js.map +1 -0
- package/dist/lib/prompt-planner.d.ts +5 -1
- package/dist/lib/prompt-planner.js +59 -622
- package/dist/lib/prompt-planner.js.map +1 -1
- package/dist/lib/prompt-rewriter.d.ts +21 -0
- package/dist/lib/prompt-rewriter.js +118 -0
- package/dist/lib/prompt-rewriter.js.map +1 -0
- package/dist/lib/selection.js +170 -36
- package/dist/lib/selection.js.map +1 -1
- package/dist/lib/telemetry.d.ts +0 -11
- package/dist/lib/telemetry.js +3 -3
- package/dist/lib/telemetry.js.map +1 -1
- package/dist/lib/vb-outcomes.d.ts +61 -0
- package/dist/lib/vb-outcomes.js +88 -0
- package/dist/lib/vb-outcomes.js.map +1 -0
- package/dist/training/variant_b/brief-loader.d.ts +5 -0
- package/dist/training/variant_b/brief-loader.js +263 -0
- package/dist/training/variant_b/brief-loader.js.map +1 -0
- package/dist/training/variant_b/flow.d.ts +56 -0
- package/dist/training/variant_b/flow.js +80 -0
- package/dist/training/variant_b/flow.js.map +1 -0
- package/dist/training/variant_b/generate.d.ts +7 -0
- package/dist/training/variant_b/generate.js +70 -0
- package/dist/training/variant_b/generate.js.map +1 -0
- package/dist/training/variant_b/judge.d.ts +8 -0
- package/dist/training/variant_b/judge.js +24 -0
- package/dist/training/variant_b/judge.js.map +1 -0
- package/dist/training/variant_b/nodes/collect.d.ts +31 -0
- package/dist/training/variant_b/nodes/collect.js +122 -0
- package/dist/training/variant_b/nodes/collect.js.map +1 -0
- package/dist/training/variant_b/nodes/generate.d.ts +22 -0
- package/dist/training/variant_b/nodes/generate.js +171 -0
- package/dist/training/variant_b/nodes/generate.js.map +1 -0
- package/dist/training/variant_b/nodes/judge.d.ts +26 -0
- package/dist/training/variant_b/nodes/judge.js +64 -0
- package/dist/training/variant_b/nodes/judge.js.map +1 -0
- package/dist/training/variant_b/nodes/promote.d.ts +15 -0
- package/dist/training/variant_b/nodes/promote.js +64 -0
- package/dist/training/variant_b/nodes/promote.js.map +1 -0
- package/dist/training/variant_b/nodes/rank.d.ts +12 -0
- package/dist/training/variant_b/nodes/rank.js +46 -0
- package/dist/training/variant_b/nodes/rank.js.map +1 -0
- package/dist/training/variant_b/nodes/train.d.ts +34 -0
- package/dist/training/variant_b/nodes/train.js +209 -0
- package/dist/training/variant_b/nodes/train.js.map +1 -0
- package/dist/training/variant_b/run-flow.d.ts +11 -0
- package/dist/training/variant_b/run-flow.js +21 -0
- package/dist/training/variant_b/run-flow.js.map +1 -0
- package/dist/training/variant_b/train.d.ts +15 -0
- package/dist/training/variant_b/train.js +37 -0
- package/dist/training/variant_b/train.js.map +1 -0
- package/dist/types.d.ts +16 -0
- package/package.json +6 -2
- package/registry/families/browser-extension-ts/files/package.json +4 -1
- package/registry/families/cli-ts/files/package.json +5 -0
- package/registry/families/electron-desktop-ts/files/package.json +3 -1
- package/registry/families/expo-react-native-ts/files/package.json +4 -0
- package/registry/families/fhenix-contracts/files/package.json +1 -1
- package/registry/families/fhevm-contracts/files/package.json +1 -1
- package/registry/families/fullstack-ts/files/package.json +3 -1
- package/registry/families/hardhat-contracts/files/package.json +3 -1
- package/registry/families/nextjs-ts/files/package.json +3 -1
- package/registry/families/react-vite-ts/files/package.json +11 -1
- package/registry/families/react-vite-ts/files/src/components/ui/badge.tsx +9 -0
- package/registry/families/react-vite-ts/files/src/components/ui/button.tsx +18 -0
- package/registry/families/react-vite-ts/files/src/components/ui/card.tsx +22 -0
- package/registry/families/react-vite-ts/manifest.json +14 -2
- package/registry/families/remix-ts/files/package.json +9 -1
- package/registry/families/sveltekit-ts/files/package.json +9 -1
- package/registry/families/tauri-desktop/files/package.json +3 -1
- package/registry/families/vue-ts/files/package.json +9 -1
- package/registry/layers/framework/expo-react-native-ts/files/tsconfig.json +2 -1
- package/registry/layers/framework/forge-foundation/files/foundry.toml +22 -1
- package/registry/layers/framework/fullstack-node-ts/files/personalize.json +5 -5
- package/registry/layers/framework/tangle-blueprint/files/TOOLCHAIN.md +16 -0
- package/registry/layers/framework/tangle-blueprint/files/rust-toolchain.toml +1 -1
- package/registry/layers/framework/tangle-blueprint/manifest.json +4 -0
- package/registry/package-to-capability.json +57 -0
|
@@ -1,623 +1,14 @@
|
|
|
1
|
-
import { sanitizePackageName } from './fs.js';
|
|
2
1
|
import { emit, traced } from './telemetry.js';
|
|
3
2
|
import { loadRegistry } from './registry.js';
|
|
4
3
|
import { selectStarter } from './selection.js';
|
|
5
|
-
import { detectCapabilities, detectIndustry, detectLane, hasAny
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
const familySets = {
|
|
15
|
-
coinbase: new Set([
|
|
16
|
-
'frontend-static',
|
|
17
|
-
'react-vite-ts',
|
|
18
|
-
'nextjs-ts',
|
|
19
|
-
'fullstack-ts',
|
|
20
|
-
'expo-react-native-ts',
|
|
21
|
-
'browser-extension-ts',
|
|
22
|
-
'electron-desktop-ts',
|
|
23
|
-
'tauri-desktop',
|
|
24
|
-
'api-service',
|
|
25
|
-
'cloudflare-worker-ts',
|
|
26
|
-
'python-api',
|
|
27
|
-
'rust-service',
|
|
28
|
-
'go-api',
|
|
29
|
-
'worker-job',
|
|
30
|
-
'go-worker',
|
|
31
|
-
'playwright-worker',
|
|
32
|
-
'evm-infra-ts',
|
|
33
|
-
'agent-service-ts',
|
|
34
|
-
'agent-service-py',
|
|
35
|
-
'forge-contracts',
|
|
36
|
-
'hardhat-contracts',
|
|
37
|
-
]),
|
|
38
|
-
tangle: new Set([
|
|
39
|
-
'frontend-static',
|
|
40
|
-
'react-vite-ts',
|
|
41
|
-
'nextjs-ts',
|
|
42
|
-
'fullstack-ts',
|
|
43
|
-
'api-service',
|
|
44
|
-
'python-api',
|
|
45
|
-
'rust-service',
|
|
46
|
-
'go-api',
|
|
47
|
-
'worker-job',
|
|
48
|
-
'go-worker',
|
|
49
|
-
'playwright-worker',
|
|
50
|
-
'evm-infra-ts',
|
|
51
|
-
'agent-service-ts',
|
|
52
|
-
'agent-service-py',
|
|
53
|
-
'agent-service-rust',
|
|
54
|
-
'tangle-blueprint',
|
|
55
|
-
]),
|
|
56
|
-
eigenlayer: new Set([
|
|
57
|
-
'frontend-static',
|
|
58
|
-
'react-vite-ts',
|
|
59
|
-
'nextjs-ts',
|
|
60
|
-
'fullstack-ts',
|
|
61
|
-
'api-service',
|
|
62
|
-
'python-api',
|
|
63
|
-
'rust-service',
|
|
64
|
-
'go-api',
|
|
65
|
-
'worker-job',
|
|
66
|
-
'go-worker',
|
|
67
|
-
'playwright-worker',
|
|
68
|
-
'evm-infra-ts',
|
|
69
|
-
'agent-service-ts',
|
|
70
|
-
'agent-service-py',
|
|
71
|
-
'agent-service-rust',
|
|
72
|
-
'eigenlayer-avs',
|
|
73
|
-
]),
|
|
74
|
-
arbitrum: new Set([
|
|
75
|
-
'frontend-static',
|
|
76
|
-
'react-vite-ts',
|
|
77
|
-
'nextjs-ts',
|
|
78
|
-
'fullstack-ts',
|
|
79
|
-
'api-service',
|
|
80
|
-
'python-api',
|
|
81
|
-
'rust-service',
|
|
82
|
-
'go-api',
|
|
83
|
-
'worker-job',
|
|
84
|
-
'go-worker',
|
|
85
|
-
'playwright-worker',
|
|
86
|
-
'evm-infra-ts',
|
|
87
|
-
'agent-service-ts',
|
|
88
|
-
'agent-service-py',
|
|
89
|
-
'agent-service-rust',
|
|
90
|
-
'forge-contracts',
|
|
91
|
-
'hardhat-contracts',
|
|
92
|
-
'stylus-contracts',
|
|
93
|
-
]),
|
|
94
|
-
xlayer: new Set([
|
|
95
|
-
'frontend-static',
|
|
96
|
-
'react-vite-ts',
|
|
97
|
-
'nextjs-ts',
|
|
98
|
-
'fullstack-ts',
|
|
99
|
-
'api-service',
|
|
100
|
-
'python-api',
|
|
101
|
-
'rust-service',
|
|
102
|
-
'go-api',
|
|
103
|
-
'worker-job',
|
|
104
|
-
'go-worker',
|
|
105
|
-
'playwright-worker',
|
|
106
|
-
'evm-infra-ts',
|
|
107
|
-
'agent-service-ts',
|
|
108
|
-
'agent-service-py',
|
|
109
|
-
'agent-service-rust',
|
|
110
|
-
'forge-contracts',
|
|
111
|
-
'hardhat-contracts',
|
|
112
|
-
]),
|
|
113
|
-
solana: new Set([
|
|
114
|
-
'frontend-static',
|
|
115
|
-
'react-vite-ts',
|
|
116
|
-
'nextjs-ts',
|
|
117
|
-
'fullstack-ts',
|
|
118
|
-
'api-service',
|
|
119
|
-
'python-api',
|
|
120
|
-
'rust-service',
|
|
121
|
-
'go-api',
|
|
122
|
-
'worker-job',
|
|
123
|
-
'go-worker',
|
|
124
|
-
'playwright-worker',
|
|
125
|
-
'agent-service-ts',
|
|
126
|
-
'agent-service-py',
|
|
127
|
-
'agent-service-rust',
|
|
128
|
-
'solana-program',
|
|
129
|
-
]),
|
|
130
|
-
};
|
|
131
|
-
return familySets[partner]?.has(family) ? partner : null;
|
|
132
|
-
}
|
|
133
|
-
function detectDatabaseSlot(text) {
|
|
134
|
-
if (text.includes('convex'))
|
|
135
|
-
return 'database:convex';
|
|
136
|
-
if (text.includes('postgres'))
|
|
137
|
-
return 'database:postgres';
|
|
138
|
-
if (text.includes('mongodb') || text.includes('mongo'))
|
|
139
|
-
return 'database:mongodb';
|
|
140
|
-
if (text.includes('sqlite'))
|
|
141
|
-
return 'database:sqlite';
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
function detectSdkSlot(text, partner) {
|
|
145
|
-
if (text.includes('solana web3') || text.includes('@solana/web3') || text.includes('wallet adapter')) {
|
|
146
|
-
return 'sdk:solana-web3';
|
|
147
|
-
}
|
|
148
|
-
if (partner === 'coinbase')
|
|
149
|
-
return 'sdk:coinbase-cdp';
|
|
150
|
-
if (text.includes('coinbase cdp') || text.includes('coinbase sdk'))
|
|
151
|
-
return 'sdk:coinbase-cdp';
|
|
152
|
-
if (partner === 'xlayer' ||
|
|
153
|
-
partner === 'arbitrum' ||
|
|
154
|
-
hasAny(text, ['walletconnect', 'wallet connect', 'okx wallet', 'metamask', 'viem', 'ethers'])) {
|
|
155
|
-
return 'sdk:evm-wallet';
|
|
156
|
-
}
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
function detectAuthSlot(text) {
|
|
160
|
-
if (text.includes('clerk'))
|
|
161
|
-
return 'auth:clerk';
|
|
162
|
-
if (text.includes('better auth') || text.includes('better-auth'))
|
|
163
|
-
return 'auth:better-auth';
|
|
164
|
-
if (text.includes('supabase auth') || text.includes('supabase-auth'))
|
|
165
|
-
return 'auth:supabase-auth';
|
|
166
|
-
return null;
|
|
167
|
-
}
|
|
168
|
-
function detectPaymentsSlot(text) {
|
|
169
|
-
if (text.includes('coinbase commerce'))
|
|
170
|
-
return 'payments:coinbase-commerce';
|
|
171
|
-
if (text.includes('stripe') || text.includes('subscription') || text.includes('billing') || text.includes('checkout')) {
|
|
172
|
-
return 'payments:stripe';
|
|
173
|
-
}
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
function detectQueueSlot(text) {
|
|
177
|
-
if (text.includes('trigger.dev') || text.includes('trigger dev'))
|
|
178
|
-
return 'queue:trigger-dev';
|
|
179
|
-
if (text.includes('bullmq') || text.includes('queue') || text.includes('background job'))
|
|
180
|
-
return 'queue:bullmq';
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
|
-
function detectTangleOraclePattern(text) {
|
|
184
|
-
return (hasAny(text, ['tangle', 'tangle network', 'tangle native']) &&
|
|
185
|
-
hasAny(text, ['oracle', 'price feed', 'attestation', 'feeder', 'operator rewards', 'slashing', 'data source']));
|
|
186
|
-
}
|
|
187
|
-
function detectTangleCustodyPattern(text) {
|
|
188
|
-
return (hasAny(text, ['tangle', 'tangle network', 'tangle native', 'frost']) &&
|
|
189
|
-
hasAny(text, ['custody', 'mpc', 'threshold signing', 'key resharing', 'policy engine', 'signing ceremony']));
|
|
190
|
-
}
|
|
191
|
-
function detectEvmDeployPattern(text) {
|
|
192
|
-
return hasAny(text, [
|
|
193
|
-
'foundry.toml',
|
|
194
|
-
'deploy script',
|
|
195
|
-
'deploy task',
|
|
196
|
-
'contract verification',
|
|
197
|
-
'verify',
|
|
198
|
-
'oklink explorer',
|
|
199
|
-
'.env template',
|
|
200
|
-
'private_key',
|
|
201
|
-
'sample erc20',
|
|
202
|
-
]);
|
|
203
|
-
}
|
|
204
|
-
function detectHardhatExplicit(text) {
|
|
205
|
-
return text.includes('hardhat') && !hasAny(text, ['foundry', 'forge']);
|
|
206
|
-
}
|
|
207
|
-
function detectEvmSupportApiPattern(text) {
|
|
208
|
-
return hasAny(text, [
|
|
209
|
-
'indexer',
|
|
210
|
-
'subgraph',
|
|
211
|
-
'events',
|
|
212
|
-
'relayer',
|
|
213
|
-
'paymaster',
|
|
214
|
-
'bundler',
|
|
215
|
-
'keeper',
|
|
216
|
-
'oracle',
|
|
217
|
-
'bridge',
|
|
218
|
-
'bridges',
|
|
219
|
-
'layerzero',
|
|
220
|
-
'oft',
|
|
221
|
-
'sendtokens',
|
|
222
|
-
'hook',
|
|
223
|
-
'analytics',
|
|
224
|
-
'portfolio',
|
|
225
|
-
'metrics',
|
|
226
|
-
'multicall',
|
|
227
|
-
'wallet balance',
|
|
228
|
-
'transaction count',
|
|
229
|
-
'gas price',
|
|
230
|
-
'websocket',
|
|
231
|
-
'/stats',
|
|
232
|
-
]);
|
|
233
|
-
}
|
|
234
|
-
function inferPartner(text) {
|
|
235
|
-
if (hasAny(text, ['coinbase', 'base network', 'coinbase commerce', 'coinbase wallet', 'coinbase cdp'])) {
|
|
236
|
-
return 'coinbase';
|
|
237
|
-
}
|
|
238
|
-
if (hasAny(text, ['tangle', 'blueprint sdk', 'cargo tangle']))
|
|
239
|
-
return 'tangle';
|
|
240
|
-
if (hasAny(text, ['eigenlayer', 'avs']))
|
|
241
|
-
return 'eigenlayer';
|
|
242
|
-
if (hasAny(text, ['x layer', 'xlayer', 'okb', 'oklink', 'okx']))
|
|
243
|
-
return 'xlayer';
|
|
244
|
-
if (hasAny(text, ['arbitrum', 'stylus']))
|
|
245
|
-
return 'arbitrum';
|
|
246
|
-
if (hasAny(text, ['solana', 'anchor', 'pda', 'wallet adapter']))
|
|
247
|
-
return 'solana';
|
|
248
|
-
return null;
|
|
249
|
-
}
|
|
250
|
-
function needsSupportApiLane(text) {
|
|
251
|
-
return hasAny(text, [
|
|
252
|
-
'order management',
|
|
253
|
-
'order history',
|
|
254
|
-
'payment webhook',
|
|
255
|
-
'quote generation',
|
|
256
|
-
'claims',
|
|
257
|
-
'transaction history',
|
|
258
|
-
'history/audit',
|
|
259
|
-
'portfolio',
|
|
260
|
-
'analytics',
|
|
261
|
-
'monitoring',
|
|
262
|
-
'indexer',
|
|
263
|
-
'websocket',
|
|
264
|
-
'database',
|
|
265
|
-
'metrics',
|
|
266
|
-
'p&l',
|
|
267
|
-
'api endpoints',
|
|
268
|
-
'consumer integration',
|
|
269
|
-
'historical data',
|
|
270
|
-
'data source',
|
|
271
|
-
'programmatic access',
|
|
272
|
-
'control plane',
|
|
273
|
-
'agent state',
|
|
274
|
-
'tool logs',
|
|
275
|
-
'memory',
|
|
276
|
-
'thread history',
|
|
277
|
-
'execution traces',
|
|
278
|
-
]);
|
|
279
|
-
}
|
|
280
|
-
function detectSolanaProductApiPattern(text) {
|
|
281
|
-
return hasAny(text, [
|
|
282
|
-
'pyth',
|
|
283
|
-
'switchboard',
|
|
284
|
-
'jupiter',
|
|
285
|
-
'openbook',
|
|
286
|
-
'analytics',
|
|
287
|
-
'leaderboard',
|
|
288
|
-
'activity feed',
|
|
289
|
-
'creator dashboard',
|
|
290
|
-
'launch calendar',
|
|
291
|
-
'pool discovery',
|
|
292
|
-
'market browser',
|
|
293
|
-
'market data',
|
|
294
|
-
'oracle integration',
|
|
295
|
-
'price feeds',
|
|
296
|
-
'dashboard',
|
|
297
|
-
'staking dashboard',
|
|
298
|
-
'royalty analytics',
|
|
299
|
-
'position explorer',
|
|
300
|
-
'pool explorer',
|
|
301
|
-
]);
|
|
302
|
-
}
|
|
303
|
-
function detectSolanaWorkerPattern(text) {
|
|
304
|
-
return hasAny(text, [
|
|
305
|
-
'keeper',
|
|
306
|
-
'liquidation',
|
|
307
|
-
'funding rate',
|
|
308
|
-
'pyth price feeds',
|
|
309
|
-
'switchboard',
|
|
310
|
-
'oracle integration',
|
|
311
|
-
'disputes',
|
|
312
|
-
'auto-deleveraging',
|
|
313
|
-
'rebalance',
|
|
314
|
-
'rebalancing',
|
|
315
|
-
'rewards distribution',
|
|
316
|
-
'reward distribution',
|
|
317
|
-
'vesting',
|
|
318
|
-
'sale monitor',
|
|
319
|
-
'auto-compound',
|
|
320
|
-
]);
|
|
321
|
-
}
|
|
322
|
-
function buildEvmContractLayers(text) {
|
|
323
|
-
const hardhatExplicit = detectHardhatExplicit(text);
|
|
324
|
-
const layers = [hardhatExplicit ? 'framework:hardhat-ts' : 'framework:forge-foundation'];
|
|
325
|
-
if (!hardhatExplicit && detectEvmDeployPattern(text)) {
|
|
326
|
-
layers.push('capability:evm-deploy-foundry');
|
|
327
|
-
}
|
|
328
|
-
if (!hardhatExplicit &&
|
|
329
|
-
hasAny(text, ['layerzero', 'oft', 'bridge tokens', 'sendtokens script', 'omnichain fungible token'])) {
|
|
330
|
-
layers.push('capability:evm-layerzero-oft');
|
|
331
|
-
}
|
|
332
|
-
else if (!hardhatExplicit &&
|
|
333
|
-
hasAny(text, ['erc-4337', 'erc4337', 'bundler', 'permissionless.js', 'gasless mint', 'account abstraction'])) {
|
|
334
|
-
layers.push('capability:evm-account-abstraction');
|
|
335
|
-
}
|
|
336
|
-
return layers;
|
|
337
|
-
}
|
|
338
|
-
function chooseAgentFamily(text) {
|
|
339
|
-
if (hasAny(text, ['rust', 'cargo', 'rig', 'rust agent'])) {
|
|
340
|
-
return {
|
|
341
|
-
family: 'agent-service-rust',
|
|
342
|
-
layers: ['framework:agent-service-rust'],
|
|
343
|
-
path: 'apps/agent',
|
|
344
|
-
variables: {
|
|
345
|
-
agentLibrary: matchesKeyword(text, 'rig') ? 'rig' : 'rust-agent',
|
|
346
|
-
},
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
if (hasAny(text, ['python', 'fastapi', 'pydanticai', 'crewai', 'autogen', 'agno', 'llamaindex', 'agentkit', 'python agent', 'unsloth', 'qlora'])) {
|
|
350
|
-
let agentLibrary = 'pydanticai';
|
|
351
|
-
if (text.includes('crewai'))
|
|
352
|
-
agentLibrary = 'crewai';
|
|
353
|
-
else if (text.includes('autogen'))
|
|
354
|
-
agentLibrary = 'autogen';
|
|
355
|
-
else if (text.includes('agno'))
|
|
356
|
-
agentLibrary = 'agno';
|
|
357
|
-
else if (text.includes('llamaindex'))
|
|
358
|
-
agentLibrary = 'llamaindex';
|
|
359
|
-
else if (text.includes('agentkit'))
|
|
360
|
-
agentLibrary = 'agentkit';
|
|
361
|
-
return {
|
|
362
|
-
family: 'agent-service-py',
|
|
363
|
-
layers: ['framework:agent-service-py'],
|
|
364
|
-
path: 'apps/agent',
|
|
365
|
-
variables: { agentLibrary },
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
let agentLibrary = 'openai-agents';
|
|
369
|
-
if (text.includes('langgraph'))
|
|
370
|
-
agentLibrary = 'langgraph';
|
|
371
|
-
else if (text.includes('mastra'))
|
|
372
|
-
agentLibrary = 'mastra';
|
|
373
|
-
else if (hasAny(text, ['anthropic sdk', 'claude sdk']))
|
|
374
|
-
agentLibrary = 'anthropic-sdk';
|
|
375
|
-
return {
|
|
376
|
-
family: 'agent-service-ts',
|
|
377
|
-
layers: ['framework:agent-service-ts'],
|
|
378
|
-
path: 'apps/agent',
|
|
379
|
-
variables: { agentLibrary },
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
function buildEvmContractVariables(text) {
|
|
383
|
-
if (hasAny(text, ['layerzero', 'oft', 'omnichain fungible token']))
|
|
384
|
-
return { contractName: 'OmnichainToken' };
|
|
385
|
-
if (hasAny(text, ['erc721', 'erc-721', 'nft collection', 'gasless mint']))
|
|
386
|
-
return { contractName: 'GaslessCollectible' };
|
|
387
|
-
if (hasAny(text, ['erc20', 'erc-20', 'sample erc20']))
|
|
388
|
-
return { contractName: 'XLayerToken' };
|
|
389
|
-
return { contractName: 'Counter' };
|
|
390
|
-
}
|
|
391
|
-
function buildSolanaProgramLayers(text) {
|
|
392
|
-
const layers = ['framework:solana-native-rust'];
|
|
393
|
-
if (hasAny(text, ['perpetual', 'futures', 'funding rate', 'liquidation', 'insurance fund', 'cross-collateral'])) {
|
|
394
|
-
layers.push('capability:solana-perps');
|
|
395
|
-
}
|
|
396
|
-
else if (hasAny(text, ['concentrated liquidity', 'tick-based liquidity', 'swap router', 'position nft'])) {
|
|
397
|
-
layers.push('capability:solana-amm');
|
|
398
|
-
}
|
|
399
|
-
else if (hasAny(text, ['nft marketplace', 'compressed nfts', 'royalty enforcement', 'bundle sales'])) {
|
|
400
|
-
layers.push('capability:solana-nft');
|
|
401
|
-
}
|
|
402
|
-
else if (hasAny(text, ['launchpad', 'fair launches', 'dutch auction', 'bonding curve', 'claim portal'])) {
|
|
403
|
-
layers.push('capability:solana-launchpad');
|
|
404
|
-
}
|
|
405
|
-
else if (hasAny(text, ['staking platform', 'veToken', 'rewards dashboard', 'auto-compound', 'validator delegation'])) {
|
|
406
|
-
layers.push('capability:solana-staking');
|
|
407
|
-
}
|
|
408
|
-
else if (hasAny(text, ['prediction market', 'binary (yes/no)', 'switchboard oracle', 'scalar', 'categorical'])) {
|
|
409
|
-
layers.push('capability:solana-prediction');
|
|
410
|
-
}
|
|
411
|
-
return layers;
|
|
412
|
-
}
|
|
413
|
-
function buildSolanaProgramVariables(text) {
|
|
414
|
-
if (hasAny(text, ['perpetual', 'futures']))
|
|
415
|
-
return { instructionName: 'InitializePerpMarket' };
|
|
416
|
-
if (hasAny(text, ['concentrated liquidity', 'amm dex', 'swap router']))
|
|
417
|
-
return { instructionName: 'InitializePool' };
|
|
418
|
-
if (hasAny(text, ['nft marketplace', 'compressed nfts']))
|
|
419
|
-
return { instructionName: 'CreateListing' };
|
|
420
|
-
if (hasAny(text, ['launchpad', 'fair launches', 'bonding curve']))
|
|
421
|
-
return { instructionName: 'CreateLaunch' };
|
|
422
|
-
if (hasAny(text, ['staking platform', 'veToken', 'auto-compound']))
|
|
423
|
-
return { instructionName: 'InitializeStakePool' };
|
|
424
|
-
if (hasAny(text, ['prediction market', 'scalar', 'categorical']))
|
|
425
|
-
return { instructionName: 'CreateMarket' };
|
|
426
|
-
return { instructionName: 'InitializeTreasury' };
|
|
427
|
-
}
|
|
428
|
-
function buildWebProject(prompt, partner, text, registry) {
|
|
429
|
-
const isNext = hasAny(text, ['next', 'next.js', 'nextjs', 'app router', 'seo']);
|
|
430
|
-
const family = isNext ? 'nextjs-ts' : 'react-vite-ts';
|
|
431
|
-
const frameworkLayer = isNext ? 'framework:nextjs-app-router' : 'framework:react-vite-ts';
|
|
432
|
-
const layers = [frameworkLayer];
|
|
433
|
-
if (hasAny(text, ['dashboard', 'metrics', 'analytics', 'control plane', 'admin'])) {
|
|
434
|
-
layers.push('capability:chart-widget');
|
|
435
|
-
}
|
|
436
|
-
const slots = {};
|
|
437
|
-
const sdkSlot = detectSdkSlot(text, partner);
|
|
438
|
-
const authSlot = detectAuthSlot(text);
|
|
439
|
-
const paymentsSlot = detectPaymentsSlot(text);
|
|
440
|
-
if (authSlot)
|
|
441
|
-
slots['auth'] = authSlot;
|
|
442
|
-
if (paymentsSlot)
|
|
443
|
-
slots['payments'] = paymentsSlot;
|
|
444
|
-
if (sdkSlot)
|
|
445
|
-
slots['sdk'] = sdkSlot;
|
|
446
|
-
const webCapabilities = registry ? detectCapabilities(text, family, registry) : [];
|
|
447
|
-
if (webCapabilities.length > 0)
|
|
448
|
-
layers.push(...webCapabilities);
|
|
449
|
-
return {
|
|
450
|
-
id: 'web',
|
|
451
|
-
path: 'apps/web',
|
|
452
|
-
spec: {
|
|
453
|
-
projectName: `${buildSlug(prompt, 'workspace')}-web`,
|
|
454
|
-
family,
|
|
455
|
-
layers: [...new Set(layers)],
|
|
456
|
-
partner: resolvePartnerForFamily(partner, family),
|
|
457
|
-
slots,
|
|
458
|
-
variables: {
|
|
459
|
-
headline: 'Ship the primary product surface first',
|
|
460
|
-
subheadline: 'This workspace starts with the user-visible surface before deepening the backend and contract lanes.',
|
|
461
|
-
},
|
|
462
|
-
primaryArtifactTargetMs: 2500,
|
|
463
|
-
},
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
function chooseWorkerFamily(text) {
|
|
467
|
-
if (hasAny(text, ['playwright', 'browser automation', 'web scraping', 'scraper', 'crawler'])) {
|
|
468
|
-
return { family: 'playwright-worker', layers: ['framework:playwright-worker'], path: 'apps/worker' };
|
|
469
|
-
}
|
|
470
|
-
if (hasAny(text, ['go worker', 'golang worker', 'go cron', 'go queue', 'go background job'])) {
|
|
471
|
-
return { family: 'go-worker', layers: ['framework:go-worker'], path: 'apps/worker' };
|
|
472
|
-
}
|
|
473
|
-
if (hasAny(text, ['python worker', 'python cron', 'python queue', 'python background job', 'celery', 'python task'])) {
|
|
474
|
-
return { family: 'python-worker', layers: ['framework:python-worker'], path: 'apps/worker' };
|
|
475
|
-
}
|
|
476
|
-
if (detectLane(text, 'agent') && hasAny(text, ['worker', 'background', 'cron', 'queue', 'runner', 'executor'])) {
|
|
477
|
-
return { family: 'worker-job', layers: ['framework:node-worker'], path: 'apps/worker' };
|
|
478
|
-
}
|
|
479
|
-
const layers = ['framework:node-worker'];
|
|
480
|
-
if (hasAny(text, ['trading', 'market', 'feed', 'stream'])) {
|
|
481
|
-
layers.push('capability:market-sim');
|
|
482
|
-
}
|
|
483
|
-
return { family: 'worker-job', layers, path: 'apps/worker' };
|
|
484
|
-
}
|
|
485
|
-
function chooseApiFamily(text) {
|
|
486
|
-
if (detectLane(text, 'x402'))
|
|
487
|
-
return { family: 'x402-service', layers: ['framework:x402-service'], path: 'apps/api' };
|
|
488
|
-
if (detectLane(text, 'mcp'))
|
|
489
|
-
return { family: 'mcp-server-ts', layers: ['framework:mcp-server-ts'], path: 'apps/mcp' };
|
|
490
|
-
if (detectLane(text, 'dspy'))
|
|
491
|
-
return { family: 'dspy-pipeline-py', layers: ['framework:dspy-pipeline-py'], path: 'apps/ai' };
|
|
492
|
-
if (detectLane(text, 'agent'))
|
|
493
|
-
return chooseAgentFamily(text);
|
|
494
|
-
if (detectLane(text, 'zk'))
|
|
495
|
-
return { family: 'zk-prover-service', layers: ['framework:zk-prover-service'], path: 'apps/prover' };
|
|
496
|
-
if (detectLane(text, 'evm-infra'))
|
|
497
|
-
return { family: 'evm-infra-ts', layers: ['framework:evm-infra-ts'], path: 'apps/api' };
|
|
498
|
-
if (hasAny(text, ['cloudflare', 'durable object', 'edge api', 'edge function', 'hono edge'])) {
|
|
499
|
-
return { family: 'cloudflare-worker-ts', layers: ['framework:cloudflare-worker-ts'], path: 'apps/edge' };
|
|
500
|
-
}
|
|
501
|
-
if (hasAny(text, ['rust', 'cargo', 'axum', 'rust api', 'rust backend'])) {
|
|
502
|
-
return { family: 'rust-service', layers: ['framework:rust-http'], path: 'apps/api' };
|
|
503
|
-
}
|
|
504
|
-
if (hasAny(text, ['python', 'fastapi', 'flask', 'django', 'python api'])) {
|
|
505
|
-
return { family: 'python-api', layers: ['framework:python-http'], path: 'apps/api' };
|
|
506
|
-
}
|
|
507
|
-
if (hasAny(text, ['golang', 'go api', 'go backend', 'go service', 'net/http'])) {
|
|
508
|
-
return { family: 'go-api', layers: ['framework:go-net-http'], path: 'apps/api' };
|
|
509
|
-
}
|
|
510
|
-
return { family: 'api-service', layers: ['framework:node-http', 'capability:logging'], path: 'apps/api' };
|
|
511
|
-
}
|
|
512
|
-
function buildApiProject(prompt, partner, text, registry) {
|
|
513
|
-
const choice = chooseApiFamily(text);
|
|
514
|
-
const layers = [...choice.layers];
|
|
515
|
-
const slots = {};
|
|
516
|
-
const databaseSlot = detectDatabaseSlot(text);
|
|
517
|
-
const sdkSlot = detectSdkSlot(text, partner);
|
|
518
|
-
const authSlot = detectAuthSlot(text);
|
|
519
|
-
const paymentsSlot = detectPaymentsSlot(text);
|
|
520
|
-
const queueSlot = detectQueueSlot(text);
|
|
521
|
-
if (databaseSlot)
|
|
522
|
-
slots['database'] = databaseSlot;
|
|
523
|
-
if (authSlot)
|
|
524
|
-
slots['auth'] = authSlot;
|
|
525
|
-
if (paymentsSlot)
|
|
526
|
-
slots['payments'] = paymentsSlot;
|
|
527
|
-
if (queueSlot)
|
|
528
|
-
slots['queue'] = queueSlot;
|
|
529
|
-
if (sdkSlot)
|
|
530
|
-
slots['sdk'] = sdkSlot;
|
|
531
|
-
if ((choice.family === 'api-service' || choice.family === 'evm-infra-ts') && detectEvmSupportApiPattern(text)) {
|
|
532
|
-
layers.push('capability:evm-protocol-api');
|
|
533
|
-
}
|
|
534
|
-
if (choice.family === 'evm-infra-ts') {
|
|
535
|
-
if (hasAny(text, ['block monitor', 'new blocks', 'gas price', 'tps', '/stats'])) {
|
|
536
|
-
layers.push('capability:evm-chain-monitor');
|
|
537
|
-
}
|
|
538
|
-
else if (hasAny(text, ['wallet balance', 'wallet address', 'multicall', 'summary table'])) {
|
|
539
|
-
layers.push('capability:evm-wallet-dashboard');
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
const apiCapabilities = registry ? detectCapabilities(text, choice.family, registry) : [];
|
|
543
|
-
if (apiCapabilities.length > 0)
|
|
544
|
-
layers.push(...apiCapabilities);
|
|
545
|
-
return {
|
|
546
|
-
id: choice.family.startsWith('agent-service-') ? 'agent' : 'api',
|
|
547
|
-
path: choice.path,
|
|
548
|
-
spec: {
|
|
549
|
-
projectName: `${buildSlug(prompt, 'workspace')}-api`,
|
|
550
|
-
family: choice.family,
|
|
551
|
-
layers: [...new Set(layers)],
|
|
552
|
-
partner: resolvePartnerForFamily(partner, choice.family),
|
|
553
|
-
slots,
|
|
554
|
-
variables: choice.variables ?? {},
|
|
555
|
-
primaryArtifactTargetMs: 2500,
|
|
556
|
-
},
|
|
557
|
-
};
|
|
558
|
-
}
|
|
559
|
-
function buildWorkerProject(prompt, partner, text) {
|
|
560
|
-
const choice = chooseWorkerFamily(text);
|
|
561
|
-
const layers = [...choice.layers];
|
|
562
|
-
const slots = {};
|
|
563
|
-
const queueSlot = detectQueueSlot(text);
|
|
564
|
-
if (queueSlot)
|
|
565
|
-
slots['queue'] = queueSlot;
|
|
566
|
-
if (choice.family === 'worker-job' && hasAny(text, ['solana', 'anchor', 'pyth', 'switchboard', 'keeper', 'liquidation'])) {
|
|
567
|
-
layers.push('capability:solana-keeper');
|
|
568
|
-
}
|
|
569
|
-
return {
|
|
570
|
-
id: 'worker',
|
|
571
|
-
path: choice.path,
|
|
572
|
-
spec: {
|
|
573
|
-
projectName: `${buildSlug(prompt, 'workspace')}-worker`,
|
|
574
|
-
family: choice.family,
|
|
575
|
-
layers,
|
|
576
|
-
partner: resolvePartnerForFamily(partner, choice.family),
|
|
577
|
-
slots,
|
|
578
|
-
variables: {
|
|
579
|
-
workerName: partner ? `${partner} worker lane` : 'workspace worker lane',
|
|
580
|
-
},
|
|
581
|
-
primaryArtifactTargetMs: 2500,
|
|
582
|
-
},
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
// Single-surface families that never become workspaces
|
|
586
|
-
const SINGLE_LANE_SIGNALS = [
|
|
587
|
-
'expo', 'react native', 'mobile app', 'ios app', 'android app',
|
|
588
|
-
'browser extension', 'chrome extension', 'manifest v3',
|
|
589
|
-
'electron', 'desktop app', 'desktop assistant',
|
|
590
|
-
'command line', 'terminal tool',
|
|
591
|
-
'streamlit', 'gradio', 'data app', 'tauri',
|
|
592
|
-
];
|
|
593
|
-
const FRONTEND_SIGNALS = [
|
|
594
|
-
'frontend', 'ui', 'website', 'landing', 'dashboard', 'web app', 'app',
|
|
595
|
-
'preview', 'next', 'react', 'platform', 'dapp', 'interface', 'portal',
|
|
596
|
-
];
|
|
597
|
-
const API_SIGNALS = [
|
|
598
|
-
'api', 'backend', 'server', 'endpoint', 'service', 'webhook', 'health check',
|
|
599
|
-
'cloudflare', 'durable object', 'edge api', 'edge function', 'payment webhook',
|
|
600
|
-
'server wallet', 'rest api', 'graphql api',
|
|
601
|
-
];
|
|
602
|
-
const FRAMEWORK_API_TERMS = ['composition api', 'options api', 'signals api', 'context api', 'hooks api'];
|
|
603
|
-
const STRONG_API_TERMS = ['backend', 'server', 'endpoint', 'webhook', 'rest api', 'graphql api', 'api service', 'api endpoint', 'health check'];
|
|
604
|
-
const WORKER_SIGNALS = [
|
|
605
|
-
'trading bot', 'background job', 'background worker', 'worker for', 'playwright worker',
|
|
606
|
-
'automation worker', 'go worker', 'golang worker', 'queue', 'cron', 'market stream', 'bot',
|
|
607
|
-
];
|
|
608
|
-
const EXPLICIT_WORKER_SIGNALS = [
|
|
609
|
-
'trading bot', 'background job', 'background worker', 'worker for', 'playwright worker',
|
|
610
|
-
'automation worker', 'go worker', 'golang worker', 'queue', 'cron', 'market stream',
|
|
611
|
-
];
|
|
612
|
-
const FULLSTACK_SIGNALS = [
|
|
613
|
-
'fullstack', 'full stack', 'dashboard with api', 'app with api', 'admin app', 'admin panel',
|
|
614
|
-
'database-backed', 'dashboard and api', 'admin flows', 'saas', 'saas app', 'saas platform',
|
|
615
|
-
'internal tool', 'back office', 'crud app',
|
|
616
|
-
];
|
|
617
|
-
const WORKSPACE_SIGNALS = [
|
|
618
|
-
'workspace', 'monorepo', 'separate backend', 'separate api',
|
|
619
|
-
'background worker', 'contract lane', 'contract lanes',
|
|
620
|
-
];
|
|
4
|
+
import { detectCapabilities, detectIndustry, detectLane, hasAny } from './keywords.js';
|
|
5
|
+
import { rewritePrompt, rewriteViaBrief } from './prompt-rewriter.js';
|
|
6
|
+
import { API_SIGNALS, EXPLICIT_WORKER_SIGNALS, FRAMEWORK_API_TERMS, FRONTEND_SIGNALS, FULLSTACK_SIGNALS, REACT_FAMILIES, SINGLE_LANE_SIGNALS, STRONG_API_TERMS, WORKER_SIGNALS, WORKSPACE_SIGNALS, } from './planner/signals.js';
|
|
7
|
+
import { detectAuthSlot, detectDatabaseSlot, detectEvmSupportApiPattern, detectHardhatExplicit, detectPaymentsSlot, detectQueueSlot, detectSdkSlot, detectSolanaProductApiPattern, detectSolanaWorkerPattern, detectTangleCustodyPattern, detectTangleOraclePattern, inferPartner, needsSupportApiLane, } from './planner/detectors.js';
|
|
8
|
+
import { buildEvmContractLayers, buildEvmContractVariables, buildSolanaProgramLayers, buildSolanaProgramVariables, } from './planner/contracts.js';
|
|
9
|
+
import { buildSlug, resolvePartnerForFamily } from './planner/helpers.js';
|
|
10
|
+
import { inferImplicitCapabilities } from './planner/implicit-caps.js';
|
|
11
|
+
import { buildApiProject, buildWebProject, buildWorkerProject, chooseApiFamily } from './planner/projects.js';
|
|
621
12
|
function detectLanes(text, partner) {
|
|
622
13
|
const hasApiRaw = hasAny(text, API_SIGNALS);
|
|
623
14
|
const apiIsFalsePositive = hasApiRaw && !hasAny(text, STRONG_API_TERMS) && hasAny(text, FRAMEWORK_API_TERMS);
|
|
@@ -789,7 +180,47 @@ function buildWorkspacePromptPlan({ prompt, partner, text, registry, }) {
|
|
|
789
180
|
},
|
|
790
181
|
};
|
|
791
182
|
}
|
|
792
|
-
export async function planPrompt({ prompt, partner = null, forceKind = null, familyHint = null, }) {
|
|
183
|
+
export async function planPrompt({ prompt, partner = null, forceKind = null, familyHint = null, rewriter = false, brief = false, }) {
|
|
184
|
+
if (brief) {
|
|
185
|
+
const preCheck = await selectStarter({ prompt, partner });
|
|
186
|
+
if (preCheck.confidence !== 'high' || preCheck.fallbackUsed) {
|
|
187
|
+
const registry = await loadRegistry();
|
|
188
|
+
const knownFamilies = [...registry.families.keys()];
|
|
189
|
+
const knownCapabilities = [];
|
|
190
|
+
for (const [key] of registry.layers) {
|
|
191
|
+
if (key.startsWith('capability:'))
|
|
192
|
+
knownCapabilities.push(key);
|
|
193
|
+
}
|
|
194
|
+
const briefResult = await rewriteViaBrief({ prompt, partner, knownFamilies, knownCapabilities });
|
|
195
|
+
if (briefResult) {
|
|
196
|
+
const plan = await planPrompt({
|
|
197
|
+
prompt: briefResult.canonicalPrompt,
|
|
198
|
+
partner,
|
|
199
|
+
forceKind,
|
|
200
|
+
familyHint,
|
|
201
|
+
rewriter: false,
|
|
202
|
+
brief: false,
|
|
203
|
+
});
|
|
204
|
+
return { ...plan, brief: briefResult.brief };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (rewriter) {
|
|
209
|
+
const preCheck = await selectStarter({ prompt, partner });
|
|
210
|
+
if (preCheck.confidence !== 'high' || preCheck.fallbackUsed) {
|
|
211
|
+
const registry = await loadRegistry();
|
|
212
|
+
const knownFamilies = [...registry.families.keys()];
|
|
213
|
+
const knownCapabilities = [];
|
|
214
|
+
for (const [key] of registry.layers) {
|
|
215
|
+
if (key.startsWith('capability:'))
|
|
216
|
+
knownCapabilities.push(key);
|
|
217
|
+
}
|
|
218
|
+
const rewrite = await rewritePrompt({ prompt, partner, knownFamilies, knownCapabilities });
|
|
219
|
+
if (rewrite) {
|
|
220
|
+
return planPrompt({ prompt: rewrite.canonicalPrompt, partner, forceKind, familyHint, rewriter: false });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
793
224
|
const { result: plan, durationMs } = await traced('planPrompt', async () => {
|
|
794
225
|
const text = prompt.toLowerCase();
|
|
795
226
|
const effectivePartner = partner ?? inferPartner(text);
|
|
@@ -861,10 +292,7 @@ export async function planPrompt({ prompt, partner = null, forceKind = null, fam
|
|
|
861
292
|
// Frontend families default to tailwind + shadcn so the layout layers and
|
|
862
293
|
// any UI components composed downstream actually render with styles.
|
|
863
294
|
// Without these, components ship Tailwind class names against an unconfigured
|
|
864
|
-
// Tailwind install — the visual result is unstyled HTML.
|
|
865
|
-
// means callers don't need to know which families are React-flavored or what
|
|
866
|
-
// their UI deps are.
|
|
867
|
-
const REACT_FAMILIES = new Set(['react-vite-ts', 'nextjs-ts', 'fullstack-ts', 'remix-ts']);
|
|
295
|
+
// Tailwind install — the visual result is unstyled HTML.
|
|
868
296
|
if (REACT_FAMILIES.has(spec.family)) {
|
|
869
297
|
const layerSet = new Set(spec.layers ?? []);
|
|
870
298
|
if (!layerSet.has('capability:tailwind')) {
|
|
@@ -874,6 +302,15 @@ export async function planPrompt({ prompt, partner = null, forceKind = null, fam
|
|
|
874
302
|
spec.layers = [...(spec.layers ?? []), 'capability:shadcn'];
|
|
875
303
|
}
|
|
876
304
|
}
|
|
305
|
+
// Archetype-based implicit capability inference. Runs last so it can see
|
|
306
|
+
// everything already attached and avoid double-adding. For web-producing
|
|
307
|
+
// families this attaches the SaaS UI trio (layout-dashboard + chart +
|
|
308
|
+
// ai-chat-ui) or the chat archetype (layout-chat + ai-chat-ui) based on
|
|
309
|
+
// the product shape in the prompt.
|
|
310
|
+
const implicitCaps = inferImplicitCapabilities(text, spec.family, new Set(spec.layers ?? []));
|
|
311
|
+
if (implicitCaps.length > 0) {
|
|
312
|
+
spec.layers = [...new Set([...(spec.layers ?? []), ...implicitCaps])];
|
|
313
|
+
}
|
|
877
314
|
return {
|
|
878
315
|
kind: 'starter',
|
|
879
316
|
confidence: starterSelection.confidence,
|