@stevecastaneda/paseo-progress 1.9.0 → 1.11.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/README.md +9 -1
- package/client/pill-popover.tsx +2 -3
- package/client/pill-state.ts +1 -2
- package/client/pills.tsx +12 -8
- package/dist/server/cli.js +29 -16
- package/index.client.tsx +1 -2
- package/package.json +1 -1
- package/server/cli.ts +28 -13
- package/shared/dashboard.ts +1 -0
- package/shared/events.ts +1 -1
- package/skills/paseo-progress/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ The panel lives in Explorer, beside the agent chat, so you can watch both. Open
|
|
|
24
24
|
|
|
25
25
|
<img src="images/4-pill.png" alt="The pill above the message box, showing two questions waiting" width="600">
|
|
26
26
|
|
|
27
|
-
On phones
|
|
27
|
+
On phones with Paseo 0.13 or later, Progress opens in Explorer too. Older phone apps have no Explorer, so there it opens as a tab of its own. On any phone, the pill stays above the message box while a run is open, so there's always a way in. SVG files and videos preview on desktop; on phones they open on the daemon host.
|
|
28
28
|
|
|
29
29
|
The first time a run starts in a worktree whose Progress panel has never been opened, the pill shows a spinner and reads **Progress** instead; pressing it opens the panel. Once the panel has been opened there, the plugin writes an empty `progress-panel-opened` file next to the progress file and the nudge never comes back, even for later runs. It doesn't open the panel by itself because Paseo switches to a workspace to open its panel, which would pull you away from whatever you're looking at.
|
|
30
30
|
|
|
@@ -145,6 +145,14 @@ The progress file, `progress.jsonl`, holds one JSON event per line, each with a
|
|
|
145
145
|
|
|
146
146
|
## Changelog
|
|
147
147
|
|
|
148
|
+
### 1.11.0
|
|
149
|
+
|
|
150
|
+
- [Improved] **Agents can change their recommendation.** When you ask an agent about an open question and explaining it changes the agent's mind, it can now change its recommendation, and the question card shows the new one. In Keep building mode, the agent also switches the work it already built to the new recommendation.
|
|
151
|
+
|
|
152
|
+
### 1.10.0
|
|
153
|
+
|
|
154
|
+
- [Improved] **Progress opens in Explorer on phones.** On Paseo 0.13 or later, the phone app opens Progress in Explorer, as the desktop app does, and lists it in Explorer's New tab menu. If a Progress tab from an earlier version is still open on your phone, you can close it.
|
|
155
|
+
|
|
148
156
|
### 1.9.0
|
|
149
157
|
|
|
150
158
|
- [Improved] **The pill's popover shows one question at a time.** You used to scroll past every open question to reach the one you wanted. Now **Question 2 of 3** sits at the top with back and next arrows. Copy an answer and the popover opens on the next question the next time you press the pill.
|
package/client/pill-popover.tsx
CHANGED
|
@@ -4,7 +4,6 @@ import React, { useRef, useState } from "react";
|
|
|
4
4
|
import { Text, View } from "react-native";
|
|
5
5
|
import type { Dashboard } from "../shared/dashboard";
|
|
6
6
|
import { useDashboard } from "./dashboard-query";
|
|
7
|
-
import { progressPanel } from "./pills";
|
|
8
7
|
import { PressScale } from "./motion";
|
|
9
8
|
import { type Attachment, isPreviewable, useAttachmentOpener } from "./attachments";
|
|
10
9
|
import { StackedDialog } from "./dialog-stack";
|
|
@@ -33,8 +32,8 @@ export function AttentionPopover({ theme, host, layout, workspaceId, close, open
|
|
|
33
32
|
const colors = theme.colors;
|
|
34
33
|
// Paseo draws the desktop popover on surface1 and the phone sheet on surface0.
|
|
35
34
|
const onSurface1 = !layout.compact;
|
|
36
|
-
//
|
|
37
|
-
const touch =
|
|
35
|
+
// The phone apps are pressed with a finger.
|
|
36
|
+
const touch = layout.platform !== "web";
|
|
38
37
|
const directory = useWorkspace(workspaceId, (workspace) => workspace.directory);
|
|
39
38
|
const query = useDashboard(host.id, workspaceId, directory);
|
|
40
39
|
const dashboard = query.data?.configured ? query.data.dashboard : null;
|
package/client/pill-state.ts
CHANGED
|
@@ -5,8 +5,7 @@ import type { AttentionResult } from "../shared/rpc";
|
|
|
5
5
|
// icon and what pressing it does can't disagree.
|
|
6
6
|
// - "attention": questions waiting or something stuck; opens a popover.
|
|
7
7
|
// - "started": a run whose panel has never been opened; a one-time nudge.
|
|
8
|
-
// - "open": on a phone,
|
|
9
|
-
// phones have no Explorer to open it from.
|
|
8
|
+
// - "open": on a phone, a one-tap way into the panel while a run is open.
|
|
10
9
|
export type Pill =
|
|
11
10
|
| { mode: "attention"; label: string; icon: "stuck" | "questions" }
|
|
12
11
|
| { mode: "started"; label: "Progress"; icon: "started" }
|
package/client/pills.tsx
CHANGED
|
@@ -18,13 +18,17 @@ export const PILL_POLL_MS = 5_000;
|
|
|
18
18
|
// still trigger a check, at most once a poll interval.
|
|
19
19
|
export const IDLE_POLL_EVERY = 6;
|
|
20
20
|
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
21
|
+
// Phones on Paseo before 0.13 have no Explorer, so there the panel is a tab of
|
|
22
|
+
// its own. addFilePanel arrived in 0.13, the release that gave phones Explorer.
|
|
23
|
+
// The tab has its own id: Paseo 0.10.1 reopens an existing tab where it
|
|
24
24
|
// already is, and earlier versions of this plugin left a "progress" tab in the
|
|
25
25
|
// phone's hidden Explorer pane, which a plugin can't move or close.
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
type ProgressPanelPlacement = { id: "progress"; location: "explorer" } | { id: "progress-tab"; location: "workspace" };
|
|
27
|
+
|
|
28
|
+
export function progressPanel(client: PluginClientContext): ProgressPanelPlacement {
|
|
29
|
+
return Platform.OS === "web" || "addFilePanel" in client
|
|
30
|
+
? { id: "progress", location: "explorer" }
|
|
31
|
+
: { id: "progress-tab", location: "workspace" };
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
type Workspace = { id: string; workspaceDirectory?: string; projectRootPath: string };
|
|
@@ -44,7 +48,7 @@ export function contributePills(client: PluginClientContext) {
|
|
|
44
48
|
// The last report per workspace; pillFor turns it into what the pill shows.
|
|
45
49
|
const reports = new Map<string, AttentionResult>();
|
|
46
50
|
let locale = resolveLocale("system");
|
|
47
|
-
const where = { phone:
|
|
51
|
+
const where = { phone: Platform.OS !== "web" };
|
|
48
52
|
const pillOf = (workspaceId: string) => pillFor(reports.get(workspaceId), where, messagesFor(locale));
|
|
49
53
|
// Bumped on each report so a poll that was already in flight can't bring the pill back.
|
|
50
54
|
let openedReports = 0;
|
|
@@ -83,9 +87,9 @@ export function contributePills(client: PluginClientContext) {
|
|
|
83
87
|
);
|
|
84
88
|
}
|
|
85
89
|
|
|
90
|
+
const panel = progressPanel(client);
|
|
86
91
|
const openPanel = (workspaceId: string) => {
|
|
87
|
-
|
|
88
|
-
client.openPanel(id, { workspaceId, location });
|
|
92
|
+
client.openPanel(panel.id, { workspaceId, location: panel.location });
|
|
89
93
|
};
|
|
90
94
|
|
|
91
95
|
const buttonFor = (workspaceId: string, mode: Pill["mode"], label: string) => mode === "attention"
|
package/dist/server/cli.js
CHANGED
|
@@ -19796,14 +19796,14 @@ var eventSchema = external_exports.discriminatedUnion("type", [
|
|
|
19796
19796
|
// True when the agent had to undo work built on another option.
|
|
19797
19797
|
changedCourse: external_exports.boolean().optional()
|
|
19798
19798
|
}),
|
|
19799
|
-
// Adds detail to an open question; its reference and default stay.
|
|
19800
19799
|
external_exports.object({
|
|
19801
19800
|
...base,
|
|
19802
19801
|
type: external_exports.literal("question.update"),
|
|
19803
19802
|
id: text,
|
|
19804
19803
|
question: text.optional(),
|
|
19805
19804
|
background: text.optional(),
|
|
19806
|
-
files: external_exports.array(fileLinkSchema).optional()
|
|
19805
|
+
files: external_exports.array(fileLinkSchema).optional(),
|
|
19806
|
+
recommend: text.optional()
|
|
19807
19807
|
}),
|
|
19808
19808
|
external_exports.object({ ...base, type: external_exports.literal("question.remove"), id: text }),
|
|
19809
19809
|
// `path` is relative to the worktree root, or absolute when outside it.
|
|
@@ -19971,6 +19971,7 @@ function reduceProgress(parsed, now) {
|
|
|
19971
19971
|
if (event.question !== void 0) question.question = event.question;
|
|
19972
19972
|
if (event.background !== void 0) question.background = event.background;
|
|
19973
19973
|
if (event.files !== void 0) question.files = [...question.files, ...event.files];
|
|
19974
|
+
if (event.recommend !== void 0) question.recommended = event.recommend;
|
|
19974
19975
|
break;
|
|
19975
19976
|
}
|
|
19976
19977
|
case "question.remove":
|
|
@@ -20702,6 +20703,13 @@ function existingQuestion(state, id) {
|
|
|
20702
20703
|
}
|
|
20703
20704
|
return question;
|
|
20704
20705
|
}
|
|
20706
|
+
function recommendOption(recommend, letters) {
|
|
20707
|
+
if (!letters.length) return recommend;
|
|
20708
|
+
if (!/^[A-Za-z]$/.test(recommend) || !letters.includes(recommend.toUpperCase())) {
|
|
20709
|
+
throw new UsageError(`--recommend must be one of the option letters: ${letters.join(", ")}.`);
|
|
20710
|
+
}
|
|
20711
|
+
return recommend.toUpperCase();
|
|
20712
|
+
}
|
|
20705
20713
|
function existingTicket(state, id) {
|
|
20706
20714
|
const wanted = required2(id, "ticket id").toUpperCase();
|
|
20707
20715
|
const ticket = state.dashboard.tickets.find((candidate) => candidate.id.toUpperCase() === wanted);
|
|
@@ -20840,13 +20848,7 @@ ${modeLine(mode)}`
|
|
|
20840
20848
|
const letters = options.map((option) => option.letter);
|
|
20841
20849
|
const duplicate = letters.find((letter, index) => letters.indexOf(letter) !== index);
|
|
20842
20850
|
if (duplicate) throw new UsageError(`Option ${duplicate} is given twice.`);
|
|
20843
|
-
|
|
20844
|
-
if (options.length) {
|
|
20845
|
-
if (!/^[A-Za-z]$/.test(recommend) || !letters.includes(recommend.toUpperCase())) {
|
|
20846
|
-
throw new UsageError(`--recommend must be one of the option letters: ${letters.join(", ")}.`);
|
|
20847
|
-
}
|
|
20848
|
-
recommend = recommend.toUpperCase();
|
|
20849
|
-
}
|
|
20851
|
+
const recommend = recommendOption(required2(stringOption(values.recommend), "--recommend (the option you would pick)"), letters);
|
|
20850
20852
|
const id = state.nextIds.question;
|
|
20851
20853
|
const big = values.big === void 0 ? void 0 : required2(stringOption(values.big), "--big reason (why the question is big)");
|
|
20852
20854
|
const rules = QUESTION_MODE_RULES[mode];
|
|
@@ -20892,21 +20894,32 @@ ${modeLine(mode)}`
|
|
|
20892
20894
|
}
|
|
20893
20895
|
},
|
|
20894
20896
|
"question update": {
|
|
20895
|
-
usage: `question update <id> [--background <text>] [--question <text>] [--file <path or http(s) URL>[=<label>] ...]`,
|
|
20896
|
-
summary: "Add detail to a question: replace its background or wording, or add files. Its reference
|
|
20897
|
-
options: { background: { type: "string" }, question: { type: "string" }, file: { type: "string", multiple: true } },
|
|
20898
|
-
run({ positionals, values, state, cwd, root }) {
|
|
20897
|
+
usage: `question update <id> [--recommend <letter or word>] [--background <text>] [--question <text>] [--file <path or http(s) URL>[=<label>] ...]`,
|
|
20898
|
+
summary: "Add detail to a question: change your recommendation while it's open, replace its background or wording, or add files. Its reference stays.",
|
|
20899
|
+
options: { recommend: { type: "string" }, background: { type: "string" }, question: { type: "string" }, file: { type: "string", multiple: true } },
|
|
20900
|
+
run({ positionals, values, state, cwd, root, mode }) {
|
|
20899
20901
|
const question = existingQuestion(state, positionals[0]);
|
|
20900
20902
|
const files = listOption(values.file).map((raw) => parseFile(raw, cwd, root));
|
|
20903
|
+
let recommend;
|
|
20904
|
+
if (values.recommend !== void 0) {
|
|
20905
|
+
if (question.answer) throw new UsageError(`${question.id} was already answered, so its recommendation can't change.`);
|
|
20906
|
+
const letters = question.options.map((option) => option.letter);
|
|
20907
|
+
recommend = recommendOption(required2(stringOption(values.recommend), "--recommend (the option you would pick)"), letters);
|
|
20908
|
+
if (recommend === question.recommended) throw new UsageError(`${question.id} already recommends ${recommend}.`);
|
|
20909
|
+
}
|
|
20901
20910
|
const event = {
|
|
20902
20911
|
type: "question.update",
|
|
20903
20912
|
id: question.id,
|
|
20904
20913
|
question: stringOption(values.question),
|
|
20905
20914
|
background: stringOption(values.background),
|
|
20906
|
-
files: files.length ? files : void 0
|
|
20915
|
+
files: files.length ? files : void 0,
|
|
20916
|
+
recommend
|
|
20907
20917
|
};
|
|
20908
|
-
if (!event.question && !event.background && !event.files) throw new UsageError("Nothing to change. Pass --background, --question, or --file.");
|
|
20909
|
-
return { event, message: `Updated ${question.id} (${question.title})` };
|
|
20918
|
+
if (!event.question && !event.background && !event.files && !recommend) throw new UsageError("Nothing to change. Pass --recommend, --background, --question, or --file.");
|
|
20919
|
+
if (!recommend) return { event, message: `Updated ${question.id} (${question.title})` };
|
|
20920
|
+
const rules = QUESTION_MODE_RULES[mode];
|
|
20921
|
+
const next = question.state === "building" ? `Undo the work built on ${question.recommended} and build ${recommend} instead.` : `Mode: ${rules.label}. ${rules.askRule({ recommend, big: Boolean(question.big) })}`;
|
|
20922
|
+
return { event, message: `${question.id} now recommends ${recommend} (was ${question.recommended}). ${next}` };
|
|
20910
20923
|
}
|
|
20911
20924
|
},
|
|
20912
20925
|
"question remove": {
|
package/index.client.tsx
CHANGED
|
@@ -9,7 +9,7 @@ const SETTINGS_ID = "settings";
|
|
|
9
9
|
|
|
10
10
|
export default function contribute(client: PluginClientContext) {
|
|
11
11
|
const stopPills = contributePills(client);
|
|
12
|
-
const panel = progressPanel();
|
|
12
|
+
const panel = progressPanel(client);
|
|
13
13
|
const stopPanel = client.addWorkspacePanel({
|
|
14
14
|
id: panel.id,
|
|
15
15
|
title: "Progress",
|
|
@@ -24,7 +24,6 @@ export default function contribute(client: PluginClientContext) {
|
|
|
24
24
|
icon: "ListChecks",
|
|
25
25
|
keywords: ["dashboard", "tickets", "status", "questions"],
|
|
26
26
|
context: "workspace",
|
|
27
|
-
// Beside the agent chat where Paseo can show both; a tab of its own on phones.
|
|
28
27
|
onSelect({ openPanel }) {
|
|
29
28
|
openPanel(panel.id, { location: panel.location });
|
|
30
29
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stevecastaneda/paseo-progress",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "A live progress dashboard per worktree for Paseo, drawn from a file agents write with the paseo-progress command.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Steve Castaneda",
|
package/server/cli.ts
CHANGED
|
@@ -121,6 +121,14 @@ function existingQuestion(state: State, id: string | undefined) {
|
|
|
121
121
|
return question;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
function recommendOption(recommend: string, letters: string[]): string {
|
|
125
|
+
if (!letters.length) return recommend;
|
|
126
|
+
if (!/^[A-Za-z]$/.test(recommend) || !letters.includes(recommend.toUpperCase())) {
|
|
127
|
+
throw new UsageError(`--recommend must be one of the option letters: ${letters.join(", ")}.`);
|
|
128
|
+
}
|
|
129
|
+
return recommend.toUpperCase();
|
|
130
|
+
}
|
|
131
|
+
|
|
124
132
|
function existingTicket(state: State, id: string | undefined) {
|
|
125
133
|
const wanted = required(id, "ticket id").toUpperCase();
|
|
126
134
|
const ticket = state.dashboard.tickets.find((candidate) => candidate.id.toUpperCase() === wanted);
|
|
@@ -269,13 +277,7 @@ const commands: Record<string, Command> = {
|
|
|
269
277
|
const letters = options.map((option) => option.letter);
|
|
270
278
|
const duplicate = letters.find((letter, index) => letters.indexOf(letter) !== index);
|
|
271
279
|
if (duplicate) throw new UsageError(`Option ${duplicate} is given twice.`);
|
|
272
|
-
|
|
273
|
-
if (options.length) {
|
|
274
|
-
if (!/^[A-Za-z]$/.test(recommend) || !letters.includes(recommend.toUpperCase())) {
|
|
275
|
-
throw new UsageError(`--recommend must be one of the option letters: ${letters.join(", ")}.`);
|
|
276
|
-
}
|
|
277
|
-
recommend = recommend.toUpperCase();
|
|
278
|
-
}
|
|
280
|
+
const recommend = recommendOption(required(stringOption(values.recommend), "--recommend (the option you would pick)"), letters);
|
|
279
281
|
const id = state.nextIds.question;
|
|
280
282
|
const big = values.big === undefined ? undefined : required(stringOption(values.big), "--big reason (why the question is big)");
|
|
281
283
|
const rules = QUESTION_MODE_RULES[mode];
|
|
@@ -323,21 +325,34 @@ const commands: Record<string, Command> = {
|
|
|
323
325
|
},
|
|
324
326
|
},
|
|
325
327
|
"question update": {
|
|
326
|
-
usage: `question update <id> [--background <text>] [--question <text>] [--file <path or http(s) URL>[=<label>] ...]`,
|
|
327
|
-
summary: "Add detail to a question: replace its background or wording, or add files. Its reference
|
|
328
|
-
options: { background: { type: "string" }, question: { type: "string" }, file: { type: "string", multiple: true } },
|
|
329
|
-
run({ positionals, values, state, cwd, root }) {
|
|
328
|
+
usage: `question update <id> [--recommend <letter or word>] [--background <text>] [--question <text>] [--file <path or http(s) URL>[=<label>] ...]`,
|
|
329
|
+
summary: "Add detail to a question: change your recommendation while it's open, replace its background or wording, or add files. Its reference stays.",
|
|
330
|
+
options: { recommend: { type: "string" }, background: { type: "string" }, question: { type: "string" }, file: { type: "string", multiple: true } },
|
|
331
|
+
run({ positionals, values, state, cwd, root, mode }) {
|
|
330
332
|
const question = existingQuestion(state, positionals[0]);
|
|
331
333
|
const files = listOption(values.file).map((raw) => parseFile(raw, cwd, root));
|
|
334
|
+
let recommend: string | undefined;
|
|
335
|
+
if (values.recommend !== undefined) {
|
|
336
|
+
if (question.answer) throw new UsageError(`${question.id} was already answered, so its recommendation can't change.`);
|
|
337
|
+
const letters = question.options.map((option) => option.letter);
|
|
338
|
+
recommend = recommendOption(required(stringOption(values.recommend), "--recommend (the option you would pick)"), letters);
|
|
339
|
+
if (recommend === question.recommended) throw new UsageError(`${question.id} already recommends ${recommend}.`);
|
|
340
|
+
}
|
|
332
341
|
const event = {
|
|
333
342
|
type: "question.update" as const,
|
|
334
343
|
id: question.id,
|
|
335
344
|
question: stringOption(values.question),
|
|
336
345
|
background: stringOption(values.background),
|
|
337
346
|
files: files.length ? files : undefined,
|
|
347
|
+
recommend,
|
|
338
348
|
};
|
|
339
|
-
if (!event.question && !event.background && !event.files) throw new UsageError("Nothing to change. Pass --background, --question, or --file.");
|
|
340
|
-
return { event, message: `Updated ${question.id} (${question.title})` };
|
|
349
|
+
if (!event.question && !event.background && !event.files && !recommend) throw new UsageError("Nothing to change. Pass --recommend, --background, --question, or --file.");
|
|
350
|
+
if (!recommend) return { event, message: `Updated ${question.id} (${question.title})` };
|
|
351
|
+
const rules = QUESTION_MODE_RULES[mode];
|
|
352
|
+
const next = question.state === "building"
|
|
353
|
+
? `Undo the work built on ${question.recommended} and build ${recommend} instead.`
|
|
354
|
+
: `Mode: ${rules.label}. ${rules.askRule({ recommend, big: Boolean(question.big) })}`;
|
|
355
|
+
return { event, message: `${question.id} now recommends ${recommend} (was ${question.recommended}). ${next}` };
|
|
341
356
|
},
|
|
342
357
|
},
|
|
343
358
|
"question remove": {
|
package/shared/dashboard.ts
CHANGED
|
@@ -308,6 +308,7 @@ export function reduceProgress(parsed: { events: ParsedLine[]; issues: FileIssue
|
|
|
308
308
|
if (event.question !== undefined) question.question = event.question;
|
|
309
309
|
if (event.background !== undefined) question.background = event.background;
|
|
310
310
|
if (event.files !== undefined) question.files = [...question.files, ...event.files];
|
|
311
|
+
if (event.recommend !== undefined) question.recommended = event.recommend;
|
|
311
312
|
break;
|
|
312
313
|
}
|
|
313
314
|
case "question.remove":
|
package/shared/events.ts
CHANGED
|
@@ -112,7 +112,6 @@ export const eventSchema = z.discriminatedUnion("type", [
|
|
|
112
112
|
// True when the agent had to undo work built on another option.
|
|
113
113
|
changedCourse: z.boolean().optional(),
|
|
114
114
|
}),
|
|
115
|
-
// Adds detail to an open question; its reference and default stay.
|
|
116
115
|
z.object({
|
|
117
116
|
...base,
|
|
118
117
|
type: z.literal("question.update"),
|
|
@@ -120,6 +119,7 @@ export const eventSchema = z.discriminatedUnion("type", [
|
|
|
120
119
|
question: text.optional(),
|
|
121
120
|
background: text.optional(),
|
|
122
121
|
files: z.array(fileLinkSchema).optional(),
|
|
122
|
+
recommend: text.optional(),
|
|
123
123
|
}),
|
|
124
124
|
z.object({ ...base, type: z.literal("question.remove"), id: text }),
|
|
125
125
|
// `path` is relative to the worktree root, or absolute when outside it.
|
|
@@ -41,7 +41,7 @@ When a decision belongs to the user, ask it with `question ask`, lettered `--opt
|
|
|
41
41
|
The user picks the question mode in Paseo's Settings, and `question ask` prints what that mode means for this question. Do what it prints. Record the user's reply ("Q7 A", or their own words) with `question answer Q7 A --words "<their words>"`, and do what that prints too.
|
|
42
42
|
|
|
43
43
|
- When the question is about how something looks, attach the screenshot or link with `--file`.
|
|
44
|
-
- When the user asks about a question, answer in chat and add the explanation with `question update Q7 --background`.
|
|
44
|
+
- When the user asks about a question, answer in chat and add the explanation with `question update Q7 --background`. If explaining it changes your mind, also pass `--recommend` with the new option, say in `--background` what changed, and do what it prints.
|
|
45
45
|
|
|
46
46
|
## 4. Finish
|
|
47
47
|
|