@proletariat/cli 0.3.34 → 0.3.36

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 (198) hide show
  1. package/dist/commands/agent/auth.d.ts +15 -3
  2. package/dist/commands/agent/auth.js +136 -15
  3. package/dist/commands/agent/index.js +11 -2
  4. package/dist/commands/agent/list.js +16 -7
  5. package/dist/commands/agent/staff/add.d.ts +1 -0
  6. package/dist/commands/agent/staff/add.js +1 -0
  7. package/dist/commands/agent/staff/index.d.ts +15 -0
  8. package/dist/commands/agent/staff/index.js +83 -0
  9. package/dist/commands/agent/staff/list.d.ts +1 -0
  10. package/dist/commands/agent/staff/list.js +1 -0
  11. package/dist/commands/agent/staff/remove.d.ts +1 -0
  12. package/dist/commands/agent/staff/remove.js +1 -0
  13. package/dist/commands/agent/status.js +32 -4
  14. package/dist/commands/agent/themes/add-names.d.ts +1 -0
  15. package/dist/commands/agent/themes/add-names.js +1 -0
  16. package/dist/commands/agent/themes/create.d.ts +1 -0
  17. package/dist/commands/agent/themes/create.js +1 -0
  18. package/dist/commands/agent/themes/index.d.ts +10 -0
  19. package/dist/commands/agent/themes/index.js +144 -0
  20. package/dist/commands/agent/themes/list.d.ts +1 -0
  21. package/dist/commands/agent/themes/list.js +1 -0
  22. package/dist/commands/agent/themes/set.d.ts +1 -0
  23. package/dist/commands/agent/themes/set.js +1 -0
  24. package/dist/commands/agents/themes/add-names.d.ts +1 -0
  25. package/dist/commands/agents/themes/add-names.js +1 -0
  26. package/dist/commands/agents/themes/create.d.ts +1 -0
  27. package/dist/commands/agents/themes/create.js +1 -0
  28. package/dist/commands/agents/themes/list.d.ts +1 -0
  29. package/dist/commands/agents/themes/list.js +1 -0
  30. package/dist/commands/board/watch.js +6 -0
  31. package/dist/commands/branch/list.d.ts +1 -0
  32. package/dist/commands/branch/list.js +43 -12
  33. package/dist/commands/branch/where.js +3 -2
  34. package/dist/commands/category/list.d.ts +2 -1
  35. package/dist/commands/category/list.js +38 -13
  36. package/dist/commands/{claude.d.ts → claude/index.d.ts} +1 -1
  37. package/dist/commands/{claude.js → claude/index.js} +12 -12
  38. package/dist/commands/claude/open.d.ts +13 -0
  39. package/dist/commands/claude/open.js +175 -0
  40. package/dist/commands/diet.js +18 -2
  41. package/dist/commands/docker/logs.js +7 -3
  42. package/dist/commands/docker/shell.js +6 -0
  43. package/dist/commands/docker/start.js +20 -4
  44. package/dist/commands/docker/sync.d.ts +4 -0
  45. package/dist/commands/docker/sync.js +30 -2
  46. package/dist/commands/epic/show.d.ts +13 -0
  47. package/dist/commands/epic/show.js +16 -0
  48. package/dist/commands/epic/view.js +27 -0
  49. package/dist/commands/execution/config.d.ts +0 -4
  50. package/dist/commands/execution/config.js +10 -32
  51. package/dist/commands/execution/index.js +2 -1
  52. package/dist/commands/execution/logs.js +1 -1
  53. package/dist/commands/execution/stop.js +2 -1
  54. package/dist/commands/execution/view.js +22 -26
  55. package/dist/commands/init.js +2 -19
  56. package/dist/commands/label/create.d.ts +20 -0
  57. package/dist/commands/label/create.js +57 -0
  58. package/dist/commands/label/delete.d.ts +17 -0
  59. package/dist/commands/label/delete.js +32 -0
  60. package/dist/commands/label/group/create.d.ts +20 -0
  61. package/dist/commands/label/group/create.js +55 -0
  62. package/dist/commands/label/group/list.d.ts +14 -0
  63. package/dist/commands/label/group/list.js +52 -0
  64. package/dist/commands/label/index.d.ts +15 -0
  65. package/dist/commands/label/index.js +58 -0
  66. package/dist/commands/label/list.d.ts +16 -0
  67. package/dist/commands/label/list.js +83 -0
  68. package/dist/commands/link/list.js +3 -2
  69. package/dist/commands/mcp-server.js +27 -1
  70. package/dist/commands/phase/template/apply.d.ts +26 -0
  71. package/dist/commands/phase/template/apply.js +14 -0
  72. package/dist/commands/phase/template/create.d.ts +23 -0
  73. package/dist/commands/phase/template/create.js +14 -0
  74. package/dist/commands/phase/template/delete.d.ts +18 -0
  75. package/dist/commands/phase/template/delete.js +61 -0
  76. package/dist/commands/phase/template/list.d.ts +17 -0
  77. package/dist/commands/phase/template/list.js +89 -0
  78. package/dist/commands/phase/template/update.d.ts +1 -0
  79. package/dist/commands/phase/template/update.js +1 -0
  80. package/dist/commands/priority/add.js +1 -1
  81. package/dist/commands/project/create.js +3 -4
  82. package/dist/commands/project/update.js +5 -8
  83. package/dist/commands/pull.js +24 -0
  84. package/dist/commands/roadmap/generate.js +1 -2
  85. package/dist/commands/session/create.d.ts +19 -0
  86. package/dist/commands/session/create.js +102 -0
  87. package/dist/commands/session/health.js +2 -21
  88. package/dist/commands/session/index.js +14 -1
  89. package/dist/commands/session/list.js +26 -7
  90. package/dist/commands/session/peek.d.ts +38 -0
  91. package/dist/commands/session/peek.js +316 -0
  92. package/dist/commands/session/poke.d.ts +27 -0
  93. package/dist/commands/session/poke.js +219 -0
  94. package/dist/commands/spec/link/depends.d.ts +18 -0
  95. package/dist/commands/spec/link/depends.js +86 -0
  96. package/dist/commands/spec/link/index.d.ts +17 -0
  97. package/dist/commands/spec/link/index.js +92 -0
  98. package/dist/commands/spec/link/remove.d.ts +18 -0
  99. package/dist/commands/spec/link/remove.js +90 -0
  100. package/dist/commands/spec/view.js +29 -0
  101. package/dist/commands/support/logs.js +2 -2
  102. package/dist/commands/template/apply.js +5 -4
  103. package/dist/commands/template/create.js +1 -1
  104. package/dist/commands/template/list.js +2 -1
  105. package/dist/commands/theme/add-names.d.ts +4 -0
  106. package/dist/commands/theme/add-names.js +11 -1
  107. package/dist/commands/theme/create.d.ts +2 -0
  108. package/dist/commands/theme/create.js +8 -0
  109. package/dist/commands/ticket/bulk.js +2 -2
  110. package/dist/commands/ticket/complete.js +2 -2
  111. package/dist/commands/ticket/create.js +21 -0
  112. package/dist/commands/ticket/delete.js +8 -0
  113. package/dist/commands/ticket/edit.js +25 -0
  114. package/dist/commands/ticket/index.js +2 -2
  115. package/dist/commands/ticket/link/block.d.ts +15 -0
  116. package/dist/commands/ticket/link/block.js +95 -0
  117. package/dist/commands/ticket/link/index.d.ts +14 -0
  118. package/dist/commands/ticket/link/index.js +96 -0
  119. package/dist/commands/ticket/list.d.ts +1 -0
  120. package/dist/commands/ticket/list.js +6 -0
  121. package/dist/commands/ticket/move.js +25 -2
  122. package/dist/commands/ticket/resolve.js +4 -5
  123. package/dist/commands/ticket/show.d.ts +13 -0
  124. package/dist/commands/ticket/show.js +16 -0
  125. package/dist/commands/ticket/template/apply.d.ts +26 -0
  126. package/dist/commands/ticket/template/apply.js +14 -0
  127. package/dist/commands/ticket/template/delete.d.ts +18 -0
  128. package/dist/commands/ticket/template/delete.js +61 -0
  129. package/dist/commands/ticket/template/list.d.ts +17 -0
  130. package/dist/commands/ticket/template/list.js +78 -0
  131. package/dist/commands/ticket/template/save.d.ts +17 -0
  132. package/dist/commands/ticket/template/save.js +97 -0
  133. package/dist/commands/ticket/view.js +30 -0
  134. package/dist/commands/work/index.js +4 -0
  135. package/dist/commands/work/ready.js +17 -0
  136. package/dist/commands/work/resolve.js +1 -1
  137. package/dist/commands/work/spawn.js +4 -4
  138. package/dist/commands/work/start.d.ts +1 -0
  139. package/dist/commands/work/start.js +203 -93
  140. package/dist/commands/work/status.d.ts +14 -0
  141. package/dist/commands/work/status.js +60 -0
  142. package/dist/commands/workflow/index.js +2 -1
  143. package/dist/commands/workflow/show.d.ts +13 -0
  144. package/dist/commands/workflow/show.js +16 -0
  145. package/dist/commands/workspace/add.js +15 -0
  146. package/dist/commands/workspace/list.js +2 -1
  147. package/dist/commands/workspace/prune.js +5 -5
  148. package/dist/lib/branch/index.d.ts +1 -0
  149. package/dist/lib/database/index.d.ts +1 -1
  150. package/dist/lib/database/index.js +20 -0
  151. package/dist/lib/execution/config.d.ts +15 -1
  152. package/dist/lib/execution/config.js +28 -0
  153. package/dist/lib/execution/devcontainer.js +3 -1
  154. package/dist/lib/execution/runners.d.ts +18 -2
  155. package/dist/lib/execution/runners.js +71 -29
  156. package/dist/lib/execution/session-utils.d.ts +11 -1
  157. package/dist/lib/execution/session-utils.js +26 -1
  158. package/dist/lib/execution/storage.d.ts +5 -0
  159. package/dist/lib/execution/storage.js +18 -3
  160. package/dist/lib/execution/types.d.ts +3 -0
  161. package/dist/lib/flags/resolver.js +1 -0
  162. package/dist/lib/mcp/helpers.d.ts +1 -2
  163. package/dist/lib/mcp/tools/board.js +4 -6
  164. package/dist/lib/mcp/tools/cli-passthrough.js +25 -6
  165. package/dist/lib/mcp/tools/diet.js +1 -0
  166. package/dist/lib/mcp/tools/epic.js +8 -3
  167. package/dist/lib/mcp/tools/index.d.ts +1 -0
  168. package/dist/lib/mcp/tools/index.js +1 -0
  169. package/dist/lib/mcp/tools/label.d.ts +6 -0
  170. package/dist/lib/mcp/tools/label.js +338 -0
  171. package/dist/lib/mcp/tools/spec.js +1 -1
  172. package/dist/lib/mcp/tools/ticket.js +57 -19
  173. package/dist/lib/mcp/tools/work.js +96 -6
  174. package/dist/lib/mcp/types.d.ts +10 -0
  175. package/dist/lib/multiline-input.js +8 -19
  176. package/dist/lib/pmo/base-command.d.ts +0 -1
  177. package/dist/lib/pmo/base-command.js +4 -5
  178. package/dist/lib/pmo/schema.d.ts +6 -0
  179. package/dist/lib/pmo/schema.js +44 -0
  180. package/dist/lib/pmo/storage/actions.js +1 -1
  181. package/dist/lib/pmo/storage/base.d.ts +6 -0
  182. package/dist/lib/pmo/storage/base.js +311 -52
  183. package/dist/lib/pmo/storage/index.d.ts +23 -1
  184. package/dist/lib/pmo/storage/index.js +59 -1
  185. package/dist/lib/pmo/storage/labels.d.ts +55 -0
  186. package/dist/lib/pmo/storage/labels.js +346 -0
  187. package/dist/lib/pmo/storage/tickets.js +17 -0
  188. package/dist/lib/pmo/storage/types.d.ts +25 -0
  189. package/dist/lib/pmo/types.d.ts +44 -0
  190. package/dist/lib/pmo/utils.js +1 -1
  191. package/dist/lib/prompt-command.d.ts +20 -0
  192. package/dist/lib/prompt-command.js +38 -2
  193. package/dist/lib/prompt-json.d.ts +36 -4
  194. package/dist/lib/prompt-json.js +129 -7
  195. package/dist/lib/styles.d.ts +37 -0
  196. package/dist/lib/styles.js +73 -0
  197. package/oclif.manifest.json +6399 -3799
  198. package/package.json +1 -1
