@contentful/experience-design-system-cli 2.2.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 (165) hide show
  1. package/README.md +532 -0
  2. package/bin/cli.js +58 -0
  3. package/dist/package.json +56 -0
  4. package/dist/src/analyze/command.d.ts +3 -0
  5. package/dist/src/analyze/command.js +175 -0
  6. package/dist/src/analyze/extract/astro.d.ts +5 -0
  7. package/dist/src/analyze/extract/astro.js +280 -0
  8. package/dist/src/analyze/extract/pipeline.d.ts +6 -0
  9. package/dist/src/analyze/extract/pipeline.js +298 -0
  10. package/dist/src/analyze/extract/react.d.ts +2 -0
  11. package/dist/src/analyze/extract/react.js +1949 -0
  12. package/dist/src/analyze/extract/slot-detection.d.ts +35 -0
  13. package/dist/src/analyze/extract/slot-detection.js +101 -0
  14. package/dist/src/analyze/extract/stencil.d.ts +2 -0
  15. package/dist/src/analyze/extract/stencil.js +293 -0
  16. package/dist/src/analyze/extract/tsx-shared.d.ts +8 -0
  17. package/dist/src/analyze/extract/tsx-shared.js +263 -0
  18. package/dist/src/analyze/extract/vue-tsx.d.ts +2 -0
  19. package/dist/src/analyze/extract/vue-tsx.js +498 -0
  20. package/dist/src/analyze/extract/vue.d.ts +5 -0
  21. package/dist/src/analyze/extract/vue.js +647 -0
  22. package/dist/src/analyze/extract/web-components.d.ts +2 -0
  23. package/dist/src/analyze/extract/web-components.js +866 -0
  24. package/dist/src/analyze/pre-classify.d.ts +17 -0
  25. package/dist/src/analyze/pre-classify.js +144 -0
  26. package/dist/src/analyze/select/command.d.ts +2 -0
  27. package/dist/src/analyze/select/command.js +256 -0
  28. package/dist/src/analyze/select/index.d.ts +6 -0
  29. package/dist/src/analyze/select/index.js +5 -0
  30. package/dist/src/analyze/select/parser.d.ts +6 -0
  31. package/dist/src/analyze/select/parser.js +53 -0
  32. package/dist/src/analyze/select/persistence.d.ts +9 -0
  33. package/dist/src/analyze/select/persistence.js +42 -0
  34. package/dist/src/analyze/select/stdout.d.ts +7 -0
  35. package/dist/src/analyze/select/stdout.js +3 -0
  36. package/dist/src/analyze/select/tui/App.d.ts +8 -0
  37. package/dist/src/analyze/select/tui/App.js +491 -0
  38. package/dist/src/analyze/select/tui/components/ComponentDetail.d.ts +20 -0
  39. package/dist/src/analyze/select/tui/components/ComponentDetail.js +43 -0
  40. package/dist/src/analyze/select/tui/components/FieldEditor.d.ts +11 -0
  41. package/dist/src/analyze/select/tui/components/FieldEditor.js +531 -0
  42. package/dist/src/analyze/select/tui/components/FinalizeDialog.d.ts +10 -0
  43. package/dist/src/analyze/select/tui/components/FinalizeDialog.js +15 -0
  44. package/dist/src/analyze/select/tui/components/HelpOverlay.d.ts +7 -0
  45. package/dist/src/analyze/select/tui/components/HelpOverlay.js +11 -0
  46. package/dist/src/analyze/select/tui/components/JsonEditor.d.ts +11 -0
  47. package/dist/src/analyze/select/tui/components/JsonEditor.js +154 -0
  48. package/dist/src/analyze/select/tui/components/JsonPanel.d.ts +11 -0
  49. package/dist/src/analyze/select/tui/components/JsonPanel.js +62 -0
  50. package/dist/src/analyze/select/tui/components/PreviewSummaryBar.d.ts +8 -0
  51. package/dist/src/analyze/select/tui/components/PreviewSummaryBar.js +29 -0
  52. package/dist/src/analyze/select/tui/components/QuitDialog.d.ts +8 -0
  53. package/dist/src/analyze/select/tui/components/QuitDialog.js +14 -0
  54. package/dist/src/analyze/select/tui/components/Sidebar.d.ts +15 -0
  55. package/dist/src/analyze/select/tui/components/Sidebar.js +48 -0
  56. package/dist/src/analyze/select/tui/components/SourcePanel.d.ts +11 -0
  57. package/dist/src/analyze/select/tui/components/SourcePanel.js +52 -0
  58. package/dist/src/analyze/select/tui/components/StatusBar.d.ts +11 -0
  59. package/dist/src/analyze/select/tui/components/StatusBar.js +6 -0
  60. package/dist/src/analyze/select/tui/components/TopBar.d.ts +10 -0
  61. package/dist/src/analyze/select/tui/components/TopBar.js +5 -0
  62. package/dist/src/analyze/select/tui/hooks/useImmediateInput.d.ts +24 -0
  63. package/dist/src/analyze/select/tui/hooks/useImmediateInput.js +68 -0
  64. package/dist/src/analyze/select/tui/hooks/useKeymap.d.ts +24 -0
  65. package/dist/src/analyze/select/tui/hooks/useKeymap.js +67 -0
  66. package/dist/src/analyze/select/tui/hooks/useSession.d.ts +19 -0
  67. package/dist/src/analyze/select/tui/hooks/useSession.js +52 -0
  68. package/dist/src/analyze/select/tui/hooks/useUndo.d.ts +8 -0
  69. package/dist/src/analyze/select/tui/hooks/useUndo.js +26 -0
  70. package/dist/src/analyze/select/types.d.ts +46 -0
  71. package/dist/src/analyze/select/types.js +20 -0
  72. package/dist/src/analyze/select-agent/command.d.ts +2 -0
  73. package/dist/src/analyze/select-agent/command.js +208 -0
  74. package/dist/src/analyze/tui/AnalyzeView.d.ts +24 -0
  75. package/dist/src/analyze/tui/AnalyzeView.js +38 -0
  76. package/dist/src/apply/api-client.d.ts +35 -0
  77. package/dist/src/apply/api-client.js +143 -0
  78. package/dist/src/apply/command.d.ts +6 -0
  79. package/dist/src/apply/command.js +787 -0
  80. package/dist/src/apply/manifest.d.ts +1 -0
  81. package/dist/src/apply/manifest.js +1 -0
  82. package/dist/src/apply/tui/SelectView.d.ts +18 -0
  83. package/dist/src/apply/tui/SelectView.js +34 -0
  84. package/dist/src/apply/tui/ServerApplyView.d.ts +32 -0
  85. package/dist/src/apply/tui/ServerApplyView.js +42 -0
  86. package/dist/src/apply/tui/ServerPreviewView.d.ts +9 -0
  87. package/dist/src/apply/tui/ServerPreviewView.js +21 -0
  88. package/dist/src/credentials-store.d.ts +8 -0
  89. package/dist/src/credentials-store.js +30 -0
  90. package/dist/src/generate/agent-runner.d.ts +86 -0
  91. package/dist/src/generate/agent-runner.js +314 -0
  92. package/dist/src/generate/command.d.ts +2 -0
  93. package/dist/src/generate/command.js +545 -0
  94. package/dist/src/generate/edit/command.d.ts +2 -0
  95. package/dist/src/generate/edit/command.js +126 -0
  96. package/dist/src/generate/prompt-builder.d.ts +18 -0
  97. package/dist/src/generate/prompt-builder.js +202 -0
  98. package/dist/src/generate/tui/GenerateView.d.ts +12 -0
  99. package/dist/src/generate/tui/GenerateView.js +10 -0
  100. package/dist/src/import/command.d.ts +2 -0
  101. package/dist/src/import/command.js +96 -0
  102. package/dist/src/import/orchestrator.d.ts +37 -0
  103. package/dist/src/import/orchestrator.js +374 -0
  104. package/dist/src/import/path-utils.d.ts +15 -0
  105. package/dist/src/import/path-utils.js +30 -0
  106. package/dist/src/import/tui/WizardApp.d.ts +10 -0
  107. package/dist/src/import/tui/WizardApp.js +906 -0
  108. package/dist/src/import/tui/steps/CredentialsStep.d.ts +15 -0
  109. package/dist/src/import/tui/steps/CredentialsStep.js +79 -0
  110. package/dist/src/import/tui/steps/DoneStep.d.ts +20 -0
  111. package/dist/src/import/tui/steps/DoneStep.js +17 -0
  112. package/dist/src/import/tui/steps/ErrorStep.d.ts +8 -0
  113. package/dist/src/import/tui/steps/ErrorStep.js +11 -0
  114. package/dist/src/import/tui/steps/GateStep.d.ts +14 -0
  115. package/dist/src/import/tui/steps/GateStep.js +20 -0
  116. package/dist/src/import/tui/steps/GenerateReviewStep.d.ts +8 -0
  117. package/dist/src/import/tui/steps/GenerateReviewStep.js +208 -0
  118. package/dist/src/import/tui/steps/PathValidationStep.d.ts +10 -0
  119. package/dist/src/import/tui/steps/PathValidationStep.js +151 -0
  120. package/dist/src/import/tui/steps/PreviewStep.d.ts +21 -0
  121. package/dist/src/import/tui/steps/PreviewStep.js +36 -0
  122. package/dist/src/import/tui/steps/RunningStep.d.ts +10 -0
  123. package/dist/src/import/tui/steps/RunningStep.js +20 -0
  124. package/dist/src/import/tui/steps/TokenInputStep.d.ts +8 -0
  125. package/dist/src/import/tui/steps/TokenInputStep.js +70 -0
  126. package/dist/src/import/tui/steps/WelcomeStep.d.ts +7 -0
  127. package/dist/src/import/tui/steps/WelcomeStep.js +33 -0
  128. package/dist/src/import/tui/steps/WizardPreviewStep.d.ts +15 -0
  129. package/dist/src/import/tui/steps/WizardPreviewStep.js +121 -0
  130. package/dist/src/import/tui/steps/preview-diff.d.ts +10 -0
  131. package/dist/src/import/tui/steps/preview-diff.js +132 -0
  132. package/dist/src/index.d.ts +1 -0
  133. package/dist/src/index.js +2 -0
  134. package/dist/src/output/format.d.ts +23 -0
  135. package/dist/src/output/format.js +110 -0
  136. package/dist/src/print/command.d.ts +2 -0
  137. package/dist/src/print/command.js +199 -0
  138. package/dist/src/print/validate/tui/ValidateView.d.ts +15 -0
  139. package/dist/src/print/validate/tui/ValidateView.js +37 -0
  140. package/dist/src/print/validate/validators/cdf-validator.d.ts +2 -0
  141. package/dist/src/print/validate/validators/cdf-validator.js +104 -0
  142. package/dist/src/print/validate/validators/dtcg-validator.d.ts +2 -0
  143. package/dist/src/print/validate/validators/dtcg-validator.js +110 -0
  144. package/dist/src/print/validate/validators/format-errors.d.ts +12 -0
  145. package/dist/src/print/validate/validators/format-errors.js +18 -0
  146. package/dist/src/program.d.ts +2 -0
  147. package/dist/src/program.js +25 -0
  148. package/dist/src/session/command.d.ts +2 -0
  149. package/dist/src/session/command.js +261 -0
  150. package/dist/src/session/db.d.ts +111 -0
  151. package/dist/src/session/db.js +1114 -0
  152. package/dist/src/session/migration.d.ts +4 -0
  153. package/dist/src/session/migration.js +117 -0
  154. package/dist/src/session/session-id.d.ts +1 -0
  155. package/dist/src/session/session-id.js +212 -0
  156. package/dist/src/session/stats.d.ts +27 -0
  157. package/dist/src/session/stats.js +89 -0
  158. package/dist/src/setup/command.d.ts +2 -0
  159. package/dist/src/setup/command.js +765 -0
  160. package/dist/src/types.d.ts +48 -0
  161. package/dist/src/types.js +1 -0
  162. package/package.json +55 -0
  163. package/skills/generate-components.md +361 -0
  164. package/skills/generate-tokens.md +194 -0
  165. package/skills/select-components.md +180 -0
