byterover-cli 3.2.0 → 3.4.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 (127) hide show
  1. package/.env.production +0 -4
  2. package/dist/agent/core/domain/swarm/types.d.ts +132 -0
  3. package/dist/agent/core/domain/swarm/types.js +128 -0
  4. package/dist/agent/core/domain/tools/constants.d.ts +2 -0
  5. package/dist/agent/core/domain/tools/constants.js +2 -0
  6. package/dist/agent/core/interfaces/i-memory-provider.d.ts +45 -0
  7. package/dist/agent/core/interfaces/i-memory-provider.js +1 -0
  8. package/dist/agent/core/interfaces/i-sandbox-service.d.ts +8 -0
  9. package/dist/agent/core/interfaces/i-swarm-coordinator.d.ts +127 -0
  10. package/dist/agent/core/interfaces/i-swarm-coordinator.js +1 -0
  11. package/dist/agent/infra/agent/service-initializer.js +48 -0
  12. package/dist/agent/infra/map/map-shared.d.ts +2 -2
  13. package/dist/agent/infra/sandbox/sandbox-service.d.ts +10 -0
  14. package/dist/agent/infra/sandbox/sandbox-service.js +13 -0
  15. package/dist/agent/infra/sandbox/tools-sdk.d.ts +25 -0
  16. package/dist/agent/infra/sandbox/tools-sdk.js +24 -1
  17. package/dist/agent/infra/swarm/adapters/byterover-adapter.d.ts +39 -0
  18. package/dist/agent/infra/swarm/adapters/byterover-adapter.js +62 -0
  19. package/dist/agent/infra/swarm/adapters/gbrain-adapter.d.ts +63 -0
  20. package/dist/agent/infra/swarm/adapters/gbrain-adapter.js +209 -0
  21. package/dist/agent/infra/swarm/adapters/local-markdown-adapter.d.ts +41 -0
  22. package/dist/agent/infra/swarm/adapters/local-markdown-adapter.js +256 -0
  23. package/dist/agent/infra/swarm/adapters/memory-wiki-adapter.d.ts +29 -0
  24. package/dist/agent/infra/swarm/adapters/memory-wiki-adapter.js +244 -0
  25. package/dist/agent/infra/swarm/adapters/obsidian-adapter.d.ts +37 -0
  26. package/dist/agent/infra/swarm/adapters/obsidian-adapter.js +201 -0
  27. package/dist/agent/infra/swarm/cli/query-renderer.d.ts +15 -0
  28. package/dist/agent/infra/swarm/cli/query-renderer.js +126 -0
  29. package/dist/agent/infra/swarm/config/swarm-config-loader.d.ts +14 -0
  30. package/dist/agent/infra/swarm/config/swarm-config-loader.js +82 -0
  31. package/dist/agent/infra/swarm/config/swarm-config-schema.d.ts +667 -0
  32. package/dist/agent/infra/swarm/config/swarm-config-schema.js +305 -0
  33. package/dist/agent/infra/swarm/provider-factory.d.ts +21 -0
  34. package/dist/agent/infra/swarm/provider-factory.js +67 -0
  35. package/dist/agent/infra/swarm/search-precision.d.ts +95 -0
  36. package/dist/agent/infra/swarm/search-precision.js +141 -0
  37. package/dist/agent/infra/swarm/swarm-coordinator.d.ts +59 -0
  38. package/dist/agent/infra/swarm/swarm-coordinator.js +436 -0
  39. package/dist/agent/infra/swarm/swarm-graph.d.ts +63 -0
  40. package/dist/agent/infra/swarm/swarm-graph.js +167 -0
  41. package/dist/agent/infra/swarm/swarm-merger.d.ts +29 -0
  42. package/dist/agent/infra/swarm/swarm-merger.js +66 -0
  43. package/dist/agent/infra/swarm/swarm-router.d.ts +12 -0
  44. package/dist/agent/infra/swarm/swarm-router.js +40 -0
  45. package/dist/agent/infra/swarm/swarm-write-router.d.ts +23 -0
  46. package/dist/agent/infra/swarm/swarm-write-router.js +45 -0
  47. package/dist/agent/infra/swarm/validation/config-validator.d.ts +16 -0
  48. package/dist/agent/infra/swarm/validation/config-validator.js +402 -0
  49. package/dist/agent/infra/swarm/validation/memory-swarm-validation-error.d.ts +33 -0
  50. package/dist/agent/infra/swarm/validation/memory-swarm-validation-error.js +27 -0
  51. package/dist/agent/infra/swarm/wizard/config-scaffolder.d.ts +36 -0
  52. package/dist/agent/infra/swarm/wizard/config-scaffolder.js +96 -0
  53. package/dist/agent/infra/swarm/wizard/provider-detector.d.ts +54 -0
  54. package/dist/agent/infra/swarm/wizard/provider-detector.js +153 -0
  55. package/dist/agent/infra/swarm/wizard/swarm-wizard.d.ts +61 -0
  56. package/dist/agent/infra/swarm/wizard/swarm-wizard.js +187 -0
  57. package/dist/agent/infra/system-prompt/contributors/index.d.ts +1 -0
  58. package/dist/agent/infra/system-prompt/contributors/index.js +1 -0
  59. package/dist/agent/infra/system-prompt/contributors/swarm-state-contributor.d.ts +15 -0
  60. package/dist/agent/infra/system-prompt/contributors/swarm-state-contributor.js +65 -0
  61. package/dist/agent/infra/tools/implementations/curate-tool.d.ts +14 -14
  62. package/dist/agent/infra/tools/implementations/curate-tool.js +2 -0
  63. package/dist/agent/infra/tools/implementations/search-knowledge-service.js +12 -2
  64. package/dist/agent/infra/tools/implementations/swarm-query-tool.d.ts +9 -0
  65. package/dist/agent/infra/tools/implementations/swarm-query-tool.js +44 -0
  66. package/dist/agent/infra/tools/implementations/swarm-store-tool.d.ts +9 -0
  67. package/dist/agent/infra/tools/implementations/swarm-store-tool.js +43 -0
  68. package/dist/agent/infra/tools/tool-provider.js +1 -0
  69. package/dist/agent/infra/tools/tool-registry.d.ts +3 -0
  70. package/dist/agent/infra/tools/tool-registry.js +25 -1
  71. package/dist/agent/resources/tools/code_exec.txt +2 -0
  72. package/dist/agent/resources/tools/swarm_query.txt +38 -0
  73. package/dist/agent/resources/tools/swarm_store.txt +35 -0
  74. package/dist/oclif/commands/curate/index.d.ts +1 -0
  75. package/dist/oclif/commands/curate/index.js +15 -1
  76. package/dist/oclif/commands/query.d.ts +1 -0
  77. package/dist/oclif/commands/query.js +17 -3
  78. package/dist/oclif/commands/search.d.ts +20 -0
  79. package/dist/oclif/commands/search.js +186 -0
  80. package/dist/oclif/commands/status.js +4 -0
  81. package/dist/oclif/commands/swarm/curate.d.ts +13 -0
  82. package/dist/oclif/commands/swarm/curate.js +81 -0
  83. package/dist/oclif/commands/swarm/onboard.d.ts +6 -0
  84. package/dist/oclif/commands/swarm/onboard.js +233 -0
  85. package/dist/oclif/commands/swarm/query.d.ts +14 -0
  86. package/dist/oclif/commands/swarm/query.js +84 -0
  87. package/dist/oclif/commands/swarm/status.d.ts +41 -0
  88. package/dist/oclif/commands/swarm/status.js +278 -0
  89. package/dist/oclif/lib/daemon-client.js +0 -1
  90. package/dist/oclif/lib/search-format.d.ts +10 -0
  91. package/dist/oclif/lib/search-format.js +25 -0
  92. package/dist/oclif/lib/task-client.d.ts +6 -0
  93. package/dist/oclif/lib/task-client.js +10 -3
  94. package/dist/server/constants.d.ts +3 -2
  95. package/dist/server/constants.js +10 -7
  96. package/dist/server/core/domain/errors/task-error.d.ts +2 -2
  97. package/dist/server/core/domain/errors/task-error.js +5 -4
  98. package/dist/server/core/domain/source/source-schema.d.ts +6 -6
  99. package/dist/server/core/domain/transport/schemas.d.ts +14 -14
  100. package/dist/server/core/domain/transport/schemas.js +3 -3
  101. package/dist/server/core/interfaces/executor/i-search-executor.d.ts +34 -0
  102. package/dist/server/core/interfaces/executor/i-search-executor.js +1 -0
  103. package/dist/server/core/interfaces/executor/index.d.ts +1 -0
  104. package/dist/server/core/interfaces/executor/index.js +1 -0
  105. package/dist/server/infra/daemon/agent-process.js +20 -7
  106. package/dist/server/infra/executor/search-executor.d.ts +17 -0
  107. package/dist/server/infra/executor/search-executor.js +30 -0
  108. package/dist/server/infra/http/provider-model-fetchers.js +1 -0
  109. package/dist/server/infra/process/feature-handlers.js +13 -0
  110. package/dist/server/infra/project/project-registry.js +13 -1
  111. package/dist/server/infra/transport/handlers/locations-handler.d.ts +2 -0
  112. package/dist/server/infra/transport/handlers/locations-handler.js +16 -1
  113. package/dist/server/infra/transport/handlers/pull-handler.js +3 -3
  114. package/dist/server/infra/transport/handlers/push-handler.js +3 -3
  115. package/dist/server/infra/transport/handlers/status-handler.js +25 -18
  116. package/dist/server/infra/transport/handlers/vc-handler.d.ts +0 -4
  117. package/dist/server/infra/transport/handlers/vc-handler.js +5 -16
  118. package/dist/server/templates/skill/SKILL.md +188 -5
  119. package/dist/server/utils/gitignore.d.ts +1 -0
  120. package/dist/server/utils/gitignore.js +36 -4
  121. package/dist/shared/transport/search-content.d.ts +28 -0
  122. package/dist/shared/transport/search-content.js +38 -0
  123. package/dist/shared/transport/types/dto.d.ts +1 -1
  124. package/dist/tui/features/status/utils/format-status.js +5 -0
  125. package/dist/tui/utils/error-messages.js +2 -2
  126. package/oclif.manifest.json +581 -317
  127. package/package.json +2 -2