@@ -30,19 +30,140 @@ export const EXIT_ERROR = 1;
30
30
  */
31
31
  export const EXIT_NEEDS_INPUT = 2;
32
32
  /**
33
- * Check if the current environment is non-TTY (piped output)
33
+ * All valid JSON envelope type discriminators.
34
+ * Used for contract tests and schema validation.
35
+ */
36
+ export const JSON_ENVELOPE_TYPES = [
37
+ 'prompt',
38
+ 'success',
39
+ 'error',
40
+ 'dry-run',
41
+ 'confirmation_needed',
42
+ 'execution_result',
43
+ ];
44
+ /**
45
+ * Required fields per envelope type for contract validation.
46
+ * Tests use this to verify no fields are accidentally removed.
47
+ */
48
+ export const JSON_ENVELOPE_REQUIRED_FIELDS = {
49
+ prompt: ['type', 'prompt', 'metadata'],
50
+ success: ['type', 'prompt', 'success', 'result', 'metadata'],
51
+ error: ['type', 'error', 'metadata'],
52
+ 'dry-run': ['type', 'valid', 'metadata'],
53
+ confirmation_needed: ['type', 'plan', 'confirm_command', 'message', 'metadata'],
54
+ execution_result: ['type', 'result', 'metadata'],
55
+ };
56
+ /**
57
+ * Validate that a parsed JSON object conforms to the machine-mode envelope schema.
58
+ *
59
+ * Returns an array of validation errors (empty = valid).
60
+ * Useful for contract tests and runtime validation of JSON output.
61
+ *
62
+ * @param obj - Parsed JSON object to validate
63
+ * @returns Array of validation error strings (empty if valid)
64
+ */
65
+ export function validateJsonEnvelope(obj) {
66
+ const errors = [];
67
+ if (typeof obj !== 'object' || obj === null) {
68
+ errors.push('Output must be a non-null object');
69
+ return errors;
70
+ }
71
+ const record = obj;
72
+ // Check type discriminator
73
+ if (!('type' in record)) {
74
+ errors.push('Missing required field: type');
75
+ return errors;
76
+ }
77
+ const type = record.type;
78
+ if (!JSON_ENVELOPE_TYPES.includes(type)) {
79
+ errors.push(`Invalid envelope type: "${type}". Must be one of: ${JSON_ENVELOPE_TYPES.join(', ')}`);
80
+ return errors;
81
+ }
82
+ // Check required fields for this type
83
+ const requiredFields = JSON_ENVELOPE_REQUIRED_FIELDS[type];
84
+ for (const field of requiredFields) {
85
+ if (!(field in record)) {
86
+ errors.push(`Missing required field for type "${type}": ${field}`);
87
+ }
88
+ }
89
+ // Validate metadata structure
90
+ if ('metadata' in record && record.metadata !== undefined) {
91
+ const metadata = record.metadata;
92
+ if (typeof metadata !== 'object' || metadata === null) {
93
+ errors.push('metadata must be a non-null object');
94
+ }
95
+ else {
96
+ if (!('command' in metadata) || typeof metadata.command !== 'string') {
97
+ errors.push('metadata.command must be a string');
98
+ }
99
+ if (!('flags' in metadata) || typeof metadata.flags !== 'object') {
100
+ errors.push('metadata.flags must be an object');
101
+ }
102
+ }
103
+ }
104
+ // Type-specific validation
105
+ if (type === 'prompt' && 'prompt' in record && record.prompt !== null) {
106
+ const prompt = record.prompt;
107
+ if (!('type' in prompt)) {
108
+ errors.push('prompt.type is required when prompt is non-null');
109
+ }
110
+ }
111
+ if (type === 'error' && 'error' in record) {
112
+ const error = record.error;
113
+ if (typeof error !== 'object' || error === null) {
114
+ errors.push('error must be a non-null object');
115
+ }
116
+ else {
117
+ if (!('code' in error) || typeof error.code !== 'string') {
118
+ errors.push('error.code must be a string');
119
+ }
120
+ if (!('message' in error) || typeof error.message !== 'string') {
121
+ errors.push('error.message must be a string');
122
+ }
123
+ }
124
+ }
125
+ if (type === 'success') {
126
+ if (record.success !== true) {
127
+ errors.push('success field must be true for success type');
128
+ }
129
+ if (record.prompt !== null) {
130
+ errors.push('prompt field must be null for success type');
131
+ }
132
+ }
133
+ if (type === 'confirmation_needed') {
134
+ if (typeof record.confirm_command !== 'string' || !record.confirm_command) {
135
+ errors.push('confirm_command must be a non-empty string');
136
+ }
137
+ }
138
+ return errors;
139
+ }
140
+ /**
141
+ * Check if the current environment is non-TTY (piped input or output)
142
+ *
143
+ * Uses the "either" strategy: returns true if EITHER stdin OR stdout is non-TTY.
144
+ * This covers the primary use case of scripts/agents calling prlt as a subprocess,
145
+ * where both stdin and stdout are typically non-TTY.
34
146
  *
35
- * @returns true if stdout is not a TTY (e.g., piped to another process)
147
+ * Returns true if:
148
+ * - stdin is not a TTY (e.g., piped input)
149
+ * - stdout is not a TTY (e.g., piped output)
150
+ * - PRLT_JSON=1 environment variable is set (overrides TTY detection)
151
+ *
152
+ * @returns true if either stdin or stdout is not a TTY, or PRLT_JSON=1 is set
36
153
  */
