byterover-cli 3.0.1 → 3.2.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.
- package/.env.production +4 -0
- package/README.md +17 -0
- package/dist/agent/core/domain/tools/constants.d.ts +1 -0
- package/dist/agent/core/domain/tools/constants.js +1 -0
- package/dist/agent/core/interfaces/cipher-services.d.ts +8 -0
- package/dist/agent/core/interfaces/i-cipher-agent.d.ts +1 -0
- package/dist/agent/infra/agent/agent-error-codes.d.ts +0 -1
- package/dist/agent/infra/agent/agent-error-codes.js +0 -1
- package/dist/agent/infra/agent/agent-error.d.ts +0 -1
- package/dist/agent/infra/agent/agent-error.js +0 -1
- package/dist/agent/infra/agent/agent-schemas.d.ts +8 -0
- package/dist/agent/infra/agent/agent-schemas.js +1 -0
- package/dist/agent/infra/agent/agent-state-manager.d.ts +1 -3
- package/dist/agent/infra/agent/agent-state-manager.js +1 -3
- package/dist/agent/infra/agent/base-agent.d.ts +1 -1
- package/dist/agent/infra/agent/base-agent.js +1 -1
- package/dist/agent/infra/agent/cipher-agent.d.ts +15 -1
- package/dist/agent/infra/agent/cipher-agent.js +188 -3
- package/dist/agent/infra/agent/index.d.ts +1 -1
- package/dist/agent/infra/agent/index.js +1 -1
- package/dist/agent/infra/agent/service-initializer.d.ts +3 -3
- package/dist/agent/infra/agent/service-initializer.js +14 -8
- package/dist/agent/infra/agent/types.d.ts +0 -1
- package/dist/agent/infra/file-system/file-system-service.js +6 -5
- package/dist/agent/infra/folder-pack/folder-pack-service.d.ts +1 -0
- package/dist/agent/infra/folder-pack/folder-pack-service.js +29 -15
- package/dist/agent/infra/llm/providers/openai.js +12 -0
- package/dist/agent/infra/llm/stream-to-text.d.ts +7 -0
- package/dist/agent/infra/llm/stream-to-text.js +14 -0
- package/dist/agent/infra/map/abstract-generator.d.ts +22 -0
- package/dist/agent/infra/map/abstract-generator.js +67 -0
- package/dist/agent/infra/map/abstract-queue.d.ts +67 -0
- package/dist/agent/infra/map/abstract-queue.js +218 -0
- package/dist/agent/infra/memory/memory-deduplicator.d.ts +44 -0
- package/dist/agent/infra/memory/memory-deduplicator.js +88 -0
- package/dist/agent/infra/memory/memory-manager.d.ts +1 -0
- package/dist/agent/infra/memory/memory-manager.js +6 -5
- package/dist/agent/infra/sandbox/curate-service.d.ts +4 -2
- package/dist/agent/infra/sandbox/curate-service.js +20 -7
- package/dist/agent/infra/sandbox/local-sandbox.d.ts +5 -0
- package/dist/agent/infra/sandbox/local-sandbox.js +57 -1
- package/dist/agent/infra/sandbox/sandbox-service.js +1 -0
- package/dist/agent/infra/sandbox/tools-sdk.d.ts +13 -1
- package/dist/agent/infra/sandbox/tools-sdk.js +9 -1
- package/dist/agent/infra/session/session-compressor.d.ts +43 -0
- package/dist/agent/infra/session/session-compressor.js +296 -0
- package/dist/agent/infra/session/session-manager.d.ts +7 -0
- package/dist/agent/infra/session/session-manager.js +9 -0
- package/dist/agent/infra/tools/implementations/curate-tool.d.ts +3 -2
- package/dist/agent/infra/tools/implementations/curate-tool.js +54 -27
- package/dist/agent/infra/tools/implementations/expand-knowledge-tool.d.ts +3 -3
- package/dist/agent/infra/tools/implementations/expand-knowledge-tool.js +34 -7
- package/dist/agent/infra/tools/implementations/ingest-resource-tool.d.ts +17 -0
- package/dist/agent/infra/tools/implementations/ingest-resource-tool.js +224 -0
- package/dist/agent/infra/tools/implementations/memory-symbol-tree.d.ts +8 -0
- package/dist/agent/infra/tools/implementations/search-knowledge-service.d.ts +1 -1
- package/dist/agent/infra/tools/implementations/search-knowledge-service.js +392 -106
- package/dist/agent/infra/tools/implementations/search-knowledge-tool.js +2 -2
- package/dist/agent/infra/tools/implementations/write-file-tool.d.ts +2 -1
- package/dist/agent/infra/tools/implementations/write-file-tool.js +16 -2
- package/dist/agent/infra/tools/tool-provider.js +1 -0
- package/dist/agent/infra/tools/tool-registry.d.ts +3 -0
- package/dist/agent/infra/tools/tool-registry.js +16 -5
- package/dist/agent/infra/tools/write-guard.d.ts +11 -0
- package/dist/agent/infra/tools/write-guard.js +48 -0
- package/dist/agent/resources/prompts/system-prompt.yml +9 -0
- package/dist/agent/resources/tools/expand_knowledge.txt +4 -0
- package/dist/agent/resources/tools/search_knowledge.txt +11 -1
- package/dist/oclif/commands/curate/index.js +4 -3
- package/dist/oclif/commands/curate/view.js +2 -2
- package/dist/oclif/commands/main.js +13 -0
- package/dist/oclif/commands/query.js +4 -3
- package/dist/oclif/commands/source/add.d.ts +12 -0
- package/dist/oclif/commands/source/add.js +42 -0
- package/dist/oclif/commands/source/index.d.ts +6 -0
- package/dist/oclif/commands/source/index.js +8 -0
- package/dist/oclif/commands/source/list.d.ts +6 -0
- package/dist/oclif/commands/source/list.js +32 -0
- package/dist/oclif/commands/source/remove.d.ts +9 -0
- package/dist/oclif/commands/source/remove.js +33 -0
- package/dist/oclif/commands/status.d.ts +5 -1
- package/dist/oclif/commands/status.js +41 -6
- package/dist/oclif/commands/worktree/add.d.ts +12 -0
- package/dist/oclif/commands/worktree/add.js +44 -0
- package/dist/oclif/commands/worktree/index.d.ts +6 -0
- package/dist/oclif/commands/worktree/index.js +8 -0
- package/dist/oclif/commands/worktree/list.d.ts +6 -0
- package/dist/oclif/commands/worktree/list.js +28 -0
- package/dist/oclif/commands/worktree/remove.d.ts +9 -0
- package/dist/oclif/commands/worktree/remove.js +35 -0
- package/dist/oclif/hooks/init/validate-brv-config.js +4 -0
- package/dist/oclif/lib/daemon-client.d.ts +4 -2
- package/dist/oclif/lib/daemon-client.js +19 -3
- package/dist/server/constants.d.ts +8 -0
- package/dist/server/constants.js +10 -0
- package/dist/server/core/domain/client/client-info.d.ts +7 -0
- package/dist/server/core/domain/client/client-info.js +11 -0
- package/dist/server/core/domain/knowledge/memory-scoring.d.ts +3 -3
- package/dist/server/core/domain/knowledge/memory-scoring.js +5 -5
- package/dist/server/core/domain/knowledge/summary-types.d.ts +4 -0
- package/dist/server/core/domain/project/worktrees-schema.d.ts +29 -0
- package/dist/server/core/domain/project/worktrees-schema.js +17 -0
- package/dist/server/core/domain/source/source-operations.d.ts +31 -0
- package/dist/server/core/domain/source/source-operations.js +201 -0
- package/dist/server/core/domain/source/source-schema.d.ts +94 -0
- package/dist/server/core/domain/source/source-schema.js +121 -0
- package/dist/server/core/domain/transport/schemas.d.ts +18 -10
- package/dist/server/core/domain/transport/schemas.js +4 -0
- package/dist/server/core/domain/transport/task-info.d.ts +2 -0
- package/dist/server/core/interfaces/client/i-client-manager.d.ts +13 -0
- package/dist/server/core/interfaces/executor/i-curate-executor.d.ts +4 -0
- package/dist/server/core/interfaces/executor/i-folder-pack-executor.d.ts +7 -3
- package/dist/server/core/interfaces/executor/i-query-executor.d.ts +2 -0
- package/dist/server/infra/client/client-manager.d.ts +1 -0
- package/dist/server/infra/client/client-manager.js +16 -0
- package/dist/server/infra/context-tree/derived-artifact.js +5 -1
- package/dist/server/infra/context-tree/file-context-tree-manifest-service.d.ts +2 -1
- package/dist/server/infra/context-tree/file-context-tree-manifest-service.js +43 -7
- package/dist/server/infra/context-tree/file-context-tree-summary-service.js +20 -2
- package/dist/server/infra/daemon/agent-process.js +15 -5
- package/dist/server/infra/executor/curate-executor.js +6 -3
- package/dist/server/infra/executor/direct-search-responder.js +5 -1
- package/dist/server/infra/executor/folder-pack-executor.js +88 -7
- package/dist/server/infra/executor/query-executor.d.ts +23 -0
- package/dist/server/infra/executor/query-executor.js +125 -23
- package/dist/server/infra/mcp/mcp-mode-detector.d.ts +7 -5
- package/dist/server/infra/mcp/mcp-mode-detector.js +11 -18
- package/dist/server/infra/mcp/mcp-server.d.ts +1 -0
- package/dist/server/infra/mcp/mcp-server.js +11 -6
- package/dist/server/infra/mcp/tools/brv-curate-tool.d.ts +2 -1
- package/dist/server/infra/mcp/tools/brv-curate-tool.js +9 -16
- package/dist/server/infra/mcp/tools/brv-query-tool.d.ts +2 -1
- package/dist/server/infra/mcp/tools/brv-query-tool.js +9 -16
- package/dist/server/infra/mcp/tools/mcp-project-context.d.ts +11 -0
- package/dist/server/infra/mcp/tools/mcp-project-context.js +54 -0
- package/dist/server/infra/process/connection-coordinator.js +11 -0
- package/dist/server/infra/process/feature-handlers.js +4 -1
- package/dist/server/infra/process/task-router.d.ts +1 -0
- package/dist/server/infra/process/task-router.js +60 -5
- package/dist/server/infra/project/resolve-project.d.ts +106 -0
- package/dist/server/infra/project/resolve-project.js +473 -0
- package/dist/server/infra/transport/handlers/index.d.ts +4 -0
- package/dist/server/infra/transport/handlers/index.js +2 -0
- package/dist/server/infra/transport/handlers/source-handler.d.ts +12 -0
- package/dist/server/infra/transport/handlers/source-handler.js +37 -0
- package/dist/server/infra/transport/handlers/status-handler.js +65 -13
- package/dist/server/infra/transport/handlers/worktree-handler.d.ts +12 -0
- package/dist/server/infra/transport/handlers/worktree-handler.js +67 -0
- package/dist/server/infra/transport/transport-connector.d.ts +10 -4
- package/dist/server/infra/transport/transport-connector.js +2 -2
- package/dist/server/utils/curate-result-parser.d.ts +4 -4
- package/dist/server/utils/path-utils.d.ts +5 -0
- package/dist/server/utils/path-utils.js +11 -1
- package/dist/shared/transport/events/client-events.d.ts +3 -0
- package/dist/shared/transport/events/client-events.js +3 -0
- package/dist/shared/transport/events/index.d.ts +13 -0
- package/dist/shared/transport/events/index.js +9 -0
- package/dist/shared/transport/events/source-events.d.ts +30 -0
- package/dist/shared/transport/events/source-events.js +5 -0
- package/dist/shared/transport/events/status-events.d.ts +5 -0
- package/dist/shared/transport/events/task-events.d.ts +4 -1
- package/dist/shared/transport/events/worktree-events.d.ts +31 -0
- package/dist/shared/transport/events/worktree-events.js +5 -0
- package/dist/shared/transport/types/dto.d.ts +26 -0
- package/dist/tui/features/commands/definitions/index.js +6 -0
- package/dist/tui/features/commands/definitions/source-add.d.ts +2 -0
- package/dist/tui/features/commands/definitions/source-add.js +48 -0
- package/dist/tui/features/commands/definitions/source-list.d.ts +2 -0
- package/dist/tui/features/commands/definitions/source-list.js +47 -0
- package/dist/tui/features/commands/definitions/source-remove.d.ts +2 -0
- package/dist/tui/features/commands/definitions/source-remove.js +38 -0
- package/dist/tui/features/commands/definitions/source.d.ts +2 -0
- package/dist/tui/features/commands/definitions/source.js +8 -0
- package/dist/tui/features/commands/definitions/worktree-add.d.ts +2 -0
- package/dist/tui/features/commands/definitions/worktree-add.js +35 -0
- package/dist/tui/features/commands/definitions/worktree-list.d.ts +2 -0
- package/dist/tui/features/commands/definitions/worktree-list.js +36 -0
- package/dist/tui/features/commands/definitions/worktree-remove.d.ts +2 -0
- package/dist/tui/features/commands/definitions/worktree-remove.js +33 -0
- package/dist/tui/features/commands/definitions/worktree.d.ts +2 -0
- package/dist/tui/features/commands/definitions/worktree.js +8 -0
- package/dist/tui/features/curate/api/create-curate-task.js +3 -1
- package/dist/tui/features/query/api/create-query-task.js +3 -1
- package/dist/tui/features/source/api/source-api.d.ts +4 -0
- package/dist/tui/features/source/api/source-api.js +22 -0
- package/dist/tui/features/status/api/get-status.js +2 -1
- package/dist/tui/features/status/utils/format-status.js +23 -1
- package/dist/tui/features/transport/components/transport-initializer.js +36 -1
- package/dist/tui/features/worktree/api/worktree-api.d.ts +4 -0
- package/dist/tui/features/worktree/api/worktree-api.js +22 -0
- package/dist/tui/repl-startup.d.ts +2 -0
- package/dist/tui/repl-startup.js +5 -3
- package/dist/tui/stores/transport-store.d.ts +6 -0
- package/dist/tui/stores/transport-store.js +6 -0
- package/oclif.manifest.json +261 -1
- package/package.json +10 -4
|
@@ -1,24 +1,36 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
1
3
|
import { z } from 'zod';
|
|
4
|
+
import { BRV_DIR, CONTEXT_TREE_DIR } from '../../../../server/constants.js';
|
|
2
5
|
import { FileContextTreeArchiveService } from '../../../../server/infra/context-tree/file-context-tree-archive-service.js';
|
|
6
|
+
import { estimateTokens } from '../../../../server/infra/executor/pre-compaction/compaction-escalation.js';
|
|
3
7
|
import { ToolName } from '../../../core/domain/tools/constants.js';
|
|
4
8
|
/**
|
|
5
9
|
* Input schema for expand knowledge tool.
|
|
10
|
+
* Accepts either a stubPath (archive drill-down) or an overviewPath (L1 overview retrieval).
|
|
6
11
|
*/
|
|
7
12
|
const ExpandKnowledgeInputSchema = z
|
|
8
13
|
.object({
|
|
14
|
+
overviewPath: z
|
|
15
|
+
.string()
|
|
16
|
+
.min(1)
|
|
17
|
+
.describe('Path to the .overview.md file (relative to context tree). ' +
|
|
18
|
+
'This is the `overviewPath` field from search results.')
|
|
19
|
+
.optional(),
|
|
9
20
|
stubPath: z
|
|
10
21
|
.string()
|
|
11
22
|
.min(1)
|
|
12
23
|
.describe('Path to the .stub.md file in _archived/. ' +
|
|
13
|
-
'This is the `path` field from search results where symbolKind === "archive_stub".')
|
|
24
|
+
'This is the `path` field from search results where symbolKind === "archive_stub".')
|
|
25
|
+
.optional(),
|
|
14
26
|
})
|
|
15
|
-
.
|
|
27
|
+
.refine((data) => (data.stubPath !== undefined) !== (data.overviewPath !== undefined), { message: 'Exactly one of stubPath or overviewPath must be provided' });
|
|
16
28
|
/**
|
|
17
29
|
* Creates the expand knowledge tool.
|
|
18
30
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
31
|
+
* Two modes:
|
|
32
|
+
* - stubPath: retrieves full content from archived knowledge entries (archive drill-down)
|
|
33
|
+
* - overviewPath: retrieves L1 overview content from .overview.md sibling files
|
|
22
34
|
*
|
|
23
35
|
* @param config - Optional configuration
|
|
24
36
|
* @returns Configured expand knowledge tool
|
|
@@ -26,10 +38,25 @@ const ExpandKnowledgeInputSchema = z
|
|
|
26
38
|
export function createExpandKnowledgeTool(config = {}) {
|
|
27
39
|
const archiveService = new FileContextTreeArchiveService();
|
|
28
40
|
return {
|
|
29
|
-
description: 'Retrieve full content from archived knowledge entries. ' +
|
|
30
|
-
'Use when search results include an archive_stub that you need to drill into.'
|
|
41
|
+
description: 'Retrieve full content from archived knowledge entries or L1 overview files. ' +
|
|
42
|
+
'Use stubPath when search results include an archive_stub that you need to drill into. ' +
|
|
43
|
+
'Use overviewPath to retrieve the structured overview for a context entry.',
|
|
31
44
|
async execute(input, _context) {
|
|
32
45
|
const parsed = ExpandKnowledgeInputSchema.parse(input);
|
|
46
|
+
if (parsed.overviewPath) {
|
|
47
|
+
const baseDir = config.baseDirectory ?? process.cwd();
|
|
48
|
+
const fullPath = join(baseDir, BRV_DIR, CONTEXT_TREE_DIR, parsed.overviewPath);
|
|
49
|
+
const overviewContent = await readFile(fullPath, 'utf8');
|
|
50
|
+
const originalPath = parsed.overviewPath.replace(/\.overview\.md$/, '.md');
|
|
51
|
+
return {
|
|
52
|
+
originalPath,
|
|
53
|
+
overviewContent,
|
|
54
|
+
tokenCount: estimateTokens(overviewContent),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (!parsed.stubPath) {
|
|
58
|
+
throw new Error('stubPath is required when overviewPath is not provided');
|
|
59
|
+
}
|
|
33
60
|
const result = await archiveService.drillDown(parsed.stubPath, config.baseDirectory);
|
|
34
61
|
return {
|
|
35
62
|
fullContent: result.fullContent,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Tool } from '../../../core/domain/tools/types.js';
|
|
2
|
+
import type { IContentGenerator } from '../../../core/interfaces/i-content-generator.js';
|
|
3
|
+
import type { IFileSystem } from '../../../core/interfaces/i-file-system.js';
|
|
4
|
+
import type { AbstractGenerationQueue } from '../../map/abstract-queue.js';
|
|
5
|
+
export interface IngestResourceConfig {
|
|
6
|
+
abstractQueue?: AbstractGenerationQueue;
|
|
7
|
+
baseDirectory?: string;
|
|
8
|
+
contentGenerator?: IContentGenerator;
|
|
9
|
+
fileSystem?: IFileSystem;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Creates the ingest_resource tool.
|
|
13
|
+
*
|
|
14
|
+
* Bulk-ingests files from a directory into the knowledge context tree.
|
|
15
|
+
* Glob → Read → LLM extraction → Curate pipeline.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createIngestResourceTool(config?: IngestResourceConfig): Tool;
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { realpath } from 'node:fs/promises';
|
|
2
|
+
import { basename, isAbsolute, join, relative, resolve } from 'node:path';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { BRV_DIR, CONTEXT_TREE_DIR } from '../../../../server/constants.js';
|
|
5
|
+
import { ToolName } from '../../../core/domain/tools/constants.js';
|
|
6
|
+
import { executeLlmMapMemory } from '../../map/llm-map-memory.js';
|
|
7
|
+
import { executeCurate } from './curate-tool.js';
|
|
8
|
+
const DEFAULT_INCLUDE = ['**/*.ts', '**/*.md', '**/*.json', '**/*.js', '**/*.py', '**/*.go', '**/*.rs'];
|
|
9
|
+
const DEFAULT_EXCLUDE = ['node_modules', '.git', '*.test.*', '*.spec.*', 'dist', 'build'];
|
|
10
|
+
const MAX_FILES = 200;
|
|
11
|
+
const MAX_FILE_LINES = 500;
|
|
12
|
+
const MAX_CONTENT_CHARS = 4000;
|
|
13
|
+
function toRelativeUnixPath(rootPath, filePath) {
|
|
14
|
+
const relativePath = isAbsolute(filePath) ? relative(rootPath, filePath) : filePath;
|
|
15
|
+
return relativePath.replaceAll('\\', '/');
|
|
16
|
+
}
|
|
17
|
+
function matchesExcludePattern(relativePath, pattern) {
|
|
18
|
+
const normalizedPath = relativePath.replaceAll('\\', '/');
|
|
19
|
+
const normalizedPattern = pattern.replaceAll('\\', '/');
|
|
20
|
+
if (!normalizedPattern.includes('*')) {
|
|
21
|
+
return normalizedPath.split('/').includes(normalizedPattern);
|
|
22
|
+
}
|
|
23
|
+
const regexPattern = normalizedPattern
|
|
24
|
+
.replaceAll('.', String.raw `\.`)
|
|
25
|
+
.replaceAll('**', '<<<DOUBLESTAR>>>')
|
|
26
|
+
.replaceAll('*', '[^/]*')
|
|
27
|
+
.replaceAll('<<<DOUBLESTAR>>>', '.*');
|
|
28
|
+
return new RegExp(`^${regexPattern}$|/${regexPattern}$|^${regexPattern}/|/${regexPattern}/`).test(normalizedPath);
|
|
29
|
+
}
|
|
30
|
+
function getDirectoryDepth(relativePath) {
|
|
31
|
+
if (!relativePath)
|
|
32
|
+
return 0;
|
|
33
|
+
return Math.max(0, relativePath.split('/').length - 1);
|
|
34
|
+
}
|
|
35
|
+
function extractHeading(content) {
|
|
36
|
+
const headingMatch = content.match(/^#\s+(.+)$/m);
|
|
37
|
+
return headingMatch?.[1]?.trim();
|
|
38
|
+
}
|
|
39
|
+
function normalizeInlineText(content) {
|
|
40
|
+
return content.replaceAll(/\s+/g, ' ').trim();
|
|
41
|
+
}
|
|
42
|
+
function buildFallbackHighlights(content) {
|
|
43
|
+
const lines = content
|
|
44
|
+
.split('\n')
|
|
45
|
+
.map((line) => line.trim())
|
|
46
|
+
.filter(Boolean)
|
|
47
|
+
.slice(0, 8);
|
|
48
|
+
return lines.length > 0 ? lines.join('\n') : undefined;
|
|
49
|
+
}
|
|
50
|
+
function getIngestTarget(filePath) {
|
|
51
|
+
const pathSegments = filePath.split('/');
|
|
52
|
+
const fileBaseName = pathSegments.at(-1)?.replace(/\.[^.]+$/, '') ?? 'unknown';
|
|
53
|
+
const topic = pathSegments.length > 1 ? (pathSegments.at(-2) ?? fileBaseName) : fileBaseName;
|
|
54
|
+
return { fileBaseName, topic };
|
|
55
|
+
}
|
|
56
|
+
function buildFallbackFacts(file) {
|
|
57
|
+
const { fileBaseName } = getIngestTarget(file.path);
|
|
58
|
+
const heading = extractHeading(file.content) ?? fileBaseName;
|
|
59
|
+
const preview = normalizeInlineText(file.content).slice(0, 220);
|
|
60
|
+
const subject = normalizeInlineText(heading).toLowerCase().replaceAll(/[^a-z0-9]+/g, '_').replaceAll(/^_+|_+$/g, '');
|
|
61
|
+
return [{
|
|
62
|
+
statement: preview.length > 0
|
|
63
|
+
? `${heading} is captured in ${file.path}: ${preview}`
|
|
64
|
+
: `${heading} is captured in ${file.path}.`,
|
|
65
|
+
...(subject.length > 0 && { subject }),
|
|
66
|
+
}];
|
|
67
|
+
}
|
|
68
|
+
function buildOperation(domain, sourceRoot, file, facts) {
|
|
69
|
+
const { fileBaseName, topic } = getIngestTarget(file.path);
|
|
70
|
+
const usableFacts = facts?.filter((fact) => fact.statement.trim().length > 0) ?? [];
|
|
71
|
+
const sourcePath = join(sourceRoot, file.path);
|
|
72
|
+
if (usableFacts.length > 0) {
|
|
73
|
+
const highlights = usableFacts.map((fact) => `**${fact.subject ?? 'Concept'}**: ${fact.statement}`).join('\n\n');
|
|
74
|
+
return {
|
|
75
|
+
confidence: 'high',
|
|
76
|
+
content: {
|
|
77
|
+
narrative: { highlights },
|
|
78
|
+
rawConcept: { files: [sourcePath] },
|
|
79
|
+
},
|
|
80
|
+
impact: 'low',
|
|
81
|
+
path: `${domain}/${topic}`,
|
|
82
|
+
reason: `Ingested from ${file.path}`,
|
|
83
|
+
title: fileBaseName,
|
|
84
|
+
type: 'ADD',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const fallbackFacts = buildFallbackFacts(file);
|
|
88
|
+
const fallbackHighlights = buildFallbackHighlights(file.content);
|
|
89
|
+
if (fallbackFacts.length === 0 && !fallbackHighlights) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
confidence: 'low',
|
|
94
|
+
content: {
|
|
95
|
+
facts: fallbackFacts,
|
|
96
|
+
...(fallbackHighlights && { narrative: { highlights: fallbackHighlights } }),
|
|
97
|
+
rawConcept: { files: [sourcePath] },
|
|
98
|
+
snippets: [file.content],
|
|
99
|
+
},
|
|
100
|
+
impact: 'low',
|
|
101
|
+
path: `${domain}/${topic}`,
|
|
102
|
+
reason: `Fallback ingest from ${file.path}`,
|
|
103
|
+
title: fileBaseName,
|
|
104
|
+
type: 'ADD',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const IngestResourceInputSchema = z
|
|
108
|
+
.object({
|
|
109
|
+
depth: z.number().int().min(1).max(5).optional().default(3).describe('Maximum directory depth to scan (default: 3, max: 5)'),
|
|
110
|
+
domain: z.string().optional().describe('Target knowledge domain (default: inferred from directory name)'),
|
|
111
|
+
exclude: z.array(z.string()).optional().describe('Glob patterns to exclude (default: node_modules, .git, *.test.*, dist, build)'),
|
|
112
|
+
include: z.array(z.string()).optional().describe('Glob patterns to include (default: *.ts, *.md, *.json, etc.)'),
|
|
113
|
+
path: z.string().min(1).describe('Directory path to ingest files from'),
|
|
114
|
+
})
|
|
115
|
+
.strict();
|
|
116
|
+
/**
|
|
117
|
+
* Creates the ingest_resource tool.
|
|
118
|
+
*
|
|
119
|
+
* Bulk-ingests files from a directory into the knowledge context tree.
|
|
120
|
+
* Glob → Read → LLM extraction → Curate pipeline.
|
|
121
|
+
*/
|
|
122
|
+
export function createIngestResourceTool(config = {}) {
|
|
123
|
+
return {
|
|
124
|
+
description: 'Bulk-ingest files from a directory into the knowledge context tree. ' +
|
|
125
|
+
'Globs files, reads contents, extracts knowledge via LLM, and adds to context tree. ' +
|
|
126
|
+
'Use for one-shot import of documentation, source files, or configuration.',
|
|
127
|
+
async execute(input, context) {
|
|
128
|
+
const params = IngestResourceInputSchema.parse(input);
|
|
129
|
+
const { abstractQueue, baseDirectory, contentGenerator, fileSystem } = config;
|
|
130
|
+
if (!contentGenerator || !fileSystem) {
|
|
131
|
+
throw new Error('ingest_resource requires contentGenerator and fileSystemService');
|
|
132
|
+
}
|
|
133
|
+
// Normalize to absolute using the injected workspace root so relative inputs like './src'
|
|
134
|
+
// resolve against the project directory, not the agent process cwd.
|
|
135
|
+
const absPath = resolve(baseDirectory ?? process.cwd(), params.path);
|
|
136
|
+
const normalizedAbsPath = await realpath(absPath).catch(() => absPath);
|
|
137
|
+
const domain = params.domain ?? (basename(absPath) || 'imported');
|
|
138
|
+
const include = params.include ?? DEFAULT_INCLUDE;
|
|
139
|
+
const exclude = params.exclude ?? DEFAULT_EXCLUDE;
|
|
140
|
+
// Step 1: Glob files — collect unique paths across all include patterns
|
|
141
|
+
const seenPaths = new Set();
|
|
142
|
+
const rawPaths = [];
|
|
143
|
+
/* eslint-disable no-await-in-loop */
|
|
144
|
+
for (const pattern of include) {
|
|
145
|
+
const globResult = await fileSystem.globFiles(pattern, {
|
|
146
|
+
cwd: normalizedAbsPath,
|
|
147
|
+
maxResults: MAX_FILES,
|
|
148
|
+
respectGitignore: true,
|
|
149
|
+
});
|
|
150
|
+
for (const file of globResult.files) {
|
|
151
|
+
const relativePath = toRelativeUnixPath(normalizedAbsPath, file.path);
|
|
152
|
+
if (relativePath.startsWith('../'))
|
|
153
|
+
continue;
|
|
154
|
+
if (getDirectoryDepth(relativePath) > params.depth)
|
|
155
|
+
continue;
|
|
156
|
+
const excluded = exclude.some((excludePattern) => matchesExcludePattern(relativePath, excludePattern));
|
|
157
|
+
if (!excluded && !seenPaths.has(file.path)) {
|
|
158
|
+
seenPaths.add(file.path);
|
|
159
|
+
rawPaths.push(file.path);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/* eslint-enable no-await-in-loop */
|
|
164
|
+
// Step 2: Read file contents (limit to MAX_FILES)
|
|
165
|
+
const fileItems = [];
|
|
166
|
+
/* eslint-disable no-await-in-loop */
|
|
167
|
+
for (const filePath of rawPaths.slice(0, MAX_FILES)) {
|
|
168
|
+
try {
|
|
169
|
+
const { content } = await fileSystem.readFile(filePath, { limit: MAX_FILE_LINES });
|
|
170
|
+
if (content.trim()) {
|
|
171
|
+
const relativePath = toRelativeUnixPath(normalizedAbsPath, filePath);
|
|
172
|
+
if (relativePath.startsWith('../'))
|
|
173
|
+
continue;
|
|
174
|
+
fileItems.push({ content: content.slice(0, MAX_CONTENT_CHARS), path: relativePath });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Skip unreadable files
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/* eslint-enable no-await-in-loop */
|
|
182
|
+
if (fileItems.length === 0) {
|
|
183
|
+
return { domains: [domain], failed: 0, ingested: 0, queued: 0 };
|
|
184
|
+
}
|
|
185
|
+
// Step 3: LLM extraction via executeLlmMapMemory
|
|
186
|
+
const mapResult = await executeLlmMapMemory({
|
|
187
|
+
concurrency: Math.min(4, Math.max(1, fileItems.length)),
|
|
188
|
+
generator: contentGenerator,
|
|
189
|
+
items: fileItems.map((f) => ({ content: f.content, path: f.path })),
|
|
190
|
+
prompt: 'Extract 1-5 concrete reusable knowledge facts from the file provided in the map details below. ' +
|
|
191
|
+
'Focus on APIs, invariants, workflows, configuration, constraints, and implementation semantics. ' +
|
|
192
|
+
'Each fact should be a terse technical statement.',
|
|
193
|
+
taskId: context?.taskId,
|
|
194
|
+
});
|
|
195
|
+
// Step 4: Convert to CurateOperations (fail-open: use whichever results are available)
|
|
196
|
+
const operations = [];
|
|
197
|
+
let unresolvedCount = 0;
|
|
198
|
+
for (const [i, file] of fileItems.entries()) {
|
|
199
|
+
const operation = buildOperation(domain, normalizedAbsPath, file, mapResult.results[i] ?? null);
|
|
200
|
+
if (operation) {
|
|
201
|
+
operations.push(operation);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
unresolvedCount++;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (operations.length === 0) {
|
|
208
|
+
return { domains: [domain], failed: fileItems.length, ingested: 0, queued: 0 };
|
|
209
|
+
}
|
|
210
|
+
// Step 5: Run curate pipeline with abstract queue hook.
|
|
211
|
+
// basePath must point to the knowledge store (.brv/context-tree), not the workspace root.
|
|
212
|
+
const contextTreePath = join(baseDirectory ?? process.cwd(), BRV_DIR, CONTEXT_TREE_DIR);
|
|
213
|
+
const curateResult = await executeCurate({ basePath: contextTreePath, operations }, context, abstractQueue);
|
|
214
|
+
return {
|
|
215
|
+
domains: [domain],
|
|
216
|
+
failed: unresolvedCount,
|
|
217
|
+
ingested: curateResult.summary.added + curateResult.summary.updated,
|
|
218
|
+
queued: abstractQueue ? operations.length : 0,
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
id: ToolName.INGEST_RESOURCE,
|
|
222
|
+
inputSchema: IngestResourceInputSchema,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
@@ -44,8 +44,16 @@ export interface SymbolMetadata {
|
|
|
44
44
|
*/
|
|
45
45
|
export interface SummaryDocLike {
|
|
46
46
|
condensationOrder: number;
|
|
47
|
+
/** First 400 chars of _index.md content, used as excerpt for propagated parent hits */
|
|
48
|
+
excerpt?: string;
|
|
47
49
|
/** Path to the _index.md file, e.g. "domain/topic/_index.md" */
|
|
48
50
|
path: string;
|
|
51
|
+
/** Frontmatter scoring parsed from _index.md — used to apply hotness/importance to propagated hits */
|
|
52
|
+
scoring?: {
|
|
53
|
+
importance?: number;
|
|
54
|
+
maturity?: string;
|
|
55
|
+
recency?: number;
|
|
56
|
+
};
|
|
49
57
|
tokenCount: number;
|
|
50
58
|
}
|
|
51
59
|
/**
|
|
@@ -44,7 +44,7 @@ export declare class SearchKnowledgeService implements ISearchKnowledgeService {
|
|
|
44
44
|
* Called during index rebuild to batch writes and avoid write amplification.
|
|
45
45
|
* Best-effort: errors are swallowed per file.
|
|
46
46
|
*/
|
|
47
|
-
flushAccessHits(contextTreePath: string): Promise<
|
|
47
|
+
flushAccessHits(contextTreePath: string): Promise<boolean>;
|
|
48
48
|
/**
|
|
49
49
|
* Search the knowledge base for relevant topics.
|
|
50
50
|
* Supports symbolic path queries, scoped search, kind/maturity filtering, and overview mode.
|