dsh-advisor 0.1.4 → 0.2.1
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/README.i18n.yaml +2 -2
- package/README.md +60 -227
- package/README.zh.md +59 -106
- package/lib/advisor-runtime.d.ts +16 -14
- package/lib/advisor-runtime.js +19 -17
- package/lib/advisor-runtime.js.map +1 -1
- package/lib/commands.d.ts +61 -0
- package/lib/commands.js +60 -2
- package/lib/commands.js.map +1 -1
- package/lib/index.d.ts +8 -0
- package/lib/index.js +92 -8
- package/lib/index.js.map +1 -1
- package/lib/tui-settings.d.ts +144 -0
- package/lib/tui-settings.js +145 -0
- package/lib/tui-settings.js.map +1 -0
- package/lib/tui.d.ts +60 -0
- package/lib/tui.js +113 -0
- package/lib/tui.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-tui settings-section seam (plan dsh-advisor-tui-settings-n9, T1) — the
|
|
3
|
+
* `tuiSettingsSections` Advisor section.
|
|
4
|
+
*
|
|
5
|
+
* dsh-tui ≥ v0.8.0 ships a `/settings` screen; optional plugins declare what
|
|
6
|
+
* is editable there by registering a SECTION over their settings namespace on
|
|
7
|
+
* the optional `tuiSettingsSections` host service
|
|
8
|
+
* (`src/dsh-adapter/settings-sections.ts` in dsh-TUI — a small host-only
|
|
9
|
+
* registry; storage + validation stay with the dsh settings service). The
|
|
10
|
+
* screen then renders the section's fields, stages edits, and writes them on
|
|
11
|
+
* save through the revision-fenced `settings.mutate` into the section's
|
|
12
|
+
* namespace user layer — for the advisor that is the already-registered
|
|
13
|
+
* `advisor` namespace (`src/settings.ts` `installAdvisorSettings`), so the
|
|
14
|
+
* section is fully writable.
|
|
15
|
+
*
|
|
16
|
+
* This module is the advisor's settings-seam surface:
|
|
17
|
+
* `installTuiSettingsSection` conditionally injects `tuiSettingsSections` and
|
|
18
|
+
* registers {@link ADVISOR_TUI_SETTINGS_SECTION} when the service exists; a
|
|
19
|
+
* profile without the `dsh-tui-settings-sections` row (dsh-tui < v0.8.0, or
|
|
20
|
+
* any non-TUI host) gets a clean no-op. The provider shapes are minimal LOCAL
|
|
21
|
+
* structural copies of the dsh-TUI types — the advisor MUST NOT import
|
|
22
|
+
* `@deepseek-harness-tui/dsh-tui` (zero new peers, plan Global Constraint),
|
|
23
|
+
* so drift against the upstream shape is bounded to the structural cast at
|
|
24
|
+
* the inject boundary and pinned by `tests/tui-settings.test.ts`.
|
|
25
|
+
*
|
|
26
|
+
* Upstream revision pin: the structural shapes mirror
|
|
27
|
+
* `@deepseek-harness-tui/dsh-tui` v0.8.0 (`src/dsh-adapter/settings-sections.ts`,
|
|
28
|
+
* dsh-TUI commit `02ff08e`). Residual drift window: an upstream
|
|
29
|
+
* addition/rename of a REQUIRED field would NOT fail the structural cast (the
|
|
30
|
+
* cast goes through `unknown`) and could cause host-side misrender rather than
|
|
31
|
+
* a compile error — re-verify against the dsh-TUI repo when bumping.
|
|
32
|
+
*
|
|
33
|
+
* Field subset (grill-me locked): the section covers the five SAFE §5.1 keys
|
|
34
|
+
* (`enabled` / `provider` / `model` / `immuneTurns` / `maxDeltaMessages`).
|
|
35
|
+
* `systemPrompt` is intentionally NOT a field — the TUI `text` control is
|
|
36
|
+
* single-line, and editing a multi-line prompt there would truncate/replace
|
|
37
|
+
* it (data loss). It stays editable via the web card or
|
|
38
|
+
* `$DSH_HOME/settings.yaml`. The TUI seam has no cross-field validation
|
|
39
|
+
* (upstream behavior, recorded): a save may set `enabled: true` with empty
|
|
40
|
+
* `provider`/`model`, which the S4 explicit model gate (spec §5.2) resolves
|
|
41
|
+
* to disabled-with-reason at runtime; the settings-service schema
|
|
42
|
+
* re-validation on mutate is the value-level backstop (a non-schema value
|
|
43
|
+
* fails the whole save before persist).
|
|
44
|
+
*
|
|
45
|
+
* @module dsh-advisor/tui-settings
|
|
46
|
+
*/
|
|
47
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
48
|
+
/** Localized (zh/en) descriptions — structural mirror of dsh-TUI's
|
|
49
|
+
* `LocalizedDescriptions` (the `/settings` section + field labels/hints). */
|
|
50
|
+
export type TuiLocalizedDescriptions = Readonly<Partial<Record<'zh' | 'en', string>>>;
|
|
51
|
+
/** One `kind: 'select'` choice — structural mirror of dsh-TUI's
|
|
52
|
+
* `TuiSettingsFieldOption`. */
|
|
53
|
+
export interface TuiSettingsFieldOption {
|
|
54
|
+
/** Stored value. */
|
|
55
|
+
value: string;
|
|
56
|
+
/** Display label (English; also the fallback). */
|
|
57
|
+
label: string;
|
|
58
|
+
/** Provider-owned translations for the label. */
|
|
59
|
+
descriptions?: TuiLocalizedDescriptions;
|
|
60
|
+
}
|
|
61
|
+
/** The write one field's draft stages when the section is saved — structural
|
|
62
|
+
* mirror of dsh-TUI's `TuiSettingsFieldWrite`. */
|
|
63
|
+
export type TuiSettingsFieldWrite = {
|
|
64
|
+
kind: 'set';
|
|
65
|
+
value: unknown;
|
|
66
|
+
} | {
|
|
67
|
+
kind: 'clear';
|
|
68
|
+
};
|
|
69
|
+
/** One editable field inside a section — structural mirror of dsh-TUI's
|
|
70
|
+
* `TuiSettingsField`. `path` uses the settings-service `mutate` path
|
|
71
|
+
* vocabulary (object keys); the advisor's §5.1 flat keys map directly as
|
|
72
|
+
* single-element paths. */
|
|
73
|
+
export interface TuiSettingsField {
|
|
74
|
+
/** Key path from the section root, in the settings service's `mutate` path
|
|
75
|
+
* vocabulary (object keys; dict keys name their entry directly). */
|
|
76
|
+
path: readonly string[];
|
|
77
|
+
/** Short field label (English; also the fallback). */
|
|
78
|
+
label: string;
|
|
79
|
+
/** Provider-owned translations for the label. */
|
|
80
|
+
descriptions?: TuiLocalizedDescriptions;
|
|
81
|
+
/** Optional one-line help rendered under the field. */
|
|
82
|
+
hint?: string;
|
|
83
|
+
/** Provider-owned translations for the hint. */
|
|
84
|
+
hintDescriptions?: TuiLocalizedDescriptions;
|
|
85
|
+
kind: 'text' | 'number' | 'boolean' | 'select';
|
|
86
|
+
/** Choices for `kind: 'select'` (ignored otherwise). */
|
|
87
|
+
options?: readonly TuiSettingsFieldOption[];
|
|
88
|
+
/** Input placeholder for `kind: 'text' | 'number'`. */
|
|
89
|
+
placeholder?: string;
|
|
90
|
+
/** Credential control (mirrors the web cards' CardSecretSpec). */
|
|
91
|
+
secret?: {
|
|
92
|
+
ref: string;
|
|
93
|
+
};
|
|
94
|
+
/** Render a stored value as draft text (defaults to the kind's conversion). */
|
|
95
|
+
format?(value: unknown): string;
|
|
96
|
+
/** The write this draft text stages, or `undefined` when the text is not a
|
|
97
|
+
* value this field accepts (defaults to the kind's conversion — an empty
|
|
98
|
+
* text/number draft stages a clear, re-inheriting the composition layer). */
|
|
99
|
+
parse?(text: string): TuiSettingsFieldWrite | undefined;
|
|
100
|
+
}
|
|
101
|
+
/** One plugin's section inside the TUI `/settings` screen — structural mirror
|
|
102
|
+
* of dsh-TUI's `TuiSettingsSection`. */
|
|
103
|
+
export interface TuiSettingsSection {
|
|
104
|
+
/** Settings namespace this section edits; the screen marks the section
|
|
105
|
+
* unavailable when the composition serves no such namespace. */
|
|
106
|
+
ns: string;
|
|
107
|
+
/** Section title (English; also the fallback). */
|
|
108
|
+
title: string;
|
|
109
|
+
/** Provider-owned translations for the title. */
|
|
110
|
+
descriptions?: TuiLocalizedDescriptions;
|
|
111
|
+
/** Editable fields, in display order. */
|
|
112
|
+
fields: readonly TuiSettingsField[];
|
|
113
|
+
}
|
|
114
|
+
/** The `tuiSettingsSections` host service key — the ONE name the T1
|
|
115
|
+
* registration condition (`installTuiSettingsSection`'s inject) and the T2
|
|
116
|
+
* hint truthfulness probe (`getConfig`'s `ctx.get`) are both keyed on. Shared
|
|
117
|
+
* so the registration condition and the render-time hint can never drift
|
|
118
|
+
* apart (S-001, plan QC fix wave). */
|
|
119
|
+
export declare const TUI_SETTINGS_SECTIONS = "tuiSettingsSections";
|
|
120
|
+
/** Test-friendly alias for the section namespace (`'advisor'`). The section
|
|
121
|
+
* itself reuses the shared {@link ADVISOR_SETTINGS_NAMESPACE} brand — a
|
|
122
|
+
* mismatched ns would silently render the section "unavailable" in the host
|
|
123
|
+
* screen (the alias exists so tests can pin the value without importing the
|
|
124
|
+
* settings module's brand). */
|
|
125
|
+
export declare const ADVISOR_TUI_SETTINGS_NS = "advisor";
|
|
126
|
+
/** The declared "Advisor" section for the dsh-tui `/settings` screen: the
|
|
127
|
+
* five safe §5.1 keys (enabled/provider/model/immuneTurns/maxDeltaMessages)
|
|
128
|
+
* with zh/en labels + hints. Field paths are single-element §5.1 flat keys,
|
|
129
|
+
* so staged edits map 1:1 onto the namespace `mutate` paths. */
|
|
130
|
+
export declare const ADVISOR_TUI_SETTINGS_SECTION: TuiSettingsSection;
|
|
131
|
+
/**
|
|
132
|
+
* Install the advisor's TUI settings-section surface: register the "Advisor"
|
|
133
|
+
* section on `tuiSettingsSections` when the host service exists (conditional
|
|
134
|
+
* inject; absent service → clean no-op). Called from `apply()` AFTER the
|
|
135
|
+
* single-reviewer claim (next to `installTuiClient`), so the section registers
|
|
136
|
+
* at most once per process. The structural accessor keeps the inject key in
|
|
137
|
+
* the standard position: the cordis Context has no `tuiSettingsSections`
|
|
138
|
+
* augmentation in this repo, so the service is read through a local
|
|
139
|
+
* structural cast. The inject child's return value (the registry disposer)
|
|
140
|
+
* is the child's own effect disposer, so the section withdraws when this
|
|
141
|
+
* fiber (or the service) goes away; a duplicate-ns registration is contained
|
|
142
|
+
* (debug log + no-op disposer, never throws — multi-fiber dedupe).
|
|
143
|
+
*/
|
|
144
|
+
export declare function installTuiSettingsSection(ctx: Context): void;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-tui settings-section seam (plan dsh-advisor-tui-settings-n9, T1) — the
|
|
3
|
+
* `tuiSettingsSections` Advisor section.
|
|
4
|
+
*
|
|
5
|
+
* dsh-tui ≥ v0.8.0 ships a `/settings` screen; optional plugins declare what
|
|
6
|
+
* is editable there by registering a SECTION over their settings namespace on
|
|
7
|
+
* the optional `tuiSettingsSections` host service
|
|
8
|
+
* (`src/dsh-adapter/settings-sections.ts` in dsh-TUI — a small host-only
|
|
9
|
+
* registry; storage + validation stay with the dsh settings service). The
|
|
10
|
+
* screen then renders the section's fields, stages edits, and writes them on
|
|
11
|
+
* save through the revision-fenced `settings.mutate` into the section's
|
|
12
|
+
* namespace user layer — for the advisor that is the already-registered
|
|
13
|
+
* `advisor` namespace (`src/settings.ts` `installAdvisorSettings`), so the
|
|
14
|
+
* section is fully writable.
|
|
15
|
+
*
|
|
16
|
+
* This module is the advisor's settings-seam surface:
|
|
17
|
+
* `installTuiSettingsSection` conditionally injects `tuiSettingsSections` and
|
|
18
|
+
* registers {@link ADVISOR_TUI_SETTINGS_SECTION} when the service exists; a
|
|
19
|
+
* profile without the `dsh-tui-settings-sections` row (dsh-tui < v0.8.0, or
|
|
20
|
+
* any non-TUI host) gets a clean no-op. The provider shapes are minimal LOCAL
|
|
21
|
+
* structural copies of the dsh-TUI types — the advisor MUST NOT import
|
|
22
|
+
* `@deepseek-harness-tui/dsh-tui` (zero new peers, plan Global Constraint),
|
|
23
|
+
* so drift against the upstream shape is bounded to the structural cast at
|
|
24
|
+
* the inject boundary and pinned by `tests/tui-settings.test.ts`.
|
|
25
|
+
*
|
|
26
|
+
* Upstream revision pin: the structural shapes mirror
|
|
27
|
+
* `@deepseek-harness-tui/dsh-tui` v0.8.0 (`src/dsh-adapter/settings-sections.ts`,
|
|
28
|
+
* dsh-TUI commit `02ff08e`). Residual drift window: an upstream
|
|
29
|
+
* addition/rename of a REQUIRED field would NOT fail the structural cast (the
|
|
30
|
+
* cast goes through `unknown`) and could cause host-side misrender rather than
|
|
31
|
+
* a compile error — re-verify against the dsh-TUI repo when bumping.
|
|
32
|
+
*
|
|
33
|
+
* Field subset (grill-me locked): the section covers the five SAFE §5.1 keys
|
|
34
|
+
* (`enabled` / `provider` / `model` / `immuneTurns` / `maxDeltaMessages`).
|
|
35
|
+
* `systemPrompt` is intentionally NOT a field — the TUI `text` control is
|
|
36
|
+
* single-line, and editing a multi-line prompt there would truncate/replace
|
|
37
|
+
* it (data loss). It stays editable via the web card or
|
|
38
|
+
* `$DSH_HOME/settings.yaml`. The TUI seam has no cross-field validation
|
|
39
|
+
* (upstream behavior, recorded): a save may set `enabled: true` with empty
|
|
40
|
+
* `provider`/`model`, which the S4 explicit model gate (spec §5.2) resolves
|
|
41
|
+
* to disabled-with-reason at runtime; the settings-service schema
|
|
42
|
+
* re-validation on mutate is the value-level backstop (a non-schema value
|
|
43
|
+
* fails the whole save before persist).
|
|
44
|
+
*
|
|
45
|
+
* @module dsh-advisor/tui-settings
|
|
46
|
+
*/
|
|
47
|
+
import { ADVISOR_SETTINGS_NAMESPACE } from './settings.js';
|
|
48
|
+
/** The `tuiSettingsSections` host service key — the ONE name the T1
|
|
49
|
+
* registration condition (`installTuiSettingsSection`'s inject) and the T2
|
|
50
|
+
* hint truthfulness probe (`getConfig`'s `ctx.get`) are both keyed on. Shared
|
|
51
|
+
* so the registration condition and the render-time hint can never drift
|
|
52
|
+
* apart (S-001, plan QC fix wave). */
|
|
53
|
+
export const TUI_SETTINGS_SECTIONS = 'tuiSettingsSections';
|
|
54
|
+
/** Test-friendly alias for the section namespace (`'advisor'`). The section
|
|
55
|
+
* itself reuses the shared {@link ADVISOR_SETTINGS_NAMESPACE} brand — a
|
|
56
|
+
* mismatched ns would silently render the section "unavailable" in the host
|
|
57
|
+
* screen (the alias exists so tests can pin the value without importing the
|
|
58
|
+
* settings module's brand). */
|
|
59
|
+
export const ADVISOR_TUI_SETTINGS_NS = 'advisor';
|
|
60
|
+
/** The declared "Advisor" section for the dsh-tui `/settings` screen: the
|
|
61
|
+
* five safe §5.1 keys (enabled/provider/model/immuneTurns/maxDeltaMessages)
|
|
62
|
+
* with zh/en labels + hints. Field paths are single-element §5.1 flat keys,
|
|
63
|
+
* so staged edits map 1:1 onto the namespace `mutate` paths. */
|
|
64
|
+
export const ADVISOR_TUI_SETTINGS_SECTION = {
|
|
65
|
+
ns: ADVISOR_SETTINGS_NAMESPACE,
|
|
66
|
+
title: 'Advisor',
|
|
67
|
+
descriptions: {
|
|
68
|
+
zh: '顾问评审设置',
|
|
69
|
+
en: 'Advisor settings',
|
|
70
|
+
},
|
|
71
|
+
fields: [
|
|
72
|
+
{
|
|
73
|
+
path: ['enabled'],
|
|
74
|
+
kind: 'boolean',
|
|
75
|
+
label: 'Enabled',
|
|
76
|
+
descriptions: { zh: '启用', en: 'Enabled' },
|
|
77
|
+
hint: 'Master switch for the advisor.',
|
|
78
|
+
hintDescriptions: { zh: '顾问总开关。', en: 'Master switch for the advisor.' },
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
path: ['provider'],
|
|
82
|
+
kind: 'text',
|
|
83
|
+
label: 'Provider',
|
|
84
|
+
descriptions: { zh: 'Provider', en: 'Provider' },
|
|
85
|
+
hint: 'Provider route; required (non-empty) when enabled.',
|
|
86
|
+
hintDescriptions: { zh: 'Provider 路由;启用时必须非空。', en: 'Provider route; required (non-empty) when enabled.' },
|
|
87
|
+
placeholder: 'e.g. deepseek-official',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
path: ['model'],
|
|
91
|
+
kind: 'text',
|
|
92
|
+
label: 'Model',
|
|
93
|
+
descriptions: { zh: 'Model', en: 'Model' },
|
|
94
|
+
hint: 'Model id; required (non-empty) when enabled.',
|
|
95
|
+
hintDescriptions: { zh: '模型 ID;启用时必须非空。', en: 'Model id; required (non-empty) when enabled.' },
|
|
96
|
+
placeholder: 'e.g. deepseek-v4-flash',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
path: ['immuneTurns'],
|
|
100
|
+
kind: 'number',
|
|
101
|
+
label: 'Immune turns',
|
|
102
|
+
descriptions: { zh: '免疫轮数', en: 'Immune turns' },
|
|
103
|
+
hint: 'Cooldown after a delivered interrupt (integer ≥ 0).',
|
|
104
|
+
hintDescriptions: { zh: '投递一次建议后的冷却轮数(整数 ≥ 0)。', en: 'Cooldown after a delivered interrupt (integer ≥ 0).' },
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
path: ['maxDeltaMessages'],
|
|
108
|
+
kind: 'number',
|
|
109
|
+
label: 'Max delta messages',
|
|
110
|
+
descriptions: { zh: '最大增量消息数', en: 'Max delta messages' },
|
|
111
|
+
hint: 'Delta window (integer ≥ 0; 0 = unbounded).',
|
|
112
|
+
hintDescriptions: { zh: '增量窗口(整数 ≥ 0;0 = 不限)。', en: 'Delta window (integer ≥ 0; 0 = unbounded).' },
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Install the advisor's TUI settings-section surface: register the "Advisor"
|
|
118
|
+
* section on `tuiSettingsSections` when the host service exists (conditional
|
|
119
|
+
* inject; absent service → clean no-op). Called from `apply()` AFTER the
|
|
120
|
+
* single-reviewer claim (next to `installTuiClient`), so the section registers
|
|
121
|
+
* at most once per process. The structural accessor keeps the inject key in
|
|
122
|
+
* the standard position: the cordis Context has no `tuiSettingsSections`
|
|
123
|
+
* augmentation in this repo, so the service is read through a local
|
|
124
|
+
* structural cast. The inject child's return value (the registry disposer)
|
|
125
|
+
* is the child's own effect disposer, so the section withdraws when this
|
|
126
|
+
* fiber (or the service) goes away; a duplicate-ns registration is contained
|
|
127
|
+
* (debug log + no-op disposer, never throws — multi-fiber dedupe).
|
|
128
|
+
*/
|
|
129
|
+
export function installTuiSettingsSection(ctx) {
|
|
130
|
+
ctx.inject([TUI_SETTINGS_SECTIONS], (tctx) => {
|
|
131
|
+
const sections = tctx.tuiSettingsSections;
|
|
132
|
+
if (sections === undefined)
|
|
133
|
+
return;
|
|
134
|
+
try {
|
|
135
|
+
return sections.register(ADVISOR_TUI_SETTINGS_SECTION);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
if (!(error instanceof Error) || !error.message.includes('already registered'))
|
|
139
|
+
throw error;
|
|
140
|
+
tctx.logger('advisor').debug('advisor tui settings section already registered — no section on this fiber (multi-fiber dedupe)');
|
|
141
|
+
return () => { };
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=tui-settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tui-settings.js","sourceRoot":"","sources":["../src/tui-settings.ts"],"names":[],"mappings":"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"}
|
package/lib/tui.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-tui client surface (plan dsh-advisor-tui-client-n8, T1) — the
|
|
3
|
+
* `tuiCommandTrees` /advisor provider.
|
|
4
|
+
*
|
|
5
|
+
* In a `dsh --profile dsh-tui` terminal session the plugin's `/advisor`
|
|
6
|
+
* command (registered through the conditional `ctx.inject(['commands'], ...)`
|
|
7
|
+
* child) already lands in the TUI `/` menu via the command-registry merge.
|
|
8
|
+
* What the TUI cannot infer is the LOCALIZED row description and the typed
|
|
9
|
+
* subcommand COMPLETION — those come from the optional `tuiCommandTrees`
|
|
10
|
+
* host service (`src/dsh-adapter/command-trees.ts` in dsh-TUI): a provider
|
|
11
|
+
* declares `root`, zh/en `descriptions`, and a `children(canonicalPath)`
|
|
12
|
+
* completion tree (root at index 0; the TUI asks at depth 2 when completing
|
|
13
|
+
* `/advisor <sub> ⋯`).
|
|
14
|
+
*
|
|
15
|
+
* This module is the advisor's TUI seam: `installTuiClient` conditionally
|
|
16
|
+
* injects `tuiCommandTrees` and registers the `/advisor` tree when the
|
|
17
|
+
* service exists; a profile without the `dsh-tui-command-trees` row (or any
|
|
18
|
+
* non-TUI host) gets a clean no-op. The provider shapes are minimal LOCAL
|
|
19
|
+
* structural copies of the dsh-TUI types — the advisor MUST NOT import
|
|
20
|
+
* `@deepseek-harness-tui/dsh-tui` (zero new peers, plan Global Constraint),
|
|
21
|
+
* so drift against the upstream shape is bounded to the structural cast at
|
|
22
|
+
* the inject boundary and pinned by `tests/tui-client.test.ts`.
|
|
23
|
+
*
|
|
24
|
+
* @module dsh-advisor/tui
|
|
25
|
+
*/
|
|
26
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
27
|
+
/** Localized (zh/en) descriptions — structural mirror of dsh-TUI's
|
|
28
|
+
* `LocalizedDescriptions` (the TUI `/` row + completion descriptions). */
|
|
29
|
+
export type TuiLocalizedDescriptions = Readonly<Partial<Record<'zh' | 'en', string>>>;
|
|
30
|
+
/** One completion node — structural mirror of dsh-TUI's
|
|
31
|
+
* `CommandCompletionNode` (`src/commands.ts` in dsh-TUI). */
|
|
32
|
+
export interface TuiCommandCompletionNode {
|
|
33
|
+
name: string;
|
|
34
|
+
aliases?: readonly string[];
|
|
35
|
+
description: string;
|
|
36
|
+
descriptions?: TuiLocalizedDescriptions;
|
|
37
|
+
tag?: string;
|
|
38
|
+
descriptionKey?: string;
|
|
39
|
+
}
|
|
40
|
+
/** A `/`-menu command tree provider — structural mirror of dsh-TUI's
|
|
41
|
+
* `TuiCommandTreeProvider`. `children` receives the canonical path with the
|
|
42
|
+
* root at index 0. */
|
|
43
|
+
export interface TuiCommandTreeProvider {
|
|
44
|
+
root: string;
|
|
45
|
+
descriptions?: TuiLocalizedDescriptions;
|
|
46
|
+
children(canonicalPath: readonly string[]): readonly TuiCommandCompletionNode[];
|
|
47
|
+
}
|
|
48
|
+
/** The `/advisor` tree root (matches the command registry name). */
|
|
49
|
+
export declare const ADVISOR_TUI_ROOT = "advisor";
|
|
50
|
+
/**
|
|
51
|
+
* Install the advisor's TUI client surface: register the `/advisor`
|
|
52
|
+
* `tuiCommandTrees` provider when the host service exists (conditional
|
|
53
|
+
* inject; absent service → clean no-op). Called from `apply()` AFTER the
|
|
54
|
+
* single-reviewer claim, so the tree registers at most once per process
|
|
55
|
+
* (a duplicate-root registration would throw in the host registry). The
|
|
56
|
+
* structural accessor keeps the inject key in the standard position: the
|
|
57
|
+
* cordis Context has no `tuiCommandTrees` augmentation in this repo, so the
|
|
58
|
+
* service is read through a local structural cast.
|
|
59
|
+
*/
|
|
60
|
+
export declare function installTuiClient(ctx: Context): void;
|
package/lib/tui.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-tui client surface (plan dsh-advisor-tui-client-n8, T1) — the
|
|
3
|
+
* `tuiCommandTrees` /advisor provider.
|
|
4
|
+
*
|
|
5
|
+
* In a `dsh --profile dsh-tui` terminal session the plugin's `/advisor`
|
|
6
|
+
* command (registered through the conditional `ctx.inject(['commands'], ...)`
|
|
7
|
+
* child) already lands in the TUI `/` menu via the command-registry merge.
|
|
8
|
+
* What the TUI cannot infer is the LOCALIZED row description and the typed
|
|
9
|
+
* subcommand COMPLETION — those come from the optional `tuiCommandTrees`
|
|
10
|
+
* host service (`src/dsh-adapter/command-trees.ts` in dsh-TUI): a provider
|
|
11
|
+
* declares `root`, zh/en `descriptions`, and a `children(canonicalPath)`
|
|
12
|
+
* completion tree (root at index 0; the TUI asks at depth 2 when completing
|
|
13
|
+
* `/advisor <sub> ⋯`).
|
|
14
|
+
*
|
|
15
|
+
* This module is the advisor's TUI seam: `installTuiClient` conditionally
|
|
16
|
+
* injects `tuiCommandTrees` and registers the `/advisor` tree when the
|
|
17
|
+
* service exists; a profile without the `dsh-tui-command-trees` row (or any
|
|
18
|
+
* non-TUI host) gets a clean no-op. The provider shapes are minimal LOCAL
|
|
19
|
+
* structural copies of the dsh-TUI types — the advisor MUST NOT import
|
|
20
|
+
* `@deepseek-harness-tui/dsh-tui` (zero new peers, plan Global Constraint),
|
|
21
|
+
* so drift against the upstream shape is bounded to the structural cast at
|
|
22
|
+
* the inject boundary and pinned by `tests/tui-client.test.ts`.
|
|
23
|
+
*
|
|
24
|
+
* @module dsh-advisor/tui
|
|
25
|
+
*/
|
|
26
|
+
/** The `/advisor` tree root (matches the command registry name). */
|
|
27
|
+
export const ADVISOR_TUI_ROOT = 'advisor';
|
|
28
|
+
/** The four typed `/advisor` subcommands surfaced as completion children.
|
|
29
|
+
* Bare `/advisor` (toggle) is the empty-argument default, not a completion
|
|
30
|
+
* child (compass S1); `USAGE` is the unknown-subcommand fallback, not a
|
|
31
|
+
* named command. */
|
|
32
|
+
const ADVISOR_SUBCOMMANDS = ['on', 'off', 'status', 'config'];
|
|
33
|
+
/** zh/en copy for the `/` menu row (shown via the host's `descriptions(root)`). */
|
|
34
|
+
const ADVISOR_TUI_DESCRIPTIONS = {
|
|
35
|
+
zh: '按会话运行的评审顾问:开启 / 关闭 / 状态 / 配置',
|
|
36
|
+
en: 'Per-session advisor: enable, disable, status, or config',
|
|
37
|
+
};
|
|
38
|
+
/** zh/en copy per completion node. `description` is the plain fallback the
|
|
39
|
+
* node carries; `descriptions` is the localized map the TUI prefers. */
|
|
40
|
+
const SUBCOMMAND_DESCRIPTIONS = {
|
|
41
|
+
on: {
|
|
42
|
+
description: 'Enable the advisor for this session',
|
|
43
|
+
descriptions: {
|
|
44
|
+
zh: '为本会话启用顾问',
|
|
45
|
+
en: 'Enable the advisor for this session',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
off: {
|
|
49
|
+
description: 'Disable the advisor for this session',
|
|
50
|
+
descriptions: {
|
|
51
|
+
zh: '为本会话禁用顾问',
|
|
52
|
+
en: 'Disable the advisor for this session',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
status: {
|
|
56
|
+
description: 'Show per-session advisor status (state, model, runtime, pending, last activity)',
|
|
57
|
+
descriptions: {
|
|
58
|
+
zh: '查看本会话顾问状态(开关、模型、运行态、待处理、最近活动)',
|
|
59
|
+
en: 'Show per-session advisor status (state, model, runtime, pending, last activity)',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
config: {
|
|
63
|
+
description: 'Show the composed advisor config (settings readback)',
|
|
64
|
+
descriptions: {
|
|
65
|
+
zh: '查看组合后的顾问配置(设置回读)',
|
|
66
|
+
en: 'Show the composed advisor config (settings readback)',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
/** The `/advisor` completion tree. `children` NEVER throws: unknown paths and
|
|
71
|
+
* a bare `[]` return an empty list (leaves have no deeper completion — the
|
|
72
|
+
* TUI asks at depth 2). */
|
|
73
|
+
const advisorTree = {
|
|
74
|
+
root: ADVISOR_TUI_ROOT,
|
|
75
|
+
descriptions: ADVISOR_TUI_DESCRIPTIONS,
|
|
76
|
+
children(canonicalPath) {
|
|
77
|
+
// Root at index 0: only `['advisor']` asks for the subcommand list.
|
|
78
|
+
if (canonicalPath.length !== 1 || canonicalPath[0] !== ADVISOR_TUI_ROOT)
|
|
79
|
+
return [];
|
|
80
|
+
return ADVISOR_SUBCOMMANDS.map((name) => ({
|
|
81
|
+
name,
|
|
82
|
+
description: SUBCOMMAND_DESCRIPTIONS[name].description,
|
|
83
|
+
descriptions: SUBCOMMAND_DESCRIPTIONS[name].descriptions,
|
|
84
|
+
}));
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Install the advisor's TUI client surface: register the `/advisor`
|
|
89
|
+
* `tuiCommandTrees` provider when the host service exists (conditional
|
|
90
|
+
* inject; absent service → clean no-op). Called from `apply()` AFTER the
|
|
91
|
+
* single-reviewer claim, so the tree registers at most once per process
|
|
92
|
+
* (a duplicate-root registration would throw in the host registry). The
|
|
93
|
+
* structural accessor keeps the inject key in the standard position: the
|
|
94
|
+
* cordis Context has no `tuiCommandTrees` augmentation in this repo, so the
|
|
95
|
+
* service is read through a local structural cast.
|
|
96
|
+
*/
|
|
97
|
+
export function installTuiClient(ctx) {
|
|
98
|
+
ctx.inject(['tuiCommandTrees'], (tctx) => {
|
|
99
|
+
const trees = tctx.tuiCommandTrees;
|
|
100
|
+
if (trees === undefined)
|
|
101
|
+
return;
|
|
102
|
+
try {
|
|
103
|
+
return trees.register(advisorTree);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
if (!(error instanceof Error) || !error.message.includes('already registered'))
|
|
107
|
+
throw error;
|
|
108
|
+
tctx.logger('advisor').debug('advisor tui tree already registered — no tree on this fiber (multi-fiber dedupe)');
|
|
109
|
+
return () => { };
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=tui.js.map
|
package/lib/tui.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AA4BH,oEAAoE;AACpE,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAA;AAEzC;;;oBAGoB;AACpB,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAA;AAItE,mFAAmF;AACnF,MAAM,wBAAwB,GAA6B;IACzD,EAAE,EAAE,8BAA8B;IAClC,EAAE,EAAE,yDAAyD;CAC9D,CAAA;AAED;wEACwE;AACxE,MAAM,uBAAuB,GAAyG;IACpI,EAAE,EAAE;QACF,WAAW,EAAE,qCAAqC;QAClD,YAAY,EAAE;YACZ,EAAE,EAAE,UAAU;YACd,EAAE,EAAE,qCAAqC;SAC1C;KACF;IACD,GAAG,EAAE;QACH,WAAW,EAAE,sCAAsC;QACnD,YAAY,EAAE;YACZ,EAAE,EAAE,UAAU;YACd,EAAE,EAAE,sCAAsC;SAC3C;KACF;IACD,MAAM,EAAE;QACN,WAAW,EAAE,iFAAiF;QAC9F,YAAY,EAAE;YACZ,EAAE,EAAE,+BAA+B;YACnC,EAAE,EAAE,iFAAiF;SACtF;KACF;IACD,MAAM,EAAE;QACN,WAAW,EAAE,sDAAsD;QACnE,YAAY,EAAE;YACZ,EAAE,EAAE,kBAAkB;YACtB,EAAE,EAAE,sDAAsD;SAC3D;KACF;CACF,CAAA;AAED;;2BAE2B;AAC3B,MAAM,WAAW,GAA2B;IAC1C,IAAI,EAAE,gBAAgB;IACtB,YAAY,EAAE,wBAAwB;IACtC,QAAQ,CAAC,aAAgC;QACvC,oEAAoE;QACpE,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,KAAK,gBAAgB;YAAE,OAAO,EAAE,CAAA;QAClF,OAAO,mBAAmB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACxC,IAAI;YACJ,WAAW,EAAE,uBAAuB,CAAC,IAAI,CAAC,CAAC,WAAW;YACtD,YAAY,EAAE,uBAAuB,CAAC,IAAI,CAAC,CAAC,YAAY;SACzD,CAAC,CAAC,CAAA;IACL,CAAC;CACF,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,GAAG,CAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE;QACvC,MAAM,KAAK,GAAI,IAA6F,CAAC,eAAe,CAAA;QAC5H,IAAI,KAAK,KAAK,SAAS;YAAE,OAAM;QAC/B,IAAI,CAAC;YACH,OAAO,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC;gBAAE,MAAM,KAAK,CAAA;YAC3F,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,kFAAkF,CAAC,CAAA;YAChH,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;QACjB,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-advisor",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "dsh plugin bundle porting the omp advisor subsystem: a per-session reviewer model that observes the primary transcript and injects severity-ranked advice (nit/concern/blocker).",
|
|
6
6
|
"repository": {
|