@@ -0,0 +1,4 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ export declare function getReviewsDir(): string;
3
+ export declare function getLegacyImportDbPath(): string;
4
+ export declare function runMigrationIfNeeded(db: DatabaseSync): void;
@@ -0,0 +1,117 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import { readdirSync, readFileSync, renameSync, statSync } from 'node:fs';
3
+ import { existsSync } from 'node:fs';
4
+ import { join, resolve } from 'node:path';
5
+ import { homedir } from 'node:os';
6
+ import { generateSessionId } from './session-id.js';
7
+ const MIGRATION_NAME = 'v1_import_and_reviews';
8
+ export function getReviewsDir() {
9
+ if (process.env.EDS_REVIEW_ARTIFACTS_DIR) {
10
+ return resolve(process.env.EDS_REVIEW_ARTIFACTS_DIR);
11
+ }
12
+ return resolve(homedir(), '.contentful', 'experience-design-system-cli', 'reviews');
13
+ }
14
+ export function getLegacyImportDbPath() {
15
+ if (process.env.EDS_IMPORT_DB_PATH) {
16
+ return resolve(process.env.EDS_IMPORT_DB_PATH);
17
+ }
18
+ return resolve(homedir(), '.contentful', 'experience-design-system-cli', 'import.db');
19
+ }
20
+ export function runMigrationIfNeeded(db) {
21
+ const alreadyRan = db.prepare('SELECT name FROM migrations WHERE name = ?').get(MIGRATION_NAME);
22
+ if (alreadyRan)
23
+ return;
24
+ const now = new Date().toISOString();
25
+ db.exec('BEGIN');
26
+ try {
27
+ migrateReviewSessions(db, now);
28
+ migrateImportDb(db, now);
29
+ db.prepare('INSERT INTO migrations (name, applied_at) VALUES (?, ?)').run(MIGRATION_NAME, now);
30
+ db.exec('COMMIT');
31
+ }
32
+ catch (e) {
33
+ db.exec('ROLLBACK');
34
+ process.stderr.write(`Warning: pipeline session migration failed — continuing without migrated data.\n${e instanceof Error ? e.message : String(e)}\n`);
35
+ }
36
+ }
37
+ function migrateReviewSessions(db, _now) {
38
+ const reviewsDir = getReviewsDir();
39
+ let entries;
40
+ try {
41
+ entries = readdirSync(reviewsDir);
42
+ }
43
+ catch {
44
+ return;
45
+ }
46
+ for (const entry of entries) {
47
+ if (entry.endsWith('.migrated'))
48
+ continue;
49
+ const sessionDir = join(reviewsDir, entry);
50
+ try {
51
+ if (!statSync(sessionDir).isDirectory())
52
+ continue;
53
+ }
54
+ catch {
55
+ continue;
56
+ }
57
+ const stateFile = join(sessionDir, 'current-review-state.json');
58
+ let snapshot;
59
+ try {
60
+ snapshot = JSON.parse(readFileSync(stateFile, 'utf8'));
61
+ }
62
+ catch {
63
+ process.stderr.write(`Warning: skipping migration of review session ${entry} — cannot read state file.\n`);
64
+ continue;
65
+ }
66
+ const sessionId = generateSessionId();
67
+ const mtime = statSync(stateFile).mtime.toISOString();
68
+ db.prepare('INSERT INTO sessions (id, name, created_at, updated_at) VALUES (?, NULL, ?, ?)').run(sessionId, mtime, mtime);
69
+ db.prepare(`INSERT INTO steps (session_id, command, status, started_at, completed_at, inputs, outputs, updated_at)
70
+ VALUES (?, 'generate refine', 'interrupted', ?, ?, ?, '{}', ?)`).run(sessionId, mtime, mtime, JSON.stringify({ rawComponents: snapshot.inputPath ?? '' }), mtime);
71
+ try {
72
+ renameSync(sessionDir, `${sessionDir}.migrated`);
73
+ }
74
+ catch {
75
+ // non-fatal
76
+ }
77
+ }
78
+ }
79
+ function migrateImportDb(db, _now) {
80
+ const importDbPath = getLegacyImportDbPath();
81
+ if (!existsSync(importDbPath))
82
+ return;
83
+ let importDb;
84
+ try {
85
+ importDb = new DatabaseSync(importDbPath, { readOnly: true });
86
+ }
87
+ catch {
88
+ return;
89
+ }
90
+ try {
91
+ const importSessions = importDb
92
+ .prepare('SELECT space_id, environment_id, started_at, updated_at FROM sessions')
93
+ .all();
94
+ for (const s of importSessions) {
95
+ const sessionId = generateSessionId();
96
+ const sessionName = `import:${s.space_id}:${s.environment_id}`;
97
+ db.prepare('INSERT INTO sessions (id, name, created_at, updated_at) VALUES (?, ?, ?, ?)').run(sessionId, sessionName, s.started_at, s.updated_at);
98
+ // import_items is no longer in the pipeline schema — legacy push history is not migrated
99
+ }
100
+ importDb.close();
101
+ try {
102
+ renameSync(importDbPath, `${importDbPath}.migrated`);
103
+ }
104
+ catch {
105
+ // non-fatal
106
+ }
107
+ }
108
+ catch (e) {
109
+ try {
110
+ importDb.close();
111
+ }
112
+ catch {
113
+ // ignore
114
+ }
115
+ throw e;
116
+ }
117
+ }
@@ -0,0 +1 @@
1
+ export declare function generateSessionId(): string;
@@ -0,0 +1,212 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ const ADJECTIVES = [
3
+ 'aged',
4
+ 'amber',
5
+ 'bare',
6
+ 'bold',
7
+ 'bright',
8
+ 'calm',
9
+ 'clear',
10
+ 'cold',
11
+ 'cool',
12
+ 'dark',
13
+ 'deep',
14
+ 'dim',
15
+ 'dry',
16
+ 'dusk',
17
+ 'dust',
18
+ 'early',
19
+ 'far',
20
+ 'fast',
21
+ 'fine',
22
+ 'flat',
23
+ 'free',
24
+ 'fresh',
25
+ 'full',
26
+ 'faint',
27
+ 'gold',
28
+ 'gray',
29
+ 'great',
30
+ 'green',
31
+ 'hard',
32
+ 'high',
33
+ 'hollow',
34
+ 'icy',
35
+ 'idle',
36
+ 'iron',
37
+ 'jade',
38
+ 'keen',
39
+ 'large',
40
+ 'lean',
41
+ 'light',
42
+ 'lone',
43
+ 'long',
44
+ 'low',
45
+ 'lunar',
46
+ 'mild',
47
+ 'misty',
48
+ 'muted',
49
+ 'neat',
50
+ 'new',
51
+ 'noble',
52
+ 'north',
53
+ 'oak',
54
+ 'odd',
55
+ 'old',
56
+ 'open',
57
+ 'pale',
58
+ 'plain',
59
+ 'proud',
60
+ 'pure',
61
+ 'quiet',
62
+ 'rapid',
63
+ 'raw',
64
+ 'red',
65
+ 'rich',
66
+ 'rigid',
67
+ 'ripe',
68
+ 'round',
69
+ 'royal',
70
+ 'rust',
71
+ 'sage',
72
+ 'salt',
73
+ 'sandy',
74
+ 'sharp',
75
+ 'short',
76
+ 'shy',
77
+ 'silent',
78
+ 'slim',
79
+ 'slow',
80
+ 'small',
81
+ 'soft',
82
+ 'solar',
83
+ 'solid',
84
+ 'stark',
85
+ 'steel',
86
+ 'stern',
87
+ 'still',
88
+ 'stone',
89
+ 'strong',
90
+ 'sweet',
91
+ 'tall',
92
+ 'teal',
93
+ 'thin',
94
+ 'thorn',
95
+ 'tidy',
96
+ 'true',
97
+ 'vast',
98
+ 'warm',
99
+ 'wide',
100
+ 'wild',
101
+ 'wise',
102
+ 'young',
103
+ ];
104
+ const NOUNS = [
105
+ 'arch',
106
+ 'ash',
107
+ 'bay',
108
+ 'beam',
109
+ 'birch',
110
+ 'blade',
111
+ 'bloom',
112
+ 'bluff',
113
+ 'bolt',
114
+ 'brook',
115
+ 'bud',
116
+ 'cape',
117
+ 'cave',
118
+ 'cedar',
119
+ 'cliff',
120
+ 'cloud',
121
+ 'coast',
122
+ 'cove',
123
+ 'creek',
124
+ 'crest',
125
+ 'dale',
126
+ 'dawn',
127
+ 'delta',
128
+ 'dew',
129
+ 'drift',
130
+ 'dune',
131
+ 'dust',
132
+ 'echo',
133
+ 'edge',
134
+ 'elk',
135
+ 'ember',
136
+ 'fen',
137
+ 'fern',
138
+ 'field',
139
+ 'flare',
140
+ 'flash',
141
+ 'flint',
142
+ 'flood',
143
+ 'floor',
144
+ 'flume',
145
+ 'fog',
146
+ 'ford',
147
+ 'forge',
148
+ 'forth',
149
+ 'frost',
150
+ 'gale',
151
+ 'glade',
152
+ 'glen',
153
+ 'glow',
154
+ 'grove',
155
+ 'gulf',
156
+ 'heath',
157
+ 'hill',
158
+ 'hollow',
159
+ 'hull',
160
+ 'isle',
161
+ 'jade',
162
+ 'knoll',
163
+ 'lake',
164
+ 'leaf',
165
+ 'ledge',
166
+ 'light',
167
+ 'loch',
168
+ 'maple',
169
+ 'marsh',
170
+ 'mead',
171
+ 'mesa',
172
+ 'mist',
173
+ 'moon',
174
+ 'moor',
175
+ 'moss',
176
+ 'mount',
177
+ 'pass',
178
+ 'peak',
179
+ 'pine',
180
+ 'plain',
181
+ 'pool',
182
+ 'ridge',
183
+ 'rift',
184
+ 'rim',
185
+ 'rise',
186
+ 'river',
187
+ 'rock',
188
+ 'root',
189
+ 'rush',
190
+ 'sand',
191
+ 'shore',
192
+ 'slope',
193
+ 'snow',
194
+ 'spark',
195
+ 'spring',
196
+ 'star',
197
+ 'storm',
198
+ 'stream',
199
+ 'tide',
200
+ 'vale',
201
+ 'vapor',
202
+ 'wake',
203
+ 'wave',
204
+ 'wind',
205
+ ];
206
+ export function generateSessionId() {
207
+ const buf = randomBytes(4);
208
+ const adjIdx = buf[0] % ADJECTIVES.length;
209
+ const nounIdx = buf[1] % NOUNS.length;
210
+ const hex = (((buf[2] << 8) | buf[3]) & 0xffff).toString(16).padStart(4, '0');
211
+ return `${ADJECTIVES[adjIdx]}-${NOUNS[nounIdx]}-${hex}`;
212
+ }
@@ -0,0 +1,27 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
2
+ export interface SessionStats {
3
+ dbPath: string;
4
+ dbBytes: number;
5
+ walBytes: number;
6
+ totalBytes: number;
7
+ sessions: {
8
+ total: number;
9
+ complete: number;
10
+ inProgress: number;
11
+ failed: number;
12
+ interrupted: number;
13
+ };
14
+ steps: number;
15
+ rawComponents: number;
16
+ oldestSession: {
17
+ id: string;
18
+ updatedAt: string;
19
+ } | null;
20
+ newestSession: {
21
+ id: string;
22
+ updatedAt: string;
23
+ } | null;
24
+ }
25
+ export declare function getStats(db: DatabaseSync, dbPath: string): SessionStats;
26
+ export declare function formatBytes(bytes: number): string;
27
+ export declare function formatStatsText(stats: SessionStats): string;
@@ -0,0 +1,89 @@
1
+ import { statSync } from 'node:fs';
2
+ export function getStats(db, dbPath) {
3
+ const pageCountRow = db.prepare('PRAGMA page_count').get();
4
+ const pageSizeRow = db.prepare('PRAGMA page_size').get();
5
+ const dbBytes = pageCountRow.page_count * pageSizeRow.page_size;
6
+ let walBytes = 0;
7
+ try {
8
+ walBytes = statSync(`${dbPath}-wal`).size;
9
+ }
10
+ catch {
11
+ // WAL file doesn't exist
12
+ }
13
+ // Session counts by status of last step
14
+ const sessionTotal = db.prepare('SELECT COUNT(*) AS n FROM sessions').get().n;
15
+ const statusCounts = db
16
+ .prepare(`SELECT last_status, COUNT(*) AS n
17
+ FROM (
18
+ SELECT session_id,
19
+ status AS last_status,
20
+ ROW_NUMBER() OVER (PARTITION BY session_id ORDER BY started_at DESC, id DESC) AS rn
21
+ FROM steps
22
+ )
23
+ WHERE rn = 1
24
+ GROUP BY last_status`)
25
+ .all();
26
+ const byStatus = {};
27
+ for (const row of statusCounts) {
28
+ byStatus[row.last_status] = row.n;
29
+ }
30
+ // Sessions with no steps at all count as in-progress
31
+ const sessionsWithSteps = statusCounts.reduce((a, r) => a + r.n, 0);
32
+ const sessionsWithNoSteps = sessionTotal - sessionsWithSteps;
33
+ const stepsTotal = db.prepare('SELECT COUNT(*) AS n FROM steps').get().n;
34
+ const rawComponents = db.prepare('SELECT COUNT(*) AS n FROM raw_components').get().n;
35
+ const oldest = db.prepare('SELECT id, updated_at FROM sessions ORDER BY updated_at ASC LIMIT 1').get();
36
+ const newest = db.prepare('SELECT id, updated_at FROM sessions ORDER BY updated_at DESC LIMIT 1').get();
37
+ return {
38
+ dbPath,
39
+ dbBytes,
40
+ walBytes,
41
+ totalBytes: dbBytes + walBytes,
42
+ sessions: {
43
+ total: sessionTotal,
44
+ complete: byStatus['complete'] ?? 0,
45
+ inProgress: (byStatus['pending'] ?? 0) + sessionsWithNoSteps,
46
+ failed: byStatus['failed'] ?? 0,
47
+ interrupted: byStatus['interrupted'] ?? 0,
48
+ },
49
+ steps: stepsTotal,
50
+ rawComponents,
51
+ oldestSession: oldest ? { id: oldest.id, updatedAt: oldest.updated_at } : null,
52
+ newestSession: newest ? { id: newest.id, updatedAt: newest.updated_at } : null,
53
+ };
54
+ }
55
+ export function formatBytes(bytes) {
56
+ if (bytes < 1024)
57
+ return `${bytes} B`;
58
+ if (bytes < 1024 * 1024)
59
+ return `${(bytes / 1024).toFixed(1)} KB`;
60
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
61
+ }
62
+ export function formatStatsText(stats) {
63
+ const lines = [
64
+ `Pipeline DB: ${stats.dbPath}`,
65
+ ``,
66
+ ` Database size ${formatBytes(stats.dbBytes).padStart(10)}`,
67
+ ` WAL file size ${formatBytes(stats.walBytes).padStart(10)}`,
68
+ ` Total on disk ${formatBytes(stats.totalBytes).padStart(10)}`,
69
+ ``,
70
+ ` Sessions ${String(stats.sessions.total).padStart(10)}`,
71
+ ` complete ${String(stats.sessions.complete).padStart(10)}`,
72
+ ` in-progress ${String(stats.sessions.inProgress).padStart(10)}`,
73
+ ` failed ${String(stats.sessions.failed).padStart(10)}`,
74
+ ` interrupted ${String(stats.sessions.interrupted).padStart(10)}`,
75
+ ``,
76
+ ` Steps ${String(stats.steps).padStart(10)}`,
77
+ ` Raw components ${String(stats.rawComponents).padStart(10)}`,
78
+ ];
79
+ if (stats.oldestSession) {
80
+ const date = stats.oldestSession.updatedAt.slice(0, 10);
81
+ lines.push(``, ` Oldest session ${date} (${stats.oldestSession.id})`);
82
+ }
83
+ if (stats.newestSession && stats.newestSession.id !== stats.oldestSession?.id) {
84
+ const date = stats.newestSession.updatedAt.slice(0, 10);
85
+ lines.push(` Newest session ${date} (${stats.newestSession.id})`);
86
+ }
87
+ lines.push(``, `Run \`session prune --older-than <age>\` to free space.`);
88
+ return lines.join('\n');
89
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerSetupCommand(program: Command): void;