@agentuity/cli 0.0.100 → 0.0.102

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 (264) hide show
  1. package/AGENTS.md +19 -188
  2. package/bin/cli.ts +13 -6
  3. package/dist/api.d.ts +1 -0
  4. package/dist/api.d.ts.map +1 -1
  5. package/dist/api.js +1 -1
  6. package/dist/api.js.map +1 -1
  7. package/dist/cli.d.ts.map +1 -1
  8. package/dist/cli.js +41 -12
  9. package/dist/cli.js.map +1 -1
  10. package/dist/cmd/ai/index.d.ts.map +1 -1
  11. package/dist/cmd/ai/index.js +6 -1
  12. package/dist/cmd/ai/index.js.map +1 -1
  13. package/dist/cmd/ai/prompt/agent.d.ts +7 -0
  14. package/dist/cmd/ai/prompt/agent.d.ts.map +1 -1
  15. package/dist/cmd/ai/prompt/agent.js +12 -323
  16. package/dist/cmd/ai/prompt/agent.js.map +1 -1
  17. package/dist/cmd/ai/prompt/api.d.ts +7 -0
  18. package/dist/cmd/ai/prompt/api.d.ts.map +1 -1
  19. package/dist/cmd/ai/prompt/api.js +12 -260
  20. package/dist/cmd/ai/prompt/api.js.map +1 -1
  21. package/dist/cmd/ai/prompt/version.d.ts +35 -0
  22. package/dist/cmd/ai/prompt/version.d.ts.map +1 -0
  23. package/dist/cmd/ai/prompt/version.js +55 -0
  24. package/dist/cmd/ai/prompt/version.js.map +1 -0
  25. package/dist/cmd/ai/prompt/web.d.ts +7 -0
  26. package/dist/cmd/ai/prompt/web.d.ts.map +1 -1
  27. package/dist/cmd/ai/prompt/web.js +12 -283
  28. package/dist/cmd/ai/prompt/web.js.map +1 -1
  29. package/dist/cmd/ai/skills/generate.d.ts +3 -0
  30. package/dist/cmd/ai/skills/generate.d.ts.map +1 -0
  31. package/dist/cmd/ai/skills/generate.js +65 -0
  32. package/dist/cmd/ai/skills/generate.js.map +1 -0
  33. package/dist/cmd/ai/skills/generator.d.ts +4 -0
  34. package/dist/cmd/ai/skills/generator.d.ts.map +1 -0
  35. package/dist/cmd/ai/skills/generator.js +402 -0
  36. package/dist/cmd/ai/skills/generator.js.map +1 -0
  37. package/dist/cmd/ai/skills/index.d.ts +4 -0
  38. package/dist/cmd/ai/skills/index.d.ts.map +1 -0
  39. package/dist/cmd/ai/skills/index.js +21 -0
  40. package/dist/cmd/ai/skills/index.js.map +1 -0
  41. package/dist/cmd/auth/signup.d.ts.map +1 -1
  42. package/dist/cmd/auth/signup.js +1 -0
  43. package/dist/cmd/auth/signup.js.map +1 -1
  44. package/dist/cmd/build/ast.d.ts +2 -1
  45. package/dist/cmd/build/ast.d.ts.map +1 -1
  46. package/dist/cmd/build/ast.js +135 -47
  47. package/dist/cmd/build/ast.js.map +1 -1
  48. package/dist/cmd/build/entry-generator.d.ts.map +1 -1
  49. package/dist/cmd/build/entry-generator.js +255 -188
  50. package/dist/cmd/build/entry-generator.js.map +1 -1
  51. package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
  52. package/dist/cmd/build/vite/agent-discovery.js +103 -45
  53. package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
  54. package/dist/cmd/build/vite/bun-dev-server.d.ts +7 -1
  55. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
  56. package/dist/cmd/build/vite/bun-dev-server.js +52 -26
  57. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
  58. package/dist/cmd/build/vite/docs-generator.d.ts +13 -0
  59. package/dist/cmd/build/vite/docs-generator.d.ts.map +1 -0
  60. package/dist/cmd/build/vite/docs-generator.js +81 -0
  61. package/dist/cmd/build/vite/docs-generator.js.map +1 -0
  62. package/dist/cmd/build/vite/index.d.ts +3 -3
  63. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  64. package/dist/cmd/build/vite/index.js +9 -7
  65. package/dist/cmd/build/vite/index.js.map +1 -1
  66. package/dist/cmd/build/vite/lifecycle-generator.d.ts +1 -1
  67. package/dist/cmd/build/vite/lifecycle-generator.d.ts.map +1 -1
  68. package/dist/cmd/build/vite/lifecycle-generator.js +19 -5
  69. package/dist/cmd/build/vite/lifecycle-generator.js.map +1 -1
  70. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
  71. package/dist/cmd/build/vite/metadata-generator.js +203 -7
  72. package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
  73. package/dist/cmd/build/vite/prompt-generator.d.ts +23 -0
  74. package/dist/cmd/build/vite/prompt-generator.d.ts.map +1 -0
  75. package/dist/cmd/build/vite/prompt-generator.js +123 -0
  76. package/dist/cmd/build/vite/prompt-generator.js.map +1 -0
  77. package/dist/cmd/build/vite/registry-generator.d.ts +3 -3
  78. package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
  79. package/dist/cmd/build/vite/registry-generator.js +644 -103
  80. package/dist/cmd/build/vite/registry-generator.js.map +1 -1
  81. package/dist/cmd/build/vite/route-discovery.d.ts +4 -0
  82. package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
  83. package/dist/cmd/build/vite/route-discovery.js.map +1 -1
  84. package/dist/cmd/build/vite/server-bundler.d.ts +4 -0
  85. package/dist/cmd/build/vite/server-bundler.d.ts.map +1 -1
  86. package/dist/cmd/build/vite/server-bundler.js +63 -17
  87. package/dist/cmd/build/vite/server-bundler.js.map +1 -1
  88. package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
  89. package/dist/cmd/build/vite/vite-asset-server-config.js +4 -0
  90. package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
  91. package/dist/cmd/build/vite/vite-builder.d.ts +1 -1
  92. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  93. package/dist/cmd/build/vite/vite-builder.js +118 -96
  94. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  95. package/dist/cmd/build/vite-bundler.js +6 -6
  96. package/dist/cmd/build/vite-bundler.js.map +1 -1
  97. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  98. package/dist/cmd/cloud/deploy.js +89 -32
  99. package/dist/cmd/cloud/deploy.js.map +1 -1
  100. package/dist/cmd/cloud/keyvalue/create-namespace.d.ts.map +1 -1
  101. package/dist/cmd/cloud/keyvalue/create-namespace.js +3 -1
  102. package/dist/cmd/cloud/keyvalue/create-namespace.js.map +1 -1
  103. package/dist/cmd/cloud/keyvalue/delete-namespace.d.ts.map +1 -1
  104. package/dist/cmd/cloud/keyvalue/delete-namespace.js +3 -1
  105. package/dist/cmd/cloud/keyvalue/delete-namespace.js.map +1 -1
  106. package/dist/cmd/cloud/keyvalue/delete.d.ts.map +1 -1
  107. package/dist/cmd/cloud/keyvalue/delete.js +3 -1
  108. package/dist/cmd/cloud/keyvalue/delete.js.map +1 -1
  109. package/dist/cmd/cloud/keyvalue/set.d.ts.map +1 -1
  110. package/dist/cmd/cloud/keyvalue/set.js +4 -2
  111. package/dist/cmd/cloud/keyvalue/set.js.map +1 -1
  112. package/dist/cmd/cloud/stream/get.d.ts.map +1 -1
  113. package/dist/cmd/cloud/stream/get.js +2 -13
  114. package/dist/cmd/cloud/stream/get.js.map +1 -1
  115. package/dist/cmd/cloud/vector/delete-namespace.d.ts +3 -0
  116. package/dist/cmd/cloud/vector/delete-namespace.d.ts.map +1 -0
  117. package/dist/cmd/cloud/vector/delete-namespace.js +77 -0
  118. package/dist/cmd/cloud/vector/delete-namespace.js.map +1 -0
  119. package/dist/cmd/cloud/vector/index.d.ts.map +1 -1
  120. package/dist/cmd/cloud/vector/index.js +21 -4
  121. package/dist/cmd/cloud/vector/index.js.map +1 -1
  122. package/dist/cmd/cloud/vector/list-namespaces.d.ts +3 -0
  123. package/dist/cmd/cloud/vector/list-namespaces.d.ts.map +1 -0
  124. package/dist/cmd/cloud/vector/list-namespaces.js +42 -0
  125. package/dist/cmd/cloud/vector/list-namespaces.js.map +1 -0
  126. package/dist/cmd/cloud/vector/stats.d.ts +3 -0
  127. package/dist/cmd/cloud/vector/stats.d.ts.map +1 -0
  128. package/dist/cmd/cloud/vector/stats.js +142 -0
  129. package/dist/cmd/cloud/vector/stats.js.map +1 -0
  130. package/dist/cmd/cloud/vector/upsert.d.ts +3 -0
  131. package/dist/cmd/cloud/vector/upsert.d.ts.map +1 -0
  132. package/dist/cmd/cloud/vector/upsert.js +192 -0
  133. package/dist/cmd/cloud/vector/upsert.js.map +1 -0
  134. package/dist/cmd/dev/file-watcher.d.ts.map +1 -1
  135. package/dist/cmd/dev/file-watcher.js +94 -33
  136. package/dist/cmd/dev/file-watcher.js.map +1 -1
  137. package/dist/cmd/dev/index.d.ts.map +1 -1
  138. package/dist/cmd/dev/index.js +298 -61
  139. package/dist/cmd/dev/index.js.map +1 -1
  140. package/dist/cmd/dev/skills.d.ts +10 -0
  141. package/dist/cmd/dev/skills.d.ts.map +1 -0
  142. package/dist/cmd/dev/skills.js +57 -0
  143. package/dist/cmd/dev/skills.js.map +1 -0
  144. package/dist/cmd/dev/sync.d.ts.map +1 -1
  145. package/dist/cmd/dev/sync.js +19 -3
  146. package/dist/cmd/dev/sync.js.map +1 -1
  147. package/dist/cmd/index.d.ts.map +1 -1
  148. package/dist/cmd/index.js +1 -0
  149. package/dist/cmd/index.js.map +1 -1
  150. package/dist/cmd/project/create.d.ts.map +1 -1
  151. package/dist/cmd/project/create.js +3 -0
  152. package/dist/cmd/project/create.js.map +1 -1
  153. package/dist/cmd/project/template-flow.d.ts +1 -0
  154. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  155. package/dist/cmd/project/template-flow.js +30 -5
  156. package/dist/cmd/project/template-flow.js.map +1 -1
  157. package/dist/cmd/setup/index.d.ts.map +1 -1
  158. package/dist/cmd/setup/index.js +1 -0
  159. package/dist/cmd/setup/index.js.map +1 -1
  160. package/dist/cmd/upgrade/index.d.ts +15 -0
  161. package/dist/cmd/upgrade/index.d.ts.map +1 -1
  162. package/dist/cmd/upgrade/index.js +59 -4
  163. package/dist/cmd/upgrade/index.js.map +1 -1
  164. package/dist/config.d.ts.map +1 -1
  165. package/dist/config.js +8 -0
  166. package/dist/config.js.map +1 -1
  167. package/dist/domain.d.ts +45 -0
  168. package/dist/domain.d.ts.map +1 -0
  169. package/dist/domain.js +200 -0
  170. package/dist/domain.js.map +1 -0
  171. package/dist/index.d.ts +0 -1
  172. package/dist/index.d.ts.map +1 -1
  173. package/dist/index.js +0 -1
  174. package/dist/index.js.map +1 -1
  175. package/dist/schema-generator.d.ts +2 -0
  176. package/dist/schema-generator.d.ts.map +1 -1
  177. package/dist/schema-generator.js +18 -0
  178. package/dist/schema-generator.js.map +1 -1
  179. package/dist/steps.d.ts +1 -1
  180. package/dist/steps.d.ts.map +1 -1
  181. package/dist/steps.js +16 -5
  182. package/dist/steps.js.map +1 -1
  183. package/dist/tui/prompt.d.ts +1 -2
  184. package/dist/tui/prompt.d.ts.map +1 -1
  185. package/dist/tui/prompt.js +8 -4
  186. package/dist/tui/prompt.js.map +1 -1
  187. package/dist/tui.d.ts +16 -0
  188. package/dist/tui.d.ts.map +1 -1
  189. package/dist/tui.js +23 -2
  190. package/dist/tui.js.map +1 -1
  191. package/dist/types.d.ts +9 -2
  192. package/dist/types.d.ts.map +1 -1
  193. package/dist/types.js +3 -3
  194. package/dist/types.js.map +1 -1
  195. package/package.json +5 -8
  196. package/src/api.ts +1 -1
  197. package/src/cli.ts +47 -12
  198. package/src/cmd/ai/index.ts +6 -1
  199. package/src/cmd/ai/prompt/agent.md +306 -0
  200. package/src/cmd/ai/prompt/agent.ts +12 -322
  201. package/src/cmd/ai/prompt/api.md +360 -0
  202. package/src/cmd/ai/prompt/api.ts +13 -260
  203. package/src/cmd/ai/prompt/version.ts +61 -0
  204. package/src/cmd/ai/prompt/web.md +509 -0
  205. package/src/cmd/ai/prompt/web.ts +12 -282
  206. package/src/cmd/ai/skills/generate.ts +75 -0
  207. package/src/cmd/ai/skills/generator.ts +519 -0
  208. package/src/cmd/ai/skills/index.ts +23 -0
  209. package/src/cmd/auth/signup.ts +1 -0
  210. package/src/cmd/build/ast.ts +161 -48
  211. package/src/cmd/build/entry-generator.ts +258 -187
  212. package/src/cmd/build/vite/agent-discovery.ts +151 -58
  213. package/src/cmd/build/vite/bun-dev-server.ts +57 -27
  214. package/src/cmd/build/vite/docs-generator.ts +87 -0
  215. package/src/cmd/build/vite/index.ts +9 -7
  216. package/src/cmd/build/vite/lifecycle-generator.ts +19 -5
  217. package/src/cmd/build/vite/metadata-generator.ts +251 -7
  218. package/src/cmd/build/vite/prompt-generator.ts +169 -0
  219. package/src/cmd/build/vite/registry-generator.ts +750 -108
  220. package/src/cmd/build/vite/route-discovery.ts +4 -0
  221. package/src/cmd/build/vite/server-bundler.ts +73 -23
  222. package/src/cmd/build/vite/vite-asset-server-config.ts +5 -0
  223. package/src/cmd/build/vite/vite-builder.ts +134 -100
  224. package/src/cmd/build/vite-bundler.ts +6 -6
  225. package/src/cmd/cloud/deploy.ts +114 -36
  226. package/src/cmd/cloud/keyvalue/create-namespace.ts +3 -1
  227. package/src/cmd/cloud/keyvalue/delete-namespace.ts +3 -1
  228. package/src/cmd/cloud/keyvalue/delete.ts +3 -1
  229. package/src/cmd/cloud/keyvalue/set.ts +4 -2
  230. package/src/cmd/cloud/stream/get.ts +2 -9
  231. package/src/cmd/cloud/vector/delete-namespace.ts +89 -0
  232. package/src/cmd/cloud/vector/index.ts +21 -4
  233. package/src/cmd/cloud/vector/list-namespaces.ts +46 -0
  234. package/src/cmd/cloud/vector/stats.ts +160 -0
  235. package/src/cmd/cloud/vector/upsert.ts +216 -0
  236. package/src/cmd/dev/file-watcher.ts +109 -34
  237. package/src/cmd/dev/index.ts +364 -60
  238. package/src/cmd/dev/skills.ts +82 -0
  239. package/src/cmd/dev/sync.ts +41 -6
  240. package/src/cmd/index.ts +1 -0
  241. package/src/cmd/project/create.ts +3 -0
  242. package/src/cmd/project/template-flow.ts +37 -5
  243. package/src/cmd/setup/index.ts +1 -0
  244. package/src/cmd/upgrade/index.ts +68 -4
  245. package/src/config.ts +9 -0
  246. package/src/domain.ts +273 -0
  247. package/src/index.ts +0 -5
  248. package/src/runtime-bootstrap.md +1 -1
  249. package/src/schema-generator.ts +23 -0
  250. package/src/steps.ts +16 -5
  251. package/src/tui/prompt.ts +11 -5
  252. package/src/tui.ts +21 -2
  253. package/src/types/md.d.ts +8 -0
  254. package/src/types.ts +12 -3
  255. package/dist/cmd/cloud/domain.d.ts +0 -17
  256. package/dist/cmd/cloud/domain.d.ts.map +0 -1
  257. package/dist/cmd/cloud/domain.js +0 -79
  258. package/dist/cmd/cloud/domain.js.map +0 -1
  259. package/dist/runtime-bootstrap.d.ts +0 -56
  260. package/dist/runtime-bootstrap.d.ts.map +0 -1
  261. package/dist/runtime-bootstrap.js +0 -95
  262. package/dist/runtime-bootstrap.js.map +0 -1
  263. package/src/cmd/cloud/domain.ts +0 -100
  264. package/src/runtime-bootstrap.ts +0 -131
