@gtrabanco/pi-agentic-workflow 0.7.1 → 0.8.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.
Files changed (40) hide show
  1. package/README.es.md +22 -3
  2. package/README.md +21 -3
  3. package/dist/config/schema.js +32 -1
  4. package/dist/config/types.d.ts +10 -1
  5. package/dist/config/types.js +2 -0
  6. package/dist/extension/factory.js +26 -15
  7. package/dist/extension/index.js +44 -1
  8. package/dist/routing/dispatch.js +39 -3
  9. package/dist/routing/types.d.ts +15 -0
  10. package/dist/routing/types.js +2 -0
  11. package/dist/settings/console.d.ts +11 -0
  12. package/dist/settings/console.js +203 -19
  13. package/dist/settings/picker.d.ts +31 -0
  14. package/dist/settings/picker.js +93 -0
  15. package/dist/settings/view.js +4 -1
  16. package/package.json +13 -8
  17. package/skills/execute-phase/SKILL.md +6 -4
  18. package/skills/execute-phase/references/BATCH_AND_PORTABILITY.md +5 -3
  19. package/skills/execute-phase/references/CLOSEOUT.md +6 -4
  20. package/skills/execute-phase/references/FOLDING.md +2 -1
  21. package/skills/execute-phase/references/UNIT_LOOP.md +2 -1
  22. package/skills/pre-execution-review/SKILL.md +1 -1
  23. package/skills/pre-execution-review/references/LEDGERS.md +10 -7
  24. package/skills/pre-execution-review/references/SNAPSHOT.md +2 -2
  25. package/skills/review-change/SKILL.md +15 -2
  26. package/skills/review-change/references/OUTPUT_AND_GUARDRAILS.md +5 -4
  27. package/skills/review-change/references/PERSIST_AND_DECIDE.md +8 -0
  28. package/skills/review-change/references/REVIEW_PROCESS.md +4 -3
  29. package/skills/review-plan/SKILL.md +1 -1
  30. package/skills/review-plan/references/CHECKS.md +1 -1
  31. package/skills/review-plan/references/OUTPUT.md +2 -2
  32. package/skills/review-spec/SKILL.md +1 -1
  33. package/skills/review-spec/references/CHECKS.md +1 -1
  34. package/skills/review-spec/references/OUTPUT.md +1 -1
  35. package/skills/ship-roadmap/SKILL.md +2 -2
  36. package/skills/ship-roadmap/references/ADVANCE.md +1 -3
  37. package/skills/ship-roadmap/references/MODEL_ROUTING.md +1 -1
  38. package/skills/workflow-status/SKILL.md +1 -1
  39. package/skills/workflow-status/references/PRE_EXECUTION.md +1 -1
  40. package/skills/workflow-status/references/SENSOR_CORE.md +1 -1
package/README.es.md CHANGED
@@ -74,7 +74,7 @@ Dos archivos JSON, ambos opcionales:
74
74
  {
75
75
  "default": { "model": "anthropic/claude-opus-4-5", "thinking": "high" },
76
76
  "commands": {
77
- "plan-feature": { "model": "anthropic/claude-sonnet-4-5", "thinking": "medium" },
77
+ "plan-feature": { "model": ["anthropic/claude-sonnet-4-5", "openai/gpt-5.2"], "thinking": "medium" },
78
78
  "review-change": { "thinking": "max" }
79
79
  },
80
80
  "onUnavailableRoute": "stop"
@@ -87,8 +87,14 @@ de arriba usa el modelo del default con thinking `max`; todo lo demás corre con
87
87
  que ya tuviera la sesión, porque la ruta por defecto del paquete es
88
88
  `{"model": "inherit", "thinking": "inherit"}`.
89
89
 
90
- - `model` debe ser `provider/modelId` — la referencia exacta que muestra
91
- `/model` — o `"inherit"`.
90
+ - `model` puede ser `provider/modelId` — la referencia exacta que muestra
91
+ `/model` — `"inherit"`, o un **array** ordenado de 1–4 referencias (una cadena
92
+ de respaldo). Para una cadena, el dispatch prueba cada referencia en orden y
93
+ aplica la primera que se resuelve y tiene credenciales, sin tocar la sesión
94
+ mientras prueba; cuando todas las entradas son inservibles el comando se
95
+ detiene (o, con `onUnavailableRoute` en `inherit`, avisa y corre con el modelo
96
+ actual), nombrando cada candidato y por qué se saltó. `plan-feature` de arriba
97
+ prueba `anthropic/claude-sonnet-4-5` primero y luego `openai/gpt-5.2`.
92
98
  - `thinking` es uno de `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`,
93
99
  o `"inherit"`.
94
100
  - Claves desconocidas, `null` y referencias mal formadas se **rechazan**, no se
@@ -125,11 +131,24 @@ vuelve.
125
131
  /agentic-workflow-settings
126
132
  ```
127
133
 
134
+ `/aw-settings` es un atajo para la misma consola.
135
+
128
136
  Muestra en qué corre cada comando ahora mismo, y qué archivo se niega a parsearse,
129
137
  y luego deja editar **un archivo a la vez** y guardar en alcance global o de
130
138
  proyecto. No guarda encima de un archivo que no sabe leer, y no toca el
131
139
  archivo de proyecto mientras el proyecto no sea de confianza.
132
140
 
141
+ Los selectores de modelo y thinking de la consola son buscables y con ventana:
142
+ escribir estrecha la lista, el cursor se queda en pantalla, un indicador de
143
+ posición muestra dónde estás y el valor en vigor viene preseleccionado y
144
+ etiquetado `(current)` / `(default route)`. Una edición de ruta pregunta qué
145
+ campo cambiar (model, thinking), así que guardar sin cambios deja el archivo
146
+ byte a byte idéntico; el campo de modelo puede construir una cadena de respaldo
147
+ ordenada (`a/m1 → b/m2`); y una sola pasada puede aplicar o limpiar una ruta en
148
+ varios comandos, avisando por comando cuando una referencia falta del registro
149
+ vivo. Fuera de una sesión TUI los selectores caen a un prompt simple, así que la
150
+ consola nunca se queda sin salida.
151
+
133
152
  ## Diagnóstico
134
153
 
135
154
  | Ves | Significa |
package/README.md CHANGED
@@ -72,7 +72,7 @@ Two JSON files, both optional:
72
72
  {
73
73
  "default": { "model": "anthropic/claude-opus-4-5", "thinking": "high" },
74
74
  "commands": {
75
- "plan-feature": { "model": "anthropic/claude-sonnet-4-5", "thinking": "medium" },
75
+ "plan-feature": { "model": ["anthropic/claude-sonnet-4-5", "openai/gpt-5.2"], "thinking": "medium" },
76
76
  "review-change": { "thinking": "max" }
77
77
  },
78
78
  "onUnavailableRoute": "stop"
@@ -85,8 +85,14 @@ above runs on the default model with `max` thinking; anything else runs on
85
85
  whatever the session already had, because the shipped default route is
86
86
  `{"model": "inherit", "thinking": "inherit"}`.
87
87
 
88
- - `model` must be `provider/modelId` — the exact reference `/model` shows — or
89
- `"inherit"`.
88
+ - `model` can be `provider/modelId` — the exact reference `/model` shows —
89
+ `"inherit"`, or an ordered **array** of 1–4 references (a fallback chain). For
90
+ a chain, dispatch probes each reference in order and applies the first one that
91
+ resolves and has credentials, without touching the session while probing; when
92
+ every entry is unusable the command stops (or, with `onUnavailableRoute`
93
+ `inherit`, warns and runs on the current model), naming each candidate and why
94
+ it was skipped. `plan-feature` above tries `anthropic/claude-sonnet-4-5` first,
95
+ then `openai/gpt-5.2`.
90
96
  - `thinking` is one of `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`,
91
97
  or `"inherit"`.
92
98
  - Unknown keys, `null`s and malformed references are **rejected**, not ignored: a
@@ -120,10 +126,22 @@ thinking level and you keep it while the model still comes back.
120
126
  /agentic-workflow-settings
121
127
  ```
122
128
 
129
+ `/aw-settings` is a shorthand for the same console.
130
+
123
131
  Shows what each command runs on right now, and which file is refusing to parse,
124
132
  then lets you edit **one file at a time** and save to global or project scope. It will not save over a file it cannot parse, and it will not
125
133
  touch the project file while the project is untrusted.
126
134
 
135
+ The console's model and thinking pickers are searchable and windowed: typing
136
+ narrows the list, the cursor stays on screen, a position indicator shows where
137
+ you are, and the value currently in force is pre-selected and labelled
138
+ `(current)` / `(default route)`. A route edit asks which field to change
139
+ (model, thinking), so a no-change save leaves the file byte-identical; the
140
+ model field can build an ordered fallback chain (`a/m1 → b/m2`); and one pass
141
+ can apply or clear a route across several commands, warning per command when a
142
+ reference is missing from the live registry. Outside a TUI session the pickers
143
+ fall back to a plain prompt, so the console never dead-ends.
144
+
127
145
  ## Troubleshooting
128
146
 
129
147
  | You see | Means |
@@ -1,4 +1,4 @@
1
- import { THINKING_LEVELS, UNAVAILABLE_ROUTE_POLICIES } from "./types.js";
1
+ import { MAX_MODEL_CHAIN, THINKING_LEVELS, UNAVAILABLE_ROUTE_POLICIES } from "./types.js";
2
2
  /**
3
3
  * Strict validator for one config file (SPEC S5-S8, D-E5).
4
4
  *
@@ -52,6 +52,37 @@ function checkRoute(value, path, issues) {
52
52
  continue;
53
53
  }
54
54
  if (key === "model") {
55
+ if (Array.isArray(entry)) {
56
+ // A chain must be non-empty, hold only references, and stay within the cap.
57
+ if (entry.length === 0) {
58
+ issues.push({
59
+ path: `${path}.model`,
60
+ message: `must be "inherit", "provider/modelId", or a non-empty chain of references, got an empty array`,
61
+ });
62
+ continue;
63
+ }
64
+ if (entry.length > MAX_MODEL_CHAIN) {
65
+ issues.push({
66
+ path: `${path}.model`,
67
+ message: `chain holds ${entry.length} entries; ${MAX_MODEL_CHAIN} is the maximum`,
68
+ });
69
+ continue;
70
+ }
71
+ let chainValid = true;
72
+ for (let index = 0; index < entry.length; index += 1) {
73
+ if (!isModelReference(entry[index])) {
74
+ issues.push({
75
+ path: `${path}.model`,
76
+ message: `chain entry ${index + 1} (${describe(entry[index])}) must be "provider/modelId"`,
77
+ });
78
+ chainValid = false;
79
+ break;
80
+ }
81
+ }
82
+ if (chainValid)
83
+ route.model = entry;
84
+ continue;
85
+ }
55
86
  if (entry !== "inherit" && !isModelReference(entry)) {
56
87
  issues.push({
57
88
  path: `${path}.model`,
@@ -14,7 +14,16 @@ export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
14
14
  export type ThinkingSetting = ThinkingLevel | "inherit";
15
15
  /** `"inherit"` or an exact `provider/modelId` reference (split at the first slash). */
16
16
  export type ModelRef = `${string}/${string}`;
17
- export type ModelSetting = "inherit" | ModelRef;
17
+ /** An ordered fallback chain of references; the first resolvable + configured entry wins (issue #154, root cause E). */
18
+ export type ModelChain = readonly ModelRef[];
19
+ /**
20
+ * `"inherit"`, one reference, or an ordered fallback chain of 1–4 references.
21
+ * A chain is probed by order, and the first entry that resolves AND has
22
+ * configured auth is applied (OB-6, OB-8, OB-9).
23
+ */
24
+ export type ModelSetting = "inherit" | ModelRef | ModelChain;
25
+ /** Cap on chain length (issue deliberation: 8 was "almost certainly a mistake"; 4 keeps the view readable). */
26
+ export declare const MAX_MODEL_CHAIN = 4;
18
27
  export interface Route {
19
28
  model: ModelSetting;
20
29
  thinking: ThinkingSetting;
@@ -9,4 +9,6 @@
9
9
  */
10
10
  /** Pi's thinking levels, mirrored here so the domain layer stays Pi-free. */
11
11
  export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
12
+ /** Cap on chain length (issue deliberation: 8 was "almost certainly a mistake"; 4 keeps the view readable). */
13
+ export const MAX_MODEL_CHAIN = 4;
12
14
  export const UNAVAILABLE_ROUTE_POLICIES = ["stop", "inherit"];
@@ -1,7 +1,7 @@
1
1
  import { loadConfig } from "../config/load.js";
2
2
  import { readCatalogue } from "../routing/catalogue.js";
3
3
  import { createRouter } from "../routing/dispatch.js";
4
- import { SETTINGS_COMMAND } from "../routing/types.js";
4
+ import { SETTINGS_COMMAND, SETTINGS_COMMAND_ALIAS } from "../routing/types.js";
5
5
  export function createExtension(deps) {
6
6
  const { registrar, surface, skillsDir, agentDir, hint, settings } = deps;
7
7
  const catalogue = readCatalogue(skillsDir);
@@ -18,7 +18,11 @@ export function createExtension(deps) {
18
18
  ctx.notify(`pi-agentic-workflow: skills/${issue.dir}: ${issue.message}`, "warning");
19
19
  }
20
20
  };
21
- const knownCommands = new Set([...catalogue.commands.map((entry) => entry.name), SETTINGS_COMMAND]);
21
+ const knownCommands = new Set([
22
+ ...catalogue.commands.map((entry) => entry.name),
23
+ SETTINGS_COMMAND,
24
+ SETTINGS_COMMAND_ALIAS,
25
+ ]);
22
26
  const router = createRouter({ surface, loadConfig: read, hint, settingsCommand: SETTINGS_COMMAND, knownCommands });
23
27
  for (const command of catalogue.commands) {
24
28
  registrar.registerCommand(command.name, {
@@ -29,21 +33,28 @@ export function createExtension(deps) {
29
33
  },
30
34
  });
31
35
  }
36
+ // One handler for both names — `/aw-settings` is a pointer to the same console
37
+ // and the same two config files, never a separate route surface (OB-5, AC6).
38
+ const settingsHandler = async (_args, ctx) => {
39
+ reportCatalogueIssues(ctx);
40
+ // Bound for the console: two verbs and the session they act on, so it cannot
41
+ // reach for `settle` or `dispatch` by accident.
42
+ const routing = { inFlight: () => router.inFlight(), undoInFlight: () => router.undoInFlight(ctx) };
43
+ try {
44
+ await settings({ catalogue, ctx, routing });
45
+ }
46
+ catch (error) {
47
+ // A console that dies mid-question must say so, not take the session down.
48
+ ctx.notify(`Settings could not be opened: ${error.message}`, "error");
49
+ }
50
+ };
32
51
  registrar.registerCommand(SETTINGS_COMMAND, {
33
52
  description: "Show and configure per-command model routing",
34
- handler: async (_args, ctx) => {
35
- reportCatalogueIssues(ctx);
36
- // Bound for the console: two verbs and the session they act on, so it cannot
37
- // reach for `settle` or `dispatch` by accident.
38
- const routing = { inFlight: () => router.inFlight(), undoInFlight: () => router.undoInFlight(ctx) };
39
- try {
40
- await settings({ catalogue, ctx, routing });
41
- }
42
- catch (error) {
43
- // A console that dies mid-question must say so, not take the session down.
44
- ctx.notify(`Settings could not be opened: ${error.message}`, "error");
45
- }
46
- },
53
+ handler: settingsHandler,
54
+ });
55
+ registrar.registerCommand(SETTINGS_COMMAND_ALIAS, {
56
+ description: "Show and configure per-command model routing",
57
+ handler: settingsHandler,
47
58
  });
48
59
  return { router, catalogue };
49
60
  }
@@ -3,6 +3,7 @@ import { fileURLToPath } from "node:url";
3
3
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
4
4
  import { loadConfig } from "../config/load.js";
5
5
  import { createExtension } from "./factory.js";
6
+ import { createPickerComponent, PICKER_MAX_VISIBLE } from "../settings/picker.js";
6
7
  import { createHintStore, stateFilePath } from "../routing/state.js";
7
8
  import { runSettingsConsole } from "../settings/console.js";
8
9
  import { readConfigFile, writeConfigFile } from "../settings/store.js";
@@ -20,6 +21,48 @@ import { SETTINGS_COMMAND } from "../routing/types.js";
20
21
  */
21
22
  const skillsDir = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "skills");
