feat(feedback): add a /feedback command recorded through the command plane

Register a global `/feedback` command so a user can record a remark about the
session without spending a model turn. `/feedback <text>` acknowledges; empty
or whitespace-only input returns a usage error.

The plugin appends no session event of its own. `dsh-commands` already writes
a `command/run` / `command/done` pair for every dispatched command, carrying
the verbatim text and the settled outcome, and both records are log-only and
non-surface. The feedback is therefore durably in the session log and invisible
to the model without this package touching the log format.

Text is never parsed, so `/feedback /plan felt slow` records that literal
content. Nothing consumes the records; capture is deliberately inert.

New group `packages/feedback/` — no existing group owns feedback capture. Its
row raises the packages/README.md word ceiling by 10, which had no headroom;
one redundant sentence there was removed to offset most of the cost.
This commit is contained in:
Turtle
2026-07-29 13:34:45 +08:00
parent 75b32f7d76
commit 0ccd3ed463
28 changed files with 774 additions and 20 deletions
@@ -0,0 +1,40 @@
/**
* Human-facing `/feedback` command. It records a remark about the session and
* does nothing else: the command registry's own `command/run` and
* `command/done` events are the whole record, so this plugin only validates the
* input and acknowledges it. Those appends are eager but unflushed, so the
* acknowledgement reports the entry is logged, not that it reached disk.
* @module @deepseek-ai/dsh-command-feedback
*/
import type { Context } from 'cordis'
import type { CommandInvocation, CommandResult } from '@deepseek-ai/dsh-commands'
export const name = 'command-feedback'
export const inject = ['commands']
const USAGE = 'Usage: /feedback <text>'
/**
* Validate and acknowledge one feedback entry. `command/run` already carries
* the verbatim text, so no further append is needed; returning an error instead
* settles that record as `kind: 'error'` and leaves no accepted feedback.
* @param invocation - receiving agent, raw command input, and UI cancellation.
* @returns an acknowledgement, or a usage error when no feedback text was supplied.
*/
function executeFeedbackCommand(invocation: CommandInvocation): CommandResult {
if (invocation.rawInput.trim().length === 0) {
return { kind: 'error', text: `Feedback text is required. ${USAGE}` }
}
return { kind: 'success', text: 'Feedback recorded.' }
}
/** Register the global `/feedback` command for every composed command adapter. */
export function apply(ctx: Context): void {
ctx.commands.register({
name: 'feedback',
description: 'record feedback about this session',
input: { hint: '<text>' },
handler: executeFeedbackCommand,
})
}