37
154
  export function isNonTTY() {
38
- return !process.stdout.isTTY;
155
+ if (process.env.PRLT_JSON === '1' || process.env.PRLT_JSON === 'true') {
156
+ return true;
157
+ }
158
+ return !process.stdout.isTTY || !process.stdin.isTTY;
39
159
  }
40
160
  /**
41
161
  * Determine if JSON output mode is active (for AI agents)
42
162
  *
43
163
  * Returns true if:
44
164
  * - The --json flag is set (or -m/--machine aliases)
45
- * - The environment is non-TTY (piped output)
165
+ * - The PRLT_JSON=1 environment variable is set
166
+ * - Either stdin or stdout is non-TTY (piped input/output)
46
167
  *
47
168
  * @param flags - Command flags object
48
169
  * @returns true if JSON mode should be used
@@ -52,7 +173,7 @@ export function shouldOutputJson(flags) {
52
173
  if (flags.json === true || flags.machine === true) {
53
174
  return true;
54
175
  }
55
- // Automatic detection for non-TTY environments
176
+ // Automatic detection for non-TTY environments (includes PRLT_JSON env var)
56
177
  return isNonTTY();
57
178
  }
58
179
  /**
@@ -64,7 +185,8 @@ export const isAgentMode = shouldOutputJson;
64
185
  *
65
186
  * Returns true if:
66
187
  * - The --json flag is set (or -m/--machine aliases)
67
- * - The environment is non-TTY (piped output)
188
+ * - The PRLT_JSON=1 environment variable is set
189
+ * - Either stdin or stdout is non-TTY (piped input/output)
68
190
  *
69
191
  * @param flags - Command flags object
70
192
  * @returns true if machine-readable output mode should be used
@@ -74,7 +196,7 @@ export function isMachineOutput(flags) {
74
196
  if (flags.json === true || flags.machine === true) {
75
197
  return true;
76
198
  }
77
- // Automatic detection for non-TTY environments
199
+ // Automatic detection for non-TTY environments (includes PRLT_JSON env var)
78
200
  return isNonTTY();
79
201
  }
80
202
  /**
@@ -3,8 +3,41 @@
3
3
  *
4
4
  * Centralized styling for consistent CLI output across all commands.
5
5
  * Avoids chalk.gray which is nearly invisible on dark terminals.
6
+ *
7
+ * In non-TTY environments, styled output is automatically suppressed:
8
+ * - Use `isPlainOutput()` to check if plain text mode is active
9
+ * - Use `stripAnsi()` to strip ANSI escape codes from strings
10
+ * - Use `plainText()` to conditionally strip ANSI based on TTY detection
6
11
  */