@@ -14,7 +14,7 @@ type OperationType = z.infer<typeof OperationType>;
14
14
  export declare const CurateInputSchema: z.ZodObject<{
15
15
  basePath: z.ZodDefault<z.ZodString>;
16
16
  operations: z.ZodArray<z.ZodObject<{
17
- confidence: z.ZodEnum<["high", "low"]>;
17
+ confidence: z.ZodDefault<z.ZodEnum<["high", "low"]>>;
18
18
  content: z.ZodOptional<z.ZodObject<{
19
19
  facts: z.ZodOptional<z.ZodArray<z.ZodObject<{
20
20
  category: z.ZodOptional<z.ZodEnum<["personal", "project", "preference", "convention", "team", "environment", "other"]>>;
@@ -24,12 +24,12 @@ export declare const CurateInputSchema: z.ZodObject<{
24
24
  }, "strip", z.ZodTypeAny, {
25
25
  statement: string;
26
26
  value?: string | undefined;
27
- category?: "environment" | "convention" | "other" | "personal" | "preference" | "project" | "team" | undefined;
27
+ category?: "personal" | "environment" | "convention" | "other" | "preference" | "project" | "team" | undefined;
28
28
  subject?: string | undefined;
29
29
  }, {
30
30
  statement: string;
31
31
  value?: string | undefined;
32
- category?: "environment" | "convention" | "other" | "personal" | "preference" | "project" | "team" | undefined;
32
+ category?: "personal" | "environment" | "convention" | "other" | "preference" | "project" | "team" | undefined;
33
33
  subject?: string | undefined;
34
34
  }>, "many">>;
35
35
  keywords: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
@@ -130,7 +130,7 @@ export declare const CurateInputSchema: z.ZodObject<{
130
130
  facts?: {
131
131
  statement: string;
132
132
  value?: string | undefined;
133
- category?: "environment" | "convention" | "other" | "personal" | "preference" | "project" | "team" | undefined;
133
+ category?: "personal" | "environment" | "convention" | "other" | "preference" | "project" | "team" | undefined;
134
134
  subject?: string | undefined;
135
135
  }[] | undefined;
136
136
  narrative?: {
@@ -166,7 +166,7 @@ export declare const CurateInputSchema: z.ZodObject<{
166
166
  facts?: {
167
167
  statement: string;
168
168
  value?: string | undefined;
169
- category?: "environment" | "convention" | "other" | "personal" | "preference" | "project" | "team" | undefined;
169
+ category?: "personal" | "environment" | "convention" | "other" | "preference" | "project" | "team" | undefined;
170
170
  subject?: string | undefined;
171
171
  }[] | undefined;
172
172
  narrative?: {
@@ -227,7 +227,7 @@ export declare const CurateInputSchema: z.ZodObject<{
227
227
  usage?: string | undefined;
228
228
  ownership?: string | undefined;
229
229
  }>>;
230
- impact: z.ZodEnum<["high", "low"]>;
230
+ impact: z.ZodDefault<z.ZodEnum<["high", "low"]>>;
231
231
  mergeTarget: z.ZodOptional<z.ZodString>;
232
232
  mergeTargetTitle: z.ZodOptional<z.ZodString>;
233
233
  path: z.ZodString;
@@ -273,7 +273,7 @@ export declare const CurateInputSchema: z.ZodObject<{
273
273
  facts?: {
274
274
  statement: string;
275
275
  value?: string | undefined;
276
- category?: "environment" | "convention" | "other" | "personal" | "preference" | "project" | "team" | undefined;
276
+ category?: "personal" | "environment" | "convention" | "other" | "preference" | "project" | "team" | undefined;
277
277
  subject?: string | undefined;
278
278
  }[] | undefined;
279
279
  narrative?: {
@@ -327,8 +327,6 @@ export declare const CurateInputSchema: z.ZodObject<{
327
327
  path: string;
328
328
  type: "ADD" | "DELETE" | "MERGE" | "UPDATE" | "UPSERT";
329
329
  reason: string;
330
- confidence: "high" | "low";
331
- impact: "high" | "low";
332
330
  summary?: string | undefined;
333
331
  title?: string | undefined;
334
332
  content?: {
@@ -338,7 +336,7 @@ export declare const CurateInputSchema: z.ZodObject<{
338
336
  facts?: {
339
337
  statement: string;
340
338
  value?: string | undefined;
341
- category?: "environment" | "convention" | "other" | "personal" | "preference" | "project" | "team" | undefined;
339
+ category?: "personal" | "environment" | "convention" | "other" | "preference" | "project" | "team" | undefined;
342
340
  subject?: string | undefined;
343
341
  }[] | undefined;
344
342
  narrative?: {
@@ -368,6 +366,7 @@ export declare const CurateInputSchema: z.ZodObject<{
368
366
  } | undefined;
369
367
  snippets?: string[] | undefined;
370
368
  } | undefined;
369
+ confidence?: "high" | "low" | undefined;
371
370
  domainContext?: {
372
371
  scope: {
373
372
  included: string[];
@@ -377,6 +376,7 @@ export declare const CurateInputSchema: z.ZodObject<{
377
376
  usage?: string | undefined;
378
377
  ownership?: string | undefined;
379
378
  } | undefined;
379
+ impact?: "high" | "low" | undefined;
380
380
  mergeTarget?: string | undefined;
381
381
  mergeTargetTitle?: string | undefined;
382
382
  subtopicContext?: {
@@ -406,7 +406,7 @@ export declare const CurateInputSchema: z.ZodObject<{
406
406
  facts?: {
407
407
  statement: string;
408
408
  value?: string | undefined;
409
- category?: "environment" | "convention" | "other" | "personal" | "preference" | "project" | "team" | undefined;
409
+ category?: "personal" | "environment" | "convention" | "other" | "preference" | "project" | "team" | undefined;
410
410
  subject?: string | undefined;
411
411
  }[] | undefined;
412
412
  narrative?: {
@@ -462,8 +462,6 @@ export declare const CurateInputSchema: z.ZodObject<{
462
462
  path: string;
463
463
  type: "ADD" | "DELETE" | "MERGE" | "UPDATE" | "UPSERT";
464
464
  reason: string;
465
- confidence: "high" | "low";
466
- impact: "high" | "low";
467
465
  summary?: string | undefined;
468
466
  title?: string | undefined;
469
467
  content?: {
@@ -473,7 +471,7 @@ export declare const CurateInputSchema: z.ZodObject<{
473
471
  facts?: {
474
472
  statement: string;
475
473
  value?: string | undefined;
476
- category?: "environment" | "convention" | "other" | "personal" | "preference" | "project" | "team" | undefined;
474
+ category?: "personal" | "environment" | "convention" | "other" | "preference" | "project" | "team" | undefined;
477
475
  subject?: string | undefined;
478
476
  }[] | undefined;
479
477
  narrative?: {
@@ -503,6 +501,7 @@ export declare const CurateInputSchema: z.ZodObject<{
503
501
  } | undefined;
504
502
  snippets?: string[] | undefined;
505
503
  } | undefined;
504
+ confidence?: "high" | "low" | undefined;
506
505
  domainContext?: {
507
506
  scope: {
508
507
  included: string[];
@@ -512,6 +511,7 @@ export declare const CurateInputSchema: z.ZodObject<{
512
511
  usage?: string | undefined;
513
512
  ownership?: string | undefined;
514
513
  } | undefined;
514
+ impact?: "high" | "low" | undefined;
515
515
  mergeTarget?: string | undefined;
516
516
  mergeTargetTitle?: string | undefined;
517
517
  subtopicContext?: {
@@ -162,11 +162,13 @@ const SubtopicContextSchema = z.object({
162
162
  const OperationSchema = z.object({
163
163
  confidence: z
164
164
  .enum(['high', 'low'])
165
+ .default('low')
165
166
  .describe('Your confidence in the accuracy and completeness of this operation. Use "high" when you have direct evidence from the source material; use "low" when the information is inferred, uncertain, or incomplete.'),
166
167
  content: ContentSchema.optional().describe('Content for ADD/UPDATE operations'),
167
168
  domainContext: DomainContextSchema.optional().describe('Domain-level context for new domains. When creating content in a NEW domain, provide this to auto-generate domain/context.md with purpose, scope, ownership, and usage. Only needed when the domain does not exist yet.'),
168
169
  impact: z
169
170
  .enum(['high', 'low'])
171
+ .default('high')
170
172
  .describe('Estimated scope of impact of this knowledge change. "high": Changes that alter core decisions, strategies, tools, or established approaches. Any change that contradicts or reverses previously curated knowledge. Updates to existing knowledge that change its core substance. Deletions are always high impact. "low": New additions to previously undocumented topics, minor corrections, supplementary details like examples and clarifications, or updates that extend existing knowledge without changing its core substance.'),
171
173
  mergeTarget: z.string().optional().describe('Target path for MERGE operation'),
172
174
  mergeTargetTitle: z.string().optional().describe('Title of the target file for MERGE operation'),
@@ -649,6 +649,11 @@ export class SearchKnowledgeService {
649
649
  */
650
650
  async search(query, options) {
651
651
  const limit = options?.limit ?? 10;
652
+ // Normalize scope: strip trailing slashes so "project/" and "project" both work.
653
+ // The symbol tree stores paths without a trailing slash, and getSubtreeDocumentIds
654
+ // does an exact node lookup, so "project/" would otherwise miss the subtree entirely
655
+ // and silently fall back to global search via the block at the end of this method.
656
+ const normalizedScope = options?.scope?.trim().replace(/\/+$/, '') || undefined;
652
657
  const resolvedBaseDirectory = await realpath(this.baseDirectory).catch(() => this.baseDirectory);
653
658
  const contextTreePath = join(resolvedBaseDirectory, BRV_DIR, CONTEXT_TREE_DIR);
654
659
  // Acquire index with parallel-safe locking; flush pending access hits before any rebuild
@@ -667,7 +672,7 @@ export class SearchKnowledgeService {
667
672
  }
668
673
  // Overview mode: return tree structure instead of search results
669
674
  if (options?.overview) {
670
- return this.buildOverviewResult(symbolTree, referenceIndex, options.scope, options.overviewDepth);
675
+ return this.buildOverviewResult(symbolTree, referenceIndex, normalizedScope, options.overviewDepth);
671
676
  }
672
677
  // Symbolic path resolution: try path-based query first
673
678
  if (isPathLikeQuery(query, symbolTree)) {
@@ -678,7 +683,12 @@ export class SearchKnowledgeService {
678
683
  }
679
684
  // Parse query for potential scope prefix (e.g. "auth jwt refresh" → scope=auth, text="jwt refresh")
680
685
  const parsed = parseSymbolicQuery(query, symbolTree);
681
- const effectiveScope = options?.scope ?? parsed.scopePath;
686
+ // Strip trailing slashes from scope so "auth/" resolves to the same
687
+ // symbol-tree node as "auth". The symbol tree stores paths without
688
+ // trailing slashes; a mismatch causes getSubtreeDocumentIds() to
689
+ // return empty → unintended fallback to global search.
690
+ const rawScope = options?.scope?.trim().replace(/\/+$/, '');
691
+ const effectiveScope = (rawScope !== undefined && rawScope !== '' ? rawScope : undefined) ?? parsed.scopePath;
682
692
  const effectiveQuery = parsed.scopePath ? parsed.textQuery : query;
683
693
  // Run text-based MiniSearch (existing pipeline), optionally scoped to a subtree
684
694
  const textResult = this.runTextSearch(effectiveQuery || query, documentMap, index, limit, effectiveScope, pathToDocumentId, symbolTree, referenceIndex, summaryMap, symbolPathDocMap, options);
@@ -0,0 +1,9 @@
1
+ import type { Tool } from '../../../core/domain/tools/types.js';
2
+ import type { ISwarmCoordinator } from '../../../core/interfaces/i-swarm-coordinator.js';
3
+ /**
4
+ * Creates the swarm_query tool for the agent to search across memory providers.
5
+ *
6
+ * @param coordinator - The swarm coordinator instance
7
+ * @returns Configured swarm query tool
8
+ */
9
+ export declare function createSwarmQueryTool(coordinator: ISwarmCoordinator): Tool;
@@ -0,0 +1,44 @@
1
+ import { z } from 'zod';
2
+ import { ToolName } from '../../../core/domain/tools/constants.js';
3
+ const SwarmQueryInputSchema = z
4
+ .object({
5
+ maxResults: z
6
+ .number()
7
+ .int()
8
+ .positive()
9
+ .optional()
10
+ .describe('Maximum number of results to return (default: 10)'),
11
+ query: z
12
+ .string()
13
+ .min(1)
14
+ .describe('Natural language query to search across all active memory providers'),
15
+ scope: z
16
+ .string()
17
+ .optional()
18
+ .describe('Optional scope to restrict search (e.g., "auth", "architecture")'),
19
+ })
20
+ .strict();
21
+ /**
22
+ * Creates the swarm_query tool for the agent to search across memory providers.
23
+ *
24
+ * @param coordinator - The swarm coordinator instance
25
+ * @returns Configured swarm query tool
26
+ */
27
+ export function createSwarmQueryTool(coordinator) {
28
+ return {
29
+ description: 'Search across all active memory providers in the memory swarm. ' +
30
+ 'Routes the query to relevant providers based on query type classification, ' +
31
+ 'executes in parallel, and returns fused, ranked results. ' +
32
+ 'Use this when you need information that may be spread across multiple knowledge sources.',
33
+ async execute(input, _context) {
34
+ const parsed = SwarmQueryInputSchema.parse(input);
35
+ return coordinator.execute({
36
+ maxResults: parsed.maxResults,
37
+ query: parsed.query,
38
+ scope: parsed.scope,
39
+ });
40
+ },
41
+ id: ToolName.SWARM_QUERY,
42
+ inputSchema: SwarmQueryInputSchema,
43
+ };
44
+ }
@@ -0,0 +1,9 @@
1
+ import type { Tool } from '../../../core/domain/tools/types.js';
2
+ import type { ISwarmCoordinator } from '../../../core/interfaces/i-swarm-coordinator.js';
3
+ /**
4
+ * Creates the swarm_store tool for the agent to write knowledge to swarm providers.
5
+ *
6
+ * @param coordinator - The swarm coordinator instance
7
+ * @returns Configured swarm store tool
8
+ */
9
+ export declare function createSwarmStoreTool(coordinator: ISwarmCoordinator): Tool;
@@ -0,0 +1,43 @@
1
+ import { z } from 'zod';
2
+ import { ToolName } from '../../../core/domain/tools/constants.js';
3
+ const SwarmStoreInputSchema = z
4
+ .object({
5
+ content: z
6
+ .string()
7
+ .min(1)
8
+ .describe('Knowledge content to store in a swarm provider'),
9
+ contentType: z
10
+ .enum(['entity', 'general', 'note'])
11
+ .optional()
12
+ .describe('Content type hint for routing: entity (people/companies), note (drafts/meetings), general'),
13
+ provider: z
14
+ .string()
15
+ .optional()
16
+ .describe('Explicit target provider ID (e.g., gbrain, local-markdown:notes). Auto-routed if omitted.'),
17
+ })
18
+ .strict();
19
+ /**
20
+ * Creates the swarm_store tool for the agent to write knowledge to swarm providers.
21
+ *
22
+ * @param coordinator - The swarm coordinator instance
23
+ * @returns Configured swarm store tool
24
+ */
25
+ export function createSwarmStoreTool(coordinator) {
26
+ return {
27
+ description: 'Store knowledge in a swarm provider (GBrain, local markdown). ' +
28
+ 'Routes by content type: entities (people, companies) go to GBrain, ' +
29
+ 'notes and drafts go to local markdown. Use the provider parameter to ' +
30
+ 'target a specific provider. Use the curate tool for project-specific ' +
31
+ 'knowledge that belongs in the context tree.',
32
+ async execute(input, _context) {
33
+ const parsed = SwarmStoreInputSchema.parse(input);
34
+ return coordinator.store({
35
+ content: parsed.content,
36
+ contentType: parsed.contentType,
37
+ provider: parsed.provider,
38
+ });
39
+ },
40
+ id: ToolName.SWARM_STORE,
41
+ inputSchema: SwarmStoreInputSchema,
42
+ };
43
+ }
@@ -29,6 +29,7 @@ export class ToolProvider {
29
29
  memoryManager: 0,
30
30
  processService: 0,
31
31
  sandboxService: 0,
32
+ swarmCoordinator: 0,
32
33
  todoStorage: 0,
33
34
  tokenizer: 0,
34
35
  }));
@@ -7,6 +7,7 @@ import type { IFileSystem } from '../../core/interfaces/i-file-system.js';
7
7
  import type { ILogger } from '../../core/interfaces/i-logger.js';
8
8
  import type { IProcessService } from '../../core/interfaces/i-process-service.js';
9
9
  import type { ISandboxService } from '../../core/interfaces/i-sandbox-service.js';
10
+ import type { ISwarmCoordinator } from '../../core/interfaces/i-swarm-coordinator.js';
10
11
  import type { ITodoStorage } from '../../core/interfaces/i-todo-storage.js';
11
12
  import type { ITokenizer } from '../../core/interfaces/i-tokenizer.js';
12
13
  import type { AbstractGenerationQueue } from '../map/abstract-queue.js';
@@ -43,6 +44,8 @@ export interface ToolServices {
43
44
  processService?: IProcessService;
44
45
  /** Sandbox service for code execution */
45
46
  sandboxService?: ISandboxService;
47
+ /** Swarm coordinator for cross-provider memory queries */
48
+ swarmCoordinator?: ISwarmCoordinator;
46
49
  /** Todo storage service for session-based todo persistence */
47
50
  todoStorage?: ITodoStorage;
48
51
  /** Tokenizer for ContextTreeStore token counting */
@@ -12,6 +12,8 @@ import { createLlmMapTool } from './implementations/llm-map-tool.js';
12
12
  import { createReadFileTool } from './implementations/read-file-tool.js';
13
13
  import { createSearchKnowledgeService } from './implementations/search-knowledge-service.js';
14
14
  import { createSearchKnowledgeTool } from './implementations/search-knowledge-tool.js';
15
+ import { createSwarmQueryTool } from './implementations/swarm-query-tool.js';
16
+ import { createSwarmStoreTool } from './implementations/swarm-store-tool.js';
15
17
  import { createWriteFileTool } from './implementations/write-file-tool.js';
16
18
  import { ToolMarker } from './tool-markers.js';
17
19
  /**
@@ -55,7 +57,7 @@ export const TOOL_REGISTRY = {
55
57
  },
56
58
  [ToolName.CODE_EXEC]: {
57
59
  descriptionFile: 'code_exec',
58
- factory({ abstractQueue, environmentContext, fileSystemService, sandboxService }) {
60
+ factory({ abstractQueue, environmentContext, fileSystemService, sandboxService, swarmCoordinator }) {
59
61
  const sandbox = getRequiredService(sandboxService, 'sandboxService');
60
62
  // Inject file system service into sandbox for Tools SDK
61
63
  if (fileSystemService && sandbox.setFileSystem) {
@@ -75,6 +77,10 @@ export const TOOL_REGISTRY = {
75
77
  if (environmentContext && sandbox.setEnvironmentContext) {
76
78
  sandbox.setEnvironmentContext(environmentContext);
77
79
  }
80
+ // Inject swarm coordinator into sandbox for tools.swarmQuery/swarmStore
81
+ if (swarmCoordinator && sandbox.setSwarmCoordinator) {
82
+ sandbox.setSwarmCoordinator(swarmCoordinator);
83
+ }
78
84
  return createCodeExecTool(sandbox);
79
85
  },
80
86
  markers: [ToolMarker.Execution],
@@ -142,6 +148,24 @@ export const TOOL_REGISTRY = {
142
148
  markers: [ToolMarker.ContextBuilding, ToolMarker.Discovery],
143
149
  requiredServices: ['fileSystemService'],
144
150
  },
151
+ [ToolName.SWARM_QUERY]: {
152
+ descriptionFile: 'swarm_query',
153
+ factory(services) {
154
+ const coordinator = getRequiredService(services.swarmCoordinator, 'swarmCoordinator');
155
+ return createSwarmQueryTool(coordinator);
156
+ },
157
+ markers: [ToolMarker.Discovery],
158
+ requiredServices: ['swarmCoordinator'],
159
+ },
160
+ [ToolName.SWARM_STORE]: {
161
+ descriptionFile: 'swarm_store',
162
+ factory(services) {
163
+ const coordinator = getRequiredService(services.swarmCoordinator, 'swarmCoordinator');
164
+ return createSwarmStoreTool(coordinator);
165
+ },
166
+ markers: [ToolMarker.Modification],
167
+ requiredServices: ['swarmCoordinator'],
168
+ },
145
169
  [ToolName.WRITE_FILE]: {
146
170
  descriptionFile: 'write_file',
147
171
  factory: (services) => createWriteFileTool(getRequiredService(services.fileSystemService, 'fileSystemService'), services.environmentContext),
@@ -156,6 +156,8 @@ All methods are async and require the async IIFE wrapper.
156
156
  - **tools.curate(operations[], options?)** — Curate knowledge (ADD/UPDATE/UPSERT/MERGE/DELETE)
157
157
  - **tools.searchKnowledge(query, options?)** — Search curated knowledge
158
158
  - **tools.detectDomains(domains[])** — Detect domains from text
159
+ - **tools.swarmQuery(query, {limit?, scope?})** — Search across all swarm memory providers (when configured)
160
+ - **tools.swarmStore({content, contentType?, provider?})** — Store knowledge in GBrain/local markdown (when configured)
159
161
 
160
162
  ## Available Globals
161
163
 
@@ -0,0 +1,38 @@
1
+ Search across all active memory providers in the memory swarm.
2
+
3
+ This tool routes your query to multiple knowledge sources (ByteRover context tree, Obsidian vaults, local markdown folders, and cloud providers), executes them in parallel, and returns fused, ranked results using Reciprocal Rank Fusion.
4
+
5
+ **When to use:**
6
+ - When information may be spread across multiple knowledge sources
7
+ - When you need a broader search than `search_knowledge` (which only searches `.brv/context-tree/`)
8
+ - When you want results from Obsidian vaults, local markdown folders, or cloud memory providers
9
+
10
+ **Parameters:**
11
+ - `query` (required): Natural language query to search across providers
12
+ - `maxResults` (optional): Maximum results to return (default: 10)
13
+ - `scope` (optional): Restrict search to a namespace/subtree
14
+
15
+ **Returns:**
16
+ - `results`: Array of ranked results with:
17
+ - `content`: The matching content
18
+ - `provider`: Which provider returned this result
19
+ - `score`: Fused relevance score (higher is better)
20
+ - `metadata.source`: Source file or identifier
21
+ - `metadata.matchType`: How the match was found (keyword, semantic, graph, temporal)
22
+ - `meta`: Execution metadata with:
23
+ - `queryType`: How the query was classified (factual, temporal, personal, relational)
24
+ - `providers`: Per-provider latency and result counts
25
+ - `totalLatencyMs`: End-to-end latency
26
+ - `costCents`: Total cost for cloud providers
27
+
28
+ **Query routing:**
29
+ - Queries are automatically classified and routed to relevant providers
30
+ - "yesterday", "last week" → temporal providers
31
+ - "I prefer", "my style" → personal/user-modeling providers
32
+ - "related to", "depends on" → relational/graph providers
33
+ - General queries → factual providers (all local sources)
34
+
35
+ **Examples:**
36
+ - Query: "auth tokens" → searches ByteRover + Obsidian + local markdown
37
+ - Query: "what changed yesterday" → routes to temporal-capable providers
38
+ - Query: "how do I usually handle errors" → routes to personal memory providers
@@ -0,0 +1,35 @@
1
+ Store knowledge in a swarm provider (GBrain, local markdown).
2
+
3
+ This tool writes knowledge to external memory providers. It auto-routes based on content type, or you can target a specific provider.
4
+
5
+ **When to use:**
6
+ - Storing structured entities (people, companies, concepts) → routes to GBrain
7
+ - Storing notes, drafts, meeting summaries → routes to local markdown
8
+ - When knowledge belongs outside the project context tree
9
+
10
+ **When NOT to use:**
11
+ - For project-specific knowledge (architecture, decisions, code patterns) → use `curate` instead
12
+ - The `curate` tool writes to .brv/context-tree with FinMem scoring and domain structure
13
+
14
+ **Parameters:**
15
+ - `content` (required): The knowledge to store
16
+ - `provider` (optional): Target provider ID (e.g., "gbrain", "local-markdown:notes"). Auto-routed if omitted.
17
+ - `contentType` (optional): Hint for routing — "entity", "note", or "general"
18
+
19
+ **Returns:**
20
+ - `success`: Whether the store succeeded
21
+ - `provider`: Which provider stored the content
22
+ - `id`: ID assigned by the provider (slug for GBrain, filename for local markdown)
23
+ - `latencyMs`: Store latency
24
+ - `error`: Error message if store failed
25
+
26
+ **Routing logic:**
27
+ - Content mentioning people, companies, roles → "entity" → GBrain
28
+ - Content with meeting notes, TODOs, drafts → "note" → local markdown
29
+ - Ambiguous content → first available writable provider
30
+ - Explicit `provider` parameter overrides all routing
31
+
32
+ **Examples:**
33
+ - Store entity: `swarm_store({ content: "Dario Amodei is CEO of Anthropic" })`
34
+ - Store note: `swarm_store({ content: "Meeting: decided to use JWT refresh rotation" })`
35
+ - Explicit target: `swarm_store({ content: "...", provider: "gbrain" })`
@@ -11,6 +11,7 @@ export default class Curate extends Command {
11
11
  files: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
12
  folder: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
13
  format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ timeout: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
14
15
  };
15
16
  protected getDaemonClientOptions(): DaemonClientOptions;
16
17
  run(): Promise<void>;
@@ -6,7 +6,7 @@ import { extractCurateOperations } from '../../../server/utils/curate-result-par
6
6
  import { TaskEvents } from '../../../shared/transport/events/index.js';
7
7
  import { formatConnectionError, hasLeakedHandles, providerMissingMessage, withDaemonRetry, } from '../../lib/daemon-client.js';
8
8
  import { writeJsonResponse } from '../../lib/json-response.js';
9
- import { waitForTaskCompletion } from '../../lib/task-client.js';
9
+ import { DEFAULT_TIMEOUT_SECONDS, MAX_TIMEOUT_SECONDS, MIN_TIMEOUT_SECONDS, waitForTaskCompletion } from '../../lib/task-client.js';
10
10
  export default class Curate extends Command {
11
11
  static args = {
12
12
  context: Args.string({
@@ -38,6 +38,9 @@ Bad examples:
38
38
  '# Folder pack with context',
39
39
  '<%= config.bin %> <%= command.id %> "Analyze authentication module" -d src/auth/',
40
40
  '',
41
+ '# Increase timeout for slow models (in seconds)',
42
+ '<%= config.bin %> <%= command.id %> "context here" --timeout 600',
43
+ '',
41
44
  '# View curate history',
42
45
  '<%= config.bin %> curate view',
43
46
  '<%= config.bin %> curate view --status completed --since 1h',
@@ -62,6 +65,12 @@ Bad examples:
62
65
  description: 'Output format (text or json)',
63
66
  options: ['text', 'json'],
64
67
  }),
68
+ timeout: Flags.integer({
69
+ default: DEFAULT_TIMEOUT_SECONDS,
70
+ description: 'Maximum seconds to wait for task completion',
71
+ max: MAX_TIMEOUT_SECONDS,
72
+ min: MIN_TIMEOUT_SECONDS,
73
+ }),
65
74
  };
66
75
  getDaemonClientOptions() {
67
76
  return {};
@@ -73,6 +82,7 @@ Bad examples:
73
82
  files: rawFlags.files,
74
83
  folder: rawFlags.folder,
75
84
  format: rawFlags.format === 'json' ? 'json' : rawFlags.format === 'text' ? 'text' : undefined,
85
+ timeout: rawFlags.timeout,
76
86
  };
77
87
  const format = flags.format ?? 'text';
78
88
  if (!this.validateInput(args, flags, format))
@@ -235,6 +245,9 @@ Bad examples:
235
245
  ...(worktreeRoot ? { worktreeRoot } : {}),
236
246
  };
237
247
  if (flags.detach) {
248
+ if (flags.timeout !== DEFAULT_TIMEOUT_SECONDS && format !== 'json') {
249
+ this.log('Note: --timeout has no effect with --detach');
250
+ }
238
251
  const ack = await client.requestWithAck(TaskEvents.CREATE, taskPayload);
239
252
  const { logId } = ack;
240
253
  if (format === 'json') {
@@ -299,6 +312,7 @@ Bad examples:
299
312
  }
300
313
  },
301
314
  taskId,
315
+ timeoutMs: (flags.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1000,
302
316
  }, (msg) => this.log(msg));
303
317
  await client.requestWithAck(TaskEvents.CREATE, taskPayload);
304
318
  await completionPromise;
@@ -8,6 +8,7 @@ export default class Query extends Command {
8
8
  static examples: string[];
9
9
  static flags: {
10
10
  format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ timeout: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
11
12
  };
12
13
  static strict: boolean;
13
14
  protected getDaemonClientOptions(): DaemonClientOptions;
@@ -4,7 +4,7 @@ import { TransportStateEventNames } from '../../server/core/domain/transport/sch
4
4
  import { TaskEvents } from '../../shared/transport/events/index.js';
5
5
  import { formatConnectionError, hasLeakedHandles, providerMissingMessage, withDaemonRetry, } from '../lib/daemon-client.js';
6
6
  import { writeJsonResponse } from '../lib/json-response.js';
7
- import { waitForTaskCompletion } from '../lib/task-client.js';
7
+ import { DEFAULT_TIMEOUT_SECONDS, MAX_TIMEOUT_SECONDS, MIN_TIMEOUT_SECONDS, waitForTaskCompletion } from '../lib/task-client.js';
8
8
  export default class Query extends Command {
9
9
  static args = {
10
10
  query: Args.string({
@@ -34,6 +34,12 @@ Bad:
34
34
  description: 'Output format (text or json)',
35
35
  options: ['text', 'json'],
36
36
  }),
37
+ timeout: Flags.integer({
38
+ default: DEFAULT_TIMEOUT_SECONDS,
39
+ description: 'Maximum seconds to wait for task completion',
40
+ max: MAX_TIMEOUT_SECONDS,
41
+ min: MIN_TIMEOUT_SECONDS,
42
+ }),
37
43
  };
38
44
  static strict = false;
39
45
  getDaemonClientOptions() {
@@ -56,7 +62,14 @@ Bad:
56
62
  if (active.providerKeyMissing) {
57
63
  throw new Error(providerMissingMessage(active.activeProvider, active.authMethod));
58
64
  }
59
- await this.submitTask({ client, format, projectRoot, query: args.query, worktreeRoot });
65
+ await this.submitTask({
66
+ client,
67
+ format,
68
+ projectRoot,
69
+ query: args.query,
70
+ timeoutMs: (flags.timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1000,
71
+ worktreeRoot,
72
+ });
60
73
  }, {
61
74
  ...this.getDaemonClientOptions(),
62
75
  onRetry: format === 'text'
@@ -82,7 +95,7 @@ Bad:
82
95
  }
83
96
  }
84
97
  async submitTask(props) {
85
- const { client, format, projectRoot, query, worktreeRoot } = props;
98
+ const { client, format, projectRoot, query, timeoutMs, worktreeRoot } = props;
86
99
  const taskId = randomUUID();
87
100
  const taskPayload = {
88
101
  clientCwd: process.cwd(),
@@ -156,6 +169,7 @@ Bad:
156
169
  }
157
170
  },
158
171
  taskId,
172
+ timeoutMs,
159
173
  }, (msg) => this.log(msg));
160
174
  await client.requestWithAck(TaskEvents.CREATE, taskPayload);
161
175
  await completionPromise;
@@ -0,0 +1,20 @@
1
+ import { Command } from '@oclif/core';
2
+ import { type DaemonClientOptions } from '../lib/daemon-client.js';
3
+ export default class Search extends Command {
4
+ static args: {
5
+ query: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: {
10
+ format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
12
+ scope: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ static strict: boolean;
15
+ protected getDaemonClientOptions(): DaemonClientOptions;
16
+ run(): Promise<void>;
17
+ private reportError;
18
+ private submitTask;
19
+ private validateInput;
20
+ }