Skip to content

feat(kap-server): add workspace fs:suggest file completion endpoint - #3019

Merged
sailist merged 1 commit into
MoonshotAI:mainfrom
sailist:feat-035-08-17-fs-suggest-api
Aug 18, 2026
Merged

feat(kap-server): add workspace fs:suggest file completion endpoint#3019
sailist merged 1 commit into
MoonshotAI:mainfrom
sailist:feat-035-08-17-fs-suggest-api

Conversation

@sailist

@sailist sailist commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — the problem is explained below.

Problem

Editor integrations need a file/folder completion backend for @ mentions: as the user types a partial name or path, the server should return a small set of highly relevant path candidates. The existing fs:search only scores basenames, has no path-pattern mode, and offers no hidden-file control, so it cannot drive @ completion.

What changed

  • New endpoint POST /api/v1/workspace/fs:suggest (session-less, same envelope/conventions as fs:search): empty query lists the workspace root; a query containing / matches path segments in order (subsequence within a segment, segment skipping allowed); a plain query fuzzy-matches basenames. Ranking prefers exact/prefix hits, then shorter names and shallower paths; limit, truncated, follow_gitignore, show_hidden, and include/exclude globs are supported, and missing-path matches return an empty list rather than an error.
  • Engine (agent-core-v2): IWorkspaceFsService.suggest enumerates candidates with rg --files (streamed line-by-line into a bounded top-N heap, so memory stays flat on large trees), which gives git-consistent nested .gitignore handling for local and remote runtimes; when rg is unavailable it falls back to the existing Node walk and emits a telemetry event. Accepted trade-offs: the rg path yields no symlink candidates, and the walk fallback does not honor nested gitignore.
  • Protocol: request/response schemas mirrored into packages/protocol (src/fs.ts, src/rest/fs.ts).
  • kimi-inspect: new "Filesystem Suggest" icon-rail view (workspace picker, query form, result table with score/match positions, raw JSON) over a typed REST client.
  • Tests: engine coverage with in-memory fs and a fake rg runner, real-HTTP route tests, protocol schema tests, and kimi-inspect client tests. Measured on a ~6000-file workspace: root listing p95 ~2-3ms, lookups p95 ~21-48ms (budget: 20ms / 100ms).

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset. (No changeset: kap-server REST addition currently consumed only by dev tooling / external editor clients, not perceivable from the CLI.)
  • Ran gen-docs skill, or this PR needs no doc update. (No user-facing surface changed.)

@changeset-bot

changeset-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: a3c00e2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Aug 18, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@a3c00e2
npx https://pkg.pr.new/@moonshot-ai/kimi-code@a3c00e2

commit: a3c00e2

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e9b2de5a71

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +501 to +505
const listed = await this.list({
path: '.',
depth: 1,
limit: req.limit,
show_hidden: req.show_hidden,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply filters before limiting the root listing

When an empty query uses include_globs, or show_hidden: true exposes a VCS directory, list applies req.limit before the subsequent filters. For example, with limit: 1, a directory sorted before a matching root-level TypeScript file can consume the only slot and then be removed by include_globs, yielding no usable candidate even though one exists and there is no pagination mechanism to retrieve it. Filter candidates before enforcing the suggestion limit.

Useful? React with 👍 / 👎.

Comment on lines +558 to +560
if (controller.signal.aborted) throw err;
this.telemetry.track2('fs_suggest_node_fallback', { reason: 'rg_error' });
return await this.suggestWithNode(query, cap);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enforce the timeout in the Node fallback

When ripgrep is unavailable or fails, this fallback receives neither the abort signal nor any other work budget. On a large or slow remote workspace, the recursive walk therefore continues past SUGGEST_TIMEOUT_MS, and the timer is only cleared after it eventually finishes, allowing one completion request to remain pending indefinitely. Pass the signal into the fallback traversal and stop with a truncated result when it fires.

Useful? React with 👍 / 👎.

Comment on lines +614 to +618
let slash = line.lastIndexOf('/');
while (slash > 0) {
const dir = line.slice(0, slash);
if (!seenDirs.has(dir)) {
seenDirs.add(dir);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Surface directories that contain no listed files

When ripgrep is available, directories are synthesized exclusively from paths emitted by rg --files. An empty directory—or one containing only ignored files—emits no file path, so a query matching that directory can never return it even though the endpoint advertises directory completions and the Node fallback does return such directories. Enumerate directories separately or supplement the ripgrep output so results do not depend on directory contents.

Useful? React with 👍 / 👎.

Comment on lines +648 to +649
try {
await Promise.all([drainStdout(), readStream(proc.stderr), proc.wait().catch(() => -1)]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check ripgrep's exit status before accepting results

When rg --files starts successfully but exits with an error status, such as code 2 for an I/O or permission error, proc.wait() resolves with that code and this expression discards it. The method then returns an empty or partial response as a successful, complete suggestion instead of taking the documented rg_error fallback; only spawn and stream exceptions currently reach that path. Preserve the exit code and treat error statuses as failures while retaining ripgrep's valid empty-result status.

Useful? React with 👍 / 👎.

Comment thread apps/kimi-inspect/src/fs/api.ts Outdated
Comment on lines +18 to +22
readonly token?: string | undefined;
readonly workspace: string;
readonly query: string;
readonly limit?: number | undefined;
readonly followGitignore?: boolean | undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove explicit undefined from optional properties

These optional properties redundantly add | undefined, and the same pattern continues through the remaining optional fields in this interface. Declare them as token?: string, limit?: number, and so on to comply with the repository's required optional-property convention.

AGENTS.md reference: AGENTS.md:L55-L57

Useful? React with 👍 / 👎.

@sailist
sailist force-pushed the feat-035-08-17-fs-suggest-api branch from e9b2de5 to 0858eab Compare August 18, 2026 03:10
@sailist
sailist force-pushed the feat-035-08-17-fs-suggest-api branch from 0858eab to a3c00e2 Compare August 18, 2026 03:29
@sailist
sailist merged commit 8267bb8 into MoonshotAI:main Aug 18, 2026
25 of 26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant