@allternit/gizzi-code 1.0.0 → 1.0.1

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 (158) hide show
  1. package/bin/gizzi +4 -0
  2. package/bin/gizzi-ink +5 -0
  3. package/package.json +137 -31
  4. package/src/Tool.ts +26 -0
  5. package/src/bootstrap/state.ts +165 -322
  6. package/src/commands.ts +209 -0
  7. package/src/context/QueuedMessageContext.tsx +63 -0
  8. package/src/context/fpsMetrics.tsx +30 -0
  9. package/src/context/mailbox.tsx +38 -0
  10. package/src/context/modalContext.tsx +58 -0
  11. package/src/context/notifications.tsx +240 -0
  12. package/src/context/overlayContext.tsx +151 -0
  13. package/src/context/promptOverlayContext.tsx +125 -0
  14. package/src/context/stats.tsx +220 -0
  15. package/src/context/voice.tsx +88 -0
  16. package/src/hooks/useCanUseTool.ts +11 -0
  17. package/src/hooks/useExitOnCtrlCD.ts +40 -187
  18. package/src/hooks/useExitOnCtrlCDWithKeybindings.ts +68 -0
  19. package/src/hooks/useKeybinding.ts +68 -209
  20. package/src/hooks/useTerminalSize.ts +31 -99
  21. package/src/hooks.ts +37 -0
  22. package/src/ink.ts +204 -0
  23. package/src/main.ts +7 -0
  24. package/src/types/command.ts +216 -0
  25. package/src/types/connectorText.ts +39 -0
  26. package/src/types/fileSuggestion.ts +15 -0
  27. package/src/types/generated/events_mono/claude_code/v1/claude_code_internal_event.ts +865 -0
  28. package/src/types/generated/events_mono/common/v1/auth.ts +100 -0
  29. package/src/types/generated/events_mono/growthbook/v1/growthbook_experiment_event.ts +223 -0
  30. package/src/types/generated/google/protobuf/timestamp.ts +187 -0
  31. package/src/types/hooks.ts +319 -0
  32. package/src/types/ids.ts +44 -0
  33. package/src/types/logs.ts +331 -0
  34. package/src/types/merged/index.ts +5 -0
  35. package/src/types/merged/message.ts +552 -0
  36. package/src/types/message.ts +927 -186
  37. package/src/types/messageQueueTypes.ts +21 -0
  38. package/src/types/notebook.ts +72 -0
  39. package/src/types/permissions.ts +447 -0
  40. package/src/types/plugin.ts +363 -0
  41. package/src/types/statusLine.ts +15 -0
  42. package/src/types/textInputTypes.ts +387 -0
  43. package/src/types/tools.ts +149 -0
  44. package/src/types/utils.ts +20 -0
  45. package/src/utils/advisor.ts +12 -0
  46. package/src/utils/api.ts +29 -0
  47. package/src/utils/array.ts +7 -0
  48. package/src/utils/attachments.ts +60 -0
  49. package/src/utils/auth.ts +3 -0
  50. package/src/utils/claudeInChrome/chromeNativeHost.ts +15 -0
  51. package/src/utils/claudeInChrome/common.ts +12 -0
  52. package/src/utils/claudeInChrome/mcpServer.ts +15 -0
  53. package/src/utils/computerUse/common.ts +19 -0
  54. package/src/utils/computerUse/mcpServer.ts +15 -0
  55. package/src/utils/config.ts +35 -0
  56. package/src/utils/context.ts +19 -0
  57. package/src/utils/cwd.ts +3 -0
  58. package/src/utils/debug.ts +3 -0
  59. package/src/utils/earlyInput.ts +7 -0
  60. package/src/utils/effort.ts +13 -0
  61. package/src/utils/embeddedTools.ts +12 -0
  62. package/src/utils/envUtils.ts +3 -0
  63. package/src/utils/errors.ts +3 -0
  64. package/src/utils/execFileNoThrow.ts +24 -0
  65. package/src/utils/feature-flags.ts +112 -0
  66. package/src/utils/file.ts +3 -0
  67. package/src/utils/fileHistory.ts +17 -0
  68. package/src/utils/fingerprint.ts +7 -0
  69. package/src/utils/format.ts +75 -305
  70. package/src/utils/frontmatterParser.ts +33 -0
  71. package/src/utils/fsOperations.ts +31 -0
  72. package/src/utils/fullscreen.ts +40 -350
  73. package/src/utils/git.ts +15 -0
  74. package/src/utils/imageResizer.ts +30 -0
  75. package/src/utils/lazySchema.ts +9 -0
  76. package/src/utils/log.ts +3 -0
  77. package/src/utils/markdownConfigLoader.ts +12 -0
  78. package/src/utils/messages.ts +13 -0
  79. package/src/utils/migration.ts +137 -0
  80. package/src/utils/model/providers.ts +15 -0
  81. package/src/utils/path.ts +3 -0
  82. package/src/utils/permissions/PermissionResult.ts +14 -0
  83. package/src/utils/permissions/autoModeState.ts +12 -0
  84. package/src/utils/permissions/filesystem.ts +12 -0
  85. package/src/utils/plugins/loadPluginOutputStyles.ts +12 -0
  86. package/src/utils/plugins/schemas.ts +25 -0
  87. package/src/utils/process.ts +16 -0
  88. package/src/utils/sandbox/sandbox-adapter.ts +19 -0
  89. package/src/utils/sessionStorage.ts +238 -472
  90. package/src/utils/settings/constants.ts +10 -0
  91. package/src/utils/settings/settings.ts +19 -0
  92. package/src/utils/settings/types.ts +18 -0
  93. package/src/utils/shell/shellToolUtils.ts +9 -0
  94. package/src/utils/sinks.ts +11 -0
  95. package/src/utils/sleep.ts +7 -0
  96. package/src/utils/sliceAnsi.ts +7 -0
  97. package/src/utils/slowOperations.ts +3 -0
  98. package/src/utils/startupProfiler.ts +11 -0
  99. package/src/utils/stringUtils.ts +106 -0
  100. package/src/utils/systemPromptType.ts +9 -0
  101. package/src/utils/teleport/api.ts +15 -0
  102. package/src/utils/textHighlighting.ts +16 -0
  103. package/src/utils/theme.ts +17 -0
  104. package/src/utils/thinking.ts +58 -0
  105. package/src/utils/tokens.ts +11 -0
  106. package/src/utils/toolResultStorage.ts +17 -0
  107. package/src/utils/worktreeModeEnabled.ts +7 -0
  108. package/WORKSPACE_INTEGRATION.md +0 -290
  109. package/chat-sessions/sessions-index.json +0 -98
  110. package/package.json.original +0 -31
  111. package/package.json.publish +0 -47
  112. package/src/bootstrap/state.d.ts +0 -169
  113. package/src/bootstrap/state.js +0 -261
  114. package/src/context/pack-builder.d.ts +0 -24
  115. package/src/context/pack-builder.js +0 -281
  116. package/src/hooks/useExitOnCtrlCD.d.ts +0 -86
  117. package/src/hooks/useExitOnCtrlCD.js +0 -130
  118. package/src/hooks/useKeybinding.d.ts +0 -89
  119. package/src/hooks/useKeybinding.js +0 -163
  120. package/src/hooks/useTerminalSize.d.ts +0 -41
  121. package/src/hooks/useTerminalSize.js +0 -90
  122. package/src/index.d.ts +0 -23
  123. package/src/index.js +0 -24
  124. package/src/runtime/brand/brand.d.ts +0 -488
  125. package/src/runtime/brand/brand.js +0 -464
  126. package/src/runtime/brand/brand.ts +0 -667
  127. package/src/runtime/services/tokenEstimation.d.ts +0 -187
  128. package/src/runtime/services/tokenEstimation.js +0 -423
  129. package/src/runtime/services/tokenEstimation.ts +0 -547
  130. package/src/runtime/tools/Tool.d.ts +0 -239
  131. package/src/runtime/tools/Tool.js +0 -285
  132. package/src/runtime/tools/Tool.ts +0 -524
  133. package/src/scheduler/TaskScheduler.d.ts +0 -98
  134. package/src/scheduler/TaskScheduler.js +0 -201
  135. package/src/scheduler/TaskScheduler.ts +0 -259
  136. package/src/state/AppStateStore.d.ts +0 -347
  137. package/src/state/AppStateStore.js +0 -439
  138. package/src/state/AppStateStore.ts +0 -658
  139. package/src/state/WorkspaceStateStore.d.ts +0 -43
  140. package/src/state/WorkspaceStateStore.js +0 -224
  141. package/src/state/WorkspaceStateStore.ts +0 -297
  142. package/src/types/message.d.ts +0 -190
  143. package/src/types/message.js +0 -77
  144. package/src/utils/format.d.ts +0 -126
  145. package/src/utils/format.js +0 -230
  146. package/src/utils/fullscreen.d.ts +0 -161
  147. package/src/utils/fullscreen.js +0 -277
  148. package/src/utils/sessionStorage.d.ts +0 -210
  149. package/src/utils/sessionStorage.js +0 -435
  150. package/src/watcher/WorkspaceWatcher.d.ts +0 -70
  151. package/src/watcher/WorkspaceWatcher.js +0 -191
  152. package/src/watcher/WorkspaceWatcher.ts +0 -251
  153. package/src/workspace/loader.d.ts +0 -30
  154. package/src/workspace/loader.js +0 -117
  155. package/src/workspace/types.d.ts +0 -91
  156. package/src/workspace/types.js +0 -7
  157. package/threads/thread_1775361175022.json +0 -9
  158. package/tsconfig.json +0 -14
