Files
deepseek-harness/packages/context/file-reference/src/grammar.ts
T
creatixchu 6bb79911ba refactor(reference): serve discovery through typert Remote faces
Replace the legacy reference.* API Proxy domain with @Remote methods on the
owning services, following the typert gateway design master adopted on
2026-08-02 (message-feedback and plugin-inventory precedents):

- FileReferenceService and SessionReferenceResolver extend TypertRemoteService;
  fileReferences/list and sessionReferenceResolver/candidates are unary Remote
  methods cancelled through the reserved trailing signal, and the candidates
  face attaches each candidate's canonical mention under the configured limit
- move the wire types to type-only ./types subpaths (FileReferenceCandidate,
  SessionReferenceMentionCandidate) and export ./typert plus ./remote artifacts
- mount both contributions in the api-remotes client assembly; ui-reference
  consumes ctx.remote instead of connection.api.references and registers zh/en
  locale dictionaries for its sections and labels
- delete the reference.* routes, schemas, map rows, client stubs, and fixtures;
  the connection fixture serves the Remote endpoints instead
- release deliverPrompt admission listeners when the agent is disposed with the
  prepared prompt still pending, and cover the reference-* RpcError codes in
  the schema spec
- add the missing tsconfig paths for the /grammar and /types subpaths (clean-
  tree vitest could not resolve @deepseek-ai/dsh-file-reference/grammar)
- regenerate the cordis catalog, capability seams, and event matrix; update the
  owning bilingual READMEs, Agent Notes, and the reference-composer golden
2026-08-17 18:35:30 +08:00

56 lines
2.2 KiB
TypeScript

/**
* Browser-safe `@file` token grammar shared by terminal and web clients.
*
* @module @deepseek-ai/dsh-file-reference/grammar
*/
import type { FileReferenceCandidate } from './types.ts'
/** Active `@` token ending at the editor cursor. */
export interface ActiveAtToken {
/** Complete token replaced when the user accepts a completion. */
prefix: string
/** Path query after `@` or `@"`. */
query: string
/** Whether the user opened a quoted path. */
quoted: boolean
}
/**
* Extract an `@path` or `@"path with spaces` token at the cursor. An `@`
* inside another token, such as an email address, is not a completion trigger.
* @param line - current editor line.
* @param cursorCol - cursor column within that line.
* @returns the active token, or `undefined` outside an `@` token.
*/
export function activeAtToken(line: string, cursorCol: number): ActiveAtToken | undefined {
const beforeCursor = line.slice(0, cursorCol)
const quoted = /(?:^|\s)(@"([^"]*))$/u.exec(beforeCursor)
if (quoted?.[1] !== undefined && quoted[2] !== undefined) {
return { prefix: quoted[1], query: quoted[2], quoted: true }
}
const plain = /(?:^|\s)(@([^\s]*))$/u.exec(beforeCursor)
if (plain?.[1] === undefined || plain[2] === undefined) return undefined
return { prefix: plain[1], query: plain[2], quoted: false }
}
/**
* Format a selected path as prompt text. Whitespace uses the quoted
* `@"path"` grammar; a quoted directory keeps that quote open after its
* trailing slash so completion can descend another level.
* @param candidate - selected file or directory.
* @param preserveQuote - retain an explicitly opened quote even when unnecessary.
* @returns the insertion value, or `undefined` for a path the editor grammar cannot represent safely.
*/
export function formatFileMention(
candidate: FileReferenceCandidate,
preserveQuote: boolean,
): string | undefined {
const path = candidate.kind === 'directory' ? `${candidate.path}/` : candidate.path
if (/[\u0000-\u001f\u007f-\u009f"]/u.test(path)) return undefined
const quoted = preserveQuote || /\s/u.test(path)
if (!quoted) return `@${path}`
if (candidate.kind === 'directory') return `@"${path}`
return `@"${path}"`
}