pi-native-output-styles 0.5.0 → 0.7.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/CHANGELOG.md +26 -0
- package/README.md +28 -3
- package/extensions/output-styles.ts +169 -22
- package/extensions/prompts/output-style-leader.md +82 -50
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Rewrote the leader brief around behaviour instead of rules. It now says what a style may and may not describe, asks for the smallest set of sentences that actually change output, and decides between edit / rewrite / create instead of following a fixed flow. Review stays report-only, and the final report is three fields: what changed, the file path, whether it was verified.
|
|
8
|
+
- The brief is now written in Chinese. It is still plain Markdown in `extensions/prompts/output-style-leader.md`.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Delegation went dormant under the rewrite. Measured over live runs on the same task, the previous brief delegated 4 of 6 times; the permissive phrasing in the rewrite produced 0 of 4, including a run whose request asked for a two-file comparison. Naming a default — "多角度任务(如 review)默认拆开" — restored it: review then split into two children, with report-only and no-child-writes both still holding.
|
|
13
|
+
|
|
14
|
+
## 0.6.0
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- `/output-style config` for the two settings worth persisting. It runs from the user config file, so both apply to every session and project:
|
|
19
|
+
- `config default <style|off>` — the style new sessions start with.
|
|
20
|
+
- `config indicator <status|widget|off>` — where the active style is shown; `status` is the default.
|
|
21
|
+
- Run `config` with no arguments for a dialog per setting. A bare key reads the value back, an unknown key is an error, and a run without a dialog UI prints the config instead.
|
|
22
|
+
- A `widget` indicator renders the style above the editor. Every refresh writes both surfaces, so switching modes can never leave a stale badge.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- `config` joins the management words in the command router. `configure this style` still routes to the agent.
|
|
27
|
+
- Clearing a saved default now merges state instead of overwriting the file, so it no longer drops the indicator setting.
|
|
28
|
+
|
|
3
29
|
## 0.5.0
|
|
4
30
|
|
|
5
31
|
One command, plus a bundled style and an agent that maintains styles.
|
package/README.md
CHANGED
|
@@ -41,7 +41,17 @@ Then start a **new** session. Extensions do not hot-reload.
|
|
|
41
41
|
/output-style 创建一个适合代码 Review 的 output style
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
**Configure it** — `/output-style config`:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
/output-style config interactive: pick the default and the indicator
|
|
48
|
+
/output-style config default caveman set the cross-session default
|
|
49
|
+
/output-style config default off clear it
|
|
50
|
+
/output-style config indicator widget where the active style shows
|
|
51
|
+
/output-style config indicator read one setting back
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The rule is one line: *`config` and a single word that names a style, `off`, or `none` are management; anything else is a request for the agent.* So `/output-style concise` activates, and `/output-style rewrite concise` asks. To review a style whose name you would otherwise activate, say more than its name.
|
|
45
55
|
|
|
46
56
|
While composing the command, a hint line under the editor shows both forms.
|
|
47
57
|
|
|
@@ -61,7 +71,7 @@ Review findings are specific: conflicting instructions, duplicate or unenforceab
|
|
|
61
71
|
|
|
62
72
|
A review reports; it does not edit. The file is written only when the request asks for a change. In observed runs the agent delegated on its own when a task had several independent angles to check, and worked solo on a small single-file review.
|
|
63
73
|
|
|
64
|
-
The brief lives in [`extensions/prompts/output-style-leader.md`](extensions/prompts/output-style-leader.md) and is plain Markdown — edit it without touching code.
|
|
74
|
+
The brief lives in [`extensions/prompts/output-style-leader.md`](extensions/prompts/output-style-leader.md) and is plain Markdown — edit it without touching code. It is written in Chinese; translate or replace it freely.
|
|
65
75
|
|
|
66
76
|
## Bundled styles
|
|
67
77
|
|
|
@@ -97,12 +107,27 @@ Precedence:
|
|
|
97
107
|
|
|
98
108
|
## Config
|
|
99
109
|
|
|
110
|
+
`/output-style config` holds the two things that are worth setting once. Both live in `~/.pi/agent/output-styles.json`, so they apply to every session and every project without re-stating anything.
|
|
111
|
+
|
|
112
|
+
| Key | Values | What it does |
|
|
113
|
+
| --- | --- | --- |
|
|
114
|
+
| `default` | a style name, or `off` | The style every new session starts with. Alias: `style`. |
|
|
115
|
+
| `indicator` | `status`, `widget`, `off` | Where the active style shows. Default `status`. |
|
|
116
|
+
|
|
117
|
+
A bare key reads the value back. An unknown key is an error, not a silent no-op. Running `config` with no arguments opens a dialog per setting; in a non-dialog run (print or JSON mode) it prints the current config instead.
|
|
118
|
+
|
|
119
|
+
The indicator is a display preference, so like the default style the personal setting wins over a project one, and a project setting applies when you have none.
|
|
120
|
+
|
|
121
|
+
To set a default without opening dialogs, `/output-style <name> --save` does the same thing.
|
|
122
|
+
|
|
123
|
+
## File locations
|
|
124
|
+
|
|
100
125
|
| | Path |
|
|
101
126
|
| --- | --- |
|
|
102
127
|
| Project styles | `<repo>/.pi/output-styles/` |
|
|
103
128
|
| User styles | `~/.pi/agent/output-styles/` |
|
|
104
129
|
| Project default (`--project`) | `<repo>/.pi/output-styles.json` |
|
|
105
|
-
| User default (`--save`) | `~/.pi/agent/output-styles.json` |
|
|
130
|
+
| User default + config (`--save`, `config`) | `~/.pi/agent/output-styles.json` |
|
|
106
131
|
|
|
107
132
|
Environment:
|
|
108
133
|
|
|
@@ -35,6 +35,7 @@ interface ExtensionUI {
|
|
|
35
35
|
setWidget(key: string, lines: string[] | undefined, options?: { placement: "aboveEditor" | "belowEditor" }): void;
|
|
36
36
|
getEditorText(): string;
|
|
37
37
|
notify(message: string, type?: NotifyType): void;
|
|
38
|
+
select?(title: string, options: string[]): Promise<string | undefined>;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
interface ExtensionContext {
|
|
@@ -143,13 +144,25 @@ export function bundledStylesDir(): string {
|
|
|
143
144
|
|
|
144
145
|
export interface StyleState {
|
|
145
146
|
active?: string;
|
|
147
|
+
indicator?: IndicatorMode;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Where the active style is shown. `status` is the default. */
|
|
151
|
+
export const INDICATOR_MODES = ["status", "widget", "off"] as const;
|
|
152
|
+
export type IndicatorMode = (typeof INDICATOR_MODES)[number];
|
|
153
|
+
|
|
154
|
+
export function isIndicatorMode(value: unknown): value is IndicatorMode {
|
|
155
|
+
return typeof value === "string" && (INDICATOR_MODES as readonly string[]).includes(value);
|
|
146
156
|
}
|
|
147
157
|
|
|
148
158
|
export function readState(file: string): StyleState {
|
|
149
159
|
try {
|
|
150
160
|
const parsed: unknown = JSON.parse(readFileSync(file, "utf8"));
|
|
151
|
-
if (parsed && typeof parsed === "object"
|
|
152
|
-
|
|
161
|
+
if (parsed && typeof parsed === "object") {
|
|
162
|
+
const state: StyleState = {};
|
|
163
|
+
if ("active" in parsed && typeof parsed.active === "string") state.active = parsed.active;
|
|
164
|
+
if ("indicator" in parsed && isIndicatorMode(parsed.indicator)) state.indicator = parsed.indicator;
|
|
165
|
+
return state;
|
|
153
166
|
}
|
|
154
167
|
} catch {
|
|
155
168
|
// missing or malformed → empty
|
|
@@ -162,6 +175,13 @@ export function writeState(file: string, state: StyleState): void {
|
|
|
162
175
|
writeFileSync(file, JSON.stringify(state, null, 2) + "\n");
|
|
163
176
|
}
|
|
164
177
|
|
|
178
|
+
// Merge, never replace: clearing the default must not drop the indicator
|
|
179
|
+
// setting, and setting the indicator must not drop the default. A key passed
|
|
180
|
+
// as undefined drops out of the JSON, which is how a key is cleared.
|
|
181
|
+
export function updateState(file: string, patch: StyleState): void {
|
|
182
|
+
writeState(file, { ...readState(file), ...patch });
|
|
183
|
+
}
|
|
184
|
+
|
|
165
185
|
export function userStateFile(): string {
|
|
166
186
|
return join(configHome(), "output-styles.json");
|
|
167
187
|
}
|
|
@@ -179,6 +199,12 @@ export function resolveActiveName(
|
|
|
179
199
|
return sessionActive ?? userState.active ?? projectState.active ?? null;
|
|
180
200
|
}
|
|
181
201
|
|
|
202
|
+
// The indicator is presentation, so it resolves from the same places as the
|
|
203
|
+
// default style but falls back to the status bar.
|
|
204
|
+
export function resolveIndicator(cwd: string): IndicatorMode {
|
|
205
|
+
return readState(userStateFile()).indicator ?? readState(projectStateFile(cwd)).indicator ?? "status";
|
|
206
|
+
}
|
|
207
|
+
|
|
182
208
|
const MARKER_PREFIX = "<!-- output-styles:";
|
|
183
209
|
|
|
184
210
|
export function styleMarker(style: Style): string {
|
|
@@ -224,16 +250,20 @@ export function parseStyleCommandArgs(args: string): StyleCommandArgs {
|
|
|
224
250
|
return { name, persist };
|
|
225
251
|
}
|
|
226
252
|
|
|
227
|
-
// One command serves
|
|
228
|
-
// words alone. Rule: the request is
|
|
229
|
-
//
|
|
230
|
-
// else is a task for the agent. `/output-style
|
|
231
|
-
// rewrite concise` asks the agent.
|
|
232
|
-
export type StyleCommandRoute =
|
|
253
|
+
// One command serves every job, so the split has to be guessable from the
|
|
254
|
+
// words alone. Rule, in order: the request is `config` when it starts with the
|
|
255
|
+
// config word; empty, `off`, `none`, or a single word naming an existing style
|
|
256
|
+
// is style management; anything else is a task for the agent. `/output-style
|
|
257
|
+
// concise` activates; `/output-style rewrite concise` asks the agent.
|
|
258
|
+
export type StyleCommandRoute =
|
|
259
|
+
| { kind: "config"; args: string }
|
|
260
|
+
| { kind: "manage" }
|
|
261
|
+
| { kind: "task"; request: string };
|
|
233
262
|
|
|
234
263
|
export function routeStyleCommand(args: string, styleNames: Iterable<string>): StyleCommandRoute {
|
|
235
264
|
const request = args.trim();
|
|
236
265
|
if (request.length === 0) return { kind: "manage" };
|
|
266
|
+
if (/^config(?=\s|$)/i.test(request)) return { kind: "config", args: request.replace(/^config\s*/i, "").trim() };
|
|
237
267
|
const words = request.split(/\s+/).filter(t => t.length > 0 && !t.startsWith("--"));
|
|
238
268
|
if (words.length === 0) return { kind: "manage" }; // flags only
|
|
239
269
|
if (words.length > 1) return { kind: "task", request };
|
|
@@ -251,13 +281,14 @@ export function resolveStyleName(name: string, styleNames: Iterable<string>): st
|
|
|
251
281
|
}
|
|
252
282
|
|
|
253
283
|
const STATUS_KEY = "output-styles";
|
|
284
|
+
const INDICATOR_KEY = "output-styles-indicator";
|
|
254
285
|
const HINT_KEY = "output-styles-hint";
|
|
255
286
|
// Persistent ghost hint shown below the editor while `/output-style` is being
|
|
256
287
|
// composed. Pi only renders inline usage ghost text for builtin commands, so
|
|
257
288
|
// this widget carries the same message for extension commands.
|
|
258
289
|
const STYLE_HINT_LINES = [
|
|
259
290
|
"/output-style <name|off> [--save] [--project]",
|
|
260
|
-
"/output-style
|
|
291
|
+
"/output-style config — default style, indicator",
|
|
261
292
|
];
|
|
262
293
|
|
|
263
294
|
// Pure matcher for the widget: show the hint while the input starts with the
|
|
@@ -360,9 +391,19 @@ export function resolveActiveStyle(cwd: string, styles?: Map<string, Style>): St
|
|
|
360
391
|
return map.get(name) ?? null;
|
|
361
392
|
}
|
|
362
393
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
394
|
+
// One renderer for every call site, so switching the mode can never leave a
|
|
395
|
+
// stale badge behind: both surfaces are written on every refresh, and only the
|
|
396
|
+
// configured one gets text.
|
|
397
|
+
function renderIndicator(ctx: ExtensionContext, style: Style | null): void {
|
|
398
|
+
if (!ctx.hasUI) return;
|
|
399
|
+
const mode = resolveIndicator(ctx.cwd);
|
|
400
|
+
const label = style ? `style: ${style.name}` : undefined;
|
|
401
|
+
if (typeof ctx.ui.setStatus === "function") {
|
|
402
|
+
ctx.ui.setStatus(STATUS_KEY, mode === "status" ? label : undefined);
|
|
403
|
+
}
|
|
404
|
+
if (typeof ctx.ui.setWidget === "function") {
|
|
405
|
+
ctx.ui.setWidget(INDICATOR_KEY, mode === "widget" && label ? [label] : undefined, { placement: "aboveEditor" });
|
|
406
|
+
}
|
|
366
407
|
}
|
|
367
408
|
|
|
368
409
|
// The Leader brief is plain Markdown next to the extension, so its wording can
|
|
@@ -396,9 +437,104 @@ export function buildStyleTask(brief: string, cwd: string, active: Style | null,
|
|
|
396
437
|
].join("\n");
|
|
397
438
|
}
|
|
398
439
|
|
|
440
|
+
export type ConfigKey = "default" | "indicator";
|
|
441
|
+
|
|
442
|
+
export interface ConfigArgs {
|
|
443
|
+
/** null means "no key given" — open the interactive flow. */
|
|
444
|
+
key: ConfigKey | null;
|
|
445
|
+
value: string;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const CONFIG_KEY_ALIASES: Record<string, ConfigKey> = {
|
|
449
|
+
default: "default",
|
|
450
|
+
style: "default",
|
|
451
|
+
indicator: "indicator",
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
// `null` means an unrecognised key; `{key: null}` means no key at all.
|
|
455
|
+
export function parseConfigArgs(args: string): ConfigArgs | null {
|
|
456
|
+
const tokens = args.trim().split(/\s+/).filter(t => t.length > 0);
|
|
457
|
+
if (tokens.length === 0) return { key: null, value: "" };
|
|
458
|
+
const key = CONFIG_KEY_ALIASES[tokens[0].toLowerCase()];
|
|
459
|
+
if (!key) return null;
|
|
460
|
+
return { key, value: tokens.slice(1).join(" ") };
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function describeConfig(cwd: string): string {
|
|
464
|
+
return `Default style (new sessions): ${readState(userStateFile()).active ?? "(none)"}\nStyle indicator: ${resolveIndicator(cwd)}`;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function applyConfigValue(key: ConfigKey, value: string, ctx: ExtensionContext, styles: Map<string, Style>): void {
|
|
468
|
+
const raw = value.trim();
|
|
469
|
+
|
|
470
|
+
if (key === "default") {
|
|
471
|
+
if (raw.length === 0) {
|
|
472
|
+
ctx.ui.notify(`Default style (new sessions): ${readState(userStateFile()).active ?? "(none)"}`, "info");
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if (OFF_WORDS[raw.toLowerCase()]) {
|
|
476
|
+
updateState(userStateFile(), { active: undefined });
|
|
477
|
+
ctx.ui.notify("Default style cleared. New sessions start with no style.", "info");
|
|
478
|
+
renderIndicator(ctx, resolveActiveStyle(ctx.cwd, styles));
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const name = resolveStyleName(raw, styles.keys());
|
|
482
|
+
if (!name) {
|
|
483
|
+
ctx.ui.notify(`Unknown style "${raw}". Available: ${[...styles.keys()].sort().join(", ") || "(none)"}`, "error");
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
updateState(userStateFile(), { active: name });
|
|
487
|
+
ctx.ui.notify(`Default style → "${name}" for every new session and project.`, "info");
|
|
488
|
+
renderIndicator(ctx, resolveActiveStyle(ctx.cwd, styles));
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if (raw.length === 0) {
|
|
493
|
+
ctx.ui.notify(`Style indicator: ${resolveIndicator(ctx.cwd)}`, "info");
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
if (!isIndicatorMode(raw)) {
|
|
497
|
+
ctx.ui.notify(`Indicator must be one of: ${INDICATOR_MODES.join(", ")}`, "error");
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
updateState(userStateFile(), { indicator: raw });
|
|
501
|
+
ctx.ui.notify(`Style indicator → ${raw}`, "info");
|
|
502
|
+
renderIndicator(ctx, resolveActiveStyle(ctx.cwd, styles));
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Interactive path: two dialogs, then a summary. Falls back to printing when
|
|
506
|
+
// the run has no dialog-capable UI (print/json modes).
|
|
507
|
+
async function openConfigDialogs(ctx: ExtensionContext, styles: Map<string, Style>): Promise<void> {
|
|
508
|
+
if (typeof ctx.ui.select !== "function") {
|
|
509
|
+
ctx.ui.notify(describeConfig(ctx.cwd), "info");
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const current = readState(userStateFile()).active ?? "(none)";
|
|
514
|
+
const chosen = await ctx.ui.select(`Default output style for new sessions (now: ${current})`, [
|
|
515
|
+
"(keep current)",
|
|
516
|
+
"(none)",
|
|
517
|
+
...[...styles.keys()].sort(),
|
|
518
|
+
]);
|
|
519
|
+
if (chosen !== undefined && chosen !== "(keep current)") {
|
|
520
|
+
updateState(userStateFile(), { active: chosen === "(none)" ? undefined : chosen });
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const mode = await ctx.ui.select(`Style indicator (now: ${resolveIndicator(ctx.cwd)})`, [
|
|
524
|
+
"(keep current)",
|
|
525
|
+
...INDICATOR_MODES,
|
|
526
|
+
]);
|
|
527
|
+
if (mode !== undefined && mode !== "(keep current)" && isIndicatorMode(mode)) {
|
|
528
|
+
updateState(userStateFile(), { indicator: mode });
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
renderIndicator(ctx, resolveActiveStyle(ctx.cwd, styles));
|
|
532
|
+
ctx.ui.notify(`Saved.\n${describeConfig(ctx.cwd)}`, "info");
|
|
533
|
+
}
|
|
534
|
+
|
|
399
535
|
export default function outputStyles(pi: ExtensionAPI): void {
|
|
400
536
|
pi.on("session_start", (_event, ctx) => {
|
|
401
|
-
|
|
537
|
+
renderIndicator(ctx, resolveActiveStyle(ctx.cwd));
|
|
402
538
|
if (started || !ctx.hasUI) return;
|
|
403
539
|
started = true;
|
|
404
540
|
startHintPoller(ctx);
|
|
@@ -412,14 +548,14 @@ export default function outputStyles(pi: ExtensionAPI): void {
|
|
|
412
548
|
try {
|
|
413
549
|
const style = resolveActiveStyle(ctx.cwd);
|
|
414
550
|
if (!style) {
|
|
415
|
-
|
|
551
|
+
renderIndicator(ctx, null);
|
|
416
552
|
return;
|
|
417
553
|
}
|
|
418
554
|
// Apply first; only reflect the style in the status line once the prompt
|
|
419
555
|
// was actually augmented, so a swallowed throw never advertises a style
|
|
420
556
|
// the turn did not apply.
|
|
421
557
|
const systemPrompt = applyStyle(event.systemPrompt ?? "", style);
|
|
422
|
-
|
|
558
|
+
renderIndicator(ctx, style);
|
|
423
559
|
return { systemPrompt };
|
|
424
560
|
} catch {
|
|
425
561
|
return; // never fail a turn over a styling concern
|
|
@@ -430,10 +566,21 @@ export default function outputStyles(pi: ExtensionAPI): void {
|
|
|
430
566
|
description:
|
|
431
567
|
"Select an output style, or ask the agent to review, rewrite, or create one. Usage: /output-style <name|off|what you want> [--save] [--project]",
|
|
432
568
|
getArgumentCompletions: argumentPrefix => styleCompletions(argumentPrefix, process.cwd()),
|
|
433
|
-
handler: (args, ctx) => {
|
|
569
|
+
handler: async (args, ctx) => {
|
|
434
570
|
const styles = discoverStyles(styleDirs(ctx.cwd));
|
|
435
571
|
const route = routeStyleCommand(args, styles.keys());
|
|
436
572
|
|
|
573
|
+
if (route.kind === "config") {
|
|
574
|
+
const parsed = parseConfigArgs(route.args);
|
|
575
|
+
if (parsed === null) {
|
|
576
|
+
ctx.ui.notify("Config keys: default <style|off>, indicator <status|widget|off>", "error");
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (parsed.key === null) await openConfigDialogs(ctx, styles);
|
|
580
|
+
else applyConfigValue(parsed.key, parsed.value, ctx, styles);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
|
|
437
584
|
if (route.kind === "task") {
|
|
438
585
|
let task: string;
|
|
439
586
|
try {
|
|
@@ -478,16 +625,16 @@ export default function outputStyles(pi: ExtensionAPI): void {
|
|
|
478
625
|
let offScope = "this session";
|
|
479
626
|
try {
|
|
480
627
|
if (persist === "user") {
|
|
481
|
-
|
|
628
|
+
updateState(userStateFile(), { active: undefined });
|
|
482
629
|
offScope = "cleared · user default";
|
|
483
630
|
} else if (persist === "project") {
|
|
484
|
-
|
|
631
|
+
updateState(projectStateFile(ctx.cwd), { active: undefined });
|
|
485
632
|
offScope = "cleared · project default";
|
|
486
633
|
}
|
|
487
634
|
} catch (err) {
|
|
488
635
|
ctx.ui.notify(`Cleared for this session, but updating the saved default failed: ${String(err)}`, "warning");
|
|
489
636
|
}
|
|
490
|
-
|
|
637
|
+
renderIndicator(ctx, null);
|
|
491
638
|
ctx.ui.notify(`Output style off (${offScope}).`, "info");
|
|
492
639
|
return;
|
|
493
640
|
}
|
|
@@ -500,16 +647,16 @@ export default function outputStyles(pi: ExtensionAPI): void {
|
|
|
500
647
|
let scope = "this session";
|
|
501
648
|
try {
|
|
502
649
|
if (persist === "user") {
|
|
503
|
-
|
|
650
|
+
updateState(userStateFile(), { active: name });
|
|
504
651
|
scope = "saved · user default";
|
|
505
652
|
} else if (persist === "project") {
|
|
506
|
-
|
|
653
|
+
updateState(projectStateFile(ctx.cwd), { active: name });
|
|
507
654
|
scope = "saved · project default";
|
|
508
655
|
}
|
|
509
656
|
} catch (err) {
|
|
510
657
|
ctx.ui.notify(`Applied for this session, but saving failed: ${String(err)}`, "warning");
|
|
511
658
|
}
|
|
512
|
-
|
|
659
|
+
renderIndicator(ctx, styles.get(name) ?? null);
|
|
513
660
|
ctx.ui.notify(`Output style → "${name}" (${scope}).`, "info");
|
|
514
661
|
},
|
|
515
662
|
});
|
|
@@ -1,71 +1,103 @@
|
|
|
1
1
|
# Output style task
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
then verify what you produced. Finish with a short report of what changed and
|
|
5
|
-
where.
|
|
3
|
+
你负责完成 output style 的修改、新建或重写。
|
|
6
4
|
|
|
7
|
-
##
|
|
5
|
+
## 目标
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
interaction style. It is not a home for project architecture, coding rules,
|
|
11
|
-
domain knowledge, tooling conventions, or repo facts. Anything like that is a
|
|
12
|
-
responsibility-boundary bug: drop it, or say where it belongs instead.
|
|
7
|
+
先理解用户真正想改变的**输出行为**,再决定怎么改。
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
Output style 只描述:
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
in one workflow call. If it is not available, do the analysis yourself.
|
|
21
|
-
Children report findings back to you and never write files. You do the final
|
|
22
|
-
write and the final summary — never hand the file off.
|
|
11
|
+
* 怎么说
|
|
12
|
+
* 怎么组织答案
|
|
13
|
+
* 信息详细到什么程度
|
|
14
|
+
* 如何与用户互动
|
|
23
15
|
|
|
24
|
-
|
|
16
|
+
不要把项目架构、编码规范、领域知识、工具使用规则、仓库事实等塞进 style。
|
|
25
17
|
|
|
26
|
-
|
|
27
|
-
asks for the change; offer the fix instead.
|
|
18
|
+
## 工作方式
|
|
28
19
|
|
|
29
|
-
|
|
30
|
-
Look for:
|
|
20
|
+
先检查:
|
|
31
21
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- content that belongs to another concern
|
|
37
|
-
- AI-slop voice: inflated claims, filler, ceremony
|
|
38
|
-
- rules that are hard to follow while actually working
|
|
22
|
+
* 用户当前使用的 style
|
|
23
|
+
* 相关已有 styles
|
|
24
|
+
* 用户描述的实际问题
|
|
25
|
+
* 必要时查看相关文档、实现或进行 research
|
|
39
26
|
|
|
40
|
-
|
|
27
|
+
不要为了“完整”而 research。只有现有信息不足以判断正确做法时才 research。
|
|
41
28
|
|
|
42
|
-
|
|
29
|
+
然后决定是:
|
|
43
30
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
31
|
+
* **修改**:保留有效部分,删除无效或造成问题的部分
|
|
32
|
+
* **重写**:当现有 style 的方向已经不对时重新设计
|
|
33
|
+
* **新建**:从目标出发写一个最小可用的 style
|
|
47
34
|
|
|
48
|
-
|
|
35
|
+
每次先判断需不需要多方视角:需要就并行分给子 agent(有 `subagent` 工具就用),review 这类要多角度查的任务默认拆开;不需要就自己做。子 agent 只报告不写文件,最终文件由你自己写。
|
|
49
36
|
|
|
50
|
-
|
|
51
|
-
kebab-case; `description` is one line and shows up in `/style`.
|
|
37
|
+
核心原则:
|
|
52
38
|
|
|
53
|
-
|
|
39
|
+
> 少写规则,直接描述期望的行为。
|
|
54
40
|
|
|
55
|
-
|
|
56
|
-
name: <kebab-case>
|
|
57
|
-
description: <one line>
|
|
58
|
-
---
|
|
59
|
-
<style body>
|
|
41
|
+
避免:
|
|
60
42
|
|
|
61
|
-
|
|
43
|
+
* 提示词腔
|
|
44
|
+
* AI 味
|
|
45
|
+
* 解释规则为什么存在
|
|
46
|
+
* 重复表达
|
|
47
|
+
* 过度约束
|
|
48
|
+
* 模糊但听起来正确的话
|
|
49
|
+
* 为了显得专业而增加细节
|
|
62
50
|
|
|
63
|
-
|
|
64
|
-
for personal or global. Never edit a style under a package install path — those
|
|
65
|
-
are read-only bundled styles; copy one out instead.
|
|
51
|
+
如果一句话不能明显改变模型的输出,就不要写。
|
|
66
52
|
|
|
67
|
-
##
|
|
53
|
+
## Review
|
|
68
54
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
55
|
+
如果用户要求审核,只报告问题,不直接修改。
|
|
56
|
+
|
|
57
|
+
重点找:
|
|
58
|
+
|
|
59
|
+
* 会导致输出变差的规则
|
|
60
|
+
* 相互冲突的规则
|
|
61
|
+
* 重复或无效规则
|
|
62
|
+
* 难以执行的规则
|
|
63
|
+
* 不属于 output style 的内容
|
|
64
|
+
* 让回答变得啰嗦、机械、难读的规则
|
|
65
|
+
|
|
66
|
+
每个问题说明:
|
|
67
|
+
|
|
68
|
+
**问题 → 影响 → 建议**
|
|
69
|
+
|
|
70
|
+
不要写赞美,也不要重新解释整个 style。
|
|
71
|
+
|
|
72
|
+
## Style 格式
|
|
73
|
+
|
|
74
|
+
```md
|
|
75
|
+
---
|
|
76
|
+
name: <lowercase-kebab-case>
|
|
77
|
+
description: <one line>
|
|
78
|
+
---
|
|
79
|
+
<style body>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
description 用一句话说明这个 style 会带来什么输出变化。
|
|
83
|
+
|
|
84
|
+
style body 应尽可能短。
|
|
85
|
+
|
|
86
|
+
## 完成前
|
|
87
|
+
|
|
88
|
+
重新阅读最终文件,从实际使用角度判断:
|
|
89
|
+
|
|
90
|
+
> 如果模型只看到这个 style,它真的会因此产生不同的、更符合目标的输出吗?
|
|
91
|
+
|
|
92
|
+
确认:
|
|
93
|
+
|
|
94
|
+
* frontmatter 正确
|
|
95
|
+
* name 与文件名一致
|
|
96
|
+
* style 可以正常加载
|
|
97
|
+
* 内容没有明显重复、冲突或越界
|
|
98
|
+
|
|
99
|
+
最后只报告:
|
|
100
|
+
|
|
101
|
+
* 做了什么
|
|
102
|
+
* 文件路径
|
|
103
|
+
* 是否完成验证
|