7
12
  import chalk from 'chalk';
13
+ /**
14
+ * Check if plain text output should be used (no colors, no emoji).
15
+ *
16
+ * Returns true when:
17
+ * - stdout is not a TTY (piped output)
18
+ * - PRLT_JSON=1 environment variable is set
19
+ * - PRLT_PLAIN=1 environment variable is set (plain text without JSON)
20
+ * - NO_COLOR environment variable is set (standard convention)
21
+ *
22
+ * @returns true if output should be plain text
23
+ */
24
+ export declare function isPlainOutput(): boolean;
25
+ /**
26
+ * Strip ANSI escape codes from a string
27
+ */
28
+ export declare function stripAnsi(text: string): string;
29
+ /**
30
+ * Strip emoji characters from a string
31
+ */
32
+ export declare function stripEmoji(text: string): string;
33
+ /**
34
+ * Convert styled text to plain text if in non-TTY mode.
35
+ * Strips ANSI codes and emoji prefixes.
36
+ *
37
+ * @param text - The styled text
38
+ * @returns Plain text in non-TTY mode, original text in TTY mode
39
+ */
40
+ export declare function plainText(text: string): string;
8
41
  /**
9
42
  * Text styles for different semantic purposes
10
43
  */
@@ -96,3 +129,7 @@ export declare const prefix: {
96
129
  export: string;
97
130
  watch: string;
98
131
  };