@@ -0,0 +1,216 @@
1
+ import { z } from 'zod';
2
+ import { createCommand } from '../../../types';
3
+ import * as tui from '../../../tui';
4
+ import { createStorageAdapter } from './util';
5
+ import { getCommand } from '../../../command-prefix';
6
+ import type { VectorUpsertParams } from '@agentuity/core';
7
+
8
+ const VectorUpsertResponseSchema = z.object({
9
+ success: z.boolean().describe('Whether the operation succeeded'),
10
+ namespace: z.string().describe('Namespace name'),
11
+ count: z.number().describe('Number of vectors upserted'),
12
+ results: z
13
+ .array(
14
+ z.object({
15
+ key: z.string().describe('Vector key'),
16
+ id: z.string().describe('Vector ID'),
17
+ })
18
+ )
19
+ .describe('Upsert results with key-to-id mappings'),
20
+ durationMs: z.number().describe('Operation duration in milliseconds'),
21
+ });
22
+
23
+ export const upsertSubcommand = createCommand({
24
+ name: 'upsert',
25
+ aliases: ['put', 'add'],
26
+ description: 'Add or update vectors in the vector storage',
27
+ tags: ['mutating', 'updates-resource', 'slow', 'requires-auth'],
28
+ idempotent: true,
29
+ requires: { auth: true, project: true },
30
+ examples: [
31
+ {
32
+ command: getCommand('vector upsert products doc1 --document "Comfortable office chair"'),
33
+ description: 'Upsert a single vector with document text',
34
+ },
35
+ {
36
+ command: getCommand(
37
+ 'vector upsert products doc1 --document "Chair" --metadata \'{"category":"furniture"}\''
38
+ ),
39
+ description: 'Upsert with metadata',
40
+ },
41
+ {
42
+ command: getCommand('vector upsert embeddings vec1 --embeddings "[0.1, 0.2, 0.3]"'),
43
+ description: 'Upsert with pre-computed embeddings',
44
+ },
45
+ {
46
+ command: getCommand('vector upsert products --file vectors.json'),
47
+ description: 'Bulk upsert from JSON file',
48
+ },
49
+ {
50
+ command: `cat vectors.json | ${getCommand('vector upsert products -')}`,
51
+ description: 'Bulk upsert from stdin',
52
+ },
53
+ ],
54
+ schema: {
55
+ args: z.object({
56
+ namespace: z.string().min(1).describe('the vector storage namespace'),
57
+ key: z
58
+ .string()
59
+ .optional()
60
+ .describe('the key for single vector upsert (not used with --file or stdin)'),
61
+ }),
62
+ options: z.object({
63
+ document: z.string().optional().describe('document text to embed'),
64
+ embeddings: z.string().optional().describe('pre-computed embeddings as JSON array'),
65
+ metadata: z.string().optional().describe('metadata as JSON object'),
66
+ file: z
67
+ .string()
68
+ .optional()
69
+ .describe('path to JSON file containing vectors, or "-" for stdin'),
70
+ }),
71
+ response: VectorUpsertResponseSchema,
72
+ },
73
+ webUrl: (ctx) => `/services/vector/${encodeURIComponent(ctx.args.namespace)}`,
74
+
75
+ async handler(ctx) {
76
+ const { args, opts, options, logger } = ctx;
77
+ const storage = await createStorageAdapter(ctx);
78
+
79
+ let documents: VectorUpsertParams[] = [];
80
+
81
+ // Check if reading from file or stdin
82
+ const fileArg = opts.file || (args.key === '-' ? '-' : undefined);
83
+
84
+ if (fileArg) {
85
+ let content: string;
86
+
87
+ if (fileArg === '-') {
88
+ // Read from stdin
89
+ const chunks: Uint8Array[] = [];
90
+ const reader = Bun.stdin.stream().getReader();
91
+
92
+ while (true) {
93
+ const { done, value } = await reader.read();
94
+ if (done) break;
95
+ chunks.push(value);
96
+ }
97
+
98
+ const decoder = new TextDecoder();
99
+ content = chunks.map((chunk) => decoder.decode(chunk, { stream: true })).join('');
100
+ } else {
101
+ // Read from file
102
+ const file = Bun.file(fileArg);
103
+ if (!(await file.exists())) {
104
+ tui.fatal(`File not found: ${fileArg}`);
105
+ }
106
+ content = await file.text();
107
+ }
108
+
109
+ try {
110
+ const parsed = JSON.parse(content.trim());
111
+ if (Array.isArray(parsed)) {
112
+ documents = parsed as VectorUpsertParams[];
113
+ } else {
114
+ documents = [parsed as VectorUpsertParams];
115
+ }
116
+ } catch {
117
+ tui.fatal('Invalid JSON in input file/stdin');
118
+ }
119
+
120
+ // Validate documents
121
+ for (const doc of documents) {
122
+ if (!doc.key || typeof doc.key !== 'string') {
123
+ tui.fatal('Each document must have a non-empty "key" property');
124
+ }
125
+ if (!doc.document && !('embeddings' in doc)) {
126
+ tui.fatal(
127
+ `Document with key "${doc.key}" must have either "document" or "embeddings" property`
128
+ );
129
+ }
130
+ }
131
+ } else {
132
+ // Single vector upsert via command line arguments
133
+ if (!args.key) {
134
+ tui.fatal('Key is required for single vector upsert. Use --file for bulk upsert.');
135
+ }
136
+
137
+ if (!opts.document && !opts.embeddings) {
138
+ tui.fatal('Either --document or --embeddings is required');
139
+ }
140
+
141
+ if (opts.document && opts.embeddings) {
142
+ tui.fatal('Cannot use both --document and --embeddings');
143
+ }
144
+
145
+ let metadata: Record<string, unknown> | undefined;
146
+ if (opts.metadata) {
147
+ try {
148
+ metadata = JSON.parse(opts.metadata);
149
+ } catch {
150
+ tui.fatal('Invalid JSON in --metadata');
151
+ }
152
+ }
153
+
154
+ if (opts.document) {
155
+ documents = [
156
+ {
157
+ key: args.key,
158
+ document: opts.document,
159
+ metadata,
160
+ },
161
+ ];
162
+ } else if (opts.embeddings) {
163
+ let embeddings: number[];
164
+ try {
165
+ embeddings = JSON.parse(opts.embeddings);
166
+ if (!Array.isArray(embeddings) || !embeddings.every((n) => typeof n === 'number')) {
167
+ throw new Error('Not an array of numbers');
168
+ }
169
+ } catch {
170
+ tui.fatal('Invalid embeddings: must be a JSON array of numbers');
171
+ }
172
+
173
+ documents = [
174
+ {
175
+ key: args.key,
176
+ embeddings,
177
+ metadata,
178
+ },
179
+ ];
180
+ }
181
+ }
182
+
183
+ if (documents.length === 0) {
184
+ tui.fatal('No documents to upsert');
185
+ }
186
+
187
+ const started = Date.now();
188
+
189
+ logger.debug(`Upserting ${documents.length} vector(s) to ${args.namespace}`);
190
+
191
+ const results = await storage.upsert(args.namespace, ...documents);
192
+ const durationMs = Date.now() - started;
193
+
194
+ if (!options.json) {
195
+ tui.success(
196
+ `Upserted ${results.length} vector(s) to ${tui.bold(args.namespace)} (${durationMs}ms)`
197
+ );
198
+
199
+ if (results.length <= 10) {
200
+ for (const result of results) {
201
+ tui.arrow(`${result.key} → ${result.id}`);
202
+ }
203
+ }
204
+ }
205
+
206
+ return {
207
+ success: true,
208
+ namespace: args.namespace,
209
+ count: results.length,
210
+ results,
211
+ durationMs,
212
+ };
213
+ },
214
+ });
215
+
216
+ export default upsertSubcommand;
@@ -5,8 +5,8 @@
5
5
  * Handles both backend (API, agents, lib) and generates restart signals.