22
23
  const thinkingLevelsInSyncWithPi = true;
24
+ /**
25
+ * The interactive slice the settings console sees: Pi's own `ui` plus the rich
26
+ * picker (P4). The picker is terminal-only, so a non-TUI mode (headless/RPC)
27
+ * gets the plain `select` path — the console never dead-ends (OB-12, PE-004).
28
+ */
29
+ function richUi(ctx) {
30
+ const base = ctx.ui;
31
+ return {
32
+ select: (title, options) => base.select(title, [...options]),
33
+ input: (title, placeholder) => base.input(title, placeholder),
34
+ confirm: (title, message) => base.confirm(title, message),
35
+ notify: (message, kind) => base.notify(message, kind),
36
+ pick: async (title, options, opts = {}) => {
37
+ if (ctx.mode !== "tui") {
38
+ const picked = await base.select(title, [...options]);
39
+ if (opts.multiple)
40
+ return picked ? [picked] : undefined;
41
+ return picked;
42
+ }
43
+ const items = options.map((value) => ({ value, label: value }));
44
+ const result = await base.custom((_tui, theme, _keybindings, done) => {
45
+ const selectListTheme = {
46
+ selectedPrefix: (text) => theme.bold(`› ${text}`),
47
+ selectedText: (text) => theme.inverse(text),
48
+ description: (text) => ` ${text}`,
49
+ scrollInfo: (text) => theme.bold(text),
50
+ noMatch: (text) => theme.italic(text),
51
+ };
52
+ return createPickerComponent({
53
+ items,
54
+ maxVisible: PICKER_MAX_VISIBLE,
55
+ theme: selectListTheme,
56
+ onSelect: (value) => done(value),
57
+ onCancel: () => done(undefined),
58
+ });
59
+ }, { overlay: true });
60
+ if (opts.multiple)
61
+ return result === undefined ? undefined : [result];
62
+ return result;
63
+ },
64
+ };
65
+ }
23
66
  function toInvocationContext(ctx) {
24
67
  return {
25
68
  cwd: ctx.cwd,
@@ -29,7 +72,7 @@ function toInvocationContext(ctx) {
29
72
  isIdle: () => ctx.isIdle(),
30
73
  isProjectTrusted: () => ctx.isProjectTrusted(),
31
74
  notify: (message, kind) => ctx.ui.notify(message, kind),
32
- ui: ctx.ui,
75
+ ui: richUi(ctx),
33
76
  availableModels: () => ctx.modelRegistry.getAll(),
34
77
  find: (provider, modelId) => ctx.modelRegistry.find(provider, modelId),
35
78
  hasConfiguredAuth: (model) => ctx.modelRegistry.hasConfiguredAuth(model),
@@ -111,7 +111,13 @@ export function createRouter({ surface, loadConfig, hint, settingsCommand, known
111
111
  const session = surface(ctx);
112
112
  const route = effectiveRoute(loaded.config, command.name);
113
113
  let target;
114
- if (route.model !== "inherit") {
114
+ /** The reference string behind `target`, for the select-failure message (a chain names the chosen entry). */
115
+ let chosenRef;
116
+ if (route.model === "inherit") {
117
+ // inherit: no probe — run on the session model (OB-9).
118
+ }
119
+ else if (typeof route.model === "string") {
120
+ // Single reference: behaviour and message shape unchanged (OB-9).
115
121
  const reference = parseModelReference(route.model);
116
122
  const found = reference ? ctx.find(reference.provider, reference.id) : undefined;
117
123
  const blocker = !found
@@ -127,6 +133,36 @@ export function createRouter({ surface, loadConfig, hint, settingsCommand, known
127
133
  }
128
134
  else {
129
135
  target = found;
136
+ chosenRef = route.model;
137
+ }
138
+ }
139
+ else {
140
+ // Chain: probe entries in order with only ctx.find + hasConfiguredAuth
141
+ // (no session mutation), collecting one skip reason per entry, and apply
142
+ // the first usable one (OB-8, OB-9, OB-10).
143
+ const reasons = [];
144
+ for (const reference of route.model) {
145
+ const parsed = parseModelReference(reference);
146
+ const found = parsed ? ctx.find(parsed.provider, parsed.id) : undefined;
147
+ if (!found) {
148
+ reasons.push(`${reference} is not in the model registry`);
149
+ continue;
150
+ }
151
+ if (!ctx.hasConfiguredAuth(found)) {
152
+ reasons.push(`${reference} has no configured credentials`);
153
+ continue;
154
+ }
155
+ target = found;
156
+ chosenRef = reference;
157
+ break;
158
+ }
159
+ if (!target) {
160
+ const tried = route.model.join(", ");
161
+ const detail = reasons.join("; ");
162
+ if (loaded.config.onUnavailableRoute !== "inherit") {
163
+ return refuse(ctx, "unavailable-route", `/${command.name} stopped: the configured model chain ${tried} is unavailable (${detail}). ${configureHint}`);
164
+ }
165
+ ctx.notify(`/${command.name}: the configured model chain ${tried} is unavailable (${detail}), so it runs on the current session model. ${configureHint}`, "warning");
130
166
  }
131
167
  }
132
168
  const snapshot = { model: ctx.model, thinking: session.getThinkingLevel() };
@@ -135,9 +171,9 @@ export function createRouter({ surface, loadConfig, hint, settingsCommand, known
135
171
  const selected = await session.setModel(target);
136
172
  if (!selected) {
137
173
  if (loaded.config.onUnavailableRoute !== "inherit") {
138
- return refuse(ctx, "unavailable-route", `/${command.name} stopped: ${route.model} could not be selected. ${configureHint}`);
174
+ return refuse(ctx, "unavailable-route", `/${command.name} stopped: ${chosenRef} could not be selected. ${configureHint}`);
139
175
  }
140
- ctx.notify(`/${command.name}: ${route.model} could not be selected, so it runs on the current session model. ${configureHint}`, "warning");
176
+ ctx.notify(`/${command.name}: ${chosenRef} could not be selected, so it runs on the current session model. ${configureHint}`, "warning");
141
177
  }
142
178
  else {
143
179
  applied.model = target;
@@ -39,12 +39,25 @@ export interface ExtensionSurface<M extends ModelRef = ModelRef> {
39
39
  getThinkingLevel(): ThinkingLevel;
40
40
  setThinkingLevel(level: ThinkingLevel): void;
41
41
  }
42
+ /** Options for the optional rich picker (OB-1, OB-3, P4/P6). */
43
+ export interface PickerOptions {
44
+ /** The value already in force, preselected when the picker opens (OB-3). */
45
+ initial?: string;
46
+ /** Allow selecting several entries (bulk apply/clear, P6). */
47
+ multiple?: boolean;
48
+ }
42
49
  /** The interactive slice of `ctx.ui` the settings console uses (AC10). */
43
50
  export interface SettingsUi {
44
51
  select(title: string, options: readonly string[]): Promise<string | undefined> | string | undefined;
45
52
  input(title: string, placeholder?: string): Promise<string | undefined> | string | undefined;
46
53
  confirm(title: string, message: string): Promise<boolean> | boolean;
47
54
  notify(message: string, kind?: "info" | "warning" | "error"): void;
55
+ /**
56
+ * Optional rich picker: filterable, windowed, with a position indicator and a
57
+ * preselected current value. A structural superset — a caller without it
58
+ * falls back to `select`/`input`. Returns `readonly string[]` when `multiple`.
59
+ */
60
+ pick?(title: string, options: readonly string[], opts?: PickerOptions): Promise<string | readonly string[] | undefined> | string | readonly string[] | undefined;
48
61
  }
49
62
  /**
50
63
  * Everything a command handler needs from the invocation, reduced to what we
@@ -78,6 +91,8 @@ export type DispatchOutcome = {
78
91
  };
79
92
  /** Slash name of the settings console (SPEC S4, AC3, AC10). */
80
93
  export declare const SETTINGS_COMMAND = "agentic-workflow-settings";
94
+ /** Shorthand alias for the same console — a pointer to the same handler, never a separate route (OB-5, AC6). */
95
+ export declare const SETTINGS_COMMAND_ALIAS = "aw-settings";
81
96
  /**
82
97
  * The router, as the settings console needs it: two verbs, already bound to the
83
98
  * session they act on. Declared once because both the console's deps and the
@@ -1,3 +1,5 @@
1
1
  export const modelRefKey = (model) => `${model.provider}/${model.id}`;
2
2
  /** Slash name of the settings console (SPEC S4, AC3, AC10). */
3
3
  export const SETTINGS_COMMAND = "agentic-workflow-settings";
4
+ /** Shorthand alias for the same console — a pointer to the same handler, never a separate route (OB-5, AC6). */
5
+ export const SETTINGS_COMMAND_ALIAS = "aw-settings";
@@ -45,11 +45,22 @@ export declare const prompts: {
45
45
  readonly setDefaultRoute: "Set the default route";
46
46
  readonly setOverride: "Set a command override";
47
47
  readonly clearOverride: "Clear a command override";
48
+ readonly bulkApply: "Apply one route to several commands";
49
+ readonly bulkClear: "Clear several overrides";
50
+ readonly addAnother: "Add another?";
48
51
  readonly policy: "Set the unavailable-route policy";
49
52
  readonly save: "Save";
50
53
  readonly cancel: "Cancel";
51
54
  readonly model: (target: string) => string;
52
55
  readonly modelPicked: (target: string) => string;
53
56
  readonly thinking: (target: string) => string;
57
+ readonly fields: "Which fields should change?";
58
+ readonly fieldsBoth: "model and thinking";
59
+ readonly fieldsModel: "model only";
60
+ readonly fieldsThinking: "thinking only";
61
+ readonly chainAction: (target: string) => string;
62
+ readonly chainAppend: "Add a fallback";
63
+ readonly chainRemoveLast: "Remove the last";
64
+ readonly chainDone: "Done";
54
65
  };
55
66
  export declare function runSettingsConsole(deps: SettingsDeps): Promise<ConsoleOutcome>;