dsh-output-styles 0.2.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 (44) hide show
  1. package/LICENSE +201 -0
  2. package/README.es.md +197 -0
  3. package/README.ja.md +197 -0
  4. package/README.ko.md +197 -0
  5. package/README.md +197 -0
  6. package/README.zh.md +197 -0
  7. package/cordis.patch.yml +40 -0
  8. package/docs/VERIFICATION.zh.md +93 -0
  9. package/lib/client.js +83 -0
  10. package/lib/index.js +415 -0
  11. package/lib/invariant-LV6hQX5s.js +442 -0
  12. package/lib/invariant.js +2 -0
  13. package/lib/types/client/index.d.ts +30 -0
  14. package/lib/types/client/index.d.ts.map +1 -0
  15. package/lib/types/client/locales.d.ts +13 -0
  16. package/lib/types/client/locales.d.ts.map +1 -0
  17. package/lib/types/config.d.ts +73 -0
  18. package/lib/types/config.d.ts.map +1 -0
  19. package/lib/types/index.d.ts +31 -0
  20. package/lib/types/index.d.ts.map +1 -0
  21. package/lib/types/invariant.d.ts +59 -0
  22. package/lib/types/invariant.d.ts.map +1 -0
  23. package/lib/types/runtime.d.ts +144 -0
  24. package/lib/types/runtime.d.ts.map +1 -0
  25. package/lib/types/style-command.d.ts +68 -0
  26. package/lib/types/style-command.d.ts.map +1 -0
  27. package/lib/types/style-library.d.ts +78 -0
  28. package/lib/types/style-library.d.ts.map +1 -0
  29. package/lib/types/types.d.ts +78 -0
  30. package/lib/types/types.d.ts.map +1 -0
  31. package/package.json +138 -0
  32. package/src/client/index.ts +104 -0
  33. package/src/client/locales.ts +14 -0
  34. package/src/config.ts +110 -0
  35. package/src/index.ts +51 -0
  36. package/src/invariant.ts +144 -0
  37. package/src/runtime.ts +439 -0
  38. package/src/style-command.ts +89 -0
  39. package/src/style-library.ts +348 -0
  40. package/src/types.ts +86 -0
  41. package/styles/concise.md +17 -0
  42. package/styles/explanatory.md +14 -0
  43. package/styles/formal.md +14 -0
  44. package/styles/step-by-step.md +16 -0