6
6
  */
7
7
 
8
- import { watch, type FSWatcher, statSync, readdirSync } from 'node:fs';
9
- import { resolve } from 'node:path';
8
+ import { watch, type FSWatcher, statSync, readdirSync, lstatSync } from 'node:fs';
9
+ import { resolve, relative } from 'node:path';
10
10
  import type { Logger } from '../../types';
11
11
  import { createAgentTemplates, createAPITemplates } from './templates';
12
12
 
@@ -34,13 +34,29 @@ export function createFileWatcher(options: FileWatcherOptions): FileWatcherManag
34
34
  let paused = false;
35
35
  let buildCooldownTimer: NodeJS.Timeout | null = null;
36
36
 
37
- // Watch the entire root directory recursively
38
- // This is simpler and more reliable than watching individual paths
39
- const watchDirs = [rootDir];
37
+ // Directories to ignore - these are NEVER traversed into
38
+ // This prevents EMFILE errors from symlink cycles in node_modules
39
+ const ignoreDirs = new Set([
40
+ '.agentuity',
41
+ '.agents',
42
+ '.claude',
43
+ '.code',
44
+ '.opencode',
45
+ 'node_modules',
46
+ '.git',
47
+ 'dist',
48
+ 'build',
49
+ '.next',
50
+ '.turbo',
51
+ ]);
40
52
 