132
+ /**
133
+ * Get a TTY-aware prefix - returns plain text alternatives in non-TTY mode
134
+ */
135
+ export declare function getPrefix(type: keyof typeof prefix): string;
@@ -3,8 +3,63 @@
3
3
  *
4
4
  * Centralized styling for consistent CLI output across all commands.
5
5
  * Avoids chalk.gray which is nearly invisible on dark terminals.
6
+ *
7
+ * In non-TTY environments, styled output is automatically suppressed:
8
+ * - Use `isPlainOutput()` to check if plain text mode is active
9
+ * - Use `stripAnsi()` to strip ANSI escape codes from strings
10
+ * - Use `plainText()` to conditionally strip ANSI based on TTY detection
6
11
  */
7
12
  import chalk from 'chalk';
13
+ import { isNonTTY } from './prompt-json.js';
14
+ /**
15
+ * Check if plain text output should be used (no colors, no emoji).
16
+ *
17
+ * Returns true when:
18
+ * - stdout is not a TTY (piped output)
19
+ * - PRLT_JSON=1 environment variable is set
20
+ * - PRLT_PLAIN=1 environment variable is set (plain text without JSON)
21
+ * - NO_COLOR environment variable is set (standard convention)
22
+ *
23
+ * @returns true if output should be plain text
24
+ */
25
+ export function isPlainOutput() {
26
+ if (process.env.PRLT_PLAIN === '1' || process.env.PRLT_PLAIN === 'true') {
27
+ return true;
28
+ }
29
+ if (process.env.NO_COLOR !== undefined) {
30
+ return true;
31
+ }
32
+ return isNonTTY();
33
+ }
34
+ // eslint-disable-next-line no-control-regex
35
+ const ANSI_REGEX = /\x1B\[[0-9;]*[a-zA-Z]/g;
36
+ /**
37
+ * Strip ANSI escape codes from a string
38
+ */
39
+ export function stripAnsi(text) {
40
+ return text.replace(ANSI_REGEX, '');
41
+ }
42
+ // eslint-disable-next-line no-misleading-character-class
43
+ const EMOJI_REGEX = /[\u{1F300}-\u{1F9FF}\u{2600}-\u{27BF}\u{FE00}-\u{FE0F}\u{200D}\u{20E3}\u{E0020}-\u{E007F}]+/gu;
44
+ /**
45
+ * Strip emoji characters from a string
46
+ */
47
+ export function stripEmoji(text) {
48
+ return text.replace(EMOJI_REGEX, '').replace(/^\s+/, '');
49
+ }
50
+ /**
51
+ * Convert styled text to plain text if in non-TTY mode.
52
+ * Strips ANSI codes and emoji prefixes.
53
+ *
54
+ * @param text - The styled text
55
+ * @returns Plain text in non-TTY mode, original text in TTY mode
56
+ */
57
+ export function plainText(text) {
58
+ if (isPlainOutput()) {
59
+ return stripEmoji(stripAnsi(text));
60
+ }
61
+ return text;
62
+ }
8
63
  /**
9
64
  * Text styles for different semantic purposes
10
65
  */
@@ -193,3 +248,21 @@ export const prefix = {
193
248
  export: '📤',
194
249
  watch: '👀',
195
250
  };
251
+ /**
252
+ * Get a TTY-aware prefix - returns plain text alternatives in non-TTY mode
253
+ */
254
+ export function getPrefix(type) {
255
+ if (isPlainOutput()) {
256
+ const plainPrefixes = {
257
+ success: '[OK]',
258
+ error: '[ERROR]',
259
+ warning: '[WARN]',
260
+ info: '[INFO]',
261
+ sync: '[SYNC]',
262
+ export: '[EXPORT]',
263
+ watch: '[WATCH]',
264
+ };
265
+ return plainPrefixes[type] || '';
266
+ }
267
+ return prefix[type];
268
+ }