dictum-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +280 -0
- package/bin/dictum.js +7 -0
- package/package.json +41 -0
- package/src/cli.ts +399 -0
- package/src/config.ts +250 -0
- package/src/core/pipeline.ts +120 -0
- package/src/core/types.ts +102 -0
- package/src/doctor.ts +276 -0
- package/src/mcp/prompts.ts +186 -0
- package/src/mcp/server.ts +363 -0
- package/src/modules.d.ts +7 -0
- package/src/polisher/anthropic.ts +90 -0
- package/src/polisher/claude_cli.ts +128 -0
- package/src/polisher/factory.ts +89 -0
- package/src/polisher/layered.ts +43 -0
- package/src/polisher/openai_compat.ts +76 -0
- package/src/polisher/rules.ts +332 -0
- package/src/polisher/templates/agent-prompt.md +18 -0
- package/src/polisher/templates/commit.md +13 -0
- package/src/polisher/templates/decompose.md +20 -0
- package/src/polisher/templates/note.md +11 -0
- package/src/polisher/templates/spec.md +29 -0
- package/src/polisher/templates.ts +151 -0
- package/src/recorder/file.ts +35 -0
- package/src/recorder/sox.ts +165 -0
- package/src/recorder/vad.ts +100 -0
- package/src/recorder/wav.ts +150 -0
- package/src/sink/clipboard.ts +153 -0
- package/src/sink/factory.ts +31 -0
- package/src/sink/stdout.ts +17 -0
- package/src/stt/factory.ts +89 -0
- package/src/stt/local_http.ts +75 -0
- package/src/stt/openai_compat.ts +83 -0
- package/src/ui/choose.ts +95 -0
- package/src/ui/keys.ts +110 -0
- package/src/ui/status.ts +91 -0
package/src/ui/status.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ui/status.ts — render a live status line from pipeline stage events.
|
|
3
|
+
*
|
|
4
|
+
* Stages map to glyphs: ● recording / ◌ transcribing / ✦ polishing, finishing
|
|
5
|
+
* with ✓ <done> <timing>. Output goes to a stream (stderr by default) so stdout
|
|
6
|
+
* stays clean for pipes. On a TTY the line is updated in place; otherwise only
|
|
7
|
+
* completed stages are printed (no spinner spam in logs/pipes).
|
|
8
|
+
*
|
|
9
|
+
* Driven by the StageEvent stream already emitted by core/pipeline.ts.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { PipelineStage, StageEvent } from "../core/types.ts"
|
|
13
|
+
|
|
14
|
+
const ACTIVE: Partial<Record<PipelineStage, { glyph: string; label: string }>> = {
|
|
15
|
+
recording: { glyph: "●", label: "recording" },
|
|
16
|
+
transcribing: { glyph: "◌", label: "transcribing" },
|
|
17
|
+
polishing: { glyph: "✦", label: "polishing" },
|
|
18
|
+
emitting: { glyph: "✦", label: "emitting" },
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const DONE_LABEL: Partial<Record<PipelineStage, string>> = {
|
|
22
|
+
recording: "recorded",
|
|
23
|
+
transcribing: "transcribed",
|
|
24
|
+
polishing: "polished",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const DEFAULT_EMIT_LABEL = "copied"
|
|
28
|
+
const CHECK = "✓"
|
|
29
|
+
const CLEAR_LINE = "\r\x1b[2K"
|
|
30
|
+
|
|
31
|
+
/** Format a millisecond duration as a short human string ("340ms" / "1.2s"). */
|
|
32
|
+
export function formatDuration(ms: number): string {
|
|
33
|
+
if (!Number.isFinite(ms) || ms < 0) return ""
|
|
34
|
+
if (ms < 1000) return `${Math.round(ms)}ms`
|
|
35
|
+
return `${(ms / 1000).toFixed(1)}s`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Render a single status line for an event, or null when there's nothing to
|
|
40
|
+
* show. `emitLabel` names the completed emit stage (e.g. "copied" for the
|
|
41
|
+
* clipboard, "written" for stdout).
|
|
42
|
+
*/
|
|
43
|
+
export function renderStageLine(
|
|
44
|
+
event: StageEvent,
|
|
45
|
+
emitLabel: string = DEFAULT_EMIT_LABEL,
|
|
46
|
+
): string | null {
|
|
47
|
+
if (event.stage === "done") return null
|
|
48
|
+
if (event.durationMs === undefined) {
|
|
49
|
+
const active = ACTIVE[event.stage]
|
|
50
|
+
return active ? `${active.glyph} ${active.label}…` : null
|
|
51
|
+
}
|
|
52
|
+
const label = event.stage === "emitting" ? emitLabel : DONE_LABEL[event.stage]
|
|
53
|
+
if (!label) return null
|
|
54
|
+
const timing = formatDuration(event.durationMs)
|
|
55
|
+
return timing ? `${CHECK} ${label} ${timing}` : `${CHECK} ${label}`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type StatusStream = { write(s: string): unknown; isTTY?: boolean }
|
|
59
|
+
|
|
60
|
+
/** Consumes pipeline StageEvents and renders a status line. */
|
|
61
|
+
export class StatusReporter {
|
|
62
|
+
private readonly stream: StatusStream
|
|
63
|
+
private readonly tty: boolean
|
|
64
|
+
private readonly emitLabel: string
|
|
65
|
+
|
|
66
|
+
constructor(
|
|
67
|
+
stream: StatusStream = process.stderr,
|
|
68
|
+
opts: { tty?: boolean; emitLabel?: string } = {},
|
|
69
|
+
) {
|
|
70
|
+
this.stream = stream
|
|
71
|
+
this.tty = opts.tty ?? Boolean(stream.isTTY)
|
|
72
|
+
this.emitLabel = opts.emitLabel ?? DEFAULT_EMIT_LABEL
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Bound so it can be passed directly as the pipeline's `onStage` callback. */
|
|
76
|
+
readonly onStage = (event: StageEvent): void => {
|
|
77
|
+
if (event.stage === "done") {
|
|
78
|
+
if (this.tty) this.stream.write("\n")
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
const line = renderStageLine(event, this.emitLabel)
|
|
82
|
+
if (!line) return
|
|
83
|
+
const active = event.durationMs === undefined
|
|
84
|
+
if (this.tty) {
|
|
85
|
+
this.stream.write(`${CLEAR_LINE}${line}${active ? "" : "\n"}`)
|
|
86
|
+
} else if (!active) {
|
|
87
|
+
// Non-TTY: only print completed stages, one per line.
|
|
88
|
+
this.stream.write(`${line}\n`)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|