41
- // Directories to ignore
53
+ // Paths to ignore for file change events (but may still be traversed)
42
54
  const ignorePaths = [
43
55
  '.agentuity',
56
+ '.agents',
57
+ '.claude',
58
+ '.code',
59
+ '.opencode',
44
60
  'node_modules',
45
61
  '.git',
46
62
  'dist',
@@ -142,10 +158,16 @@ export function createFileWatcher(options: FileWatcherOptions): FileWatcherManag
142
158
  const contents = readdirSync(absPath);
143
159
  if (contents.length === 0) {
144
160
  // Check if this is an agent or API directory
145
- if (normalizedPath.startsWith('src/agent/') || normalizedPath.includes('/src/agent/')) {
161
+ if (
162
+ normalizedPath.startsWith('src/agent/') ||
163
+ normalizedPath.includes('/src/agent/')
164
+ ) {
146
165
  logger.debug('Agent directory created: %s', changedFile);
147
166
  createAgentTemplates(absPath);
148
- } else if (normalizedPath.startsWith('src/api/') || normalizedPath.includes('/src/api/')) {
167
+ } else if (
168
+ normalizedPath.startsWith('src/api/') ||
169
+ normalizedPath.includes('/src/api/')
170
+ ) {
149
171
  logger.debug('API directory created: %s', changedFile);
150
172
  createAPITemplates(absPath);
151
173
  }
@@ -162,47 +184,100 @@ export function createFileWatcher(options: FileWatcherOptions): FileWatcherManag
162
184
  }
163
185
 
164
186
  /**
165
- * Start watching files
187
+ * Recursively collect all directories to watch, skipping ignored directories.
188
+ * This prevents EMFILE errors from symlink cycles in node_modules.
166
189
  */
167
- function start() {
168
- logger.debug('Starting file watchers for hot reload...');
190
+ function collectWatchDirs(dir: string, visited: Set<string> = new Set()): string[] {
191
+ const dirs: string[] = [dir];
169
192
 
170
- // Watch root directory (already absolute path)
171
- for (const watchPath of watchDirs) {
172
- try {
173
- logger.trace('Setting up watcher for: %s', watchPath);
193
+ try {
194
+ // Use lstat to check for symlinks - get the real path to detect cycles
195
+ const stat = lstatSync(dir);
174
196
 
175
- const watcher = watch(watchPath, { recursive: true }, (eventType, changedFile) => {
176
- handleFileChange(eventType, changedFile, watchPath);
177
- });
197
+ // Skip symlinks to prevent following circular symlinks
198
+ if (stat.isSymbolicLink()) {
199
+ logger.trace('Skipping symlink: %s', dir);
200
+ return [];
201
+ }
178
202
 
179
- watchers.push(watcher);
180
- logger.trace('Watcher started for: %s', watchPath);
181
- } catch (error) {
182
- logger.warn('Failed to start watcher for %s: %s', watchPath, error);
203
+ // Track visited inodes to detect cycles
204
+ const key = `${stat.dev}:${stat.ino}`;
205
+ if (visited.has(key)) {
206
+ logger.trace('Skipping already visited directory (cycle detected): %s', dir);
207
+ return [];
208
+ }
209
+ visited.add(key);
210
+
211
+ const entries = readdirSync(dir, { withFileTypes: true });
212
+
213
+ for (const entry of entries) {
214
+ if (!entry.isDirectory()) continue;
215
+
216
+ const name = entry.name;
217
+
218
+ // Skip ignored directories entirely - this is the key fix
219
+ if (ignoreDirs.has(name)) {
220
+ logger.trace('Skipping ignored directory: %s', resolve(dir, name));
221
+ continue;
222
+ }
223
+
224
+ // Skip hidden directories (except specific ones like .env folders)
225
+ if (name.startsWith('.')) {
226
+ logger.trace('Skipping hidden directory: %s', resolve(dir, name));
227
+ continue;
228
+ }
229
+
230
+ const fullPath = resolve(dir, name);
231
+ dirs.push(...collectWatchDirs(fullPath, visited));
183
232
  }
233
+ } catch (error) {
234
+ logger.trace('Error reading directory %s: %s', dir, error);
184
235
  }
185
236
 
186
- // Watch additional paths if provided
237
+ return dirs;
238
+ }
239
+
240
+ /**
241
+ * Start watching files
242
+ */
243
+ function start() {
244
+ logger.debug('Starting file watchers for hot reload...');
245
+
246
+ // Collect all directories to watch, excluding node_modules and other ignored dirs
247
+ const allDirs = collectWatchDirs(rootDir);
248
+
249
+ // Add additional paths
187
250
  if (additionalPaths && additionalPaths.length > 0) {
188
251
  for (const additionalPath of additionalPaths) {
189
252
  const fullPath = resolve(rootDir, additionalPath);
190
- try {
191
- logger.trace('Setting up watcher for additional path: %s', fullPath);
253
+ allDirs.push(...collectWatchDirs(fullPath));
254
+ }
255
+ }
192
256
 
193
- const watcher = watch(fullPath, { recursive: true }, (eventType, changedFile) => {
194
- handleFileChange(eventType, changedFile, fullPath);
195
- });
257
+ // De-duplicate directories
258
+ const uniqueDirs = [...new Set(allDirs)];
196
259
 
197
- watchers.push(watcher);
198
- logger.trace('Watcher started for additional path: %s', fullPath);
199
- } catch (error) {
200
- logger.warn('Failed to start watcher for %s: %s', fullPath, error);
201
- }
260
+ logger.debug('Collected %d directories to watch', uniqueDirs.length);
261
+
262
+ // Watch each directory non-recursively
263
+ for (const watchPath of uniqueDirs) {
264
+ try {
265
+ // Use non-recursive watch to avoid traversing into node_modules
266
+ const watcher = watch(watchPath, { recursive: false }, (eventType, changedFile) => {
267
+ // Construct relative path from rootDir for consistent handling
268
+ const relPath = changedFile
269
+ ? relative(rootDir, resolve(watchPath, changedFile))
270
+ : relative(rootDir, watchPath);
271
+ handleFileChange(eventType, relPath || changedFile, rootDir);
272
+ });
273
+
274
+ watchers.push(watcher);
275
+ } catch (error) {
276
+ logger.trace('Failed to start watcher for %s: %s', watchPath, error);
202
277
  }
203
278
  }
204
279
 
205
- logger.debug('File watchers started (%d paths)', watchers.length);
280
+ logger.debug('File watchers started (%d directories)', watchers.length);
206
281
  }
207
282
 
208
283
  /**