@amoree-code/ocean 0.0.0-stage → 1.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/LICENSE +21 -0
- package/README.md +284 -2
- package/dist/application/auth/auth-orchestrator.js +77 -0
- package/dist/application/clients/clients.js +646 -0
- package/dist/application/clients/editors.js +398 -0
- package/dist/application/clients/hooks.js +335 -0
- package/dist/application/clients/native.js +410 -0
- package/dist/application/clients/opencode-plugin.js +158 -0
- package/dist/application/clients/registry.js +188 -0
- package/dist/application/gateway/webhook-gateway.js +137 -0
- package/dist/application/install/provider-installer.js +184 -0
- package/dist/application/integrations/charter-copies.js +76 -0
- package/dist/application/integrations/claude-vscode.js +106 -0
- package/dist/application/integrations/engine-entry-repair.js +105 -0
- package/dist/application/integrations/skill-filter.js +254 -0
- package/dist/application/layout/layout-apply.js +443 -0
- package/dist/application/layout/layout-plan.js +236 -0
- package/dist/application/layout/workspace-template.js +34 -0
- package/dist/application/onboarding/state.js +23 -0
- package/dist/application/onboarding/steps.js +249 -0
- package/dist/application/profiles/profile-distribution.js +53 -0
- package/dist/application/profiles/profile-suggestion.js +39 -0
- package/dist/application/profiles/profile-upgrade-service.js +208 -0
- package/dist/application/pull/history-index.js +476 -0
- package/dist/application/pull/project-match.js +132 -0
- package/dist/application/pull/pull.js +202 -0
- package/dist/application/pull/server-capture.js +307 -0
- package/dist/application/scheduler/local-scheduler.js +210 -0
- package/dist/composition/runtime.js +64 -0
- package/dist/engine-root.js +9 -0
- package/dist/infrastructure/filesystem/profile-loader.js +46 -0
- package/dist/infrastructure/filesystem/runtime-config-loader.js +29 -0
- package/dist/infrastructure/filesystem/skill-loader.js +84 -0
- package/dist/infrastructure/filesystem/workspace-capability.js +37 -0
- package/dist/infrastructure/observability/runtime-logger.js +16 -0
- package/dist/infrastructure/process/agent-tool-executor.js +99 -0
- package/dist/infrastructure/process/cli-process.js +49 -0
- package/dist/infrastructure/process/interactive-process.js +145 -0
- package/dist/infrastructure/process/process-supervisor.js +194 -0
- package/dist/infrastructure/process/service.js +252 -0
- package/dist/infrastructure/providers/browser-provider.js +2 -0
- package/dist/infrastructure/providers/claude-sdk.js +137 -0
- package/dist/infrastructure/providers/client-home.js +14 -0
- package/dist/infrastructure/providers/fake-browser-provider.js +146 -0
- package/dist/infrastructure/providers/ollama-embedder.js +74 -0
- package/dist/infrastructure/providers/openai-compatible.js +268 -0
- package/dist/infrastructure/providers/playwright-browser-provider.js +380 -0
- package/dist/infrastructure/providers/provider-capabilities.js +50 -0
- package/dist/infrastructure/providers/provider-invocation.js +184 -0
- package/dist/infrastructure/providers/provider-registry.js +77 -0
- package/dist/infrastructure/providers/providers.js +134 -0
- package/dist/infrastructure/wrappers/wrapper-manager.js +374 -0
- package/dist/interfaces/cli/auth-command.js +54 -0
- package/dist/interfaces/cli/browser-bridge-command.js +93 -0
- package/dist/interfaces/cli/browser-command.js +119 -0
- package/dist/interfaces/cli/capture-command.js +125 -0
- package/dist/interfaces/cli/client-test-command.js +133 -0
- package/dist/interfaces/cli/clients-command.js +129 -0
- package/dist/interfaces/cli/command-registry.js +1211 -0
- package/dist/interfaces/cli/context-command.js +172 -0
- package/dist/interfaces/cli/daily-command.js +131 -0
- package/dist/interfaces/cli/governance-command.js +55 -0
- package/dist/interfaces/cli/handoff-command.js +35 -0
- package/dist/interfaces/cli/help-command.js +207 -0
- package/dist/interfaces/cli/idea-command.js +47 -0
- package/dist/interfaces/cli/intercept-command.js +317 -0
- package/dist/interfaces/cli/layout-command.js +27 -0
- package/dist/interfaces/cli/market-command.js +52 -0
- package/dist/interfaces/cli/mcp-gateway-command.js +30 -0
- package/dist/interfaces/cli/memory-command.js +317 -0
- package/dist/interfaces/cli/migrate-command.js +26 -0
- package/dist/interfaces/cli/observe-command.js +78 -0
- package/dist/interfaces/cli/onboard-command.js +123 -0
- package/dist/interfaces/cli/operate-command.js +59 -0
- package/dist/interfaces/cli/profiles-command.js +70 -0
- package/dist/interfaces/cli/pull-command.js +54 -0
- package/dist/interfaces/cli/setup-command.js +143 -0
- package/dist/interfaces/cli/tasks-command.js +249 -0
- package/dist/main.js +49 -0
- package/dist/node-warnings.js +17 -0
- package/dist/version.js +8 -0
- package/market/mcp/chrome-devtools/ocean.capability.json +28 -0
- package/market/mcp/gateway/ocean.capability.json +21 -0
- package/market/mcp/obsidian/ocean.capability.json +21 -0
- package/market/mcp/ocean/ocean.capability.json +21 -0
- package/market/mcp/playwright/ocean.capability.json +23 -0
- package/node_modules/@ocean/brain/dist/brain/brain-eval.js +29 -0
- package/node_modules/@ocean/brain/dist/brain/brain-markdown.js +226 -0
- package/node_modules/@ocean/brain/dist/brain/brain-reindex.js +247 -0
- package/node_modules/@ocean/brain/dist/brain/brain-service.js +320 -0
- package/node_modules/@ocean/brain/dist/capture/inbox-sync.js +22 -0
- package/node_modules/@ocean/brain/dist/evidence/verification-report.js +13 -0
- package/node_modules/@ocean/brain/dist/infrastructure/brain/brain-index.js +285 -0
- package/node_modules/@ocean/brain/dist/memory/brain-dump.js +145 -0
- package/node_modules/@ocean/brain/dist/memory/daily-narrative.js +142 -0
- package/node_modules/@ocean/brain/dist/memory/index-sync.js +124 -0
- package/node_modules/@ocean/brain/dist/memory/lesson-activation.js +93 -0
- package/node_modules/@ocean/brain/dist/memory/lesson-learning.js +188 -0
- package/node_modules/@ocean/brain/dist/memory/lessons.js +321 -0
- package/node_modules/@ocean/brain/dist/memory/memory-audit.js +100 -0
- package/node_modules/@ocean/brain/dist/memory/memory-digest.js +49 -0
- package/node_modules/@ocean/brain/dist/memory/memory-harvest.js +345 -0
- package/node_modules/@ocean/brain/dist/memory/memory-paths.js +174 -0
- package/node_modules/@ocean/brain/dist/memory/memory-review.js +278 -0
- package/node_modules/@ocean/brain/dist/memory/memory-trash.js +152 -0
- package/node_modules/@ocean/brain/dist/memory/memory-write.js +216 -0
- package/node_modules/@ocean/brain/dist/memory/model-narrative.js +129 -0
- package/node_modules/@ocean/brain/dist/memory/profile-facts.js +103 -0
- package/node_modules/@ocean/brain/dist/memory/session-promotion.js +61 -0
- package/node_modules/@ocean/brain/dist/memory/session-summary.js +113 -0
- package/node_modules/@ocean/brain/package.json +11 -0
- package/node_modules/@ocean/context/dist/context/context-compression.js +66 -0
- package/node_modules/@ocean/context/dist/context/context-cost.js +587 -0
- package/node_modules/@ocean/context/dist/context/context-ladder.js +363 -0
- package/node_modules/@ocean/context/dist/context/context-packet.js +213 -0
- package/node_modules/@ocean/context/dist/context/context-references.js +255 -0
- package/node_modules/@ocean/context/dist/context/intent-router.js +259 -0
- package/node_modules/@ocean/context/dist/context/resource-injection.js +36 -0
- package/node_modules/@ocean/context/package.json +11 -0
- package/node_modules/@ocean/core/dist/domain/brain/brain-record.js +22 -0
- package/node_modules/@ocean/core/dist/domain/brain/record-scope.js +22 -0
- package/node_modules/@ocean/core/dist/domain/capabilities/browser-contract.js +64 -0
- package/node_modules/@ocean/core/dist/domain/capabilities/capability-contract.js +16 -0
- package/node_modules/@ocean/core/dist/domain/capabilities/capability-manifest.js +72 -0
- package/node_modules/@ocean/core/dist/domain/context/context-validator.js +5 -0
- package/node_modules/@ocean/core/dist/domain/context/context.js +34 -0
- package/node_modules/@ocean/core/dist/domain/context/project-id.js +30 -0
- package/node_modules/@ocean/core/dist/domain/evidence/evidence.js +16 -0
- package/node_modules/@ocean/core/dist/domain/mcp/mcp-contract.js +36 -0
- package/node_modules/@ocean/core/dist/domain/mcp/memory-contract.js +41 -0
- package/node_modules/@ocean/core/dist/domain/ports/brain-index-port.js +7 -0
- package/node_modules/@ocean/core/dist/domain/ports/browser-port.js +8 -0
- package/node_modules/@ocean/core/dist/domain/ports/embedder-port.js +2 -0
- package/node_modules/@ocean/core/dist/domain/ports/platform-ports.js +2 -0
- package/node_modules/@ocean/core/dist/domain/ports/runtime-ports.js +2 -0
- package/node_modules/@ocean/core/dist/domain/ports/session-store-port.js +2 -0
- package/node_modules/@ocean/core/dist/domain/process/process-events.js +5 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-distribution.js +19 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-policy.js +34 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-selection.js +73 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-upgrade.js +123 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-validator.js +5 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile.js +218 -0
- package/node_modules/@ocean/core/dist/domain/profiles/runtime-config.js +30 -0
- package/node_modules/@ocean/core/dist/domain/providers/provider-capability.js +28 -0
- package/node_modules/@ocean/core/dist/domain/providers/provider.js +2 -0
- package/node_modules/@ocean/core/dist/domain/redaction/redaction.js +86 -0
- package/node_modules/@ocean/core/dist/domain/runs/agent-tools.js +92 -0
- package/node_modules/@ocean/core/dist/domain/runs/execution-plan.js +71 -0
- package/node_modules/@ocean/core/dist/domain/runs/run-contract.js +38 -0
- package/node_modules/@ocean/core/dist/domain/sessions/entry-contract.js +31 -0
- package/node_modules/@ocean/core/dist/domain/sessions/handoff.js +78 -0
- package/node_modules/@ocean/core/dist/domain/sessions/session.js +58 -0
- package/node_modules/@ocean/core/dist/domain/skills/default-skills.js +29 -0
- package/node_modules/@ocean/core/dist/domain/skills/skill-validator.js +5 -0
- package/node_modules/@ocean/core/dist/domain/skills/skill.js +11 -0
- package/node_modules/@ocean/core/dist/domain/tasks/task-id.js +85 -0
- package/node_modules/@ocean/core/dist/shared/fs-utils.js +212 -0
- package/node_modules/@ocean/core/dist/shared/local-date.js +16 -0
- package/node_modules/@ocean/core/dist/shared/paths.js +324 -0
- package/node_modules/@ocean/core/dist/shared/project-resolution.js +1065 -0
- package/node_modules/@ocean/core/dist/shared/task-reference.js +133 -0
- package/node_modules/@ocean/core/package.json +14 -0
- package/node_modules/@ocean/loop/dist/handoff/handoff-service.js +133 -0
- package/node_modules/@ocean/loop/dist/hooks/lifecycle-hooks.js +15 -0
- package/node_modules/@ocean/loop/dist/hooks/session-end-hook.js +183 -0
- package/node_modules/@ocean/loop/dist/hooks/session-start-hook.js +237 -0
- package/node_modules/@ocean/loop/dist/infrastructure/persistence/session-store.js +690 -0
- package/node_modules/@ocean/loop/dist/infrastructure/persistence/session-validator.js +5 -0
- package/node_modules/@ocean/loop/dist/loops/iteration-graph.js +147 -0
- package/node_modules/@ocean/loop/dist/loops/task-loop-record.js +2 -0
- package/node_modules/@ocean/loop/dist/loops/task-loop.js +281 -0
- package/node_modules/@ocean/loop/dist/operations/memory-knowledge-operations.js +263 -0
- package/node_modules/@ocean/loop/dist/operations/operation-contract.js +227 -0
- package/node_modules/@ocean/loop/dist/operations/operation-scope.js +28 -0
- package/node_modules/@ocean/loop/dist/operations/project-operations.js +93 -0
- package/node_modules/@ocean/loop/dist/operations/record-io.js +65 -0
- package/node_modules/@ocean/loop/dist/operations/record-operations.js +45 -0
- package/node_modules/@ocean/loop/dist/operations/task-linker.js +40 -0
- package/node_modules/@ocean/loop/dist/operations/task-operations.js +279 -0
- package/node_modules/@ocean/loop/dist/operations/write-guard.js +157 -0
- package/node_modules/@ocean/loop/dist/runs/prompt-assembly.js +112 -0
- package/node_modules/@ocean/loop/dist/runs/run-agent.js +425 -0
- package/node_modules/@ocean/loop/dist/runs/run-authorization.js +24 -0
- package/node_modules/@ocean/loop/dist/runs/tool-loop.js +173 -0
- package/node_modules/@ocean/loop/dist/sessions/session-closeout.js +150 -0
- package/node_modules/@ocean/loop/dist/sessions/session-pointer.js +133 -0
- package/node_modules/@ocean/loop/dist/sessions/session-projects.js +26 -0
- package/node_modules/@ocean/loop/dist/sessions/session-retention.js +132 -0
- package/node_modules/@ocean/loop/dist/tasks/archive-tasks.js +210 -0
- package/node_modules/@ocean/loop/dist/tasks/checkpoint-task.js +31 -0
- package/node_modules/@ocean/loop/dist/tasks/claim-task.js +126 -0
- package/node_modules/@ocean/loop/dist/tasks/list-tasks.js +113 -0
- package/node_modules/@ocean/loop/dist/tasks/new-task.js +42 -0
- package/node_modules/@ocean/loop/dist/tasks/renumber-tasks.js +134 -0
- package/node_modules/@ocean/loop/dist/tasks/verification-readiness.js +78 -0
- package/node_modules/@ocean/loop/dist/tasks/verify-task.js +234 -0
- package/node_modules/@ocean/loop/package.json +11 -0
- package/node_modules/@ocean/market/dist/adapters/mcp-adapter.js +64 -0
- package/node_modules/@ocean/market/dist/adapters/owned-directory.js +89 -0
- package/node_modules/@ocean/market/dist/adapters/plugin-adapter.js +39 -0
- package/node_modules/@ocean/market/dist/adapters/skill-adapter.js +32 -0
- package/node_modules/@ocean/market/dist/capability/adapter.js +2 -0
- package/node_modules/@ocean/market/dist/capability/catalog.js +63 -0
- package/node_modules/@ocean/market/dist/capability/content-digest.js +40 -0
- package/node_modules/@ocean/market/dist/capability/registry.js +81 -0
- package/node_modules/@ocean/market/dist/install/default-marketplace.js +47 -0
- package/node_modules/@ocean/market/dist/install/lock.js +34 -0
- package/node_modules/@ocean/market/dist/install/marketplace-service.js +165 -0
- package/node_modules/@ocean/market/dist/mcp/browser-bridge.js +263 -0
- package/node_modules/@ocean/market/dist/mcp/mcp-connection.js +67 -0
- package/node_modules/@ocean/market/dist/mcp/mcp-gateway.js +317 -0
- package/node_modules/@ocean/market/dist/mcp/mcp-launch.js +102 -0
- package/node_modules/@ocean/market/dist/mcp/mcp-runner.js +32 -0
- package/node_modules/@ocean/market/dist/security/description-scan.js +45 -0
- package/node_modules/@ocean/market/dist/security/permissions.js +57 -0
- package/node_modules/@ocean/market/dist/skills/core-skill-sync.js +113 -0
- package/node_modules/@ocean/market/dist/skills/signal-detection.js +84 -0
- package/node_modules/@ocean/market/dist/skills/skill-hub.js +220 -0
- package/node_modules/@ocean/market/package.json +11 -0
- package/node_modules/@ocean/services/dist/browser/browser-service.js +124 -0
- package/node_modules/@ocean/services/dist/browser/browser-session.js +287 -0
- package/node_modules/@ocean/services/dist/browser/browser-task-runner.js +70 -0
- package/node_modules/@ocean/services/dist/doctor/workspace-doctor.js +547 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/call-ledger.js +114 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/mcp-client.js +162 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/obsidian-server.js +251 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/ocean-server.js +880 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/server-version.js +20 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/tool-registry.js +29 -0
- package/node_modules/@ocean/services/dist/obsidian/conflict-log.js +68 -0
- package/node_modules/@ocean/services/dist/obsidian/inbox-promotion.js +80 -0
- package/node_modules/@ocean/services/dist/obsidian/vault-discovery.js +113 -0
- package/node_modules/@ocean/services/dist/obsidian/vault-ingestion.js +77 -0
- package/node_modules/@ocean/services/dist/obsidian/vault-sync.js +73 -0
- package/node_modules/@ocean/services/dist/obsidian/vault-writer.js +75 -0
- package/node_modules/@ocean/services/package.json +11 -0
- package/ocean-marketplace.json +415 -0
- package/package.json +67 -4
- package/skills/core/business-logic/SKILL.md +22 -0
- package/skills/core/catch-up/SKILL.md +17 -0
- package/skills/core/core-thinking/SKILL.md +11 -0
- package/skills/core/design-thinking/SKILL.md +19 -0
- package/skills/core/graft/SKILL.md +156 -0
- package/skills/core/loop/SKILL.md +20 -0
- package/skills/core/session-handoff/SKILL.md +15 -0
- package/skills/core/use-browser/SKILL.md +69 -0
- package/skills/core/verification/SKILL.md +11 -0
- package/skills/index.json +56 -0
- package/templates/README.md +4 -0
- package/templates/profiles/default.json +51 -0
- package/templates/profiles/developer.json +51 -0
- package/templates/profiles/reviewer.json +51 -0
- package/templates/profiles/strategist.json +51 -0
- package/templates/workspace/00-inbox/INBOX.md +7 -0
- package/templates/workspace/00-inbox/candidates/.gitkeep +0 -0
- package/templates/workspace/00-inbox/native/.gitkeep +0 -0
- package/templates/workspace/01-daily/.gitkeep +0 -0
- package/templates/workspace/02-personal/CONFLICTS.md +7 -0
- package/templates/workspace/02-personal/MEMORY.md +15 -0
- package/templates/workspace/02-personal/goals/.gitkeep +0 -0
- package/templates/workspace/02-personal/identity/.gitkeep +0 -0
- package/templates/workspace/02-personal/preferences/.gitkeep +0 -0
- package/templates/workspace/03-professional/.gitkeep +0 -0
- package/templates/workspace/04-projects/PROJECTS.md +5 -0
- package/templates/workspace/04-projects/_template/docs/.gitkeep +0 -0
- package/templates/workspace/04-projects/_template/memory/.gitkeep +0 -0
- package/templates/workspace/04-projects/_template/tasks/.gitkeep +0 -0
- package/templates/workspace/05-knowledge/KNOWLEDGE.md +8 -0
- package/templates/workspace/05-knowledge/decisions/.gitkeep +0 -0
- package/templates/workspace/05-knowledge/lessons/.gitkeep +0 -0
- package/templates/workspace/06-templates/TEMPLATES.md +25 -0
- package/templates/workspace/06-templates/decision.md +8 -0
- package/templates/workspace/06-templates/intelligence.md +32 -0
- package/templates/workspace/06-templates/project-claude.md +48 -0
- package/templates/workspace/06-templates/session.md +38 -0
- package/templates/workspace/06-templates/task.md +42 -0
- package/templates/workspace/MANIFEST.json +51 -0
- package/templates/workspace/bridge/config/CONFIG.md +21 -0
- package/templates/workspace/bridge/config/runtime.json +4 -0
- package/templates/workspace/charter/core.md +104 -0
- package/templates/workspace/charter/policies/README.md +31 -0
- package/templates/workspace/charter/policies/claude-code-context.md +90 -0
- package/templates/workspace/charter/policies/concise-output.md +29 -0
- package/templates/workspace/charter/policies/context.md +91 -0
- package/templates/workspace/charter/policies/contracts/task.md +189 -0
- package/templates/workspace/charter/policies/git.md +60 -0
- package/templates/workspace/charter/policies/graph.md +47 -0
- package/templates/workspace/charter/policies/knowledge.md +33 -0
- package/templates/workspace/charter/policies/lifecycle.md +119 -0
- package/templates/workspace/charter/policies/memory.md +67 -0
- package/templates/workspace/charter/policies/minimal-change.md +43 -0
- package/templates/workspace/charter/policies/models.md +57 -0
- package/templates/workspace/charter/policies/privacy-terms.txt +3 -0
- package/templates/workspace/charter/policies/response.md +39 -0
- package/templates/workspace/charter/policies/skill-observer.md +44 -0
- package/templates/workspace/charter/policies/stack.md +23 -0
- package/templates/workspace/charter/policies/task.md +139 -0
- package/templates/workspace/charter/policies/verification.md +29 -0
- package/templates/workspace/sessions/.gitkeep +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { brainSearch } from "./brain-service.js";
|
|
2
|
+
export async function evaluateRecall(cases, options) {
|
|
3
|
+
const k = options.k ?? 5;
|
|
4
|
+
const misses = [];
|
|
5
|
+
for (const item of cases) {
|
|
6
|
+
const expect = [item.expect].flat();
|
|
7
|
+
const { results } = await brainSearch({
|
|
8
|
+
query: item.question,
|
|
9
|
+
limit: k,
|
|
10
|
+
root: options.root,
|
|
11
|
+
embedder: options.embedder ?? null,
|
|
12
|
+
indexPort: options.indexPort,
|
|
13
|
+
scope: item.project === undefined ? undefined : { project: item.project },
|
|
14
|
+
});
|
|
15
|
+
const got = results.map((hit) => hit.file);
|
|
16
|
+
const found = results.some((hit) => expect.includes(hit.path) || expect.includes(hit.file));
|
|
17
|
+
if (!found)
|
|
18
|
+
misses.push({ question: item.question, expect, got });
|
|
19
|
+
}
|
|
20
|
+
const hits = cases.length - misses.length;
|
|
21
|
+
return {
|
|
22
|
+
k,
|
|
23
|
+
total: cases.length,
|
|
24
|
+
hits,
|
|
25
|
+
recall: cases.length ? hits / cases.length : 0,
|
|
26
|
+
misses,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=brain-eval.js.map
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { validateBrainRecord } from "@ocean/core/domain/brain/brain-record.js";
|
|
3
|
+
import { normalizeScope } from "@ocean/core/domain/brain/record-scope.js";
|
|
4
|
+
import { parseFrontmatter } from "@ocean/core/shared/fs-utils.js";
|
|
5
|
+
// Pure markdown layer for T-228: turns one markdown file's raw text into a canonical
|
|
6
|
+
// BrainRecord plus a per-field conformance report, extracts its wikilinks, and chunks its
|
|
7
|
+
// body. No I/O here — the caller (brain-reindex.ts) reads the file and passes the text in.
|
|
8
|
+
function splitFrontmatter(source) {
|
|
9
|
+
if (!source.startsWith("---"))
|
|
10
|
+
return { block: "", body: source };
|
|
11
|
+
const end = source.indexOf("\n---", 3);
|
|
12
|
+
if (end < 0)
|
|
13
|
+
return { block: source.slice(3), body: "" };
|
|
14
|
+
const afterMarker = source.indexOf("\n", end + 1);
|
|
15
|
+
return {
|
|
16
|
+
block: source.slice(3, end),
|
|
17
|
+
body: afterMarker < 0 ? "" : source.slice(afterMarker + 1),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
// Tags can be declared inline (`tags: [a, b]`), as a block list, or omitted entirely.
|
|
21
|
+
function extractTags(block) {
|
|
22
|
+
const lines = block.split("\n");
|
|
23
|
+
const inlineIndex = lines.findIndex((line) => /^\s{0,2}tags:\s*\[/.test(line));
|
|
24
|
+
if (inlineIndex >= 0) {
|
|
25
|
+
const match = /\[(.*)\]/.exec(lines[inlineIndex]);
|
|
26
|
+
if (!match)
|
|
27
|
+
return [];
|
|
28
|
+
return match[1]
|
|
29
|
+
.split(",")
|
|
30
|
+
.map((tag) => tag.trim().replace(/^["']|["']$/g, ""))
|
|
31
|
+
.filter(Boolean);
|
|
32
|
+
}
|
|
33
|
+
const headerIndex = lines.findIndex((line) => /^\s{0,2}tags:\s*$/.test(line));
|
|
34
|
+
if (headerIndex < 0)
|
|
35
|
+
return [];
|
|
36
|
+
const tags = [];
|
|
37
|
+
for (let index = headerIndex + 1; index < lines.length; index += 1) {
|
|
38
|
+
const match = /^\s+-\s*(.+)$/.exec(lines[index]);
|
|
39
|
+
if (!match)
|
|
40
|
+
break;
|
|
41
|
+
tags.push(match[1].trim().replace(/^["']|["']$/g, ""));
|
|
42
|
+
}
|
|
43
|
+
return tags;
|
|
44
|
+
}
|
|
45
|
+
function firstHeading(body) {
|
|
46
|
+
const match = /^#\s+(.+)$/m.exec(body);
|
|
47
|
+
return match ? match[1].trim() : null;
|
|
48
|
+
}
|
|
49
|
+
// relPath is the record's identity of last resort: `<store-relative path, no .md>`.
|
|
50
|
+
export function normalizeRecord(relPath, rawSource) {
|
|
51
|
+
// Normalized up front (see fs-utils.ts parseFrontmatter): splitFrontmatter and chunkBody
|
|
52
|
+
// both match line regexes against `$`, which a CRLF trailing `\r` (e.g. a Windows git
|
|
53
|
+
// checkout) defeats since `.` excludes line terminators.
|
|
54
|
+
const source = rawSource.replace(/\r\n/g, "\n");
|
|
55
|
+
const { block, body } = splitFrontmatter(source);
|
|
56
|
+
const fields = parseFrontmatter(source);
|
|
57
|
+
const fallbackId = relPath.replace(/\.md$/i, "");
|
|
58
|
+
const declaredOrAliased = (canonical, ...aliases) => {
|
|
59
|
+
if (fields[canonical] !== undefined)
|
|
60
|
+
return { value: fields[canonical], state: "declared" };
|
|
61
|
+
for (const alias of aliases) {
|
|
62
|
+
if (fields[alias] !== undefined)
|
|
63
|
+
return { value: fields[alias], state: "aliased" };
|
|
64
|
+
}
|
|
65
|
+
return { value: undefined, state: "defaulted" };
|
|
66
|
+
};
|
|
67
|
+
const idField = declaredOrAliased("id", "name");
|
|
68
|
+
const id = idField.value ?? fallbackId;
|
|
69
|
+
const idState = idField.value ? idField.state : "defaulted";
|
|
70
|
+
const titleField = declaredOrAliased("title", "name");
|
|
71
|
+
const heading = firstHeading(body);
|
|
72
|
+
const title = titleField.value ?? heading ?? fallbackId;
|
|
73
|
+
const titleState = titleField.value
|
|
74
|
+
? titleField.state
|
|
75
|
+
: heading
|
|
76
|
+
? "aliased"
|
|
77
|
+
: "defaulted";
|
|
78
|
+
const summaryField = declaredOrAliased("summary", "description");
|
|
79
|
+
const summary = summaryField.value ?? "";
|
|
80
|
+
const tags = extractTags(block);
|
|
81
|
+
const tagsState = tags.length > 0 ? "declared" : "defaulted";
|
|
82
|
+
const typeField = declaredOrAliased("type");
|
|
83
|
+
const confidenceField = declaredOrAliased("confidence");
|
|
84
|
+
const createdField = declaredOrAliased("created");
|
|
85
|
+
const updatedField = declaredOrAliased("updated");
|
|
86
|
+
const lastConfirmedField = declaredOrAliased("last_confirmed_at", "last_verified");
|
|
87
|
+
const scopeField = declaredOrAliased("scope");
|
|
88
|
+
// A project memory record (project/<id>/…, T-296) or project doc (project-docs/<id>/…, T-307)
|
|
89
|
+
// without a declared scope belongs to its project, never to every project.
|
|
90
|
+
const owningProject = /^project(?:-docs)?\/([^/]+)\//.exec(relPath)?.[1];
|
|
91
|
+
const scope = normalizeScope(scopeField.value ??
|
|
92
|
+
(owningProject ? `project:${owningProject}` : undefined));
|
|
93
|
+
const record = validateBrainRecord({
|
|
94
|
+
id,
|
|
95
|
+
title,
|
|
96
|
+
summary,
|
|
97
|
+
tags,
|
|
98
|
+
type: typeField.value ?? "unknown",
|
|
99
|
+
confidence: confidenceField.value ?? "unknown",
|
|
100
|
+
created: createdField.value ?? null,
|
|
101
|
+
updated: updatedField.value ?? null,
|
|
102
|
+
lastConfirmedAt: lastConfirmedField.value ?? null,
|
|
103
|
+
scope: scope.scope,
|
|
104
|
+
});
|
|
105
|
+
const conformance = {
|
|
106
|
+
id: idState,
|
|
107
|
+
title: titleState,
|
|
108
|
+
summary: summaryField.state,
|
|
109
|
+
tags: tagsState,
|
|
110
|
+
type: typeField.state,
|
|
111
|
+
confidence: confidenceField.state,
|
|
112
|
+
created: createdField.state,
|
|
113
|
+
updated: updatedField.state,
|
|
114
|
+
lastConfirmedAt: lastConfirmedField.state,
|
|
115
|
+
// An invalid scope is counted by reindex's invalidScopes, not as a conformance state.
|
|
116
|
+
scope: scopeField.value === undefined || scope.state === "defaulted"
|
|
117
|
+
? "defaulted"
|
|
118
|
+
: "declared",
|
|
119
|
+
};
|
|
120
|
+
return { record, conformance, body, rawScope: scopeField.value };
|
|
121
|
+
}
|
|
122
|
+
// Extracts `[[target]]`, `[[target|alias]]`, `[[target#heading]]`, ignoring fenced code
|
|
123
|
+
// blocks (``` ... ```) so a documentation example is never mistaken for a real link.
|
|
124
|
+
export function extractWikilinks(body) {
|
|
125
|
+
const withoutCode = body.replace(/```[\s\S]*?```/g, "");
|
|
126
|
+
const links = [];
|
|
127
|
+
const pattern = /\[\[([^\]|#]+)(?:#([^\]|]+))?(?:\|([^\]]+))?\]\]/g;
|
|
128
|
+
let match;
|
|
129
|
+
// biome-ignore lint/suspicious/noAssignInExpressions: standard regex-exec loop
|
|
130
|
+
while ((match = pattern.exec(withoutCode)) !== null) {
|
|
131
|
+
links.push({
|
|
132
|
+
target: match[1].trim(),
|
|
133
|
+
heading: match[2]?.trim() ?? null,
|
|
134
|
+
alias: match[3]?.trim() ?? null,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return links;
|
|
138
|
+
}
|
|
139
|
+
const MAX_CHUNK_CHARS = 2_000;
|
|
140
|
+
// Splits on `##` headings (level-2+ only; a level-1 `#` title stays attached to the chunk
|
|
141
|
+
// that follows it), then further splits any resulting section longer than MAX_CHUNK_CHARS at
|
|
142
|
+
// a paragraph boundary. Ordinals are stable and start at 0.
|
|
143
|
+
export function chunkBody(body) {
|
|
144
|
+
const sections = [];
|
|
145
|
+
const lines = body.split("\n");
|
|
146
|
+
let currentHeading = null;
|
|
147
|
+
let currentLines = [];
|
|
148
|
+
const flush = () => {
|
|
149
|
+
const text = currentLines.join("\n").trim();
|
|
150
|
+
if (text)
|
|
151
|
+
sections.push({ heading: currentHeading, text });
|
|
152
|
+
currentLines = [];
|
|
153
|
+
};
|
|
154
|
+
for (const line of lines) {
|
|
155
|
+
const heading = /^##\s+(.+)$/.exec(line);
|
|
156
|
+
if (heading) {
|
|
157
|
+
flush();
|
|
158
|
+
currentHeading = heading[1].trim();
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
currentLines.push(line);
|
|
162
|
+
}
|
|
163
|
+
flush();
|
|
164
|
+
if (sections.length === 0)
|
|
165
|
+
return [];
|
|
166
|
+
const chunks = [];
|
|
167
|
+
let ordinal = 0;
|
|
168
|
+
for (const section of sections) {
|
|
169
|
+
if (section.text.length <= MAX_CHUNK_CHARS) {
|
|
170
|
+
chunks.push({
|
|
171
|
+
ordinal: ordinal++,
|
|
172
|
+
heading: section.heading,
|
|
173
|
+
text: section.text,
|
|
174
|
+
});
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const paragraphs = section.text.split(/\n\n+/);
|
|
178
|
+
let buffer = "";
|
|
179
|
+
for (const paragraph of paragraphs) {
|
|
180
|
+
const candidate = buffer ? `${buffer}\n\n${paragraph}` : paragraph;
|
|
181
|
+
if (candidate.length > MAX_CHUNK_CHARS && buffer) {
|
|
182
|
+
chunks.push({
|
|
183
|
+
ordinal: ordinal++,
|
|
184
|
+
heading: section.heading,
|
|
185
|
+
text: buffer,
|
|
186
|
+
});
|
|
187
|
+
buffer = paragraph;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
buffer = candidate;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (buffer)
|
|
194
|
+
chunks.push({
|
|
195
|
+
ordinal: ordinal++,
|
|
196
|
+
heading: section.heading,
|
|
197
|
+
text: buffer,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return chunks;
|
|
201
|
+
}
|
|
202
|
+
// Arabic harakat (U+064B-U+065F, U+0670) and tatweel (U+0640) are stripped so a diacritized
|
|
203
|
+
// word matches its bare form under FTS5's unicode61 tokenizer (which itself only removes
|
|
204
|
+
// diacritics from the *default* Unicode diacritics list, and only when told to — this
|
|
205
|
+
// normalization is what actually makes indexing and querying agree). أ/إ/آ fold to ا and ي
|
|
206
|
+
// folds to ی(kmr) — wait: folds ك to ک is a Sorani/Kurmanji orthography convenience. ة and ه
|
|
207
|
+
// (Sorani) are intentionally left alone: folding would collide with distinct Sorani/Kurmanji
|
|
208
|
+
// words. Applied identically at index time and query time so the tokenizer sees the same
|
|
209
|
+
// bytes both times.
|
|
210
|
+
const HARAKAT_AND_TATWEEL = /[ً-ٰٟـ]/g;
|
|
211
|
+
export function normalizeForSearch(text) {
|
|
212
|
+
return text
|
|
213
|
+
.normalize("NFKC")
|
|
214
|
+
.replace(HARAKAT_AND_TATWEEL, "")
|
|
215
|
+
.replace(/[أإآ]/g, "ا")
|
|
216
|
+
.replace(/ي/g, "ی")
|
|
217
|
+
.replace(/ك/g, "ک")
|
|
218
|
+
.toLowerCase();
|
|
219
|
+
}
|
|
220
|
+
export function idFromRelPath(relPath) {
|
|
221
|
+
return relPath.replace(/\.md$/i, "");
|
|
222
|
+
}
|
|
223
|
+
export function basenameStem(relPath) {
|
|
224
|
+
return path.basename(relPath, path.extname(relPath));
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=brain-markdown.js.map
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { INDEX_DIR, KNOWLEDGE_DIR, listProjectDirs, oceanRoot, resolveWithin, STORE_DIR, } from "@ocean/core/shared/paths.js";
|
|
5
|
+
import { basenameStem, chunkBody, extractWikilinks, normalizeForSearch, normalizeRecord, } from "./brain-markdown.js";
|
|
6
|
+
const STORES = ["memory", "knowledge"];
|
|
7
|
+
const MAX_FILES = 10_000;
|
|
8
|
+
async function walk(root, depth = 0) {
|
|
9
|
+
if (depth === 0) {
|
|
10
|
+
try {
|
|
11
|
+
await stat(root);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
let entries;
|
|
18
|
+
try {
|
|
19
|
+
entries = await readdir(root, { withFileTypes: true });
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
const files = [];
|
|
25
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
26
|
+
if (entry.name.startsWith("."))
|
|
27
|
+
continue;
|
|
28
|
+
if (entry.isDirectory()) {
|
|
29
|
+
if (entry.name === "archive")
|
|
30
|
+
continue;
|
|
31
|
+
files.push(...(await walk(path.join(root, entry.name), depth + 1)));
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (entry.name.endsWith(".md"))
|
|
35
|
+
files.push(path.join(root, entry.name));
|
|
36
|
+
}
|
|
37
|
+
return files;
|
|
38
|
+
}
|
|
39
|
+
async function corpusFiles(root) {
|
|
40
|
+
const found = [];
|
|
41
|
+
const add = async (prefix, storeRoot) => {
|
|
42
|
+
for (const file of await walk(storeRoot))
|
|
43
|
+
found.push({
|
|
44
|
+
prefix,
|
|
45
|
+
storePath: `${prefix}/${path.relative(storeRoot, file).split(path.sep).join("/")}`,
|
|
46
|
+
file,
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
for (const store of STORES)
|
|
50
|
+
await add(store, resolveWithin(root, STORE_DIR[store]));
|
|
51
|
+
// listProjectDirs sorts by id; an id seen twice (two category folders) is indexed once.
|
|
52
|
+
const seen = new Set();
|
|
53
|
+
for (const { id, dir } of listProjectDirs(root)) {
|
|
54
|
+
if (seen.has(id))
|
|
55
|
+
continue;
|
|
56
|
+
seen.add(id);
|
|
57
|
+
await add(`project/${id}`, resolveWithin(root, dir, "memory"));
|
|
58
|
+
await add(`project-docs/${id}`, resolveWithin(root, dir, "docs"));
|
|
59
|
+
}
|
|
60
|
+
return found;
|
|
61
|
+
}
|
|
62
|
+
// Cheap staleness probe: hashes every indexed markdown file's bytes, in the same (sorted path,
|
|
63
|
+
// sha256) shape reindexBrain folds into meta.corpus_hash, without parsing frontmatter or
|
|
64
|
+
// building chunks. Used by brainSearch to report `stale: true` when the index is older than the
|
|
65
|
+
// files it was built from, without paying for a full reindex on every search.
|
|
66
|
+
export async function computeCorpusHash(root = oceanRoot()) {
|
|
67
|
+
const entries = [];
|
|
68
|
+
for (const { storePath, file } of await corpusFiles(root)) {
|
|
69
|
+
const source = await readFile(file, "utf8");
|
|
70
|
+
entries.push({
|
|
71
|
+
storePath,
|
|
72
|
+
contentHash: createHash("sha256").update(source).digest("hex"),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
entries.sort((a, b) => a.storePath.localeCompare(b.storePath));
|
|
76
|
+
return createHash("sha256")
|
|
77
|
+
.update(entries
|
|
78
|
+
.map((entry) => `${entry.storePath}:${entry.contentHash}`)
|
|
79
|
+
.join("\n"))
|
|
80
|
+
.digest("hex");
|
|
81
|
+
}
|
|
82
|
+
// Resolves one wikilink target to a store-relative doc path, per the fixed precedence order:
|
|
83
|
+
// exact declared id anywhere in the corpus, the same store's `<target>.md`, that store's
|
|
84
|
+
// `<target>/README.md`, then a corpus-wide unique file stem. Anything else is dangling.
|
|
85
|
+
// Exactly two or more stem matches is reported separately as ambiguous, not silently guessed.
|
|
86
|
+
function resolveLink(targetRaw, sourceStore, byId, byPath, byStem) {
|
|
87
|
+
const cleaned = targetRaw.replace(/^\.\//, "");
|
|
88
|
+
const declaredId = byId.get(cleaned);
|
|
89
|
+
if (declaredId !== undefined)
|
|
90
|
+
return { resolved: declaredId, ambiguous: false };
|
|
91
|
+
const storeRelative = `${sourceStore}/${cleaned}.md`;
|
|
92
|
+
if (byPath.has(storeRelative))
|
|
93
|
+
return { resolved: storeRelative, ambiguous: false };
|
|
94
|
+
const readmePath = `${sourceStore}/${cleaned}/README.md`;
|
|
95
|
+
if (byPath.has(readmePath))
|
|
96
|
+
return { resolved: readmePath, ambiguous: false };
|
|
97
|
+
const stem = cleaned.split("/").pop() ?? cleaned;
|
|
98
|
+
const stemMatches = byStem.get(stem) ?? [];
|
|
99
|
+
if (stemMatches.length === 1)
|
|
100
|
+
return { resolved: stemMatches[0], ambiguous: false };
|
|
101
|
+
if (stemMatches.length > 1)
|
|
102
|
+
return { resolved: null, ambiguous: true };
|
|
103
|
+
return { resolved: null, ambiguous: false };
|
|
104
|
+
}
|
|
105
|
+
export async function reindexBrain(options) {
|
|
106
|
+
const root = options.root ?? oceanRoot();
|
|
107
|
+
const embedder = options.embedder ?? null;
|
|
108
|
+
const walked = [];
|
|
109
|
+
let totalSeen = 0;
|
|
110
|
+
for (const { prefix: store, storePath: relative, file } of await corpusFiles(root)) {
|
|
111
|
+
totalSeen += 1;
|
|
112
|
+
if (totalSeen > MAX_FILES)
|
|
113
|
+
throw new Error(`brain reindex found more than ${MAX_FILES} markdown files — refusing to index an unbounded corpus`);
|
|
114
|
+
const source = await readFile(file, "utf8");
|
|
115
|
+
const { record, conformance, body, rawScope } = normalizeRecord(relative, source);
|
|
116
|
+
const links = extractWikilinks(body).map((link) => ({
|
|
117
|
+
targetRaw: link.target,
|
|
118
|
+
heading: link.heading,
|
|
119
|
+
alias: link.alias,
|
|
120
|
+
}));
|
|
121
|
+
walked.push({
|
|
122
|
+
storePath: relative,
|
|
123
|
+
store,
|
|
124
|
+
record,
|
|
125
|
+
conformance,
|
|
126
|
+
rawScope,
|
|
127
|
+
body,
|
|
128
|
+
contentHash: createHash("sha256").update(source).digest("hex"),
|
|
129
|
+
bytes: Buffer.byteLength(source, "utf8"),
|
|
130
|
+
links,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
walked.sort((a, b) => a.storePath.localeCompare(b.storePath));
|
|
134
|
+
// Duplicate *declared* ids fail loudly — a collision between two files whose ids only
|
|
135
|
+
// coincide because both defaulted to their own path can never happen (paths are unique).
|
|
136
|
+
const declaredIds = new Map();
|
|
137
|
+
for (const doc of walked) {
|
|
138
|
+
if (doc.conformance.id === "defaulted")
|
|
139
|
+
continue;
|
|
140
|
+
const existing = declaredIds.get(doc.record.id);
|
|
141
|
+
if (existing) {
|
|
142
|
+
throw new Error(`duplicate brain record id '${doc.record.id}' declared in both ${existing} and ${doc.storePath}`);
|
|
143
|
+
}
|
|
144
|
+
declaredIds.set(doc.record.id, doc.storePath);
|
|
145
|
+
}
|
|
146
|
+
const byId = new Map();
|
|
147
|
+
const byPath = new Map();
|
|
148
|
+
const byStem = new Map();
|
|
149
|
+
for (const doc of walked) {
|
|
150
|
+
byId.set(doc.record.id, doc.storePath);
|
|
151
|
+
byPath.set(doc.storePath, doc.storePath);
|
|
152
|
+
const stem = basenameStem(doc.storePath);
|
|
153
|
+
byStem.set(stem, [...(byStem.get(stem) ?? []), doc.storePath]);
|
|
154
|
+
}
|
|
155
|
+
const docInputs = [];
|
|
156
|
+
const linkInputs = [];
|
|
157
|
+
const danglingLinks = [];
|
|
158
|
+
const ambiguousLinks = [];
|
|
159
|
+
const conformanceTally = {};
|
|
160
|
+
const scopeTally = {};
|
|
161
|
+
const invalidScopes = [];
|
|
162
|
+
let chunkCount = 0;
|
|
163
|
+
for (const doc of walked) {
|
|
164
|
+
scopeTally[doc.record.scope] = (scopeTally[doc.record.scope] ?? 0) + 1;
|
|
165
|
+
if (doc.record.scope === "invalid")
|
|
166
|
+
invalidScopes.push({ path: doc.storePath, scope: doc.rawScope ?? "" });
|
|
167
|
+
for (const [field, state] of Object.entries(doc.conformance)) {
|
|
168
|
+
conformanceTally[field] ??= { aliased: 0, defaulted: 0 };
|
|
169
|
+
if (state === "aliased")
|
|
170
|
+
conformanceTally[field].aliased += 1;
|
|
171
|
+
if (state === "defaulted")
|
|
172
|
+
conformanceTally[field].defaulted += 1;
|
|
173
|
+
}
|
|
174
|
+
const chunks = chunkBody(doc.body);
|
|
175
|
+
chunkCount += chunks.length;
|
|
176
|
+
docInputs.push({
|
|
177
|
+
id: doc.record.id,
|
|
178
|
+
path: doc.storePath,
|
|
179
|
+
title: doc.record.title,
|
|
180
|
+
summary: doc.record.summary,
|
|
181
|
+
tags: doc.record.tags,
|
|
182
|
+
type: doc.record.type,
|
|
183
|
+
scope: doc.record.scope,
|
|
184
|
+
confidence: doc.record.confidence,
|
|
185
|
+
created: doc.record.created,
|
|
186
|
+
updated: doc.record.updated,
|
|
187
|
+
lastConfirmedAt: doc.record.lastConfirmedAt,
|
|
188
|
+
contentHash: doc.contentHash,
|
|
189
|
+
bytes: doc.bytes,
|
|
190
|
+
conformance: doc.conformance,
|
|
191
|
+
chunks: chunks.map((chunk) => ({
|
|
192
|
+
ordinal: chunk.ordinal,
|
|
193
|
+
heading: chunk.heading,
|
|
194
|
+
text: chunk.text,
|
|
195
|
+
normalizedText: normalizeForSearch(chunk.text),
|
|
196
|
+
})),
|
|
197
|
+
});
|
|
198
|
+
for (const link of doc.links) {
|
|
199
|
+
const { resolved, ambiguous } = resolveLink(link.targetRaw, doc.store, byId, byPath, byStem);
|
|
200
|
+
if (ambiguous)
|
|
201
|
+
ambiguousLinks.push({ source: doc.storePath, target: link.targetRaw });
|
|
202
|
+
else if (!resolved)
|
|
203
|
+
danglingLinks.push({ source: doc.storePath, target: link.targetRaw });
|
|
204
|
+
linkInputs.push({
|
|
205
|
+
sourcePath: doc.storePath,
|
|
206
|
+
targetRaw: link.targetRaw,
|
|
207
|
+
targetPath: resolved,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const corpusHash = createHash("sha256")
|
|
212
|
+
.update(walked.map((doc) => `${doc.storePath}:${doc.contentHash}`).join("\n"))
|
|
213
|
+
.digest("hex");
|
|
214
|
+
const outDir = resolveWithin(root, INDEX_DIR);
|
|
215
|
+
const outFile = path.join(outDir, "brain.sqlite");
|
|
216
|
+
await mkdir(outDir, { recursive: true });
|
|
217
|
+
await writeFile(path.join(outDir, ".gitignore"), "*\n");
|
|
218
|
+
await options.indexPort.buildIndex(docInputs, linkInputs, outFile, {
|
|
219
|
+
corpusHash,
|
|
220
|
+
embedder,
|
|
221
|
+
});
|
|
222
|
+
let legacyIndexPresent = false;
|
|
223
|
+
try {
|
|
224
|
+
await stat(resolveWithin(root, KNOWLEDGE_DIR, "index.sqlite3"));
|
|
225
|
+
legacyIndexPresent = true;
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
legacyIndexPresent = false;
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
docs: docInputs.length,
|
|
232
|
+
chunks: chunkCount,
|
|
233
|
+
links: linkInputs.length,
|
|
234
|
+
danglingLinks,
|
|
235
|
+
ambiguousLinks,
|
|
236
|
+
conformance: conformanceTally,
|
|
237
|
+
scopes: scopeTally,
|
|
238
|
+
invalidScopes,
|
|
239
|
+
legacyIndexPresent,
|
|
240
|
+
vectors: Boolean(embedder),
|
|
241
|
+
outFile,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
export function brainIndexPath(root = oceanRoot()) {
|
|
245
|
+
return path.join(root, INDEX_DIR, "brain.sqlite");
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=brain-reindex.js.map
|