@unbrained/pm-cli 2026.9.19 → 2026.9.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +954 -924
  3. package/dist/cli/main.d.ts +10 -76
  4. package/dist/cli/main.js +1 -2
  5. package/dist/cli/register-operations.js +1 -1
  6. package/dist/cli/runtime/activation.d.ts +51 -0
  7. package/dist/cli/runtime/activation.js +2 -0
  8. package/dist/cli/runtime/invocation-options.d.ts +41 -0
  9. package/dist/cli/runtime/invocation-options.js +2 -0
  10. package/dist/cli/runtime/selection.d.ts +29 -0
  11. package/dist/cli/runtime/selection.js +2 -0
  12. package/dist/cli/runtime/telemetry-outcome.d.ts +20 -0
  13. package/dist/cli/runtime/telemetry-outcome.js +2 -0
  14. package/dist/cli-bundle/bundle-manifest.json +150 -150
  15. package/dist/cli-bundle/chunks/{chunk-QA4GUEKJ.js → chunk-2QLXN6HZ.js} +46 -46
  16. package/dist/cli-bundle/chunks/chunk-2UNYUB4A.js +2 -0
  17. package/dist/cli-bundle/chunks/chunk-4SI67A2F.js +8 -0
  18. package/dist/cli-bundle/chunks/chunk-7YUYLYJQ.js +6 -0
  19. package/dist/cli-bundle/chunks/{chunk-7CN53F4S.js → chunk-FBWAECBI.js} +3 -3
  20. package/dist/cli-bundle/chunks/chunk-GRHSNUQO.js +20 -0
  21. package/dist/cli-bundle/chunks/chunk-HAFSMFJK.js +3 -0
  22. package/dist/cli-bundle/chunks/chunk-JBJUOOJI.js +238 -0
  23. package/dist/cli-bundle/chunks/chunk-K7RWRJLT.js +29 -0
  24. package/dist/cli-bundle/chunks/chunk-NHRB3L5T.js +2 -0
  25. package/dist/cli-bundle/chunks/{chunk-CCLWCESZ.js → chunk-QZTS23BV.js} +2 -2
  26. package/dist/cli-bundle/chunks/{chunk-K4VAU4HU.js → chunk-SDLBF4PY.js} +1252 -1252
  27. package/dist/cli-bundle/chunks/{register-list-query-RMZNHSII.js → register-list-query-5V43MYPN.js} +6 -6
  28. package/dist/cli-bundle/chunks/{register-mutation-HJGSGX3H.js → register-mutation-DXES37MA.js} +4 -4
  29. package/dist/cli-bundle/chunks/register-operations-LJU5H2CU.js +2 -0
  30. package/dist/cli-bundle/chunks/register-setup-HJUNGYBB.js +2 -0
  31. package/dist/cli-bundle/focused-chunks/chunk-3XPBKIV6.js +12 -0
  32. package/dist/cli-bundle/focused-chunks/{chunk-7Y7PAC56.js → chunk-5BLRMUMK.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/{chunk-5SZ4JSPZ.js → chunk-6MNKQKT6.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/{chunk-IKPR4VVN.js → chunk-DPBTIFW7.js} +2 -2
  35. package/dist/cli-bundle/focused-chunks/{chunk-J3GN4SQT.js → chunk-DRNXMOSV.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/{chunk-P4BA7OX7.js → chunk-FXAZPSU6.js} +7 -7
  37. package/dist/cli-bundle/focused-chunks/{chunk-ARK4FIO4.js → chunk-HYPK5OPA.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/{chunk-I4WDON3K.js → chunk-IA5CCSBB.js} +2 -2
  39. package/dist/cli-bundle/focused-chunks/{chunk-CJ4QA4RW.js → chunk-ISEOBALS.js} +2 -2
  40. package/dist/cli-bundle/focused-chunks/{chunk-ZI2YBJFH.js → chunk-K5ZCBC6Y.js} +3 -3
  41. package/dist/cli-bundle/focused-chunks/{chunk-V3YMNAO7.js → chunk-L6L4XCLY.js} +2 -2
  42. package/dist/cli-bundle/focused-chunks/{chunk-TDHVROGL.js → chunk-N6KI3LWF.js} +2 -2
  43. package/dist/cli-bundle/focused-chunks/chunk-PC6ZZVNP.js +20 -0
  44. package/dist/cli-bundle/focused-chunks/chunk-RQL5WLNZ.js +30 -0
  45. package/dist/cli-bundle/focused-chunks/{chunk-ZDBKYZFE.js → chunk-U2SB2BMM.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/{chunk-YDFEWXXZ.js → chunk-UIS5FVHH.js} +53 -53
  47. package/dist/cli-bundle/main.js +26 -27
  48. package/dist/cli-bundle/sdk-authoring.js +1 -1
  49. package/dist/cli-bundle/sdk-contracts.js +1 -1
  50. package/dist/cli-bundle/sdk-core.js +39 -39
  51. package/dist/cli-bundle/sdk-governance.js +1 -1
  52. package/dist/cli-bundle/sdk-graph.js +1 -1
  53. package/dist/cli-bundle/sdk-merge.js +42 -42
  54. package/dist/cli-bundle/sdk-query.js +1 -1
  55. package/dist/cli-bundle/sdk-runtime.js +1 -1
  56. package/dist/cli-bundle/sdk-testing.js +1 -1
  57. package/dist/cli-bundle/sdk.js +8 -8
  58. package/dist/core/diagnostics/remediation.js +1 -1
  59. package/dist/core/extensions/extension-types.d.ts +2 -2
  60. package/dist/core/extensions/loader.d.ts +9 -15
  61. package/dist/core/extensions/loader.js +1 -1
  62. package/dist/core/extensions/manifest-parser.d.ts +5 -0
  63. package/dist/core/extensions/manifest-parser.js +2 -0
  64. package/dist/core/extensions/registration-contracts.d.ts +34 -0
  65. package/dist/core/extensions/registration-contracts.js +2 -0
  66. package/dist/core/governance/issue-codes.d.ts +10 -1
  67. package/dist/core/governance/issue-codes.js +1 -1
  68. package/dist/core/validate/fix-planning.d.ts +3 -3
  69. package/dist/core/validate/fix-planning.js +1 -1
  70. package/dist/sdk/agent/refusal-reachability.d.ts +1 -1
  71. package/dist/sdk/completion/bash.d.ts +3 -0
  72. package/dist/sdk/completion/bash.js +2 -0
  73. package/dist/sdk/completion/fish.d.ts +3 -0
  74. package/dist/sdk/completion/fish.js +1054 -0
  75. package/dist/sdk/completion/shared.d.ts +103 -0
  76. package/dist/sdk/completion/shared.js +2 -0
  77. package/dist/sdk/completion/zsh.d.ts +3 -0
  78. package/dist/sdk/completion/zsh.js +1109 -0
  79. package/dist/sdk/completion.d.ts +5 -30
  80. package/dist/sdk/completion.js +1 -2160
  81. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
  82. package/dist/sdk/governance/closure-timestamps.d.ts +32 -0
  83. package/dist/sdk/governance/closure-timestamps.js +2 -0
  84. package/dist/sdk/governance/validate-normalization.d.ts +2 -0
  85. package/dist/sdk/governance/validate-normalization.js +1 -1
  86. package/dist/sdk/governance/validate.js +1 -1
  87. package/dist/sdk/governance.d.ts +1 -0
  88. package/dist/sdk/governance.js +1 -1
  89. package/dist/sdk/public-surface.json +1 -1
  90. package/dist/sdk/query/search/corpus.d.ts +27 -0
  91. package/dist/sdk/query/search/corpus.js +2 -0
  92. package/dist/sdk/query/search/filters.d.ts +27 -0
  93. package/dist/sdk/query/search/filters.js +2 -0
  94. package/dist/sdk/query/search/input.d.ts +28 -0
  95. package/dist/sdk/query/search/input.js +2 -0
  96. package/dist/sdk/query/search/lexical.d.ts +34 -0
  97. package/dist/sdk/query/search/lexical.js +2 -0
  98. package/dist/sdk/query/search/response.d.ts +34 -0
  99. package/dist/sdk/query/search/response.js +2 -0
  100. package/dist/sdk/query/search/semantic.d.ts +229 -0
  101. package/dist/sdk/query/search/semantic.js +3 -0
  102. package/dist/sdk/query/search/types.d.ts +123 -0
  103. package/dist/sdk/query/search/types.js +2 -0
  104. package/dist/sdk/query/search.d.ts +13 -364
  105. package/dist/sdk/query/search.js +1 -2
  106. package/dist/sdk/runtime/actions.d.ts +4 -0
  107. package/dist/sdk/runtime/actions.js +2 -0
  108. package/dist/sdk/runtime/context.d.ts +38 -0
  109. package/dist/sdk/runtime/context.js +2 -0
  110. package/dist/sdk/runtime/schema.d.ts +4 -0
  111. package/dist/sdk/runtime/schema.js +2 -0
  112. package/dist/sdk/runtime.d.ts +90 -95
  113. package/dist/sdk/runtime.js +1 -1
  114. package/docs/ARCHITECTURE.md +68 -0
  115. package/docs/HISTORY_GOVERNANCE_REPAIR.md +66 -0
  116. package/docs/README.md +2 -0
  117. package/docs/VALIDATION_PORTABILITY.md +82 -0
  118. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +4 -3
  119. package/marketplace.json +2 -2
  120. package/package.json +6 -6
  121. package/packages/pm-beads/package.json +1 -1
  122. package/packages/pm-calendar/package.json +1 -1
  123. package/packages/pm-command-kit/package.json +1 -1
  124. package/packages/pm-digital-twin/package.json +1 -1
  125. package/packages/pm-governance-audit/package.json +1 -1
  126. package/packages/pm-guide-shell/extensions/guide-shell/runtime.ts +23 -17
  127. package/packages/pm-guide-shell/package.json +1 -1
  128. package/packages/pm-kanban/package.json +1 -1
  129. package/packages/pm-lifecycle-hooks/package.json +1 -1
  130. package/packages/pm-linked-test-adapters/package.json +1 -1
  131. package/packages/pm-search-advanced/package.json +1 -1
  132. package/packages/pm-templates/package.json +1 -1
  133. package/packages/pm-todos/package.json +1 -1
  134. package/packages/pm-vcs/package.json +1 -1
  135. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  136. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  137. package/dist/cli-bundle/chunks/chunk-AACGU5IA.js +0 -3
  138. package/dist/cli-bundle/chunks/chunk-J7NP7QLW.js +0 -6
  139. package/dist/cli-bundle/chunks/chunk-KHN4NMKT.js +0 -238
  140. package/dist/cli-bundle/chunks/chunk-KYG2QKNY.js +0 -29
  141. package/dist/cli-bundle/chunks/chunk-OQS2Y6VN.js +0 -2
  142. package/dist/cli-bundle/chunks/chunk-QTHR3LXF.js +0 -20
  143. package/dist/cli-bundle/chunks/chunk-RVMDPIGD.js +0 -2
  144. package/dist/cli-bundle/chunks/chunk-WASMH647.js +0 -8
  145. package/dist/cli-bundle/chunks/register-operations-LBEMFUMM.js +0 -2
  146. package/dist/cli-bundle/chunks/register-setup-MU557JAA.js +0 -2
  147. package/dist/cli-bundle/focused-chunks/chunk-I32L3FWW.js +0 -12
  148. package/dist/cli-bundle/focused-chunks/chunk-OOZHQFBF.js +0 -20
  149. package/dist/cli-bundle/focused-chunks/chunk-QL4MO5M2.js +0 -30
@@ -0,0 +1,27 @@
1
+ import type { ItemDocument, ItemFormat, ItemMetadata, PmSettings } from "../../../types/index.js";
2
+ import { type SearchMode } from "../search-rendering.js";
3
+ /** Collect linked file and document paths eligible for optional search corpus enrichment. */
4
+ declare function collectLinkedPaths(item: ItemMetadata): Array<{
5
+ scope: "project" | "global";
6
+ path: string;
7
+ }>;
8
+ interface ContainmentRoot {
9
+ resolved: string;
10
+ realpath: string;
11
+ }
12
+ interface LinkedCorpusRoots {
13
+ projectContainmentRoot: ContainmentRoot | null;
14
+ globalContainmentRoot: ContainmentRoot | null;
15
+ }
16
+ /** Resolve the workspace and global roots used to contain linked-content reads. */
17
+ declare function resolveLinkedCorpusRoots(projectRoot: string, globalRoot: string): Promise<LinkedCorpusRoots>;
18
+ /** Read eligible linked content while retaining lexical and canonical path containment checks. */
19
+ declare function loadLinkedCorpus(document: ItemDocument, roots: LinkedCorpusRoots): Promise<string>;
20
+ /** Load the searchable item corpus for the resolved tracker. */
21
+ declare function loadDocuments(pmRoot: string, itemFormat: ItemFormat, typeToFolder: Record<string, string>, schema: PmSettings["schema"]): Promise<{
22
+ documents: ItemDocument[];
23
+ warnings: string[];
24
+ }>;
25
+ /** Build an item-ID lookup of linked content for provider execution and reranking. */
26
+ declare function collectLinkedCorpusById(includeLinked: boolean, effectiveMode: SearchMode, filteredDocuments: ItemDocument[]): Promise<Map<string, string>>;
27
+ export { collectLinkedCorpusById, collectLinkedPaths, loadDocuments, loadLinkedCorpus, resolveLinkedCorpusRoots };
@@ -0,0 +1,2 @@
1
+ !(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6e743cc1-3ff0-58b1-bf11-351d1655864c")}catch(e2){}})();import fs from"node:fs/promises";import path from"node:path";import{getActiveExtensionRegistrations,runActiveOnReadHooks}from"../../../core/extensions/index.js";import{collectRegisteredItemFieldNames}from"../../../core/extensions/item-fields.js";import{isPathWithinDirectory}from"../../../core/fs/path-utils.js";import{parseItemDocument}from"../../../core/item/item-format.js";import{listAllDocumentCandidatesCached}from"../../../core/store/item-metadata-cache.js";import{listAllItemMetadata}from"../../../core/store/item-store.js";import{getItemPath,resolveGlobalPmRoot}from"../../../core/store/paths.js";function collectLinkedPaths(item){const fromFiles=(item.files??[]).map(entry=>({scope:entry.scope,path:entry.path.trim()}));const fromDocs=(item.docs??[]).map(entry=>({scope:entry.scope,path:entry.path.trim()}));const fromTests=(item.tests??[]).filter(entry=>typeof entry.path==="string"&&entry.path.trim().length>0).map(entry=>({scope:entry.scope,path:entry.path.trim()}));const sorted=[...fromFiles,...fromDocs,...fromTests].filter(entry=>entry.path.length>0).sort((a,b)=>a.scope.localeCompare(b.scope)||a.path.localeCompare(b.path));const deduped=new Map;for(const entry of sorted){deduped.set(`${entry.scope}:${entry.path}`,entry)}return[...deduped.values()]}async function resolveContainmentRoot(root){const resolved=path.resolve(root);try{const realpathRoot=await fs.realpath(resolved);return{resolved,realpath:realpathRoot}}catch{return null}}async function resolveLinkedCorpusRoots(projectRoot,globalRoot){const[projectContainmentRoot,globalContainmentRoot]=await Promise.all([resolveContainmentRoot(projectRoot),resolveContainmentRoot(globalRoot)]);return{projectContainmentRoot,globalContainmentRoot}}async function loadLinkedCorpus(document,roots){const linkedPaths=collectLinkedPaths(document.metadata);const chunks=[];for(const linkedPath of linkedPaths){const containmentRoot=linkedPath.scope==="global"?roots.globalContainmentRoot:roots.projectContainmentRoot;if(!containmentRoot){continue}const resolved=path.resolve(containmentRoot.resolved,linkedPath.path);if(!isPathWithinDirectory(containmentRoot.resolved,resolved)){continue}let linkedRealpath;try{linkedRealpath=await fs.realpath(resolved)}catch{continue}if(!isPathWithinDirectory(containmentRoot.realpath,linkedRealpath)){continue}try{await runActiveOnReadHooks({path:resolved,scope:linkedPath.scope});chunks.push(await fs.readFile(resolved,"utf8"))}catch{}}return chunks.join("\n")}async function loadDocuments(pmRoot,itemFormat,typeToFolder,schema){const extensionFieldNames=collectRegisteredItemFieldNames(getActiveExtensionRegistrations());const readDocumentBody=async(metadata,preferredPath,preferredFormat)=>{const tryRead=async(targetPath,format)=>{await runActiveOnReadHooks({path:targetPath,scope:"project"});const raw=await fs.readFile(targetPath,"utf8");const parsed=parseItemDocument(raw,{format,schema,extensionFieldNames,onWarning:warning=>listWarnings.push(warning)});return parsed.body};try{return await tryRead(preferredPath,preferredFormat)}catch{const alternateFormat=preferredFormat==="toon"?"json_markdown":"toon";const alternatePath=getItemPath(pmRoot,metadata.type,metadata.id,alternateFormat,typeToFolder);try{return await tryRead(alternatePath,alternateFormat)}catch{listWarnings.push(`item_list_item_read_failed:${path.relative(pmRoot,alternatePath)}`);return""}}};const listWarnings=[];const cachedDocuments=await listAllDocumentCandidatesCached(pmRoot,itemFormat,typeToFolder,listWarnings,schema);const documents=[];if(cachedDocuments.length===0){const itemMetadataDocuments=await listAllItemMetadata(pmRoot,itemFormat,typeToFolder,listWarnings,schema);for(const metadata of itemMetadataDocuments){const preferredPath=getItemPath(pmRoot,metadata.type,metadata.id,itemFormat,typeToFolder);const body=await readDocumentBody(metadata,preferredPath,itemFormat);documents.push({metadata,body})}return{documents,warnings:[...new Set(listWarnings)].sort((left,right)=>left.localeCompare(right))}}for(const cachedDocument of cachedDocuments){if(typeof cachedDocument.body==="string"){documents.push({metadata:cachedDocument.metadata,body:cachedDocument.body});continue}const body=await readDocumentBody(cachedDocument.metadata,cachedDocument.item_path,cachedDocument.item_format);documents.push({metadata:cachedDocument.metadata,body})}return{documents,warnings:[...new Set(listWarnings)].sort((left,right)=>left.localeCompare(right))}}async function collectLinkedCorpusById(includeLinked,effectiveMode,filteredDocuments){const linkedCorpusById=new Map;if(!includeLinked||effectiveMode!=="keyword"&&effectiveMode!=="hybrid"){return linkedCorpusById}const projectRoot=process.cwd();const linkedCorpusRoots=await resolveLinkedCorpusRoots(projectRoot,resolveGlobalPmRoot(projectRoot));const linkedCorpusEntries=await Promise.all(filteredDocuments.map(async document=>[document.metadata.id,await loadLinkedCorpus(document,linkedCorpusRoots)]));for(const[id,corpus]of linkedCorpusEntries){linkedCorpusById.set(id,corpus)}return linkedCorpusById}export{collectLinkedCorpusById,collectLinkedPaths,loadDocuments,loadLinkedCorpus,resolveLinkedCorpusRoots};
2
+ //# sourceMappingURL=corpus.js.map
@@ -0,0 +1,27 @@
1
+ import { type LifecycleClassifier } from "../../../core/governance/metadata-coverage.js";
2
+ import { type ItemTypeRegistry } from "../../../core/item/type-registry.js";
3
+ import type { ItemDocument, ItemStatus } from "../../../types/index.js";
4
+ import { type SearchOptions } from "../search-contracts.js";
5
+ /** Normalize an unknown collection to its string entries for field filtering and scoring. */
6
+ declare function stringArray(value: unknown): string[];
7
+ /** Extract annotation text entries used by exact-phrase matching and lexical scoring. */
8
+ declare function textEntries(value: unknown): Array<{
9
+ text: string;
10
+ }>;
11
+ /** Extract dependency metadata entries used by filtering and lexical scoring. */
12
+ declare function dependencyEntries(value: unknown): Array<{
13
+ id: string;
14
+ kind: string;
15
+ }>;
16
+ /** Collect searchable text fields used by exact-phrase filtering. */
17
+ declare function collectExactPhraseFields(document: ItemDocument): string[];
18
+ /** Check whether any searchable field contains the normalized exact phrase. */
19
+ declare function documentContainsExactPhrase(document: ItemDocument, normalizedQuery: string): boolean;
20
+ /** Apply exact-title and exact-phrase restrictions before search ranking. */
21
+ declare function applyExactQueryFilters(items: ItemDocument[], normalizedQuery: string, options: {
22
+ titleExact: boolean;
23
+ phraseExact: boolean;
24
+ }): ItemDocument[];
25
+ /** Apply metadata and runtime field predicates to the loaded item corpus. */
26
+ declare function applyFilters(items: ItemDocument[], options: SearchOptions, typeRegistry: ItemTypeRegistry, runtimeFieldFilters: Record<string, unknown>, statusFilter: ItemStatus[] | undefined, lifecycleClassifier: LifecycleClassifier): ItemDocument[];
27
+ export { applyExactQueryFilters, applyFilters, collectExactPhraseFields, dependencyEntries, documentContainsExactPhrase, stringArray, textEntries };
@@ -0,0 +1,2 @@
1
+ !(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a5e4bd88-a099-559f-8194-7b56f325eb0e")}catch(e2){}})();import{hasContentFieldFilter,itemMatchesContentFilters}from"../../../core/governance/content-fields.js";import{hasMissingMetadataFilter,itemMatchesMissingMetadata}from"../../../core/governance/metadata-coverage.js";import{matchesRuntimeFilters}from"../../../core/schema/runtime-field-filters.js";import{buildEventCorpus,buildPlanFlatCorpus,buildReminderCorpus}from"../../../core/search/corpus.js";import{EXIT_CODE}from"../../../core/shared/constants.js";import{PmCliError}from"../../../core/shared/errors.js";import{matchesTimestampFilters}from"../../../core/shared/time.js";import{resolveContentFieldFilters,resolveMissingMetadataFilters}from"../list.js";import{parsePriorityFilterSet,parseStringFilterSet,parseTypeFilterSet}from"../multi-value-filters.js";import{normalizeSearchPhrase,parseSearchDeadline,parseTimestampWindow}from"../search-contracts.js";function stringArray(value){return Array.isArray(value)?value.filter(entry=>typeof entry==="string"):[]}function textEntries(value){return Array.isArray(value)?value.filter(entry=>typeof entry==="object"&&entry!==null&&typeof entry.text==="string"):[]}function dependencyEntries(value){return Array.isArray(value)?value.filter(entry=>typeof entry==="object"&&entry!==null&&typeof entry.id==="string"&&typeof entry.kind==="string"):[]}function collectExactPhraseFields(document){const item=document.metadata;return[item.title,item.description,item.status,stringArray(item.tags).join(" "),document.body,textEntries(item.comments).map(entry=>entry.text).join(" "),textEntries(item.notes).map(entry=>entry.text).join(" "),textEntries(item.learnings).map(entry=>entry.text).join(" "),buildReminderCorpus(item).join(" "),buildEventCorpus(item).join(" "),dependencyEntries(item.dependencies).map(entry=>`${entry.id} ${entry.kind}`).join(" "),buildPlanFlatCorpus(item)]}function documentContainsExactPhrase(document,normalizedQuery){return collectExactPhraseFields(document).some(fieldValue=>normalizeSearchPhrase(fieldValue).includes(normalizedQuery))}function applyExactQueryFilters(items,normalizedQuery,options){if(!options.titleExact&&!options.phraseExact){return items}return items.filter(document=>{if(options.titleExact&&normalizeSearchPhrase(document.metadata.title)!==normalizedQuery){return false}if(options.phraseExact&&!documentContainsExactPhrase(document,normalizedQuery)){return false}return true})}function assertSearchAssigneeFilter(assigneeFilter){if(assigneeFilter&&[...assigneeFilter].some(value=>value.toLowerCase()==="none"||value.toLowerCase()==="null")){throw new PmCliError('--assignee no longer accepts "none" or "null".',EXIT_CODE.USAGE)}}function resolveSearchMetadataFilterSet(options,typeRegistry,statusFilter){const assigneeFilter=parseStringFilterSet(options.assignee,{label:"--assignee"});assertSearchAssigneeFilter(assigneeFilter);const missingMetadataFilters=resolveMissingMetadataFilters(options);const contentFieldFilters=resolveContentFieldFilters(options);return{typeFilter:parseTypeFilterSet(options.type,typeRegistry),tagFilter:parseStringFilterSet(options.tag,{label:"--tag",normalize:tag=>tag.toLowerCase()}),priorityFilter:parsePriorityFilterSet(options.priority),deadlineBefore:parseSearchDeadline(options.deadlineBefore,"deadline-before"),deadlineAfter:parseSearchDeadline(options.deadlineAfter,"deadline-after"),updatedAfter:parseTimestampWindow(options.updatedAfter,"updated-after"),updatedBefore:parseTimestampWindow(options.updatedBefore,"updated-before"),createdAfter:parseTimestampWindow(options.createdAfter,"created-after"),createdBefore:parseTimestampWindow(options.createdBefore,"created-before"),assigneeFilter,sprintFilter:parseStringFilterSet(options.sprint,{label:"--sprint"}),releaseFilter:parseStringFilterSet(options.release,{label:"--release"}),parentFilter:options.parent?.trim(),statusSet:statusFilter&&statusFilter.length>0?new Set(statusFilter):void 0,missingMetadataFilters,missingMetadataActive:hasMissingMetadataFilter(missingMetadataFilters),contentFieldFilters,contentFiltersActive:hasContentFieldFilter(contentFieldFilters)}}function matchesScalarSearchFilters(item,filters){return matchesIdentitySearchFilters(item,filters)&&matchesTimestampFilters(item,filters)&&matchesOwnerSearchFilters(item,filters)}function matchesIdentitySearchFilters(item,filters){if(filters.statusSet&&!filters.statusSet.has(item.status))return false;if(filters.typeFilter&&!filters.typeFilter.has(item.type))return false;const tagFilter=filters.tagFilter;if(tagFilter&&!stringArray(item.tags).some(tag=>tagFilter.has(tag)))return false;if(filters.priorityFilter!==void 0&&!filters.priorityFilter.has(item.priority))return false;return true}function matchesOwnerSearchFilters(item,filters){if(filters.assigneeFilter!==void 0&&(item.assignee===void 0||!filters.assigneeFilter.has(item.assignee)))return false;if(filters.sprintFilter!==void 0&&(item.sprint===void 0||!filters.sprintFilter.has(item.sprint)))return false;if(filters.releaseFilter!==void 0&&(item.release===void 0||!filters.releaseFilter.has(item.release)))return false;if(filters.parentFilter!==void 0&&item.parent!==filters.parentFilter)return false;return true}function matchesSearchMetadataFilters(document,filters,runtimeFieldFilters,lifecycleClassifier){const item=document.metadata;if(!matchesScalarSearchFilters(item,filters)){return false}if(!matchesRuntimeFilters(item,runtimeFieldFilters)){return false}if(filters.missingMetadataActive&&!itemMatchesMissingMetadata(item,filters.missingMetadataFilters,lifecycleClassifier)){return false}if(filters.contentFiltersActive&&!itemMatchesContentFilters(item,filters.contentFieldFilters)){return false}return true}function applyFilters(items,options,typeRegistry,runtimeFieldFilters,statusFilter,lifecycleClassifier){const filters=resolveSearchMetadataFilterSet(options,typeRegistry,statusFilter);return items.filter(document=>matchesSearchMetadataFilters(document,filters,runtimeFieldFilters,lifecycleClassifier))}export{applyExactQueryFilters,applyFilters,collectExactPhraseFields,dependencyEntries,documentContainsExactPhrase,stringArray,textEntries};
2
+ //# sourceMappingURL=filters.js.map
@@ -0,0 +1,28 @@
1
+ import { type SearchOptions } from "../search-contracts.js";
2
+ import type { PreparedSearchInput } from "./types.js";
3
+ declare const INLINE_QUERY_FILTER_FIELDS: readonly ["tag", "status", "type", "priority"];
4
+ type InlineQueryFilterField = (typeof INLINE_QUERY_FILTER_FIELDS)[number];
5
+ /** Result of extracting inline `field:value` tokens from a raw search query. */
6
+ interface InlineQueryParse {
7
+ /** The query with all recognized inline filter tokens removed. */
8
+ residualQuery: string;
9
+ /** Recognized inline filters keyed by field name; first occurrence per field wins. */
10
+ inlineFilters: Partial<Record<InlineQueryFilterField, string>>;
11
+ }
12
+ /**
13
+ * Split a raw search query into its residual keyword text and any inline
14
+ * `field:value` filter tokens (GH-157). Recognized fields are
15
+ * {@link INLINE_QUERY_FILTER_FIELDS}; the value runs to the end of the token so
16
+ * colon-bearing values like `tag:area:search` parse as `{ tag: "area:search" }`.
17
+ * Only the first occurrence of each field is captured — later duplicates are left
18
+ * in the residual query so they are never silently dropped. Tokens whose prefix
19
+ * is not a recognized field (`foo:bar`) stay in the residual query verbatim.
20
+ */
21
+ declare function parseInlineQueryFilters(query: string): InlineQueryParse;
22
+ /** Merge inline `field:value` filters into a search options object (GH-157). Explicit `--field` flags always win: an inline token is applied only when the corresponding option is not already set, and a conflicting inline token is recorded as a `search_inline_filter_ignored:<field>:flag_takes_precedence` warning so the override is observable rather than silent. Returns a fresh options object — the caller's input is never mutated. */
23
+ declare function applyInlineQueryFilters(options: SearchOptions, inlineFilters: Partial<Record<InlineQueryFilterField, string>>, warnings: string[]): SearchOptions;
24
+ /** Resolve inline filters, explicit options, tokenization and projection before accessing the search corpus. */
25
+ declare function prepareSearchInput(rawQuery: string, rawOptions: SearchOptions): PreparedSearchInput;
26
+ /** Reject incompatible paging options before search execution. */
27
+ declare function assertSearchPagingOptions(options: SearchOptions): void;
28
+ export { applyInlineQueryFilters, assertSearchPagingOptions, parseInlineQueryFilters, prepareSearchInput };
@@ -0,0 +1,2 @@
1
+ !(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5ff2b814-42b0-50fc-92dd-17e9fc521982")}catch(e2){}})();import{EXIT_CODE}from"../../../core/shared/constants.js";import{PmCliError}from"../../../core/shared/errors.js";import{toNonEmptyStringOrUndefined}from"../../../core/shared/primitives.js";import{parseLimit}from"../parsers.js";import{normalizeSearchPhrase,parseMinScoreOverride,parseSearchBoolean,parseSearchMatchMode,parseSearchProjection,parseSearchTokens}from"../search-contracts.js";const INLINE_QUERY_FILTER_FIELDS=["tag","status","type","priority"];function parseInlineQueryFilters(query){const inlineFilters={};const residualTokens=[];for(const token of query.split(/\s+/).filter(entry=>entry.length>0)){const separatorIndex=token.indexOf(":");const field=separatorIndex>0?token.slice(0,separatorIndex).toLowerCase():"";const value=separatorIndex>0?token.slice(separatorIndex+1):"";const matchedField=INLINE_QUERY_FILTER_FIELDS.includes(field)?field:void 0;if(matchedField&&value.length>0&&inlineFilters[matchedField]===void 0){inlineFilters[matchedField]=value;continue}residualTokens.push(token)}return{residualQuery:residualTokens.join(" "),inlineFilters}}function applyInlineQueryFilters(options,inlineFilters,warnings){const merged={...options};for(const field of INLINE_QUERY_FILTER_FIELDS){const inlineValue=inlineFilters[field];if(inlineValue===void 0){continue}if(toNonEmptyStringOrUndefined(merged[field])!==void 0){warnings.push(`search_inline_filter_ignored:${field}:flag_takes_precedence`);continue}merged[field]=inlineValue}return merged}function prepareSearchInput(rawQuery,rawOptions){const inlineWarnings=[];const inlineParse=parseInlineQueryFilters(rawQuery);const hasInlineFilters=Object.keys(inlineParse.inlineFilters).length>0;if(hasInlineFilters&&inlineParse.residualQuery.trim().length===0){throw new PmCliError("Inline field:value tokens consumed the entire query, leaving no search terms.",EXIT_CODE.USAGE,{examples:["pm search auth tag:area:search","pm list --tag area:search --status open"],nextSteps:["Add keyword terms alongside the inline filters, or use pm list for pure tag/status/type/priority filtering."]})}const query=hasInlineFilters?inlineParse.residualQuery:rawQuery;const options=applyInlineQueryFilters(rawOptions,inlineParse.inlineFilters,inlineWarnings);return{query,options,inlineWarnings,highlight:options.highlight===true,includeLinked:parseSearchBoolean(options.includeLinked),titleExact:parseSearchBoolean(options.titleExact),phraseExact:parseSearchBoolean(options.phraseExact),matchMode:parseSearchMatchMode(typeof options.matchMode==="string"?options.matchMode:void 0),minScoreOverride:parseMinScoreOverride(options.minScore),countOnly:options.count===true,tokens:parseSearchTokens(query),normalizedQuery:normalizeSearchPhrase(query),limit:parseLimit(options.limit),projection:parseSearchProjection(options,rawQuery),modeWasExplicit:typeof options.mode==="string"&&options.mode.trim().length>0}}function assertSearchPagingOptions(options){if(options.after!==void 0&&options.count===true){throw new PmCliError("Search --after cannot be combined with --count.",EXIT_CODE.USAGE)}}export{applyInlineQueryFilters,assertSearchPagingOptions,parseInlineQueryFilters,prepareSearchInput};
2
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1,34 @@
1
+ import { type RuntimeStatusRegistry } from "../../../core/schema/runtime-schema.js";
2
+ import type { ItemDocument } from "../../../types/index.js";
3
+ import { type SearchTuning } from "../search-contracts.js";
4
+ import type { SearchHit, SearchHitHighlight } from "./types.js";
5
+ /** Count query-token occurrences in a searchable field for lexical scoring. */
6
+ declare function countOccurrences(haystack: string, needle: string): number;
7
+ /**
8
+ * Wrap every case-insensitive occurrence of any query token in `text` with the
9
+ * {@link HIGHLIGHT_OPEN}/{@link HIGHLIGHT_CLOSE} markers. Token boundaries follow
10
+ * the same substring semantics as the lexical scorer (a token highlights inside
11
+ * longer words, matching how it scored), and overlapping matches are coalesced
12
+ * by the single combined alternation so markers never nest.
13
+ */
14
+ declare function markTokenRuns(text: string, tokens: string[]): string;
15
+ /**
16
+ * Build a single highlighted snippet for one field value (GH-157), or null when
17
+ * no token matches. The snippet is a window of {@link HIGHLIGHT_SNIPPET_RADIUS}
18
+ * characters on each side of the first match with the matching token runs
19
+ * wrapped in markers, prefixed/suffixed with an ellipsis when the field text was
20
+ * trimmed. Returning null lets the caller skip fields with no concrete textual
21
+ * match (e.g. a field flagged purely by the phrase-coverage bonus).
22
+ */
23
+ declare function highlightFieldSnippet(text: string, tokens: string[]): string | null;
24
+ /** Produce per-field matched-text snippets for a hit (GH-157, `--highlight`). Iterates the hit's already-sorted `matched_fields`, retains only the document-derived searchable fields (skipping synthetic markers like `id`, `semantic`, `rerank`, and `linked_content`), and emits a snippet for each field that still contains a concrete token match. */
25
+ declare function buildHitHighlights(document: ItemDocument, matchedFields: string[], tokens: string[]): SearchHitHighlight[];
26
+ /** Score one item using weighted fields and the exact-ID ranking guarantee. */
27
+ declare function scoreDocument(document: ItemDocument, tokens: string[], normalizedQuery: string, linkedCorpus: string, tuning: SearchTuning, idPrefix: string, applyCoverageBonus?: boolean): SearchHit | null;
28
+ /** Order hits by descending relevance with deterministic item tie breakers. */
29
+ declare function sortHits(items: SearchHit[], statusRegistry: RuntimeStatusRegistry): SearchHit[];
30
+ /** Compute the lexical contribution used when blending keyword and semantic relevance. */
31
+ declare function buildHybridLexicalScore(document: ItemDocument, tokens: string[], normalizedQuery: string, includeLinked: boolean, linkedCorpusById: Map<string, string>, tuning: SearchTuning, idPrefix: string, applyCoverageBonus?: boolean): SearchHit | null;
32
+ /** Normalize provider score ranges before combining retrieval results. */
33
+ declare function normalizeScoreMap(scoreById: Map<string, number>): Map<string, number>;
34
+ export { buildHitHighlights, buildHybridLexicalScore, countOccurrences, highlightFieldSnippet, markTokenRuns, normalizeScoreMap, scoreDocument, sortHits };
@@ -0,0 +1,2 @@
1
+ !(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5a927991-1dbe-5906-a0eb-7da2534b6905")}catch(e2){}})();import{isTerminalStatus}from"../../../core/item/status.js";import{buildEventCorpus,buildPlanFlatCorpus,buildReminderCorpus}from"../../../core/search/corpus.js";import{tokenizeAlphaNumeric}from"../../../core/shared/text-normalization.js";import{compareTimestampStrings}from"../../../core/shared/time.js";import{normalizeSearchPhrase}from"../search-contracts.js";import{dependencyEntries,stringArray,textEntries}from"./filters.js";const LONG_QUERY_TOKEN_THRESHOLD=2;const LONG_QUERY_TITLE_EXACT_BONUS=120;const LONG_QUERY_PHRASE_MULTIPLIER=6;const ALL_TERMS_COVERAGE_BONUS=40;const EXACT_ID_MATCH_SCORE=1e3;const SHORT_ID_MATCH_SCORE=900;const HIGHLIGHT_OPEN="\xAB";const HIGHLIGHT_CLOSE="\xBB";const HIGHLIGHT_SNIPPET_RADIUS=60;function countOccurrences(haystack,needle){let count=0;let index=0;while(true){const foundAt=haystack.indexOf(needle,index);if(foundAt<0){return count}count+=1;index=foundAt+needle.length}}const SEARCHABLE_FIELD_BUILDERS=[{name:"title",weightKey:"title_weight",value:document=>document.metadata.title},{name:"description",weightKey:"description_weight",value:document=>document.metadata.description},{name:"tags",weightKey:"tags_weight",value:document=>stringArray(document.metadata.tags).join(" ")},{name:"status",weightKey:"status_weight",value:document=>typeof document.metadata.status==="string"?document.metadata.status:""},{name:"body",weightKey:"body_weight",value:document=>document.body},{name:"comments",weightKey:"comments_weight",value:document=>textEntries(document.metadata.comments).map(entry=>entry.text).join(" ")},{name:"notes",weightKey:"notes_weight",value:document=>textEntries(document.metadata.notes).map(entry=>entry.text).join(" ")},{name:"learnings",weightKey:"learnings_weight",value:document=>textEntries(document.metadata.learnings).map(entry=>entry.text).join(" ")},{name:"reminders",weightKey:"reminders_weight",value:document=>buildReminderCorpus(document.metadata).join(" ")},{name:"events",weightKey:"events_weight",value:document=>buildEventCorpus(document.metadata).join(" ")},{name:"dependencies",weightKey:"dependencies_weight",value:document=>dependencyEntries(document.metadata.dependencies).map(entry=>`${entry.id} ${entry.kind}`).join(" ")},{name:"plan",weightKey:"body_weight",value:document=>buildPlanFlatCorpus(document.metadata)}];const SEARCHABLE_FIELD_BUILDER_BY_NAME=new Map(SEARCHABLE_FIELD_BUILDERS.map(builder=>[builder.name,builder]));function markTokenRuns(text,tokens){const escaped=tokens.filter(token=>token.length>0).sort((left,right)=>right.length-left.length).map(value=>value.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));if(escaped.length===0){return text}const pattern=new RegExp(escaped.join("|"),"gi");return text.replace(pattern,match=>`${HIGHLIGHT_OPEN}${match}${HIGHLIGHT_CLOSE}`)}function highlightFieldSnippet(text,tokens){if(text.length===0){return null}const lowerText=text.toLowerCase();let firstMatchIndex=-1;let firstMatchLength=0;for(const token of tokens){if(token.length===0){continue}const index=lowerText.indexOf(token);if(index>=0&&(firstMatchIndex<0||index<firstMatchIndex)){firstMatchIndex=index;firstMatchLength=token.length}}if(firstMatchIndex<0){return null}const windowStart=Math.max(0,firstMatchIndex-HIGHLIGHT_SNIPPET_RADIUS);const windowEnd=Math.min(text.length,firstMatchIndex+firstMatchLength+HIGHLIGHT_SNIPPET_RADIUS);const marked=markTokenRuns(text.slice(windowStart,windowEnd),tokens);const prefix=windowStart>0?"\u2026":"";const suffix=windowEnd<text.length?"\u2026":"";return`${prefix}${marked}${suffix}`}function buildHitHighlights(document,matchedFields,tokens){const highlights=[];for(const field of matchedFields){const builder=SEARCHABLE_FIELD_BUILDER_BY_NAME.get(field);if(builder===void 0){continue}const snippet=highlightFieldSnippet(builder.value(document),tokens);if(snippet!==null){highlights.push({field,snippet})}}return highlights}function buildSearchableScoringFields(document,linkedCorpus,tuning){return[...SEARCHABLE_FIELD_BUILDERS.map(builder=>({name:builder.name,value:builder.value(document),weight:tuning[builder.weightKey]})),{name:"linked_content",value:linkedCorpus,weight:tuning.linked_content_weight}]}function buildExactIdSearchHit(item,normalizedQuery,idPrefix){const normalizedId=normalizeSearchPhrase(item.id);const normalizedIdPrefix=typeof idPrefix==="string"?idPrefix.trim().toLowerCase():"";const normalizedIdPrefixPhrase=normalizeSearchPhrase(normalizedIdPrefix);const normalizedShortId=normalizedIdPrefixPhrase.length>0&&normalizedId.startsWith(normalizedIdPrefixPhrase)?normalizedId.slice(normalizedIdPrefixPhrase.length).trim():normalizedId;if(normalizedQuery!==normalizedId&&normalizedQuery!==normalizedShortId){return null}return{item,score:normalizedQuery===normalizedId?EXACT_ID_MATCH_SCORE:SHORT_ID_MATCH_SCORE,matched_fields:["id"],matched_all_terms:true,exact_id_match:true}}function scoreTokenMatches(tokens,titleTokenCounts,searchableFields,tuning,state){for(const token of tokens){const exactTitleMatches=titleTokenCounts.get(token)??0;if(exactTitleMatches>0){state.score+=exactTitleMatches*tuning.title_exact_bonus;state.matched.add("title");state.matchedTokens.add(token)}for(const field of searchableFields){const occurrences=countOccurrences(field.value.toLowerCase(),token);if(occurrences>0){state.score+=occurrences*field.weight;state.matched.add(field.name);state.matchedTokens.add(token)}}}}function scorePhraseMatches(item,normalizedQuery,searchableFields,state){const normalizedTitle=normalizeSearchPhrase(item.title);if(normalizedTitle===normalizedQuery){state.score+=LONG_QUERY_TITLE_EXACT_BONUS;state.matched.add("title")}for(const field of searchableFields){const phraseOccurrences=countOccurrences(normalizeSearchPhrase(field.value),normalizedQuery);if(phraseOccurrences>0){state.score+=phraseOccurrences*field.weight*LONG_QUERY_PHRASE_MULTIPLIER;state.matched.add(field.name)}}}function scoreDocument(document,tokens,normalizedQuery,linkedCorpus,tuning,idPrefix,applyCoverageBonus=false){const item=document.metadata;const exactIdHit=buildExactIdSearchHit(item,normalizedQuery,idPrefix);if(exactIdHit){return exactIdHit}const titleTokenCounts=new Map;for(const token of tokenizeAlphaNumeric(item.title)){titleTokenCounts.set(token,(titleTokenCounts.get(token)??0)+1)}const searchableFields=buildSearchableScoringFields(document,linkedCorpus,tuning);const state={score:0,matched:new Set,matchedTokens:new Set};scoreTokenMatches(tokens,titleTokenCounts,searchableFields,tuning,state);const distinctTokens=new Set(tokens);const matchedAllTerms=distinctTokens.size>0&&state.matchedTokens.size===distinctTokens.size;if(applyCoverageBonus&&distinctTokens.size>1&&matchedAllTerms){state.score+=ALL_TERMS_COVERAGE_BONUS}const isLongPhraseQuery=tokens.length>=LONG_QUERY_TOKEN_THRESHOLD&&normalizedQuery.includes(" ");if(isLongPhraseQuery){scorePhraseMatches(item,normalizedQuery,searchableFields,state)}if(state.score<=0){return null}return{item,score:state.score,matched_fields:[...state.matched].sort((a,b)=>a.localeCompare(b)),matched_all_terms:matchedAllTerms}}function sortHits(items,statusRegistry){return[...items].sort((a,b)=>{const byScore=b.score-a.score;if(byScore!==0)return byScore;const aTerminal=isTerminalStatus(a.item.status,statusRegistry);const bTerminal=isTerminalStatus(b.item.status,statusRegistry);if(aTerminal!==bTerminal){return aTerminal?1:-1}const byPriority=a.item.priority-b.item.priority;if(byPriority!==0)return byPriority;const byUpdated=compareTimestampStrings(b.item.updated_at,a.item.updated_at);if(byUpdated!==0)return byUpdated;return a.item.id.localeCompare(b.item.id)})}function buildHybridLexicalScore(document,tokens,normalizedQuery,includeLinked,linkedCorpusById,tuning,idPrefix,applyCoverageBonus=false){return scoreDocument(document,tokens,normalizedQuery,includeLinked?linkedCorpusById.get(document.metadata.id)??"":"",tuning,idPrefix,applyCoverageBonus)}function normalizeScoreMap(scoreById){if(scoreById.size===0){return new Map}let minScore=Number.POSITIVE_INFINITY;let maxScore=Number.NEGATIVE_INFINITY;for(const score of scoreById.values()){minScore=Math.min(minScore,score);maxScore=Math.max(maxScore,score)}if(maxScore===minScore){return new Map([...scoreById.keys()].map(id=>[id,1]))}const normalized=new Map;for(const[id,score]of scoreById){normalized.set(id,(score-minScore)/(maxScore-minScore))}return normalized}export{buildHitHighlights,buildHybridLexicalScore,countOccurrences,highlightFieldSnippet,markTokenRuns,normalizeScoreMap,scoreDocument,sortHits};
2
+ //# sourceMappingURL=lexical.js.map
@@ -0,0 +1,34 @@
1
+ import { type QueryExpansionConfig, type RerankConfig } from "../../../core/search/relevance.js";
2
+ import type { ItemDocument } from "../../../types/index.js";
3
+ import { type WorkspaceMemoryRollup } from "../../workspace-memory.js";
4
+ import { type SearchMatchMode, type SearchModeSource, type SearchOptions, type SearchProjectionConfig } from "../search-contracts.js";
5
+ import { type SearchMode } from "../search-rendering.js";
6
+ import type { PreparedSearchInput, SearchHit, SearchResponseContext, SearchResult, SearchResultItem } from "./types.js";
7
+ /** Build a zero-hit response preserving the requested search mode and filters. */
8
+ declare function emptySearchResult(query: string, mode: SearchMode, matchMode: SearchMatchMode, options: SearchOptions, includeLinked: boolean, scoreThreshold: number, hybridSemanticWeight: number, queryExpansion: QueryExpansionConfig, rerank: RerankConfig, projection: SearchProjectionConfig, warnings: string[], runtimeFieldFilters: Record<string, unknown> | undefined, countOnly: boolean, modeSource: SearchModeSource): SearchResult;
9
+ /** Resolve a projected search field from item metadata or search-hit evidence. */
10
+ declare function readSearchFieldValue(hit: SearchHit, field: string): unknown;
11
+ /** Convert ranked hits into the requested compact, full or custom-field rows. */
12
+ declare function projectSearchHits(hits: SearchHit[], projection: SearchProjectionConfig): SearchResultItem[];
13
+ /** Build an empty result with the resolved search settings and warnings. */
14
+ declare function buildEmptySearchResultFromContext(response: SearchResponseContext, countOnly: boolean): SearchResult;
15
+ /** Return the matched total without materializing projected hit rows. */
16
+ declare function buildCountOnlySearchResult(response: SearchResponseContext, total: number): SearchResult;
17
+ /** Attach bounded historical workspace rollups when the query and workspace qualify. */
18
+ declare function withSearchWorkspaceMemory(result: SearchResult, memory: {
19
+ cache_status: "fresh" | "rebuilt";
20
+ matches: WorkspaceMemoryRollup[];
21
+ } | undefined): SearchResult;
22
+ /** Add matched-field snippets only when highlighting was requested. */
23
+ declare function attachSearchHighlights(prepared: PreparedSearchInput, filteredDocuments: ItemDocument[], limited: SearchHit[]): {
24
+ hits: SearchHit[];
25
+ projection: SearchProjectionConfig;
26
+ };
27
+ /** Apply search pagination and projection to the final ranked hit set. */
28
+ declare function buildSearchResultForHits(response: SearchResponseContext, projectedItems: SearchResultItem[], total: number, limitedCount: number, effectiveProjection: SearchProjectionConfig, pageExtras: {
29
+ has_more?: boolean;
30
+ next_cursor?: string;
31
+ applied_limit: number;
32
+ truncated?: true;
33
+ }): SearchResult;
34
+ export { attachSearchHighlights, buildCountOnlySearchResult, buildEmptySearchResultFromContext, buildSearchResultForHits, emptySearchResult, projectSearchHits, readSearchFieldValue, withSearchWorkspaceMemory };
@@ -0,0 +1,2 @@
1
+ !(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="984a6fdc-9383-5f89-8e71-b0d3ed2a35cc")}catch(e2){}})();import{toItemRecord}from"../../../core/item/item-record.js";import{nowIso}from"../../../core/shared/time.js";import{buildCompactSearchFilterSummary,buildVerboseSearchFilters}from"../search-rendering.js";import{buildHitHighlights}from"./lexical.js";function emptySearchResult(query,mode,matchMode,options,includeLinked,scoreThreshold,hybridSemanticWeight,queryExpansion,rerank,projection,warnings,runtimeFieldFilters,countOnly,modeSource){const countExtras=countOnly?{total:0,count_only:true}:{};const compactSummaryMode=projection.mode==="compact"&&options.compact===true;if(compactSummaryMode){const compactFilters=buildCompactSearchFilterSummary({mode,matchMode,options,includeLinked,titleExact:options.titleExact===true,phraseExact:options.phraseExact===true,scoreThreshold,hybridSemanticWeight,runtimeFieldFilters});return{query:query.trim(),mode,...compactModeSource(modeSource),items:[],count:0,...countExtras,filters:compactFilters,...warnings.length>0?{warnings}:{}}}const projectionFields=projection.mode==="full"?null:[...projection.fields];return{query:query.trim(),mode,mode_source:modeSource,items:[],count:0,...countExtras,filters:buildVerboseSearchFilters({effectiveMode:mode,matchMode,options,includeLinked,titleExact:options.titleExact===true,phraseExact:options.phraseExact===true,scoreThreshold,hybridSemanticWeight,queryExpansion,rerank,runtimeFieldFilters:runtimeFieldFilters??{}}),projection:{mode:projection.mode,fields:projectionFields},now:nowIso(),...warnings.length>0?{warnings}:{}}}function readSearchFieldValue(hit,field){const normalized=field.trim();if(normalized.length===0){return null}if(normalized==="score"){return hit.score}if(normalized==="matched_fields"){return hit.matched_fields}if(normalized.startsWith("item.")){const itemKey=normalized.slice("item.".length);if(itemKey.length===0){return null}const itemRecord2=toItemRecord(hit.item);return itemRecord2[itemKey]??null}const hitRecord=hit;const itemRecord=toItemRecord(hit.item);if(Object.prototype.hasOwnProperty.call(itemRecord,normalized)){return itemRecord[normalized]??null}if(Object.prototype.hasOwnProperty.call(hitRecord,normalized)){return hitRecord[normalized]??null}return null}function projectSearchHits(hits,projection){if(projection.mode==="full"){return hits.map(({matched_all_terms:_matchedAllTerms,exact_id_match:_exactIdMatch,...hit})=>hit)}return hits.map(hit=>{const projected={};for(const field of projection.fields){projected[field]=readSearchFieldValue(hit,field)}return projected})}function buildEmptySearchResultFromContext(response,countOnly){return emptySearchResult(response.query,response.effectiveMode,response.matchMode,response.options,response.includeLinked,response.scoreThreshold,response.hybridSemanticWeight,response.queryExpansion,response.rerank,response.projection,response.warnings,response.runtimeFieldFilters,countOnly,response.modeSource)}function buildCountOnlySearchResult(response,total){if(response.projection.mode==="compact"&&response.options.compact===true){return withSearchWarnings({query:response.query.trim(),mode:response.effectiveMode,...compactModeSource(response.modeSource),items:[],count:total,total,count_only:true,filters:buildCompactSearchFilterSummary({mode:response.effectiveMode,matchMode:response.matchMode,options:response.options,includeLinked:response.includeLinked,titleExact:response.titleExact,phraseExact:response.phraseExact,scoreThreshold:response.scoreThreshold,hybridSemanticWeight:response.hybridSemanticWeight,runtimeFieldFilters:response.runtimeFieldFilters})},response.warnings)}const projectionFields=response.projection.mode==="full"?null:[...response.projection.fields];return withSearchWarnings({query:response.query.trim(),mode:response.effectiveMode,mode_source:response.modeSource,items:[],count:total,total,count_only:true,filters:buildVerboseSearchFilters({effectiveMode:response.effectiveMode,matchMode:response.matchMode,options:response.options,includeLinked:response.includeLinked,titleExact:response.titleExact,phraseExact:response.phraseExact,scoreThreshold:response.scoreThreshold,hybridSemanticWeight:response.hybridSemanticWeight,queryExpansion:response.queryExpansion,rerank:response.rerank,runtimeFieldFilters:response.runtimeFieldFilters}),projection:{mode:response.projection.mode,fields:projectionFields},now:nowIso()},response.warnings)}function compactModeSource(source){return source==="settings"?{mode_source:source}:{}}function withSearchWarnings(result,warnings){if(warnings.length===0){return result}return{...result,warnings}}function withSearchWorkspaceMemory(result,memory){return memory?{...result,workspace_memory:memory}:result}function attachSearchHighlights(prepared,filteredDocuments,limited){if(!prepared.highlight){return{hits:limited,projection:prepared.projection}}const documentById=new Map(filteredDocuments.map(document=>[document.metadata.id,document]));const hits=limited.map(hit=>({...hit,highlights:buildHitHighlights(documentById.get(hit.item.id),hit.matched_fields,prepared.tokens)}));if(prepared.projection.mode==="full"||prepared.projection.fields.includes("highlights")){return{hits,projection:prepared.projection}}return{hits,projection:{mode:prepared.projection.mode,fields:[...prepared.projection.fields,"highlights"]}}}function buildSearchResultForHits(response,projectedItems,total,limitedCount,effectiveProjection,pageExtras){const truncationExtras=limitedCount<total?{total}:{};if(response.projection.mode==="compact"&&response.options.compact===true){return withSearchWarnings({query:response.query.trim(),mode:response.effectiveMode,...compactModeSource(response.modeSource),items:projectedItems,count:projectedItems.length,...truncationExtras,...pageExtras,filters:buildCompactSearchFilterSummary({mode:response.effectiveMode,matchMode:response.matchMode,options:response.options,includeLinked:response.includeLinked,titleExact:response.titleExact,phraseExact:response.phraseExact,scoreThreshold:response.scoreThreshold,hybridSemanticWeight:response.hybridSemanticWeight,runtimeFieldFilters:response.runtimeFieldFilters})},response.warnings)}const finalProjectionFields=effectiveProjection.mode==="full"?null:[...effectiveProjection.fields];return{query:response.query.trim(),mode:response.effectiveMode,mode_source:response.modeSource,items:projectedItems,count:projectedItems.length,...truncationExtras,...pageExtras,filters:buildVerboseSearchFilters({effectiveMode:response.effectiveMode,matchMode:response.matchMode,options:response.options,includeLinked:response.includeLinked,titleExact:response.titleExact,phraseExact:response.phraseExact,scoreThreshold:response.scoreThreshold,hybridSemanticWeight:response.hybridSemanticWeight,queryExpansion:response.queryExpansion,rerank:response.rerank,runtimeFieldFilters:response.runtimeFieldFilters}),projection:{mode:effectiveProjection.mode,fields:finalProjectionFields},now:nowIso(),...response.warnings.length>0?{warnings:response.warnings}:{}}}export{attachSearchHighlights,buildCountOnlySearchResult,buildEmptySearchResultFromContext,buildSearchResultForHits,emptySearchResult,projectSearchHits,readSearchFieldValue,withSearchWorkspaceMemory};
2
+ //# sourceMappingURL=response.js.map
@@ -0,0 +1,229 @@
1
+ import { type Bm25Params } from "../../../core/search/bm25.js";
2
+ import { type EmbeddingProviderConfig, type EmbeddingProviderResolution } from "../../../core/search/providers.js";
3
+ import { type QueryExpansionConfig, type RerankConfig } from "../../../core/search/relevance.js";
4
+ import { type VectorQueryHit, type VectorStoreConfig, type VectorStoreResolution } from "../../../core/search/vector-stores.js";
5
+ import { toNonEmptyStringOrUndefined } from "../../../core/shared/primitives.js";
6
+ import type { ItemDocument, PmSettings } from "../../../types/index.js";
7
+ import { type SearchOptions } from "../search-contracts.js";
8
+ import { type SearchMode } from "../search-rendering.js";
9
+ import type { SearchHit } from "./types.js";
10
+ interface ExtensionSearchProviderContext {
11
+ query: string;
12
+ mode: SearchMode;
13
+ tokens: string[];
14
+ options: SearchOptions;
15
+ settings: PmSettings;
16
+ documents: ItemDocument[];
17
+ }
18
+ interface ExtensionSearchProviderHit {
19
+ id: string;
20
+ score: number;
21
+ matched_fields?: string[];
22
+ }
23
+ type ExtensionSearchProviderQuery = (context: ExtensionSearchProviderContext) => Promise<ExtensionSearchProviderHit[] | {
24
+ hits?: ExtensionSearchProviderHit[];
25
+ }> | ExtensionSearchProviderHit[] | {
26
+ hits?: ExtensionSearchProviderHit[];
27
+ };
28
+ interface ExtensionSearchProviderQueryExpansionContext {
29
+ query: string;
30
+ mode: Exclude<SearchMode, "keyword">;
31
+ settings: PmSettings;
32
+ }
33
+ /** Optional provider hook used to expand a query before semantic retrieval. */
34
+ type ExtensionSearchProviderQueryExpansion = (context: ExtensionSearchProviderQueryExpansionContext) => Promise<string[] | {
35
+ queries?: string[];
36
+ }> | string[] | {
37
+ queries?: string[];
38
+ };
39
+ interface ExtensionSearchProviderRerankCandidate {
40
+ id: string;
41
+ text: string;
42
+ score: number;
43
+ }
44
+ interface ExtensionSearchProviderRerankContext {
45
+ query: string;
46
+ mode: "hybrid";
47
+ model: string;
48
+ top_k: number;
49
+ settings: PmSettings;
50
+ candidates: ExtensionSearchProviderRerankCandidate[];
51
+ }
52
+ /** Optional provider hook used to rerank retrieved candidates. */
53
+ type ExtensionSearchProviderRerank = (context: ExtensionSearchProviderRerankContext) => Promise<Array<{
54
+ id?: unknown;
55
+ score?: unknown;
56
+ }> | {
57
+ hits?: Array<{
58
+ id?: unknown;
59
+ score?: unknown;
60
+ }>;
61
+ }> | Array<{
62
+ id?: unknown;
63
+ score?: unknown;
64
+ }> | {
65
+ hits?: Array<{
66
+ id?: unknown;
67
+ score?: unknown;
68
+ }>;
69
+ };
70
+ type ExtensionVectorQuery = (context: {
71
+ vector: number[];
72
+ limit: number;
73
+ settings: PmSettings;
74
+ }) => Promise<VectorQueryHit[]> | VectorQueryHit[];
75
+ type ExtensionVectorUpsert = (context: {
76
+ points: Array<{
77
+ id: string;
78
+ vector: number[];
79
+ payload: Record<string, unknown>;
80
+ }>;
81
+ settings: PmSettings;
82
+ }) => Promise<void> | void;
83
+ type ExtensionVectorAdapter = {
84
+ adapterName: string;
85
+ query?: ExtensionVectorQuery;
86
+ upsert?: ExtensionVectorUpsert;
87
+ };
88
+ type ImplicitSemanticFallbackReason = "timeout" | "connection" | "error";
89
+ /**
90
+ * Aggregate the `code` strings found along an error's `cause` chain.
91
+ *
92
+ * undici (Node's fetch) collapses connection errors to the generic message
93
+ * "fetch failed" and stashes the real syscall code (e.g. `ECONNREFUSED`) on
94
+ * `error.cause.code`. Walking the chain (bounded depth) lets the fallback
95
+ * classifier label a downed/unreachable Ollama backend as "connection" rather
96
+ * than the catch-all "error".
97
+ */
98
+ export declare function collectErrorCauseCodes(error: unknown): string;
99
+ /**
100
+ * Classify why an implicit/explicit semantic search degraded to keyword mode,
101
+ * inspecting both the error message and the {@link collectErrorCauseCodes} chain
102
+ * so undici's generic "fetch failed" is recognised as a connection failure.
103
+ */
104
+ export declare function classifyImplicitSemanticFallbackReason(error: unknown): ImplicitSemanticFallbackReason;
105
+ /** Describe why an explicitly requested semantic mode fell back to keyword retrieval. */
106
+ declare function buildExplicitSemanticFallbackWarning(requestedMode: SearchMode, error: unknown): string;
107
+ interface VectorIndexRecoveryRegistrations {
108
+ commands?: readonly {
109
+ command: string;
110
+ }[];
111
+ }
112
+ interface VectorIndexRecovery {
113
+ command: string;
114
+ args: string[];
115
+ follow_up_command?: string;
116
+ follow_up_args?: string[];
117
+ }
118
+ /** Resolve an executable reindex path from the active extension capability surface. */
119
+ export declare function resolveVectorIndexRecovery(registrations: VectorIndexRecoveryRegistrations | null): VectorIndexRecovery;
120
+ /** Compare the vectorization-status ledger against the filtered corpus and, when the vector index is behind, emit a one-line stderr warning plus a structured `vector_index_stale:N` entry in the JSON warnings array. Best-effort: ledger read failures fall through silently — the existing semantic/hybrid fallback paths cover backend errors. */
121
+ declare function maybeEmitVectorIndexStaleWarning(pmRoot: string, filteredDocuments: ItemDocument[], warnings: string[]): Promise<void>;
122
+ /** Format token-efficient vector-index staleness and executable recovery warnings. */
123
+ export declare function formatVectorIndexRecoveryWarnings(staleCount: number, recovery: VectorIndexRecovery): string[];
124
+ /** Resolve the configured semantic provider and vector-store dependencies or report their absence. */
125
+ declare function requireSemanticDependencies(requestedMode: Exclude<SearchMode, "keyword">, providerResolution: EmbeddingProviderResolution, vectorResolution: VectorStoreResolution, hasExtensionVectorQuery: boolean): {
126
+ provider: EmbeddingProviderConfig;
127
+ vectorStore: VectorStoreConfig | null;
128
+ };
129
+ /** Normalize optional provider identifiers to trimmed non-empty strings. */
130
+ declare const toOptionalNonEmptyString: typeof toNonEmptyStringOrUndefined;
131
+ interface ExtensionSearchProviderHooks {
132
+ providerName: string;
133
+ query?: ExtensionSearchProviderQuery;
134
+ queryExpansion?: ExtensionSearchProviderQueryExpansion;
135
+ rerank?: ExtensionSearchProviderRerank;
136
+ }
137
+ /** Find an active extension search provider by its declared name. */
138
+ declare function resolveExtensionSearchProviderByName(providerName: string | undefined): ExtensionSearchProviderHooks | null;
139
+ /** Resolve the configured extension-owned search provider. */
140
+ declare function resolveExtensionSearchProvider(settings: PmSettings): {
141
+ providerName: string;
142
+ query: ExtensionSearchProviderQuery;
143
+ } | null;
144
+ /** Resolve the configured extension-owned vector-store adapter. */
145
+ declare function resolveExtensionVectorAdapter(settings: PmSettings): ExtensionVectorAdapter | null;
146
+ /** Normalize extension retrieval results to item IDs and numeric scores. */
147
+ declare function normalizeExtensionProviderHits(providerName: string, raw: unknown, filteredById: Map<string, ItemDocument>): SearchHit[];
148
+ /** Join semantic scores to the filtered item corpus. */
149
+ declare function buildSemanticHits(vectorHits: VectorQueryHit[], filteredById: Map<string, ItemDocument>): {
150
+ semanticHits: SearchHit[];
151
+ semanticScores: Map<string, number>;
152
+ };
153
+ /** Blend lexical and semantic results while preserving exact-ID precedence. */
154
+ declare function combineHybridHits(filteredById: Map<string, ItemDocument>, semanticScores: Map<string, number>, keywordHits: SearchHit[], hybridSemanticWeight: number, semanticFieldLabel?: string): SearchHit[];
155
+ /** Built-in BM25 activation outcome for semantic/hybrid search (pm-75k9): - `"explicit"`: `search.provider` is `bm25` — the user opted into offline lexical ranking, so it is used even if an embedding provider is configured. - `"auto-fallback"`: `search.provider` is `auto` and neither an embedding provider nor an extension search provider is available — BM25 is used in place of degrading to the naive field-weighted keyword scorer. - `null`: BM25 does not apply; the existing embedding/extension path runs. */
156
+ type BuiltInBm25Mode = "explicit" | "auto-fallback";
157
+ /** Select the built-in BM25 path from effective search settings. */
158
+ declare function resolveBuiltInBm25Mode(params: {
159
+ configuredProvider: string | undefined;
160
+ hasEmbeddingProvider: boolean;
161
+ hasExtensionSearchProvider: boolean;
162
+ }): BuiltInBm25Mode | null;
163
+ /**
164
+ * Rank the filtered corpus with the offline BM25 provider (pm-75k9). Builds a
165
+ * BM25 index over each document's resolved search corpus (honoring
166
+ * `search.corpus_fields`), scores the query, and shapes hits for the requested
167
+ * mode: semantic mode returns pure BM25-ranked hits tagged
168
+ * `matched_fields: ["bm25"]`, while hybrid mode blends the BM25 scores with the
169
+ * locally computed keyword scores via {@link combineHybridHits}. The exact
170
+ * full-ID / short-ID guarantee is preserved in both modes via
171
+ * {@link forceExactIdHitsToTop}. No network, embedding service, or vector store
172
+ * is involved, so this path cannot fail on a backend error.
173
+ */
174
+ declare function computeBuiltInBm25Hits(params: {
175
+ requestedMode: Exclude<SearchMode, "keyword">;
176
+ query: string;
177
+ filteredDocuments: ItemDocument[];
178
+ keywordHits: SearchHit[];
179
+ corpusFields: string[];
180
+ bm25Params: Bm25Params;
181
+ hybridSemanticWeight: number;
182
+ }): SearchHit[];
183
+ interface SemanticQueryContext {
184
+ requestedMode: Exclude<SearchMode, "keyword">;
185
+ query: string;
186
+ filteredDocuments: ItemDocument[];
187
+ keywordHits: SearchHit[];
188
+ hybridSemanticWeight: number;
189
+ limit: number | undefined;
190
+ maxResults: number;
191
+ provider: EmbeddingProviderConfig;
192
+ vectorStore: VectorStoreConfig | null;
193
+ extensionVectorAdapter: ExtensionVectorAdapter | null;
194
+ queryExpansion: QueryExpansionConfig;
195
+ queryExpansionExtension: {
196
+ providerName: string;
197
+ expand: ExtensionSearchProviderQueryExpansion;
198
+ } | null;
199
+ rerank: RerankConfig;
200
+ rerankExtension: {
201
+ providerName: string;
202
+ rerank: ExtensionSearchProviderRerank;
203
+ } | null;
204
+ warnings: string[];
205
+ settings: PmSettings;
206
+ embeddingTimeoutMs?: number;
207
+ vectorQueryTimeoutMs?: number;
208
+ }
209
+ interface SemanticQueryResult {
210
+ hits: SearchHit[];
211
+ vectorMatchCount: number;
212
+ }
213
+ /** Merge vector retrieval batches by item ID for query expansion. */
214
+ declare function mergeVectorHitsById(vectorHitGroups: VectorQueryHit[][]): VectorQueryHit[];
215
+ /** Build the candidate text corpus supplied to reranking providers. */
216
+ declare function buildRerankCorpus(document: ItemDocument): string;
217
+ /** Execute provider retrieval and ranking with the existing fallback and warning contracts. */
218
+ declare function computeSemanticOrHybridHits(context: SemanticQueryContext): Promise<SemanticQueryResult>;
219
+ /** Resolve the active provider implementation for configured query expansion. */
220
+ declare function resolveQueryExpansionExtension(queryExpansion: QueryExpansionConfig): {
221
+ providerName: string;
222
+ expand: ExtensionSearchProviderQueryExpansion;
223
+ } | null;
224
+ /** Resolve the active provider implementation for configured reranking. */
225
+ declare function resolveRerankExtension(settings: PmSettings): {
226
+ providerName: string;
227
+ rerank: ExtensionSearchProviderRerank;
228
+ } | null;
229
+ export { buildExplicitSemanticFallbackWarning, buildRerankCorpus, buildSemanticHits, BuiltInBm25Mode, combineHybridHits, computeBuiltInBm25Hits, computeSemanticOrHybridHits, ExtensionSearchProviderQueryExpansion, ExtensionSearchProviderRerank, maybeEmitVectorIndexStaleWarning, mergeVectorHitsById, normalizeExtensionProviderHits, requireSemanticDependencies, resolveBuiltInBm25Mode, resolveExtensionSearchProvider, resolveExtensionSearchProviderByName, resolveExtensionVectorAdapter, resolveQueryExpansionExtension, resolveRerankExtension, toOptionalNonEmptyString };
@@ -0,0 +1,3 @@
1
+ !(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="0175d395-3f87-5a93-8d4f-a3150aaf16ae")}catch(e2){}})();import{getActiveExtensionRegistrations}from"../../../core/extensions/index.js";import{resolveRegisteredSearchProvider,resolveRegisteredVectorStoreAdapter}from"../../../core/extensions/runtime-registrations.js";import{buildBm25Index,flattenSearchCorpusText,scoreBm25Query,tokenizeBm25}from"../../../core/search/bm25.js";import{readVectorizationStatusLedger}from"../../../core/search/cache.js";import{buildSearchCorpus}from"../../../core/search/corpus.js";import{executeEmbeddingRequest}from"../../../core/search/providers.js";import{buildDeterministicQueryExpansions,mergeQueryExpansions,normalizeQueryExpansionOutput,normalizeRerankOutput,rerankCandidatesWithEmbeddings}from"../../../core/search/relevance.js";import{collectStaleVectorizationIds}from"../../../core/search/staleness.js";import{executeVectorQuery}from"../../../core/search/vector-stores.js";import{EXIT_CODE}from"../../../core/shared/constants.js";import{PmCliError}from"../../../core/shared/errors.js";import{toNonEmptyStringOrUndefined}from"../../../core/shared/primitives.js";import{normalizeScoreMap}from"./lexical.js";function collectErrorCauseCodes(error){const codes=[];let current=error;for(let depth=0;depth<5&&current&&typeof current==="object";depth+=1){const code=current.code;if(typeof code==="string"){codes.push(code.toLowerCase())}current=current.cause}return codes.join(" ")}function classifyImplicitSemanticFallbackReason(error){const message=(error instanceof Error?error.message:String(error)).toLowerCase();const causeCodes=collectErrorCauseCodes(error);const haystack=`${message} ${causeCodes}`;if(haystack.includes("timed out")||haystack.includes("timeout")||haystack.includes("etimedout")){return"timeout"}if(haystack.includes("econnrefused")||haystack.includes("connection refused")||haystack.includes("connect ")||haystack.includes("enotfound")||haystack.includes("eai_again")||haystack.includes("econnreset")||haystack.includes("fetch failed")){return"connection"}return"error"}function buildExplicitSemanticFallbackWarning(requestedMode,error){const reason=classifyImplicitSemanticFallbackReason(error);return`search_${requestedMode}_fallback:${reason}:using_keyword_mode`}function resolveVectorIndexRecovery(registrations){const hasReindex=registrations?.commands?.some(registration=>registration.command.trim()==="reindex")===true;return hasReindex?{command:"pm reindex --mode hybrid",args:["reindex","--mode","hybrid"]}:{command:"pm install search-advanced --project",args:["install","search-advanced","--project"],follow_up_command:"pm reindex --mode hybrid",follow_up_args:["reindex","--mode","hybrid"]}}async function maybeEmitVectorIndexStaleWarning(pmRoot,filteredDocuments,warnings){try{const ledger=await readVectorizationStatusLedger(pmRoot);if(ledger.warnings.length>0){warnings.push(...ledger.warnings)}const staleIds=collectStaleVectorizationIds(filteredDocuments.map(document=>({id:document.metadata.id,updated_at:document.metadata.updated_at})),ledger.entries);if(staleIds.length===0){return}const recovery=resolveVectorIndexRecovery(getActiveExtensionRegistrations());warnings.push(...formatVectorIndexRecoveryWarnings(staleIds.length,recovery));process.stderr.write(`[pm] warning: ${staleIds.length} item${staleIds.length===1?" is":"s are"} new or modified since the last reindex and ${staleIds.length===1?"is":"are"} NOT in the semantic index yet \u2014 run '${recovery.command}'${recovery.follow_up_command?`, then '${recovery.follow_up_command}'`:""}. (Write-time embedding is governed by search.mutation_refresh_policy; staleness means the embed was skipped, failed, or the backend was unreachable.)
2
+ `)}catch{}}function formatVectorIndexRecoveryWarnings(staleCount,recovery){return[`vector_index_stale:${staleCount}`,`vector_index_recovery:${recovery.args.join(" ")}`,...recovery.follow_up_args?[`vector_index_recovery_follow_up:${recovery.follow_up_args.join(" ")}`]:[]]}function requireSemanticDependencies(requestedMode,providerResolution,vectorResolution,hasExtensionVectorQuery){if(!providerResolution.active){throw new PmCliError(`Search mode '${requestedMode}' requires a configured embedding provider in settings.providers.openai or settings.providers.ollama`,EXIT_CODE.USAGE)}if(!vectorResolution.active&&!hasExtensionVectorQuery){throw new PmCliError(`Search mode '${requestedMode}' requires a configured vector store in settings.vector_store.qdrant/settings.vector_store.lancedb or an extension adapter selected by settings.vector_store.adapter`,EXIT_CODE.USAGE)}return{provider:providerResolution.active,vectorStore:vectorResolution.active??null}}const toOptionalNonEmptyString=toNonEmptyStringOrUndefined;function resolveExtensionSearchProviderByName(providerName){const registrations=getActiveExtensionRegistrations();const resolved=resolveRegisteredSearchProvider(registrations,providerName);if(!resolved){return null}const runtimeDefinition=resolved.runtime_definition??resolved.definition;const query=runtimeDefinition.query;const queryExpansion=runtimeDefinition.queryExpansion??runtimeDefinition.query_expansion;const rerank=runtimeDefinition.rerank;const registeredName=toOptionalNonEmptyString(runtimeDefinition.name)??toOptionalNonEmptyString(resolved.definition.name)??providerName;if(!registeredName){return null}const hooks={providerName:registeredName,...typeof query==="function"?{query}:{},...typeof queryExpansion==="function"?{queryExpansion}:{},...typeof rerank==="function"?{rerank}:{}};if(!hooks.query&&!hooks.queryExpansion&&!hooks.rerank){return null}return hooks}function resolveExtensionSearchProvider(settings){const providerName=toOptionalNonEmptyString(settings.search?.provider);const resolved=resolveExtensionSearchProviderByName(providerName);if(!resolved?.query){return null}return{providerName:resolved.providerName,query:resolved.query}}function resolveExtensionVectorAdapter(settings){const registrations=getActiveExtensionRegistrations();const adapterName=toOptionalNonEmptyString(settings.vector_store?.adapter);const resolved=resolveRegisteredVectorStoreAdapter(registrations,adapterName);if(!resolved){return null}const runtimeDefinition=resolved.runtime_definition??resolved.definition;const query=runtimeDefinition.query;if(typeof query!=="function"){return null}const upsert=runtimeDefinition.upsert;const runtimeAdapterName=toOptionalNonEmptyString(runtimeDefinition.name)??toOptionalNonEmptyString(resolved.definition.name)??adapterName??"extension";return{adapterName:runtimeAdapterName,query,...typeof upsert==="function"?{upsert}:{}}}function normalizeExtensionProviderHits(providerName,raw,filteredById){const rawHits=Array.isArray(raw)?raw:raw?.hits;if(!Array.isArray(rawHits)){throw new PmCliError(`Extension search provider "${providerName}" must return an array of hits or { hits: [...] }`,EXIT_CODE.GENERIC_FAILURE)}const seen=new Set;const hits=[];for(const rawHit of rawHits){if(typeof rawHit!=="object"||rawHit===null){continue}const id=toOptionalNonEmptyString(rawHit.id);const score=rawHit.score;if(!id||typeof score!=="number"||!Number.isFinite(score)||seen.has(id)){continue}const document=filteredById.get(id);if(!document){continue}const matchedFieldsRaw=rawHit.matched_fields;const matchedFields=Array.isArray(matchedFieldsRaw)&&matchedFieldsRaw.every(entry=>typeof entry==="string")?[...new Set(matchedFieldsRaw.map(entry=>entry.trim()).filter(entry=>entry.length>0))].sort((a,b)=>a.localeCompare(b)):[`provider:${providerName}`];seen.add(id);hits.push({item:document.metadata,score,matched_fields:matchedFields})}return hits}function buildSemanticHits(vectorHits,filteredById){const semanticHits=[];const semanticScores=new Map;for(const vectorHit of vectorHits){if(semanticScores.has(vectorHit.id)){continue}const document=filteredById.get(vectorHit.id);if(!document){continue}semanticScores.set(vectorHit.id,vectorHit.score);semanticHits.push({item:document.metadata,score:vectorHit.score,matched_fields:["semantic"]})}return{semanticHits,semanticScores}}function forceExactIdHitsToTop(rankedHits,keywordHits){const exactIdHits=keywordHits.filter(hit=>hit.exact_id_match===true);if(exactIdHits.length===0){return rankedHits}const exactIdIds=new Set(exactIdHits.map(hit=>hit.item.id));const remaining=rankedHits.filter(hit=>!exactIdIds.has(hit.item.id));const maxRemainingScore=remaining.reduce((max,hit)=>Math.max(max,hit.score),0);const orderedExactHits=exactIdHits.sort((left,right)=>right.score-left.score);const bandBase=maxRemainingScore+orderedExactHits.length+1;const promoted=orderedExactHits.map((hit,index)=>({...hit,score:bandBase-index,matched_fields:["id"]}));return[...promoted,...remaining]}function combineHybridHits(filteredById,semanticScores,keywordHits,hybridSemanticWeight,semanticFieldLabel="semantic"){const keywordScores=new Map(keywordHits.map(entry=>[entry.item.id,entry.score]));const keywordMatches=new Map(keywordHits.map(entry=>[entry.item.id,entry.matched_fields]));const normalizedSemantic=normalizeScoreMap(semanticScores);const normalizedKeyword=normalizeScoreMap(keywordScores);const candidateIds=new Set([...semanticScores.keys(),...keywordScores.keys()]);const keywordWeight=1-hybridSemanticWeight;return[...candidateIds].map(id=>{const document=filteredById.get(id);const semanticScore=normalizedSemantic.get(id)??0;const keywordScore=normalizedKeyword.get(id)??0;const combinedScore=semanticScore*hybridSemanticWeight+keywordScore*keywordWeight;if(combinedScore<=0){return null}const matchedFields=new Set;if(semanticScores.has(id)){matchedFields.add(semanticFieldLabel)}for(const field of keywordMatches.get(id)??[]){matchedFields.add(field)}return{item:document.metadata,score:combinedScore,matched_fields:[...matchedFields].sort((a,b)=>a.localeCompare(b))}}).filter(entry=>entry!==null)}function resolveBuiltInBm25Mode(params){const normalized=params.configuredProvider?.trim().toLowerCase();if(normalized==="bm25"){return"explicit"}if(normalized==="auto"&&!params.hasEmbeddingProvider&&!params.hasExtensionSearchProvider){return"auto-fallback"}return null}function computeBuiltInBm25Hits(params){const bm25Documents=params.filteredDocuments.map(document=>({id:document.metadata.id,text:flattenSearchCorpusText(buildSearchCorpus(document,{fields:params.corpusFields}))}));const index=buildBm25Index(bm25Documents);const bm25Scores=scoreBm25Query(index,tokenizeBm25(params.query),params.bm25Params);const filteredById=new Map(params.filteredDocuments.map(document=>[document.metadata.id,document]));if(params.requestedMode==="semantic"){const semanticHits=[...bm25Scores].map(([id,score])=>({item:filteredById.get(id).metadata,score,matched_fields:["bm25"]}));return forceExactIdHitsToTop(semanticHits,params.keywordHits)}const hybridHits=combineHybridHits(filteredById,bm25Scores,params.keywordHits,params.hybridSemanticWeight,"bm25");return forceExactIdHitsToTop(hybridHits,params.keywordHits)}function mergeVectorHitsById(vectorHitGroups){const bestById=new Map;for(const group of vectorHitGroups){for(const hit of group){const existing=bestById.get(hit.id);if(!existing||hit.score>existing.score){bestById.set(hit.id,hit)}}}const merged=[...bestById.values()];merged.sort((left,right)=>{if(left.score!==right.score){return right.score-left.score}return left.id.localeCompare(right.id)});return merged}function buildRerankCorpus(document){const metadata=document.metadata;const tags=Array.isArray(metadata?.tags)?metadata.tags.join(" "):"";return[metadata?.title,metadata?.description,metadata?.type,metadata?.status,tags,document.body].map(entry=>typeof entry==="string"?entry.trim():"").filter(entry=>entry.length>0).join("\n")}async function resolveExpandedSemanticQueries(context,queryTrimmed){const baseExpandedQueries=context.queryExpansion.enabled?buildDeterministicQueryExpansions(queryTrimmed,context.queryExpansion.max_queries):[queryTrimmed];const expandedQueries=baseExpandedQueries.length>0?baseExpandedQueries:[queryTrimmed];if(!context.queryExpansion.enabled){return expandedQueries}if(context.queryExpansionExtension?.expand){try{const rawExpansion=await Promise.resolve(context.queryExpansionExtension.expand({query:queryTrimmed,mode:context.requestedMode,settings:context.settings}));return mergeQueryExpansions(expandedQueries,normalizeQueryExpansionOutput(rawExpansion),context.queryExpansion.max_queries)}catch{context.warnings.push(`search_query_expansion_provider_failed:${context.queryExpansionExtension.providerName}:using_builtin`);return expandedQueries}}if(context.queryExpansion.provider&&context.queryExpansion.provider!=="openai"&&context.queryExpansion.provider!=="ollama"){context.warnings.push(`search_query_expansion_provider_unavailable:${context.queryExpansion.provider}:using_builtin`)}return expandedQueries}async function executeSemanticVectorQuery(context,semanticVector,semanticLimit,vectorQueryOptions){if(context.extensionVectorAdapter?.query){try{return await Promise.resolve(context.extensionVectorAdapter.query({vector:semanticVector,limit:semanticLimit,settings:context.settings}))}catch(error){if(!context.vectorStore){throw new PmCliError(`Extension vector adapter query failed and no built-in fallback store is configured (${error instanceof Error?error.message:String(error)})`,EXIT_CODE.GENERIC_FAILURE)}context.warnings.push(`search_vector_adapter_failed:${context.extensionVectorAdapter.adapterName}:using_builtin`);return await executeVectorQuery(context.vectorStore,semanticVector,semanticLimit,vectorQueryOptions)}}if(context.vectorStore){return await executeVectorQuery(context.vectorStore,semanticVector,semanticLimit,vectorQueryOptions)}throw new PmCliError("Semantic search requires either a configured vector store or an extension vector adapter query handler",EXIT_CODE.USAGE)}function buildRerankCandidateContexts(hybridHits,filteredById,topK){const sortedForCandidates=[...hybridHits].sort((left,right)=>{if(left.score!==right.score){return right.score-left.score}return left.item.id.localeCompare(right.item.id)});return sortedForCandidates.slice(0,topK).map(hit=>{const document=filteredById.get(hit.item.id);return document?{hit,text:buildRerankCorpus(document)}:null}).filter(entry=>entry!==null)}async function resolveExtensionRerankScores(context,queryTrimmed,candidateContexts){if(!context.rerankExtension?.rerank){return null}try{const rawRerank=await Promise.resolve(context.rerankExtension.rerank({query:queryTrimmed,mode:"hybrid",model:context.rerank.model,top_k:context.rerank.top_k,settings:context.settings,candidates:candidateContexts.map(entry=>({id:entry.hit.item.id,text:entry.text,score:entry.hit.score}))}));const normalizedRerank=normalizeRerankOutput(rawRerank);if(normalizedRerank.length>0){return new Map(normalizedRerank.map(entry=>[entry.id,entry.score]))}context.warnings.push(`search_rerank_provider_invalid_response:${context.rerankExtension.providerName}:using_builtin`);return null}catch{context.warnings.push(`search_rerank_provider_failed:${context.rerankExtension.providerName}:using_builtin`);return null}}async function resolveRerankScores(context,queryTrimmed,candidateContexts){const extensionScores=await resolveExtensionRerankScores(context,queryTrimmed,candidateContexts);if(extensionScores){return extensionScores}const rerankCandidates=candidateContexts.map(entry=>({id:entry.hit.item.id,text:entry.text}));try{return await rerankCandidatesWithEmbeddings(context.provider,context.rerank.model,queryTrimmed,rerankCandidates,context.embeddingTimeoutMs)}catch{context.warnings.push("search_rerank_failed:using_hybrid_scores");return null}}function applyRerankScores(hybridHits,rerankScores){const rerankedIds=new Set(rerankScores.keys());const rerankedHits=hybridHits.map(hit=>{const rerankScore=rerankScores.get(hit.item.id);if(rerankScore===void 0){return hit}const matchedFields=new Set(hit.matched_fields);matchedFields.add("rerank");return{...hit,score:rerankScore,matched_fields:[...matchedFields].sort((left,right)=>left.localeCompare(right))}});rerankedHits.sort((left,right)=>{const leftWasReranked=rerankedIds.has(left.item.id);const rightWasReranked=rerankedIds.has(right.item.id);if(leftWasReranked!==rightWasReranked){return leftWasReranked?-1:1}if(left.score!==right.score){return right.score-left.score}return left.item.id.localeCompare(right.item.id)});return rerankedHits}async function applyHybridRerank(context,queryTrimmed,hybridHits,filteredById){if(!context.rerank.enabled||hybridHits.length<=1){return hybridHits}const candidateContexts=buildRerankCandidateContexts(hybridHits,filteredById,context.rerank.top_k);const rerankScores=await resolveRerankScores(context,queryTrimmed,candidateContexts);return rerankScores&&rerankScores.size>0?applyRerankScores(hybridHits,rerankScores):hybridHits}async function computeSemanticOrHybridHits(context){const semanticLimit=Math.max(context.limit??context.maxResults,context.maxResults);const embeddingOptions=context.embeddingTimeoutMs!==void 0?{timeout_ms:context.embeddingTimeoutMs}:{};const vectorQueryOptions=context.vectorQueryTimeoutMs!==void 0?{timeout_ms:context.vectorQueryTimeoutMs}:{};const queryTrimmed=context.query.trim();const expandedQueries=await resolveExpandedSemanticQueries(context,queryTrimmed);const queryVectors=await executeEmbeddingRequest(context.provider,expandedQueries,embeddingOptions);const queryVectorGroups=await Promise.all(queryVectors.map(async semanticVector=>await executeSemanticVectorQuery(context,semanticVector,semanticLimit,vectorQueryOptions)));const vectorHits=mergeVectorHitsById(queryVectorGroups);const filteredById=new Map(context.filteredDocuments.map(document=>[document.metadata.id,document]));const{semanticHits,semanticScores}=buildSemanticHits(vectorHits,filteredById);const vectorMatchCount=semanticScores.size;if(context.requestedMode==="semantic"){return{hits:forceExactIdHitsToTop(semanticHits,context.keywordHits),vectorMatchCount}}const hybridHits=await applyHybridRerank(context,queryTrimmed,combineHybridHits(filteredById,semanticScores,context.keywordHits,context.hybridSemanticWeight),filteredById);return{hits:forceExactIdHitsToTop(hybridHits,context.keywordHits),vectorMatchCount}}function resolveQueryExpansionExtension(queryExpansion){const provider=resolveExtensionSearchProviderByName(queryExpansion.provider??void 0);return provider?.queryExpansion?{providerName:provider.providerName,expand:provider.queryExpansion}:null}function resolveRerankExtension(settings){const provider=resolveExtensionSearchProviderByName(toOptionalNonEmptyString(settings.search?.provider));return provider?.rerank?{providerName:provider.providerName,rerank:provider.rerank}:null}export{buildExplicitSemanticFallbackWarning,buildRerankCorpus,buildSemanticHits,classifyImplicitSemanticFallbackReason,collectErrorCauseCodes,combineHybridHits,computeBuiltInBm25Hits,computeSemanticOrHybridHits,formatVectorIndexRecoveryWarnings,maybeEmitVectorIndexStaleWarning,mergeVectorHitsById,normalizeExtensionProviderHits,requireSemanticDependencies,resolveBuiltInBm25Mode,resolveExtensionSearchProvider,resolveExtensionSearchProviderByName,resolveExtensionVectorAdapter,resolveQueryExpansionExtension,resolveRerankExtension,resolveVectorIndexRecovery,toOptionalNonEmptyString};
3
+ //# sourceMappingURL=semantic.js.map