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
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { existsSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { basename, join } from 'node:path';
|
|
3
|
+
import { BRV_DIR, PROJECT_CONFIG_FILE, SOURCES_FILE } from '../../../constants.js';
|
|
4
|
+
import { getSourceStatuses, loadSources, SourcesFileSchema, } from './source-schema.js';
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Add source
|
|
7
|
+
// ============================================================================
|
|
8
|
+
/**
|
|
9
|
+
* Adds a read-only knowledge source from another project's context tree.
|
|
10
|
+
*
|
|
11
|
+
* Validates: target is a brv project, not self, not duplicate, not circular.
|
|
12
|
+
* Writes to `.brv/sources.json`.
|
|
13
|
+
*/
|
|
14
|
+
export function addSource(projectRoot, targetPath, alias) {
|
|
15
|
+
// 1. Local project must have .brv/
|
|
16
|
+
const localConfigPath = join(projectRoot, BRV_DIR, PROJECT_CONFIG_FILE);
|
|
17
|
+
if (!existsSync(localConfigPath)) {
|
|
18
|
+
return { message: `Current project has no .brv/ — run 'brv' first to initialize.`, success: false };
|
|
19
|
+
}
|
|
20
|
+
// 2. Resolve target to canonical path
|
|
21
|
+
let targetRoot;
|
|
22
|
+
try {
|
|
23
|
+
targetRoot = realpathSync(targetPath);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return { message: `Target path does not exist: ${targetPath}`, success: false };
|
|
27
|
+
}
|
|
28
|
+
// 3. Target must be a brv project
|
|
29
|
+
const targetConfigPath = join(targetRoot, BRV_DIR, PROJECT_CONFIG_FILE);
|
|
30
|
+
if (!existsSync(targetConfigPath)) {
|
|
31
|
+
return { message: `Target "${targetRoot}" is not a ByteRover project (no .brv/config.json).`, success: false };
|
|
32
|
+
}
|
|
33
|
+
// 4. Not self
|
|
34
|
+
let canonicalProjectRoot;
|
|
35
|
+
try {
|
|
36
|
+
canonicalProjectRoot = realpathSync(projectRoot);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
canonicalProjectRoot = projectRoot;
|
|
40
|
+
}
|
|
41
|
+
if (targetRoot === canonicalProjectRoot) {
|
|
42
|
+
return { message: 'Cannot add a source pointing to the current project.', success: false };
|
|
43
|
+
}
|
|
44
|
+
// 5. Read existing file — refuse to mutate if malformed
|
|
45
|
+
const existing = readSourcesFile(projectRoot);
|
|
46
|
+
if (existing.error) {
|
|
47
|
+
return { message: existing.error, success: false };
|
|
48
|
+
}
|
|
49
|
+
// 6. Not duplicate
|
|
50
|
+
const isDuplicate = existing.data.sources.some((source) => {
|
|
51
|
+
try {
|
|
52
|
+
return realpathSync(source.projectRoot) === targetRoot;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return source.projectRoot === targetRoot;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
if (isDuplicate) {
|
|
59
|
+
return { message: `Source "${targetRoot}" already added.`, success: false };
|
|
60
|
+
}
|
|
61
|
+
// 7. Not circular
|
|
62
|
+
if (detectCircularSource(canonicalProjectRoot, targetRoot)) {
|
|
63
|
+
return {
|
|
64
|
+
message: `Circular source detected: "${basename(targetRoot)}" already references this project as a source.`,
|
|
65
|
+
success: false,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// 8. Derive alias — reject empty/whitespace-only
|
|
69
|
+
if (alias !== undefined && alias.trim() === '') {
|
|
70
|
+
return { message: 'Alias must not be empty.', success: false };
|
|
71
|
+
}
|
|
72
|
+
const derivedAlias = alias ?? basename(targetRoot);
|
|
73
|
+
// 9. Ensure alias uniqueness — append suffix if collision
|
|
74
|
+
const finalAlias = ensureUniqueAlias(derivedAlias, existing.data.sources);
|
|
75
|
+
// 10. Append and write
|
|
76
|
+
const newSource = {
|
|
77
|
+
addedAt: new Date().toISOString(),
|
|
78
|
+
alias: finalAlias,
|
|
79
|
+
projectRoot: targetRoot,
|
|
80
|
+
readOnly: true,
|
|
81
|
+
};
|
|
82
|
+
existing.data.sources.push(newSource);
|
|
83
|
+
writeSourcesFile(projectRoot, existing.data);
|
|
84
|
+
return { message: `Added source "${targetRoot}" as "${finalAlias}".`, success: true };
|
|
85
|
+
}
|
|
86
|
+
// ============================================================================
|
|
87
|
+
// Remove source
|
|
88
|
+
// ============================================================================
|
|
89
|
+
/**
|
|
90
|
+
* Removes a knowledge source by alias or path.
|
|
91
|
+
*/
|
|
92
|
+
export function removeSource(projectRoot, aliasOrPath) {
|
|
93
|
+
const existing = readSourcesFile(projectRoot);
|
|
94
|
+
if (existing.error) {
|
|
95
|
+
return { message: existing.error, success: false };
|
|
96
|
+
}
|
|
97
|
+
if (existing.data.sources.length === 0) {
|
|
98
|
+
return { message: 'No knowledge sources configured.', success: false };
|
|
99
|
+
}
|
|
100
|
+
// Try match by alias first, then by canonical path
|
|
101
|
+
let matchIndex = existing.data.sources.findIndex((source) => source.alias === aliasOrPath);
|
|
102
|
+
if (matchIndex === -1) {
|
|
103
|
+
// Try matching by path
|
|
104
|
+
let canonicalTarget;
|
|
105
|
+
try {
|
|
106
|
+
canonicalTarget = realpathSync(aliasOrPath);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
canonicalTarget = aliasOrPath;
|
|
110
|
+
}
|
|
111
|
+
matchIndex = existing.data.sources.findIndex((source) => {
|
|
112
|
+
try {
|
|
113
|
+
return realpathSync(source.projectRoot) === canonicalTarget;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return source.projectRoot === canonicalTarget;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
if (matchIndex === -1) {
|
|
121
|
+
return { message: `No source found matching "${aliasOrPath}".`, success: false };
|
|
122
|
+
}
|
|
123
|
+
const removed = existing.data.sources.splice(matchIndex, 1)[0];
|
|
124
|
+
writeSourcesFile(projectRoot, existing.data);
|
|
125
|
+
return { message: `Removed source "${removed.alias}" (${removed.projectRoot}).`, success: true };
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Returns status for all sources in the project.
|
|
129
|
+
* Surfaces malformed file errors instead of silently returning empty.
|
|
130
|
+
*/
|
|
131
|
+
export function listSourceStatuses(projectRoot) {
|
|
132
|
+
const existing = readSourcesFile(projectRoot);
|
|
133
|
+
if (existing.error) {
|
|
134
|
+
return { error: existing.error, statuses: [] };
|
|
135
|
+
}
|
|
136
|
+
if (existing.data.sources.length === 0) {
|
|
137
|
+
return { statuses: [] };
|
|
138
|
+
}
|
|
139
|
+
return { statuses: getSourceStatuses(existing.data.sources) };
|
|
140
|
+
}
|
|
141
|
+
// ============================================================================
|
|
142
|
+
// Circular source detection
|
|
143
|
+
// ============================================================================
|
|
144
|
+
/**
|
|
145
|
+
* Checks if adding projectRoot → targetRoot would create a circular dependency.
|
|
146
|
+
* A circular reference exists if the target project already has a source pointing
|
|
147
|
+
* back to the current project (direct cycle only — no transitive check in v1).
|
|
148
|
+
*/
|
|
149
|
+
export function detectCircularSource(projectRoot, targetRoot) {
|
|
150
|
+
const targetSources = loadSources(targetRoot);
|
|
151
|
+
if (!targetSources) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
return targetSources.sources.some((source) => {
|
|
155
|
+
try {
|
|
156
|
+
return realpathSync(source.projectRoot) === projectRoot;
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return source.projectRoot === projectRoot;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function readSourcesFile(projectRoot) {
|
|
164
|
+
const filePath = join(projectRoot, BRV_DIR, SOURCES_FILE);
|
|
165
|
+
if (!existsSync(filePath)) {
|
|
166
|
+
return { data: { sources: [], version: 1 } };
|
|
167
|
+
}
|
|
168
|
+
let raw;
|
|
169
|
+
try {
|
|
170
|
+
raw = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return {
|
|
174
|
+
data: { sources: [], version: 1 },
|
|
175
|
+
error: `Malformed ${SOURCES_FILE}: file is not valid JSON. Back up or delete the file to recover.`,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const result = SourcesFileSchema.safeParse(raw);
|
|
179
|
+
if (!result.success) {
|
|
180
|
+
return {
|
|
181
|
+
data: { sources: [], version: 1 },
|
|
182
|
+
error: `Malformed ${SOURCES_FILE}: schema validation failed. Back up or delete the file to recover.`,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return { data: result.data };
|
|
186
|
+
}
|
|
187
|
+
function writeSourcesFile(projectRoot, data) {
|
|
188
|
+
const filePath = join(projectRoot, BRV_DIR, SOURCES_FILE);
|
|
189
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
190
|
+
}
|
|
191
|
+
function ensureUniqueAlias(baseAlias, existingSources) {
|
|
192
|
+
const existingAliases = new Set(existingSources.map((source) => source.alias));
|
|
193
|
+
if (!existingAliases.has(baseAlias)) {
|
|
194
|
+
return baseAlias;
|
|
195
|
+
}
|
|
196
|
+
let counter = 2;
|
|
197
|
+
while (existingAliases.has(`${baseAlias}-${counter}`)) {
|
|
198
|
+
counter++;
|
|
199
|
+
}
|
|
200
|
+
return `${baseAlias}-${counter}`;
|
|
201
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const SourceSchema: z.ZodObject<{
|
|
3
|
+
addedAt: z.ZodString;
|
|
4
|
+
alias: z.ZodString;
|
|
5
|
+
projectRoot: z.ZodString;
|
|
6
|
+
readOnly: z.ZodLiteral<true>;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
addedAt: string;
|
|
9
|
+
alias: string;
|
|
10
|
+
projectRoot: string;
|
|
11
|
+
readOnly: true;
|
|
12
|
+
}, {
|
|
13
|
+
addedAt: string;
|
|
14
|
+
alias: string;
|
|
15
|
+
projectRoot: string;
|
|
16
|
+
readOnly: true;
|
|
17
|
+
}>;
|
|
18
|
+
export declare const SourcesFileSchema: z.ZodObject<{
|
|
19
|
+
sources: z.ZodArray<z.ZodObject<{
|
|
20
|
+
addedAt: z.ZodString;
|
|
21
|
+
alias: z.ZodString;
|
|
22
|
+
projectRoot: z.ZodString;
|
|
23
|
+
readOnly: z.ZodLiteral<true>;
|
|
24
|
+
}, "strip", z.ZodTypeAny, {
|
|
25
|
+
addedAt: string;
|
|
26
|
+
alias: string;
|
|
27
|
+
projectRoot: string;
|
|
28
|
+
readOnly: true;
|
|
29
|
+
}, {
|
|
30
|
+
addedAt: string;
|
|
31
|
+
alias: string;
|
|
32
|
+
projectRoot: string;
|
|
33
|
+
readOnly: true;
|
|
34
|
+
}>, "many">;
|
|
35
|
+
version: z.ZodLiteral<1>;
|
|
36
|
+
}, "strip", z.ZodTypeAny, {
|
|
37
|
+
version: 1;
|
|
38
|
+
sources: {
|
|
39
|
+
addedAt: string;
|
|
40
|
+
alias: string;
|
|
41
|
+
projectRoot: string;
|
|
42
|
+
readOnly: true;
|
|
43
|
+
}[];
|
|
44
|
+
}, {
|
|
45
|
+
version: 1;
|
|
46
|
+
sources: {
|
|
47
|
+
addedAt: string;
|
|
48
|
+
alias: string;
|
|
49
|
+
projectRoot: string;
|
|
50
|
+
readOnly: true;
|
|
51
|
+
}[];
|
|
52
|
+
}>;
|
|
53
|
+
export type Source = z.infer<typeof SourceSchema>;
|
|
54
|
+
export type SourcesFile = z.infer<typeof SourcesFileSchema>;
|
|
55
|
+
export interface SearchOrigin {
|
|
56
|
+
alias?: string;
|
|
57
|
+
contextTreeRoot: string;
|
|
58
|
+
origin: 'local' | 'shared';
|
|
59
|
+
originKey: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Derives a stable, short origin key from a canonical path.
|
|
63
|
+
* Uses first 12 hex chars of SHA-256 to avoid alias-based collisions.
|
|
64
|
+
*/
|
|
65
|
+
export declare function deriveOriginKey(canonicalPath: string): string;
|
|
66
|
+
export interface LoadedSources {
|
|
67
|
+
mtime: number;
|
|
68
|
+
/** Search origins derived from valid sources (callers can search them) */
|
|
69
|
+
origins: SearchOrigin[];
|
|
70
|
+
/** All configured sources (including broken ones — for status display) */
|
|
71
|
+
sources: Source[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Loads and validates `.brv/sources.json` from a project root.
|
|
75
|
+
*
|
|
76
|
+
* Returns null if the file does not exist.
|
|
77
|
+
* Broken sources (target `.brv/` missing) are included in `sources` but excluded
|
|
78
|
+
* from `origins` — callers decide how to surface them (status vs search).
|
|
79
|
+
*/
|
|
80
|
+
export declare function loadSources(projectRoot: string): LoadedSources | null;
|
|
81
|
+
export interface SourceStatus {
|
|
82
|
+
alias: string;
|
|
83
|
+
contextTreeSize?: number;
|
|
84
|
+
projectRoot: string;
|
|
85
|
+
valid: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validates each source and returns status for display.
|
|
89
|
+
*
|
|
90
|
+
* A source is valid only when both `.brv/config.json` AND `.brv/context-tree/`
|
|
91
|
+
* exist — matching what loadSources() requires before including a source in
|
|
92
|
+
* search origins. When valid, `contextTreeSize` counts `.md` files.
|
|
93
|
+
*/
|
|
94
|
+
export declare function getSourceStatuses(sources: Source[]): SourceStatus[];
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { BRV_DIR, CONTEXT_TREE_DIR, PROJECT_CONFIG_FILE, SOURCES_FILE } from '../../../constants.js';
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Schema
|
|
8
|
+
// ============================================================================
|
|
9
|
+
export const SourceSchema = z.object({
|
|
10
|
+
addedAt: z.string(),
|
|
11
|
+
alias: z.string().min(1),
|
|
12
|
+
projectRoot: z.string().min(1),
|
|
13
|
+
readOnly: z.literal(true),
|
|
14
|
+
});
|
|
15
|
+
export const SourcesFileSchema = z.object({
|
|
16
|
+
sources: z.array(SourceSchema),
|
|
17
|
+
version: z.literal(1),
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* Derives a stable, short origin key from a canonical path.
|
|
21
|
+
* Uses first 12 hex chars of SHA-256 to avoid alias-based collisions.
|
|
22
|
+
*/
|
|
23
|
+
export function deriveOriginKey(canonicalPath) {
|
|
24
|
+
return createHash('sha256').update(canonicalPath).digest('hex').slice(0, 12);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Loads and validates `.brv/sources.json` from a project root.
|
|
28
|
+
*
|
|
29
|
+
* Returns null if the file does not exist.
|
|
30
|
+
* Broken sources (target `.brv/` missing) are included in `sources` but excluded
|
|
31
|
+
* from `origins` — callers decide how to surface them (status vs search).
|
|
32
|
+
*/
|
|
33
|
+
export function loadSources(projectRoot) {
|
|
34
|
+
const filePath = join(projectRoot, BRV_DIR, SOURCES_FILE);
|
|
35
|
+
if (!existsSync(filePath)) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const mtime = statSync(filePath).mtimeMs;
|
|
39
|
+
let raw;
|
|
40
|
+
try {
|
|
41
|
+
raw = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return { mtime, origins: [], sources: [] };
|
|
45
|
+
}
|
|
46
|
+
const result = SourcesFileSchema.safeParse(raw);
|
|
47
|
+
if (!result.success) {
|
|
48
|
+
return { mtime, origins: [], sources: [] };
|
|
49
|
+
}
|
|
50
|
+
const origins = [];
|
|
51
|
+
for (const source of result.data.sources) {
|
|
52
|
+
const targetConfigPath = join(source.projectRoot, BRV_DIR, PROJECT_CONFIG_FILE);
|
|
53
|
+
if (!existsSync(targetConfigPath)) {
|
|
54
|
+
// Broken source — skip from origins but keep in sources for status display
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
let canonicalRoot;
|
|
58
|
+
try {
|
|
59
|
+
canonicalRoot = realpathSync(source.projectRoot);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const contextTreeRoot = join(canonicalRoot, BRV_DIR, CONTEXT_TREE_DIR);
|
|
65
|
+
if (!existsSync(contextTreeRoot)) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
origins.push({
|
|
69
|
+
alias: source.alias,
|
|
70
|
+
contextTreeRoot,
|
|
71
|
+
origin: 'shared',
|
|
72
|
+
originKey: deriveOriginKey(canonicalRoot),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return { mtime, origins, sources: result.data.sources };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Validates each source and returns status for display.
|
|
79
|
+
*
|
|
80
|
+
* A source is valid only when both `.brv/config.json` AND `.brv/context-tree/`
|
|
81
|
+
* exist — matching what loadSources() requires before including a source in
|
|
82
|
+
* search origins. When valid, `contextTreeSize` counts `.md` files.
|
|
83
|
+
*/
|
|
84
|
+
export function getSourceStatuses(sources) {
|
|
85
|
+
return sources.map((source) => {
|
|
86
|
+
const targetConfigPath = join(source.projectRoot, BRV_DIR, PROJECT_CONFIG_FILE);
|
|
87
|
+
const targetContextTree = join(source.projectRoot, BRV_DIR, CONTEXT_TREE_DIR);
|
|
88
|
+
const valid = existsSync(targetConfigPath) && existsSync(targetContextTree);
|
|
89
|
+
let contextTreeSize;
|
|
90
|
+
if (valid) {
|
|
91
|
+
contextTreeSize = countMarkdownFiles(targetContextTree);
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
alias: source.alias,
|
|
95
|
+
contextTreeSize,
|
|
96
|
+
projectRoot: source.projectRoot,
|
|
97
|
+
valid,
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Recursively counts .md files in a directory.
|
|
103
|
+
*/
|
|
104
|
+
function countMarkdownFiles(dir) {
|
|
105
|
+
let count = 0;
|
|
106
|
+
try {
|
|
107
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
108
|
+
for (const entry of entries) {
|
|
109
|
+
if (entry.isDirectory()) {
|
|
110
|
+
count += countMarkdownFiles(join(dir, entry.name));
|
|
111
|
+
}
|
|
112
|
+
else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
113
|
+
count++;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Directory unreadable — return 0
|
|
119
|
+
}
|
|
120
|
+
return count;
|
|
121
|
+
}
|
|
@@ -515,24 +515,28 @@ export declare const TaskExecuteSchema: z.ZodObject<{
|
|
|
515
515
|
taskId: z.ZodString;
|
|
516
516
|
/** Task type */
|
|
517
517
|
type: z.ZodEnum<["curate", "curate-folder", "query"]>;
|
|
518
|
+
/** Workspace root for scoped query/curate */
|
|
519
|
+
worktreeRoot: z.ZodOptional<z.ZodString>;
|
|
518
520
|
}, "strip", z.ZodTypeAny, {
|
|
519
521
|
type: "curate" | "query" | "curate-folder";
|
|
520
522
|
content: string;
|
|
521
523
|
taskId: string;
|
|
522
524
|
clientId: string;
|
|
523
525
|
files?: string[] | undefined;
|
|
524
|
-
projectPath?: string | undefined;
|
|
525
|
-
folderPath?: string | undefined;
|
|
526
526
|
clientCwd?: string | undefined;
|
|
527
|
+
folderPath?: string | undefined;
|
|
528
|
+
projectPath?: string | undefined;
|
|
529
|
+
worktreeRoot?: string | undefined;
|
|
527
530
|
}, {
|
|
528
531
|
type: "curate" | "query" | "curate-folder";
|
|
529
532
|
content: string;
|
|
530
533
|
taskId: string;
|
|
531
534
|
clientId: string;
|
|
532
535
|
files?: string[] | undefined;
|
|
533
|
-
projectPath?: string | undefined;
|
|
534
|
-
folderPath?: string | undefined;
|
|
535
536
|
clientCwd?: string | undefined;
|
|
537
|
+
folderPath?: string | undefined;
|
|
538
|
+
projectPath?: string | undefined;
|
|
539
|
+
worktreeRoot?: string | undefined;
|
|
536
540
|
}>;
|
|
537
541
|
/**
|
|
538
542
|
* task:cancel - Transport tells Agent to cancel a task
|
|
@@ -689,15 +693,15 @@ export declare const TaskCreatedSchema: z.ZodObject<{
|
|
|
689
693
|
content: string;
|
|
690
694
|
taskId: string;
|
|
691
695
|
files?: string[] | undefined;
|
|
692
|
-
folderPath?: string | undefined;
|
|
693
696
|
clientCwd?: string | undefined;
|
|
697
|
+
folderPath?: string | undefined;
|
|
694
698
|
}, {
|
|
695
699
|
type: "curate" | "query" | "curate-folder";
|
|
696
700
|
content: string;
|
|
697
701
|
taskId: string;
|
|
698
702
|
files?: string[] | undefined;
|
|
699
|
-
folderPath?: string | undefined;
|
|
700
703
|
clientCwd?: string | undefined;
|
|
704
|
+
folderPath?: string | undefined;
|
|
701
705
|
}>;
|
|
702
706
|
/**
|
|
703
707
|
* task:started - Agent begins processing the task
|
|
@@ -960,22 +964,26 @@ export declare const TaskCreateRequestSchema: z.ZodObject<{
|
|
|
960
964
|
taskId: z.ZodString;
|
|
961
965
|
/** Task type */
|
|
962
966
|
type: z.ZodEnum<["curate", "curate-folder", "query"]>;
|
|
967
|
+
/** Workspace root for scoped query/curate (stable linked root or projectRoot if unlinked) */
|
|
968
|
+
worktreeRoot: z.ZodOptional<z.ZodString>;
|
|
963
969
|
}, "strip", z.ZodTypeAny, {
|
|
964
970
|
type: "curate" | "query" | "curate-folder";
|
|
965
971
|
content: string;
|
|
966
972
|
taskId: string;
|
|
967
973
|
files?: string[] | undefined;
|
|
968
|
-
projectPath?: string | undefined;
|
|
969
|
-
folderPath?: string | undefined;
|
|
970
974
|
clientCwd?: string | undefined;
|
|
975
|
+
folderPath?: string | undefined;
|
|
976
|
+
projectPath?: string | undefined;
|
|
977
|
+
worktreeRoot?: string | undefined;
|
|
971
978
|
}, {
|
|
972
979
|
type: "curate" | "query" | "curate-folder";
|
|
973
980
|
content: string;
|
|
974
981
|
taskId: string;
|
|
975
982
|
files?: string[] | undefined;
|
|
976
|
-
projectPath?: string | undefined;
|
|
977
|
-
folderPath?: string | undefined;
|
|
978
983
|
clientCwd?: string | undefined;
|
|
984
|
+
folderPath?: string | undefined;
|
|
985
|
+
projectPath?: string | undefined;
|
|
986
|
+
worktreeRoot?: string | undefined;
|
|
979
987
|
}>;
|
|
980
988
|
/**
|
|
981
989
|
* Response after task creation
|
|
@@ -315,6 +315,8 @@ export const TaskExecuteSchema = z.object({
|
|
|
315
315
|
taskId: z.string(),
|
|
316
316
|
/** Task type */
|
|
317
317
|
type: z.enum(['curate', 'curate-folder', 'query']),
|
|
318
|
+
/** Workspace root for scoped query/curate */
|
|
319
|
+
worktreeRoot: z.string().optional(),
|
|
318
320
|
});
|
|
319
321
|
/**
|
|
320
322
|
* task:cancel - Transport tells Agent to cancel a task
|
|
@@ -492,6 +494,8 @@ export const TaskCreateRequestSchema = z.object({
|
|
|
492
494
|
taskId: z.string().uuid('Invalid taskId format - must be UUID'),
|
|
493
495
|
/** Task type */
|
|
494
496
|
type: TaskTypeSchema,
|
|
497
|
+
/** Workspace root for scoped query/curate (stable linked root or projectRoot if unlinked) */
|
|
498
|
+
worktreeRoot: z.string().optional(),
|
|
495
499
|
});
|
|
496
500
|
/**
|
|
497
501
|
* Response after task creation
|
|
@@ -114,4 +114,17 @@ export interface IClientManager {
|
|
|
114
114
|
* @param clientId - The client's Socket.IO ID
|
|
115
115
|
*/
|
|
116
116
|
unregister(clientId: string): void;
|
|
117
|
+
/**
|
|
118
|
+
* Update a client's project path, even if already associated.
|
|
119
|
+
* Used for reassociation after worktree add/remove operations.
|
|
120
|
+
* Moves the client from the old project index to the new one,
|
|
121
|
+
* and fires onProjectEmpty if the old project has no remaining external clients.
|
|
122
|
+
*
|
|
123
|
+
* No-op if client is unknown.
|
|
124
|
+
*
|
|
125
|
+
* @param clientId - The client's Socket.IO ID
|
|
126
|
+
* @param newProjectPath - The new project path to associate
|
|
127
|
+
* @returns The previous project path (undefined if client not found or not previously associated)
|
|
128
|
+
*/
|
|
129
|
+
updateProjectPath(clientId: string, newProjectPath: string): string | undefined;
|
|
117
130
|
}
|
|
@@ -10,8 +10,12 @@ export interface CurateExecuteOptions {
|
|
|
10
10
|
content: string;
|
|
11
11
|
/** Optional file paths for --files flag */
|
|
12
12
|
files?: string[];
|
|
13
|
+
/** Canonical project root where .brv/ lives (for post-processing: snapshot, summary, manifest) */
|
|
14
|
+
projectRoot?: string;
|
|
13
15
|
/** Task ID for event routing (required for concurrent task isolation) */
|
|
14
16
|
taskId: string;
|
|
17
|
+
/** Workspace root — linked subdir or same as projectRoot for direct projects */
|
|
18
|
+
worktreeRoot?: string;
|
|
15
19
|
}
|
|
16
20
|
/**
|
|
17
21
|
* ICurateExecutor - Executes curate tasks with an injected CipherAgent.
|
|
@@ -4,14 +4,18 @@ import type { ICipherAgent } from '../../../../agent/core/interfaces/i-cipher-ag
|
|
|
4
4
|
* Agent uses its default session (Single-Session pattern).
|
|
5
5
|
*/
|
|
6
6
|
export interface FolderPackExecuteOptions {
|
|
7
|
-
/** Client's working directory for resolving relative paths */
|
|
7
|
+
/** Client's working directory for resolving relative paths (shell semantics) */
|
|
8
8
|
clientCwd?: string;
|
|
9
9
|
/** Optional context to guide the analysis */
|
|
10
10
|
content?: string;
|
|
11
|
-
/** Folder path to pack (relative to clientCwd or absolute) */
|
|
12
|
-
folderPath
|
|
11
|
+
/** Folder path to pack (relative to clientCwd or absolute). When absent, defaults to worktreeRoot. */
|
|
12
|
+
folderPath?: string;
|
|
13
|
+
/** Canonical project root where .brv/ lives (for temp file location) */
|
|
14
|
+
projectRoot?: string;
|
|
13
15
|
/** Task ID for event routing (required for concurrent task isolation) */
|
|
14
16
|
taskId: string;
|
|
17
|
+
/** Workspace root — linked subdir or same as projectRoot. Used as default folderPath when none supplied. */
|
|
18
|
+
worktreeRoot?: string;
|
|
15
19
|
}
|
|
16
20
|
/**
|
|
17
21
|
* IFolderPackExecutor - Executes folder pack + curate tasks with an injected CipherAgent.
|
|
@@ -8,6 +8,8 @@ export interface QueryExecuteOptions {
|
|
|
8
8
|
query: string;
|
|
9
9
|
/** Task ID for event routing (required for concurrent task isolation) */
|
|
10
10
|
taskId: string;
|
|
11
|
+
/** Stable workspace root for scoping search and cache isolation */
|
|
12
|
+
worktreeRoot?: string;
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* IQueryExecutor - Executes query tasks with an injected CipherAgent.
|
|
@@ -41,6 +41,7 @@ export declare class ClientManager implements IClientManager {
|
|
|
41
41
|
register(clientId: string, type: ClientType, projectPath?: string): void;
|
|
42
42
|
setAgentName(clientId: string, agentName: string): void;
|
|
43
43
|
unregister(clientId: string): void;
|
|
44
|
+
updateProjectPath(clientId: string, newProjectPath: string): string | undefined;
|
|
44
45
|
private addToProjectIndex;
|
|
45
46
|
/**
|
|
46
47
|
* Check if a project has no remaining external clients.
|
|
@@ -111,6 +111,22 @@ export class ClientManager {
|
|
|
111
111
|
// Notify idle timeout policy
|
|
112
112
|
this.clientDisconnectedCallback?.();
|
|
113
113
|
}
|
|
114
|
+
updateProjectPath(clientId, newProjectPath) {
|
|
115
|
+
const client = this.clients.get(clientId);
|
|
116
|
+
if (!client)
|
|
117
|
+
return undefined;
|
|
118
|
+
const oldPath = client.updateProjectPath(newProjectPath);
|
|
119
|
+
// Move between project indexes
|
|
120
|
+
if (oldPath) {
|
|
121
|
+
this.removeFromProjectIndex(clientId, oldPath);
|
|
122
|
+
}
|
|
123
|
+
this.addToProjectIndex(clientId, newProjectPath);
|
|
124
|
+
// Check if old project is now empty
|
|
125
|
+
if (oldPath && oldPath !== newProjectPath && client.isExternalClient) {
|
|
126
|
+
this.checkProjectEmpty(oldPath);
|
|
127
|
+
}
|
|
128
|
+
return oldPath;
|
|
129
|
+
}
|
|
114
130
|
addToProjectIndex(clientId, projectPath) {
|
|
115
131
|
let members = this.projectClients.get(projectPath);
|
|
116
132
|
if (!members) {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - isArchiveStub() — searchable stubs (included in BM25 index and fingerprint)
|
|
7
7
|
* - isExcludedFromSync() — union of above (excluded from snapshot/sync/merge/push)
|
|
8
8
|
*/
|
|
9
|
-
import { ARCHIVE_DIR, FULL_ARCHIVE_EXTENSION, MANIFEST_FILE, STUB_EXTENSION, SUMMARY_INDEX_FILE } from '../../constants.js';
|
|
9
|
+
import { ABSTRACT_EXTENSION, ARCHIVE_DIR, FULL_ARCHIVE_EXTENSION, MANIFEST_FILE, OVERVIEW_EXTENSION, STUB_EXTENSION, SUMMARY_INDEX_FILE } from '../../constants.js';
|
|
10
10
|
import { toUnixPath } from './path-utils.js';
|
|
11
11
|
/**
|
|
12
12
|
* Returns true if the given relative path is a derived artifact
|
|
@@ -24,6 +24,10 @@ export function isDerivedArtifact(relativePath) {
|
|
|
24
24
|
return true;
|
|
25
25
|
if (fileName === MANIFEST_FILE)
|
|
26
26
|
return true;
|
|
27
|
+
if (fileName.endsWith(ABSTRACT_EXTENSION))
|
|
28
|
+
return true;
|
|
29
|
+
if (fileName.endsWith(OVERVIEW_EXTENSION))
|
|
30
|
+
return true;
|
|
27
31
|
if (segments.includes(ARCHIVE_DIR) && fileName.endsWith(FULL_ARCHIVE_EXTENSION))
|
|
28
32
|
return true;
|
|
29
33
|
return false;
|
|
@@ -44,7 +44,8 @@ export declare class FileContextTreeManifestService implements IContextTreeManif
|
|
|
44
44
|
private scanForManifest;
|
|
45
45
|
/**
|
|
46
46
|
* Recursively collect stat data for all source files (for fingerprint).
|
|
47
|
-
* Excludes derived artifacts.
|
|
47
|
+
* Excludes derived artifacts except .abstract.md siblings, which are included
|
|
48
|
+
* so abstract generation invalidates the manifest without a second tree walk.
|
|
48
49
|
*/
|
|
49
50
|
private scanSourceStats;
|
|
50
51
|
}
|