@@ -0,0 +1,348 @@
1
+ /**
2
+ * Style-library loading: one style per `*.md` file (frontmatter + body),
3
+ * with optional Claude Code `outputStyles` JSON compatibility (single entry
4
+ * or an array of entries per file).
5
+ *
6
+ * A style file that does not parse is skipped with a warning; the plugin
7
+ * stays loadable. Structural ambiguity — a duplicate style name, a style
8
+ * named `off`, or two styles declaring `force` — fails the load because it
9
+ * would silently change which body gets injected.
10
+ * @module dsh-output-styles/style-library
11
+ */
12
+
13
+ import { readdirSync, readFileSync } from 'node:fs'
14
+ import type { Dirent } from 'node:fs'
15
+ import { join } from 'node:path'
16
+ import { parse as parseYaml } from 'yaml'
17
+ import { OFF } from './types.ts'
18
+
19
+ /** Character set for style names: letters, digits, spaces, and hyphens only. */
20
+ export const STYLE_NAME_RE = /^[\p{L}\p{N} -]+$/u
21
+
22
+ /**
23
+ * Whether a name is a legal style name and switch target: at least one
24
+ * letter or digit, only letters/digits/spaces/hyphens, and no leading or
25
+ * trailing space. Spaces are the only whitespace allowed, so a name is
26
+ * always a single switchable line the model can echo back. `off` passes this
27
+ * check but is a reserved target rejected by the library.
28
+ * @param name - candidate style name.
29
+ * @returns whether the name is legal.
30
+ */
31
+ export function isValidStyleName(name: string): boolean {
32
+ if (name === '' || name !== name.trim()) return false
33
+ if (!STYLE_NAME_RE.test(name)) return false
34
+ return /[\p{L}\p{N}]/u.test(name)
35
+ }
36
+
37
+ /** One loaded style: library metadata plus the raw injectable body. */
38
+ export interface OutputStyle {
39
+ /** Switch target accepted by `/style`; letters, digits, spaces, and hyphens. */
40
+ readonly name: string
41
+ /** One user-facing sentence on what the style does. */
42
+ readonly description: string
43
+ /** Optional guidance on when the style is useful; shown in listings. */
44
+ readonly whenToUse?: string
45
+ /** The raw directive injected into the system prompt, before truncation. */
46
+ readonly body: string
47
+ /** Library file this style came from, relative to the style directory. */
48
+ readonly file: string
49
+ /** Source format (`md` frontmatter or `json` compatibility entry). */
50
+ readonly format: 'md' | 'json'
51
+ /**
52
+ * Keep the harness prompt (identity, persona, tool guidance) when this
53
+ * style is active (Claude Code `keep-coding-instructions`). When false,
54
+ * the style replaces the whole system prompt; default false, matching
55
+ * Claude Code.
56
+ */
57
+ readonly keepCodingInstructions: boolean
58
+ /** Apply this style unconditionally, overriding any session selection. */
59
+ readonly force: boolean
60
+ }
61
+
62
+ /** Report a style file the loader skipped or a tolerated oddity. */
63
+ type Warn = (message: string) => void
64
+
65
+ /**
66
+ * Load the style library from one or more directories. Later directories
67
+ * override earlier ones on a same-named style (the Claude Code
68
+ * "closest-to-the-working-directory wins" rule); duplicates within one
69
+ * directory still fail the load. Deterministic order: directories in the
70
+ * given order, files within a directory sorted by code unit.
71
+ * @param stylesDirs - absolute directories, lowest priority first.
72
+ * @param options.compatJson - whether `*.json` entries are loaded.
73
+ * @param warn - warning sink (skipped files, unknown frontmatter keys).
74
+ * @returns the library keyed by style name, in directory/file order.
75
+ * @throws when a directory is unreadable, a style is named `off`, two files
76
+ * in one directory declare the same name, or two styles declare `force`.
77
+ */
78
+ export function loadStyleLibrary(
79
+ stylesDirs: readonly string[],
80
+ options: { readonly compatJson: boolean },
81
+ warn: Warn,
82
+ ): ReadonlyMap<string, OutputStyle> {
83
+ const styles = new Map<string, OutputStyle>()
84
+ for (const dir of stylesDirs) {
85
+ for (const [name, style] of loadStyleDir(dir, options, warn)) {
86
+ styles.set(name, style) // a later directory overrides an earlier one
87
+ }
88
+ }
89
+ const forced = [...styles.values()].filter(style => style.force)
90
+ if (forced.length > 1) {
91
+ throw new Error(
92
+ `dsh-output-styles: styles ${forced.map(style => style.file).join(' and ')} both declare force; at most one style may be forced`,
93
+ )
94
+ }
95
+ return styles
96
+ }
97
+
98
+ /** Load every style in one directory (duplicates within it fail the load). */
99
+ function loadStyleDir(
100
+ stylesDir: string,
101
+ options: { readonly compatJson: boolean },
102
+ warn: Warn,
103
+ ): ReadonlyMap<string, OutputStyle> {
104
+ let entries: Dirent[]
105
+ try {
106
+ entries = readdirSync(stylesDir, { withFileTypes: true })
107
+ } catch (cause) {
108
+ throw new Error(`dsh-output-styles: style directory ${stylesDir} is unreadable`, { cause })
109
+ }
110
+ entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)
111
+ const styles = new Map<string, OutputStyle>()
112
+ const addStyle = (style: OutputStyle): void => {
113
+ if (style.name === OFF) {
114
+ throw new Error(`dsh-output-styles: style ${style.file} is named "${OFF}", which is reserved for switching output styles off`)
115
+ }
116
+ if (styles.has(style.name)) {
117
+ throw new Error(`dsh-output-styles: duplicate style name "${style.name}" (${styles.get(style.name)?.file} and ${style.file})`)
118
+ }
119
+ styles.set(style.name, style)
120
+ }
121
+ for (const entry of entries) {
122
+ if (!entry.isFile()) continue
123
+ const file = entry.name
124
+ if (file.endsWith('.md')) {
125
+ const parsed = parseMarkdownStyle(file, readStyleFile(stylesDir, file, warn))
126
+ if (parsed.oddity !== undefined) warn(parsed.oddity)
127
+ if (parsed.problem !== undefined) {
128
+ warn(`skipping ${file}: ${parsed.problem}`)
129
+ continue
130
+ }
131
+ if (parsed.style !== undefined) addStyle(parsed.style)
132
+ } else if (file.endsWith('.json')) {
133
+ if (!options.compatJson) {
134
+ warn(`ignoring ${file}: JSON style loading is disabled (compatJson: false)`)
135
+ continue
136
+ }
137
+ for (const style of parseJsonFile(file, readStyleFile(stylesDir, file, warn), warn)) {
138
+ addStyle(style)
139
+ }
140
+ }
141
+ }
142
+ return styles
143
+ }
144
+
145
+ /** Read one library file; an unreadable file is a skipped-file warning. */
146
+ function readStyleFile(stylesDir: string, file: string, warn: Warn): string | undefined {
147
+ try {
148
+ return readFileSync(join(stylesDir, file), 'utf8')
149
+ } catch (cause) {
150
+ warn(`skipping ${file}: unreadable (${cause instanceof Error ? cause.message : String(cause)})`)
151
+ return undefined
152
+ }
153
+ }
154
+
155
+ /** Parse a `---`-fenced frontmatter style file. */
156
+ function parseMarkdownStyle(file: string, source: string | undefined): { style?: OutputStyle; problem?: string; oddity?: string } {
157
+ if (source === undefined) return {}
158
+ const open = /^---[ \t]*\r?\n/.exec(source)
159
+ if (open === null) {
160
+ return { problem: 'missing `---` frontmatter block' }
161
+ }
162
+ const close = /\r?\n---[ \t]*(?:\r?\n|$)/.exec(source.slice(open[0].length))
163
+ if (close === null) {
164
+ return { problem: 'unterminated `---` frontmatter block' }
165
+ }
166
+ const frontmatter = source.slice(open[0].length, open[0].length + close.index)
167
+ const bodyStart = open[0].length + close.index + close[0].length
168
+ const body = source.slice(bodyStart).trim()
169
+ const parsed = parseFrontmatter(file, frontmatter)
170
+ if (parsed.problem !== undefined) return { problem: parsed.problem }
171
+ if (body === '') {
172
+ return { problem: 'style body must be non-empty' }
173
+ }
174
+ const fields = parsed.fields
175
+ if (fields === undefined) return {}
176
+ const style = {
177
+ name: fields.name,
178
+ description: fields.description,
179
+ ...fields.whenToUse === undefined ? {} : { whenToUse: fields.whenToUse },
180
+ body,
181
+ file,
182
+ format: 'md' as const,
183
+ keepCodingInstructions: fields.keepCodingInstructions,
184
+ force: fields.force,
185
+ }
186
+ return parsed.oddity === undefined ? { style } : { style, oddity: parsed.oddity }
187
+ }
188
+
189
+ /** Frontmatter fields shared by the two source formats. */
190
+ interface StyleFields {
191
+ name: string
192
+ description: string
193
+ whenToUse?: string
194
+ keepCodingInstructions: boolean
195
+ force: boolean
196
+ }
197
+
198
+ /** Attach a present oddity without an explicit-undefined optional key. */
199
+ function withOddity<T extends object>(value: T, oddity: string | undefined): T | (T & { oddity: string }) {
200
+ return oddity === undefined ? value : { ...value, oddity }
201
+ }
202
+
203
+ /** The file name a `name`-less style inherits: the file name without its extension. */
204
+ function defaultStyleName(file: string): string {
205
+ return file.slice(0, file.length - '.md'.length)
206
+ }
207
+
208
+ /** Keys both frontmatter formats accept, plus the booleans they share. */
209
+ const FRONTMATTER_KEYS = new Set([
210
+ 'name',
211
+ 'description',
212
+ 'whenToUse',
213
+ 'keep-coding-instructions',
214
+ 'force',
215
+ ])
216
+
217
+ /** Validate one frontmatter block into {@link StyleFields}. */
218
+ function parseFrontmatter(file: string, frontmatter: string): { fields?: StyleFields; problem?: string; oddity?: string } {
219
+ let raw: unknown
220
+ try {
221
+ raw = parseYaml(frontmatter)
222
+ } catch (cause) {
223
+ return { problem: `frontmatter is not valid YAML (${cause instanceof Error ? cause.message : String(cause)})` }
224
+ }
225
+ if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
226
+ return { problem: 'frontmatter must be a mapping of scalar fields' }
227
+ }
228
+ const record = raw as Record<string, unknown>
229
+ const oddityKeys = Object.keys(record).filter(key => !FRONTMATTER_KEYS.has(key))
230
+ const oddity = oddityKeys.length > 0 ? `${file}: ignoring unknown frontmatter field${oddityKeys.length > 1 ? 's' : ''} ${oddityKeys.join(', ')}` : undefined
231
+ const { name, description, whenToUse } = record
232
+ const effectiveName = name === undefined ? defaultStyleName(file) : name
233
+ if (typeof effectiveName !== 'string' || !isValidStyleName(effectiveName)) {
234
+ return withOddity({ problem: 'frontmatter name must be letters, digits, spaces, or hyphens, with at least one letter or digit and no leading/trailing space' }, oddity)
235
+ }
236
+ if (typeof description !== 'string' || description.trim() === '') {
237
+ return withOddity({ problem: 'frontmatter description must be a non-empty string' }, oddity)
238
+ }
239
+ if (whenToUse !== undefined && typeof whenToUse !== 'string') {
240
+ return withOddity({ problem: 'frontmatter whenToUse must be a string when present' }, oddity)
241
+ }
242
+ const booleans = booleanFields(record, 'frontmatter')
243
+ if (booleans.problem !== undefined) return withOddity({ problem: booleans.problem }, oddity)
244
+ return withOddity({
245
+ fields: {
246
+ name: effectiveName,
247
+ description: description.trim(),
248
+ ...whenToUse === undefined ? {} : { whenToUse: whenToUse.trim() },
249
+ keepCodingInstructions: booleans.keepCodingInstructions,
250
+ force: booleans.force,
251
+ },
252
+ }, oddity)
253
+ }
254
+
255
+ /** Read the two shared boolean flags, defaulting each to false. */
256
+ function booleanFields(record: Record<string, unknown>, source: string): { keepCodingInstructions: boolean; force: boolean; problem?: string } {
257
+ const keep = record['keep-coding-instructions']
258
+ if (keep !== undefined && typeof keep !== 'boolean') {
259
+ return { keepCodingInstructions: false, force: false, problem: `${source} keep-coding-instructions must be a boolean when present` }
260
+ }
261
+ const force = record['force']
262
+ if (force !== undefined && typeof force !== 'boolean') {
263
+ return { keepCodingInstructions: false, force: false, problem: `${source} force must be a boolean when present` }
264
+ }
265
+ return { keepCodingInstructions: keep ?? false, force: force ?? false }
266
+ }
267
+
268
+ /**
269
+ * Parse a Claude Code `outputStyles` JSON file: one entry or an array of
270
+ * entries (the legacy `settings.json` collection form). Bad entries are
271
+ * skipped with one warning each; a bad file skips the whole file.
272
+ */
273
+ function parseJsonFile(file: string, source: string | undefined, warn: Warn): OutputStyle[] {
274
+ if (source === undefined) return []
275
+ let raw: unknown
276
+ try {
277
+ raw = JSON.parse(source)
278
+ } catch (cause) {
279
+ warn(`skipping ${file}: invalid JSON (${cause instanceof Error ? cause.message : String(cause)})`)
280
+ return []
281
+ }
282
+ const records = Array.isArray(raw) ? raw : [raw]
283
+ const styles: OutputStyle[] = []
284
+ for (const [index, record] of records.entries()) {
285
+ const label = Array.isArray(raw) ? `${file}#${index + 1}` : file
286
+ const entry = parseJsonEntry(label, record)
287
+ if (entry.oddity !== undefined) warn(entry.oddity)
288
+ if (entry.problem !== undefined) {
289
+ warn(`skipping ${label}: ${entry.problem}`)
290
+ continue
291
+ }
292
+ if (entry.style !== undefined) styles.push(entry.style)
293
+ }
294
+ return styles
295
+ }
296
+
297
+ /** Parse one Claude Code `outputStyles` JSON entry. */
298
+ function parseJsonEntry(label: string, record: unknown): { style?: OutputStyle; problem?: string; oddity?: string } {
299
+ if (record === null || typeof record !== 'object' || Array.isArray(record)) {
300
+ return { problem: 'must be a JSON object' }
301
+ }
302
+ const raw = record as Record<string, unknown>
303
+ const { name, description, prompt, whenToUse } = raw
304
+ if (typeof name !== 'string' || !isValidStyleName(name)) {
305
+ return { problem: 'name must be letters, digits, spaces, or hyphens, with at least one letter or digit and no leading/trailing space' }
306
+ }
307
+ if (typeof description !== 'string' || description.trim() === '') {
308
+ return { problem: 'description must be a non-empty string' }
309
+ }
310
+ if (typeof prompt !== 'string' || prompt.trim() === '') {
311
+ return { problem: 'prompt must be a non-empty string' }
312
+ }
313
+ const oddityKeys = Object.keys(raw).filter(key => !FRONTMATTER_KEYS.has(key) && key !== 'prompt')
314
+ const oddity = oddityKeys.length > 0 ? `${label}: ignoring unknown JSON field${oddityKeys.length > 1 ? 's' : ''} ${oddityKeys.join(', ')}` : undefined
315
+ if (whenToUse !== undefined && typeof whenToUse !== 'string') {
316
+ return withOddity({ problem: 'whenToUse must be a string when present' }, oddity)
317
+ }
318
+ const booleans = booleanFields(raw, 'JSON')
319
+ if (booleans.problem !== undefined) return withOddity({ problem: booleans.problem }, oddity)
320
+ return withOddity({
321
+ style: {
322
+ name,
323
+ description: description.trim(),
324
+ ...whenToUse === undefined ? {} : { whenToUse: whenToUse.trim() },
325
+ body: prompt.trim(),
326
+ file: label,
327
+ format: 'json',
328
+ keepCodingInstructions: booleans.keepCodingInstructions,
329
+ force: booleans.force,
330
+ },
331
+ }, oddity)
332
+ }
333
+
334
+ /**
335
+ * Apply the style-body budget: bodies at most `maxChars` code points pass
336
+ * through; longer bodies are cut at the budget and closed with `marker` (the
337
+ * marker itself is not counted against the budget). The cut is code-point
338
+ * safe, so a multi-unit emoji is never split in half.
339
+ * @param body - the raw style body.
340
+ * @param maxChars - budget in code points; at least 1.
341
+ * @param marker - text appended at the truncation point.
342
+ * @returns the body as it will be injected.
343
+ */
344
+ export function truncateStyle(body: string, maxChars: number, marker: string): string {
345
+ const chars = Array.from(body)
346
+ if (chars.length <= maxChars) return body
347
+ return chars.slice(0, maxChars).join('') + marker
348
+ }
package/src/types.ts ADDED
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Durable domain vocabulary and type-table merges owned by this package.
3
+ *
4
+ * The selection domain is the single source of the per-session style choice;
5
+ * its record schema doubles as the durable validation boundary. The `style`
6
+ * projection key is declared here (its one home) and re-exported from the
7
+ * package root so consumers receive the `SessionProjectionMap` merge.
8
+ * @module dsh-output-styles/types
9
+ */
10
+
11
+ import type { SessionId } from '@deepseek-ai/dsh-session'
12
+ import { z as zod } from 'zod'
13
+ import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain'
14
+
15
+ /** The reserved switch target that removes a session's selection. */
16
+ export const OFF = 'off'
17
+
18
+ /**
19
+ * Provenance marker stored with every selection record. It states who wrote
20
+ * the record, so a session's style choice can be attributed to this plugin
21
+ * when the log is rebuilt or audited.
22
+ */
23
+ export const STYLE_SOURCE = { kind: 'plugin', plugin: 'dsh-output-styles' } as const
24
+
25
+ /** One durable per-session selection record. */
26
+ export const styleSelectionSchema = zod.object({
27
+ /** Selected style name; a name present in the style library at write time. */
28
+ style: zod.string().min(1),
29
+ /** Producer marker; always this plugin's own {@link STYLE_SOURCE}. */
30
+ source: zod.object({
31
+ kind: zod.literal('plugin'),
32
+ plugin: zod.string().min(1),
33
+ }),
34
+ })
35
+
36
+ /** Durable per-session selection record value. */
37
+ export interface StyleSelection extends zod.infer<typeof styleSelectionSchema> {}
38
+
39
+ /**
40
+ * The plugin's storage domain: one `selection` record per session, keyed by
41
+ * the session id. Versioned independently from the session log format.
42
+ */
43
+ export const OUTPUT_STYLE_DOMAIN = defineDomain({
44
+ name: 'output_style',
45
+ version: 1,
46
+ tables: {
47
+ selection: domainTable<SessionId, StyleSelection>(styleSelectionSchema),
48
+ },
49
+ })
50
+
51
+ /** One option a client renders for the `style` projection. */
52
+ export interface StyleOption {
53
+ /** Switch target accepted by `/style`. */
54
+ value: string
55
+ /** Human-readable style name; the style's own name when no label exists. */
56
+ name: string
57
+ /** One user-facing sentence on what the style does. */
58
+ description: string
59
+ /** Optional guidance on when the style is useful; shown in pickers and listings. */
60
+ whenToUse?: string | undefined
61
+ }
62
+
63
+ /** Whole wire value of the `style` session projection. */
64
+ export interface StyleSelectionView {
65
+ /** Every switchable style, in library order. */
66
+ options: StyleOption[]
67
+ /** Current selection, or null when the session has none. */
68
+ currentValue: string | null
69
+ }
70
+
71
+ /** Validates the `style` projection's wire payload before it leaves the host. */
72
+ export const styleSelectionViewSchema = zod.object({
73
+ options: zod.array(zod.object({
74
+ value: zod.string().min(1),
75
+ name: zod.string().min(1),
76
+ description: zod.string().min(1),
77
+ whenToUse: zod.string().min(1).optional(),
78
+ })),
79
+ currentValue: zod.string().min(1).nullable(),
80
+ })
81
+
82
+ declare module '@deepseek-ai/dsh-session-projection/types' {
83
+ interface SessionProjectionMap {
84
+ style: StyleSelectionView
85
+ }
86
+ }
@@ -0,0 +1,17 @@
1
+ ---
2
+ name: concise
3
+ description: Terse, direct answers — minimal prose, no preamble.
4
+ whenToUse: Daily coding work, tool-heavy sessions, or when prompt length matters.
5
+ keep-coding-instructions: true
6
+ ---
7
+
8
+ You are in the concise output style for this conversation.
9
+
10
+ - Lead with the direct answer; skip preamble, restatements, and filler.
11
+ - Prefer short paragraphs and bullets; cut explanations that are obvious from the code.
12
+ - 回答语言跟随用户语言:中文提问用中文回答,英文提问用英文回答。
13
+ - 只陈述结论与必要理由;不确定之处用一句话说明,不要展开。
14
+ - Code and commands over prose when they carry the information.
15
+ - End when the task is done; do not add closing summaries unless asked.
16
+
17
+ 保持简洁:直接给出答案、必要代码与最短说明;不要开场白、客套、重复总结或多余的过渡句。
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: explanatory
3
+ description: Educational answers with short "Insights" that teach as you work.
4
+ whenToUse: Learning a codebase, onboarding, or when you want the reasoning behind each change.
5
+ keep-coding-instructions: true
6
+ ---
7
+
8
+ You are in the explanatory output style for this conversation.
9
+
10
+ - Before or after each significant action, add a short insight that explains the underlying mechanism or design decision in plain language.
11
+ - Frame insights as the why behind the what: why this approach, why this order, why this edge case matters.
12
+ - Keep insights self-contained — a reader skipping them must still get a complete answer.
13
+ - 回答语言跟随用户语言:中文提问用中文回答,英文提问用英文回答。
14
+ - Do not let the insights lengthen the core work; the answer itself stays as terse as it would be without them.
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: formal
3
+ description: Formal, precise prose with complete sentences and defined terms.
4
+ whenToUse: Reports, documentation, release notes, or audiences expecting a formal register.
5
+ keep-coding-instructions: true
6
+ ---
7
+
8
+ You are in the formal output style for this conversation.
9
+
10
+ - Write complete, well-formed sentences; avoid fragments, bullet-style shortcuts, and casual filler.
11
+ - Define terms on first use and prefer precise vocabulary over colloquial phrasing.
12
+ - Structure the response with clear headings and a short conclusion.
13
+ - 回答语言跟随用户语言:中文提问用中文回答,英文提问用英文回答。
14
+ - Stay neutral in tone; state assumptions and limitations explicitly when they affect the conclusion.
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: step-by-step
3
+ description: Numbered reasoning steps with explicit intermediate results.
4
+ whenToUse: Debugging, design decisions, or when the user asks to see the reasoning.
5
+ keep-coding-instructions: true
6
+ ---
7
+
8
+ You are in the step-by-step output style for this conversation.
9
+
10
+ - Break the work into numbered steps; state the goal of each step before executing it.
11
+ - Show intermediate results and how each step feeds the next.
12
+ - 回答语言跟随用户语言:中文提问用中文回答,英文提问用英文回答。
13
+ - When a step fails or an assumption breaks, say so and replan explicitly.
14
+ - Close with a final numbered step summarizing the outcome.
15
+
16
+ 按步骤工作:每个步骤先写目标、再执行、再给出中间结果;假设被推翻或某步失败时显式说明并调整计划;最后一步总结结果。