@@ -1,336 +1,106 @@
1
1
  /**
2
- * @fileoverview Format utilities for gizzi-code integration
3
- * @module utils/format
4
- *
5
- * Provides formatting utilities for durations, byte sizes, and numbers.
6
- * These functions are commonly used in CLI output and display components.
2
+ * Format utilities
7
3
  */
8
4
 
9
- /**
10
- * Time unit definitions for duration formatting
11
- * @constant {Record<string, number>}
12
- */
13
- const TIME_UNITS = {
14
- millisecond: 1,
15
- second: 1000,
16
- minute: 60 * 1000,
17
- hour: 60 * 60 * 1000,
18
- day: 24 * 60 * 60 * 1000,
19
- week: 7 * 24 * 60 * 60 * 1000,
20
- month: 30 * 24 * 60 * 60 * 1000,
21
- year: 365 * 24 * 60 * 60 * 1000,
22
- };
23
-
24
- /**
25
- * Byte unit definitions for size formatting
26
- * @constant {string[]}
27
- */
28
- const BYTE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'];
29
-
30
- /**
31
- * Number format options
32
- * @interface NumberFormatOptions
33
- */
34
- export interface NumberFormatOptions {
35
- /** Minimum number of decimal places */
36
- readonly minimumFractionDigits?: number;
37
- /** Maximum number of decimal places */
38
- readonly maximumFractionDigits?: number;
39
- /** Use thousands separator */
40
- readonly useGrouping?: boolean;
41
- /** Locale for formatting (default: 'en-US') */
42
- readonly locale?: string;
5
+ export function formatDuration(ms: number): string {
6
+ if (ms < 1000) return `${ms}ms`
7
+ if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`
8
+ const minutes = Math.floor(ms / 60000)
9
+ const seconds = ((ms % 60000) / 1000).toFixed(0)
10
+ return `${minutes}m ${seconds}s`
43
11
  }
44
12
 
45
- /**
46
- * Duration format options
47
- * @interface DurationFormatOptions
48
- */
49
- export interface DurationFormatOptions {
50
- /** Format style (default: 'auto') */
51
- readonly style?: 'short' | 'long' | 'narrow' | 'auto';
52
- /** Maximum number of units to display (default: 2) */
53
- readonly maxUnits?: number;
54
- /** Include milliseconds in output */
55
- readonly includeMs?: boolean;
56
- /** Round to nearest unit */
57
- readonly round?: boolean;
13
+ export function formatBytes(bytes: number): string {
14
+ if (bytes === 0) return '0 B'
15
+ const k = 1024
16
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
17
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
18
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`
58
19
  }
59
20
 
60
- /**
61
- * Byte format options
62
- * @interface ByteFormatOptions
63
- */
64
- export interface ByteFormatOptions {
65
- /** Number of decimal places (default: 2) */
66
- readonly decimals?: number;
67
- /** Use binary units (KiB, MiB) instead of decimal (KB, MB) */
68
- readonly binary?: boolean;
69
- /** Include space between value and unit */
70
- readonly space?: boolean;
21
+ export function formatNumber(num: number): string {
22
+ return new Intl.NumberFormat().format(num)
71
23
  }
72
24
 
73
- /**
74
- * Formats a duration in milliseconds to a human-readable string
75
- *
76
- * @param {number} ms - Duration in milliseconds
77
- * @param {DurationFormatOptions} [options] - Format options
78
- * @returns {string} Formatted duration string
79
- * @example
80
- * ```typescript
81
- * formatDuration(5000); // "5s"
82
- * formatDuration(65000); // "1m 5s"
83
- * formatDuration(3600000); // "1h"
84
- * formatDuration(90000, { style: 'long' }); // "1 minute 30 seconds"
85
- * ```
86
- */
87
- export function formatDuration(
88
- ms: number,
89
- options: DurationFormatOptions = {}
90
- ): string {
91
- const {
92
- style = 'auto',
93
- maxUnits = 2,
94
- includeMs = false,
95
- round = false,
96
- } = options;
97
-
98
- // Handle edge cases
99
- if (!Number.isFinite(ms) || ms < 0) {
100
- return style === 'long' ? 'unknown duration' : '--';
101
- }
102
-
103
- if (ms === 0) {
104
- return style === 'long' ? '0 seconds' : '0s';
105
- }
106
-
107
- // Round if requested
108
- const duration = round ? Math.round(ms) : ms;
109
-
110
- // Determine appropriate units
111
- const units: Array<{ value: number; short: string; long: string }> = [
112
- { value: TIME_UNITS.year, short: 'y', long: 'year' },
113
- { value: TIME_UNITS.month, short: 'mo', long: 'month' },
114
- { value: TIME_UNITS.week, short: 'w', long: 'week' },
115
- { value: TIME_UNITS.day, short: 'd', long: 'day' },
116
- { value: TIME_UNITS.hour, short: 'h', long: 'hour' },
117
- { value: TIME_UNITS.minute, short: 'm', long: 'minute' },
118
- { value: TIME_UNITS.second, short: 's', long: 'second' },
119
- ];
120
-
121
- // Add milliseconds if requested
122
- if (includeMs || duration < TIME_UNITS.second) {
123
- units.push({ value: TIME_UNITS.millisecond, short: 'ms', long: 'millisecond' });
124
- }
125
-
126
- const parts: string[] = [];
127
- let remaining = Math.abs(duration);
128
-
129
- for (const unit of units) {
130
- if (remaining >= unit.value) {
131
- const count = Math.floor(remaining / unit.value);
132
- remaining %= unit.value;
133
-
134
- const useLong = style === 'long';
135
- const unitStr = useLong
136
- ? `${unit.long}${count !== 1 ? 's' : ''}`
137
- : unit.short;
138
-
139
- parts.push(useLong ? `${count} ${unitStr}` : `${count}${unitStr}`);
140
-
141
- if (parts.length >= maxUnits) {
142
- break;
143
- }
144
- }
145
- }
146
-
147
- if (parts.length === 0) {
148
- return style === 'long' ? '0 seconds' : '0s';
149
- }
150
-
151
- return parts.join(style === 'long' ? ' ' : ' ');
25
+ export function formatCompact(num: number): string {
26
+ return new Intl.NumberFormat('en', { notation: 'compact' }).format(num)
152
27
  }
153
28
 
154
- /**
155
- * Formats a byte size to a human-readable string
156
- *
157
- * @param {number} bytes - Size in bytes
158
- * @param {ByteFormatOptions} [options] - Format options
159
- * @returns {string} Formatted size string
160
- * @example
161
- * ```typescript
162
- * formatBytes(1024); // "1 KB"
163
- * formatBytes(1536); // "1.50 KB"
164
- * formatBytes(1048576); // "1 MB"
165
- * formatBytes(1024, { binary: true }); // "1 KiB"
166
- * ```
167
- */
168
- export function formatBytes(
169
- bytes: number,
170
- options: ByteFormatOptions = {}
171
- ): string {
172
- const { decimals = 2, binary = false, space = true } = options;
173
-
174
- // Handle edge cases
175
- if (!Number.isFinite(bytes)) {
176
- return '0 B';
177
- }
178
-
179
- const absBytes = Math.abs(bytes);
180
-
181
- if (absBytes === 0) {
182
- return '0 B';
183
- }
184
-
185
- // Use 1024 for binary (KiB, MiB) or 1000 for decimal (KB, MB)
186
- const base = binary ? 1024 : 1000;
187
- const binarySuffix = binary ? 'i' : '';
188
-
189
- // Find appropriate unit
190
- const exponent = Math.min(
191
- Math.floor(Math.log(absBytes) / Math.log(base)),
192
- BYTE_UNITS.length - 1
193
- );
194
-
195
- const value = bytes / Math.pow(base, exponent);
196
- const unit = BYTE_UNITS[exponent] + (exponent > 0 ? binarySuffix : '');
197
-
198
- // Format with specified decimal places
199
- const formattedValue = value.toFixed(decimals);
200
- const trimmedValue = parseFloat(formattedValue).toString();
201
-
202
- return space ? `${trimmedValue} ${unit}` : `${trimmedValue}${unit}`;
29
+ export function formatPercentage(value: number, total: number): string {
30
+ if (total === 0) return '0%'
31
+ return `${((value / total) * 100).toFixed(1)}%`
203
32
  }
204
33
 
205
- /**
206
- * Formats a number with locale-aware formatting
207
- *
208
- * @param {number} num - Number to format
209
- * @param {NumberFormatOptions} [options] - Format options
210
- * @returns {string} Formatted number string
211
- * @example
212
- * ```typescript
213
- * formatNumber(1234567); // "1,234,567"
214
- * formatNumber(1234.5678, { maximumFractionDigits: 2 }); // "1,234.57"
215
- * formatNumber(0.001234, { minimumFractionDigits: 6 }); // "0.001234"
216
- * ```
217
- */
218
- export function formatNumber(
219
- num: number,
220
- options: NumberFormatOptions = {}
221
- ): string {
222
- const {
223
- minimumFractionDigits,
224
- maximumFractionDigits = 2,
225
- useGrouping = true,
226
- locale = 'en-US',
227
- } = options;
228
-
229
- // Handle edge cases
230
- if (!Number.isFinite(num)) {
231
- if (Number.isNaN(num)) return 'NaN';
232
- return num > 0 ? '∞' : '-∞';
233
- }
234
-
235
- try {
236
- return new Intl.NumberFormat(locale, {
237
- minimumFractionDigits,
238
- maximumFractionDigits,
239
- useGrouping,
240
- }).format(num);
241
- } catch {
242
- // Fallback for environments without Intl support
243
- const absNum = Math.abs(num);
244
- const sign = num < 0 ? '-' : '';
245
-
246
- // Handle integer part with grouping
247
- const integerPart = Math.floor(absNum);
248
- const fractionPart = absNum - integerPart;
249
-
250
- let result = useGrouping
251
- ? integerPart.toLocaleString(locale)
252
- : integerPart.toString();
34
+ export function formatDate(date: Date | number | string): string {
35
+ const d = new Date(date)
36
+ return d.toLocaleDateString()
37
+ }
253
38
 
254
- // Add fraction part if needed
255
- const minDecimals = minimumFractionDigits ?? 0;
256
- const maxDecimals = maximumFractionDigits ?? 2;
39
+ export function formatDateTime(date: Date | number | string): string {
40
+ const d = new Date(date)
41
+ return d.toLocaleString()
42
+ }
257
43
 
258
- if (fractionPart > 0 || minDecimals > 0) {
259
- const fractionStr = fractionPart
260
- .toFixed(maxDecimals)
261
- .slice(2)
262
- .padEnd(minDecimals, '0');
263
- // Trim trailing zeros if no minimum required
264
- const trimmedFraction =
265
- minDecimals === 0
266
- ? fractionStr.replace(/0+$/, '')
267
- : fractionStr;
268
- if (trimmedFraction.length > 0) {
269
- result += '.' + trimmedFraction;
270
- }
271
- }
44
+ export function formatRelativeTime(date: Date | number | string): string {
45
+ const now = Date.now()
46
+ const then = new Date(date).getTime()
47
+ const diff = now - then
48
+
49
+ const seconds = Math.floor(diff / 1000)
50
+ const minutes = Math.floor(seconds / 60)
51
+ const hours = Math.floor(minutes / 60)
52
+ const days = Math.floor(hours / 24)
53
+
54
+ if (seconds < 60) return 'just now'
55
+ if (minutes < 60) return `${minutes}m ago`
56
+ if (hours < 24) return `${hours}h ago`
57
+ if (days < 7) return `${days}d ago`
58
+ return formatDate(date)
59
+ }
272
60
 
273
- return sign + result;
274
- }
61
+ export function truncate(str: string, maxLength: number): string {
62
+ if (str.length <= maxLength) return str
63
+ return str.slice(0, maxLength - 3) + '...'
275
64
  }
276
65
 
277
- /**
278
- * Formats a number as a percentage
279
- *
280
- * @param {number} value - Value to format (0.5 = 50%)
281
- * @param {number} [decimals=1] - Number of decimal places
282
- * @returns {string} Formatted percentage string
283
- * @example
284
- * ```typescript
285
- * formatPercentage(0.5); // "50%"
286
- * formatPercentage(0.1234, 2); // "12.34%"
287
- * ```
288
- */
289
- export function formatPercentage(value: number, decimals: number = 1): string {
290
- if (!Number.isFinite(value)) {
291
- return '--%';
292
- }
293
- return `${(value * 100).toFixed(decimals)}%`;
66
+ export function capitalize(str: string): string {
67
+ return str.charAt(0).toUpperCase() + str.slice(1)
294
68
  }
295
69
 
296
- /**
297
- * Formats a number as compact notation (1K, 1M, etc.)
298
- *
299
- * @param {number} num - Number to format
300
- * @param {number} [decimals=1] - Number of decimal places
301
- * @returns {string} Compact formatted string
302
- * @example
303
- * ```typescript
304
- * formatCompact(1234); // "1.2K"
305
- * formatCompact(1000000); // "1M"
306
- * formatCompact(1234567890); // "1.2B"
307
- * ```
308
- */
309
- export function formatCompact(num: number, decimals: number = 1): string {
310
- if (!Number.isFinite(num) || num === 0) {
311
- return '0';
312
- }
70
+ export function camelCase(str: string): string {
71
+ return str.replace(/[-_](.)/g, (_, char) => char.toUpperCase())
72
+ }
313
73
 
314
- const absNum = Math.abs(num);
315
- const sign = num < 0 ? '-' : '';
74
+ export function kebabCase(str: string): string {
75
+ return str.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '')
76
+ }
316
77
 
317
- const units = ['', 'K', 'M', 'B', 'T'];
318
- const unitIndex = Math.min(
319
- Math.floor(Math.log10(absNum) / 3),
320
- units.length - 1
321
- );
78
+ export function snakeCase(str: string): string {
79
+ return str.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '')
80
+ }
322
81
 
323
- const value = absNum / Math.pow(1000, unitIndex);
324
- const formatted = value.toFixed(decimals).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1');
82
+ export function padLeft(str: string, length: number, char = ' '): string {
83
+ return char.repeat(Math.max(0, length - str.length)) + str
84
+ }
325
85
 
326
- return `${sign}${formatted}${units[unitIndex]}`;
86
+ export function padRight(str: string, length: number, char = ' '): string {
87
+ return str + char.repeat(Math.max(0, length - str.length))
327
88
  }
328
89
 
329
- // Default export
330
90
  export default {
331
91
  formatDuration,
332
92
  formatBytes,
333
93
  formatNumber,
334
- formatPercentage,
335
94
  formatCompact,
336
- };
95
+ formatPercentage,
96
+ formatDate,
97
+ formatDateTime,
98
+ formatRelativeTime,
99
+ truncate,
100
+ capitalize,
101
+ camelCase,
102
+ kebabCase,
103
+ snakeCase,
104
+ padLeft,
105
+ padRight,
106
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Frontmatter Parser
3
+ */
4
+
5
+ export interface Frontmatter {
6
+ [key: string]: unknown
7
+ }
8
+
9
+ export function parseFrontmatter(content: string): { frontmatter: Frontmatter; body: string } {
10
+ const lines = content.split('\n')
11
+ if (lines[0] !== '---') {
12
+ return { frontmatter: {}, body: content }
13
+ }
14
+
15
+ const endIndex = lines.findIndex((line, i) => i > 0 && line === '---')
16
+ if (endIndex === -1) {
17
+ return { frontmatter: {}, body: content }
18
+ }
19
+
20
+ const frontmatterText = lines.slice(1, endIndex).join('\n')
21
+ const body = lines.slice(endIndex + 1).join('\n')
22
+
23
+ // Simple YAML-like parsing
24
+ const frontmatter: Frontmatter = {}
25
+ for (const line of frontmatterText.split('\n')) {
26
+ const match = line.match(/^(\w+):\s*(.*)$/)
27
+ if (match) {
28
+ frontmatter[match[1]] = match[2]
29
+ }
30
+ }
31
+
32
+ return { frontmatter, body }
33
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * File System Operations
3
+ */
4
+
5
+ import { readFile, writeFile, mkdir } from 'fs/promises'
6
+
7
+ export async function readFileSafe(path: string): Promise<string | null> {
8
+ try {
9
+ return await readFile(path, 'utf-8')
10
+ } catch {
11
+ return null
12
+ }
13
+ }
14
+
15
+ export async function writeFileSafe(path: string, content: string): Promise<boolean> {
16
+ try {
17
+ await writeFile(path, content)
18
+ return true
19
+ } catch {
20
+ return false
21
+ }
22
+ }
23
+
24
+ export async function mkdirSafe(path: string): Promise<boolean> {
25
+ try {
26
+ await mkdir(path, { recursive: true })
27
+ return true
28
+ } catch {
29
+ return false
30
+ }
31
+ }