@slycode/slycode 0.2.4 → 0.2.6

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 (166) hide show
  1. package/dist/data/scaffold-templates/seed-cards-existing.json +8 -0
  2. package/dist/data/scaffold-templates/seed-cards-new.json +8 -0
  3. package/dist/messaging/index.js +9 -2
  4. package/dist/messaging/index.js.map +1 -1
  5. package/dist/messaging/stt.d.ts +3 -1
  6. package/dist/messaging/stt.js +76 -0
  7. package/dist/messaging/stt.js.map +1 -1
  8. package/dist/messaging/types.d.ts +3 -1
  9. package/dist/scripts/scaffold.js +29 -7
  10. package/dist/web/.next/BUILD_ID +1 -1
  11. package/dist/web/.next/build-manifest.json +4 -4
  12. package/dist/web/.next/server/app/_global-error/page/build-manifest.json +2 -2
  13. package/dist/web/.next/server/app/_global-error/page.js +1 -1
  14. package/dist/web/.next/server/app/_global-error/page.js.nft.json +1 -1
  15. package/dist/web/.next/server/app/_global-error.html +2 -2
  16. package/dist/web/.next/server/app/_global-error.rsc +1 -1
  17. package/dist/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  18. package/dist/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  19. package/dist/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  20. package/dist/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  21. package/dist/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  22. package/dist/web/.next/server/app/_not-found/page/build-manifest.json +2 -2
  23. package/dist/web/.next/server/app/_not-found/page.js +1 -1
  24. package/dist/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  25. package/dist/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  26. package/dist/web/.next/server/app/_not-found.html +1 -1
  27. package/dist/web/.next/server/app/_not-found.rsc +2 -2
  28. package/dist/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  29. package/dist/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  30. package/dist/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  31. package/dist/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  32. package/dist/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  33. package/dist/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  34. package/dist/web/.next/server/app/api/cli-assets/fix/route.js +1 -1
  35. package/dist/web/.next/server/app/api/cli-assets/fix/route.js.nft.json +1 -1
  36. package/dist/web/.next/server/app/api/cli-assets/import/route.js +1 -1
  37. package/dist/web/.next/server/app/api/cli-assets/import/route.js.nft.json +1 -1
  38. package/dist/web/.next/server/app/api/cli-assets/route.js +1 -1
  39. package/dist/web/.next/server/app/api/cli-assets/route.js.nft.json +1 -1
  40. package/dist/web/.next/server/app/api/cli-assets/store/preview/route.js +1 -1
  41. package/dist/web/.next/server/app/api/cli-assets/store/preview/route.js.nft.json +1 -1
  42. package/dist/web/.next/server/app/api/cli-assets/store/route.js +1 -1
  43. package/dist/web/.next/server/app/api/cli-assets/store/route.js.nft.json +1 -1
  44. package/dist/web/.next/server/app/api/cli-assets/sync/route.js +1 -1
  45. package/dist/web/.next/server/app/api/cli-assets/sync/route.js.nft.json +1 -1
  46. package/dist/web/.next/server/app/api/cli-assets/updates/route.js +1 -1
  47. package/dist/web/.next/server/app/api/cli-assets/updates/route.js.nft.json +1 -1
  48. package/dist/web/.next/server/app/api/dashboard/route.js +1 -1
  49. package/dist/web/.next/server/app/api/dashboard/route.js.nft.json +1 -1
  50. package/dist/web/.next/server/app/api/file/route.js +1 -1
  51. package/dist/web/.next/server/app/api/file/route.js.nft.json +1 -1
  52. package/dist/web/.next/server/app/api/git-status/route.js +1 -1
  53. package/dist/web/.next/server/app/api/git-status/route.js.nft.json +1 -1
  54. package/dist/web/.next/server/app/api/kanban/route.js +1 -1
  55. package/dist/web/.next/server/app/api/kanban/route.js.nft.json +1 -1
  56. package/dist/web/.next/server/app/api/kanban/stream/route.js +1 -1
  57. package/dist/web/.next/server/app/api/kanban/stream/route.js.nft.json +1 -1
  58. package/dist/web/.next/server/app/api/projects/[id]/route.js +1 -1
  59. package/dist/web/.next/server/app/api/projects/[id]/route.js.nft.json +1 -1
  60. package/dist/web/.next/server/app/api/projects/reorder/route.js +1 -1
  61. package/dist/web/.next/server/app/api/projects/reorder/route.js.nft.json +1 -1
  62. package/dist/web/.next/server/app/api/projects/route.js +1 -1
  63. package/dist/web/.next/server/app/api/projects/route.js.nft.json +1 -1
  64. package/dist/web/.next/server/app/api/scheduler/route.js +1 -1
  65. package/dist/web/.next/server/app/api/scheduler/route.js.nft.json +1 -1
  66. package/dist/web/.next/server/app/api/search/route.js +1 -1
  67. package/dist/web/.next/server/app/api/search/route.js.nft.json +1 -1
  68. package/dist/web/.next/server/app/api/sly-actions/invalidate/route.js +1 -1
  69. package/dist/web/.next/server/app/api/sly-actions/invalidate/route.js.nft.json +1 -1
  70. package/dist/web/.next/server/app/api/sly-actions/route.js +1 -1
  71. package/dist/web/.next/server/app/api/sly-actions/route.js.nft.json +1 -1
  72. package/dist/web/.next/server/app/api/sly-actions/stream/route.js +1 -1
  73. package/dist/web/.next/server/app/api/sly-actions/stream/route.js.nft.json +1 -1
  74. package/dist/web/.next/server/app/api/transcribe/route.js +5 -2
  75. package/dist/web/.next/server/app/api/transcribe/route.js.nft.json +1 -1
  76. package/dist/web/.next/server/app/page/build-manifest.json +2 -2
  77. package/dist/web/.next/server/app/page.js +2 -2
  78. package/dist/web/.next/server/app/page.js.nft.json +1 -1
  79. package/dist/web/.next/server/app/page_client-reference-manifest.js +1 -1
  80. package/dist/web/.next/server/app/project/[id]/page/build-manifest.json +2 -2
  81. package/dist/web/.next/server/app/project/[id]/page.js +2 -2
  82. package/dist/web/.next/server/app/project/[id]/page.js.nft.json +1 -1
  83. package/dist/web/.next/server/app/project/[id]/page_client-reference-manifest.js +1 -1
  84. package/dist/web/.next/server/chunks/[externals]__c6831f39._.js +3 -0
  85. package/dist/web/.next/server/chunks/[externals]_node:async_hooks_b485b2a4._.js +3 -0
  86. package/dist/web/.next/server/chunks/[externals]_node:http_c3a9e25f._.js +3 -0
  87. package/dist/web/.next/server/chunks/[root-of-the-server]__0234f869._.js +3 -0
  88. package/dist/web/.next/server/chunks/[root-of-the-server]__0f8687da._.js +3 -0
  89. package/dist/web/.next/server/chunks/[root-of-the-server]__104b2da3._.js +4 -0
  90. package/dist/web/.next/server/chunks/[root-of-the-server]__12f6cd6f._.js +3 -0
  91. package/dist/web/.next/server/chunks/{[root-of-the-server]__d219e3f0._.js → [root-of-the-server]__15fc9266._.js} +2 -2
  92. package/dist/web/.next/server/chunks/[root-of-the-server]__1cdc76ef._.js +3 -0
  93. package/dist/web/.next/server/chunks/{[root-of-the-server]__933e6077._.js → [root-of-the-server]__1ec21ccc._.js} +2 -2
  94. package/dist/web/.next/server/chunks/{[root-of-the-server]__baa99257._.js → [root-of-the-server]__279e9bf3._.js} +2 -2
  95. package/dist/web/.next/server/chunks/{[root-of-the-server]__46b023d4._.js → [root-of-the-server]__2d1f0ed9._.js} +2 -2
  96. package/dist/web/.next/server/chunks/[root-of-the-server]__3998d59e._.js +19 -0
  97. package/dist/web/.next/server/chunks/{[root-of-the-server]__e9b0e744._.js → [root-of-the-server]__3c5ef8ec._.js} +2 -2
  98. package/dist/web/.next/server/chunks/[root-of-the-server]__3dc5531f._.js +5 -0
  99. package/dist/web/.next/server/chunks/[root-of-the-server]__403750fd._.js +3 -0
  100. package/dist/web/.next/server/chunks/[root-of-the-server]__41df0777._.js +3 -0
  101. package/dist/web/.next/server/chunks/{[root-of-the-server]__b9e7d34c._.js → [root-of-the-server]__4244617a._.js} +2 -2
  102. package/dist/web/.next/server/chunks/{[root-of-the-server]__949bb248._.js → [root-of-the-server]__47dd878e._.js} +2 -2
  103. package/dist/web/.next/server/chunks/{[root-of-the-server]__bf286c26._.js → [root-of-the-server]__5e08b942._.js} +2 -2
  104. package/dist/web/.next/server/chunks/{[root-of-the-server]__d38c7a96._.js → [root-of-the-server]__71bb3374._.js} +2 -2
  105. package/dist/web/.next/server/chunks/{[root-of-the-server]__6d330d40._.js → [root-of-the-server]__7603305e._.js} +2 -2
  106. package/dist/web/.next/server/chunks/{[root-of-the-server]__aa814a86._.js → [root-of-the-server]__7c476ad6._.js} +2 -2
  107. package/dist/web/.next/server/chunks/{[root-of-the-server]__bbb4b3ac._.js → [root-of-the-server]__846ca56f._.js} +2 -2
  108. package/dist/web/.next/server/chunks/[root-of-the-server]__934c9bb4._.js +5 -0
  109. package/dist/web/.next/server/chunks/{[root-of-the-server]__4d0d3464._.js → [root-of-the-server]__98d88050._.js} +2 -2
  110. package/dist/web/.next/server/chunks/{[root-of-the-server]__4c7995bf._.js → [root-of-the-server]__b273cc05._.js} +2 -2
  111. package/dist/web/.next/server/chunks/{[root-of-the-server]__bc55c42a._.js → [root-of-the-server]__d0f4efec._.js} +2 -2
  112. package/dist/web/.next/server/chunks/[root-of-the-server]__d6362272._.js +3 -0
  113. package/dist/web/.next/server/chunks/{[root-of-the-server]__ad64e04f._.js → [root-of-the-server]__de1277ee._.js} +2 -2
  114. package/dist/web/.next/server/chunks/[root-of-the-server]__e00fb85f._.js +9 -0
  115. package/dist/web/.next/server/chunks/[root-of-the-server]__e4988941._.js +4 -0
  116. package/dist/web/.next/server/chunks/[root-of-the-server]__f3e501b6._.js +3 -0
  117. package/dist/web/.next/server/chunks/{[root-of-the-server]__f1fe18e6._.js → [root-of-the-server]__f97e93fa._.js} +2 -2
  118. package/dist/web/.next/server/chunks/a9bf9_@aws-sdk_core_dist-es_submodules_protocols_json_AwsRestJsonProtocol_764d7a83.js +3 -0
  119. package/dist/web/.next/server/chunks/node_modules_@aws-sdk_credential-provider-env_dist-es_index_55d96139.js +3 -0
  120. package/dist/web/.next/server/chunks/node_modules_@aws-sdk_nested-clients_dist-es_submodules_signin_index_6712dec9.js +3 -0
  121. package/dist/web/.next/server/chunks/node_modules_@aws-sdk_nested-clients_dist-es_submodules_sso-oidc_index_df09d547.js +3 -0
  122. package/dist/web/.next/server/chunks/node_modules_@aws-sdk_nested-clients_dist-es_submodules_sts_index_c78619b3.js +3 -0
  123. package/dist/web/.next/server/chunks/node_modules_@smithy_core_dist-es_submodules_event-streams_index_73e35402.js +3 -0
  124. package/dist/web/.next/server/chunks/node_modules_ec9ae6c4._.js +18 -0
  125. package/dist/web/.next/server/chunks/src_677020aa._.js +1 -1
  126. package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__077f472c._.js +3 -0
  127. package/dist/web/.next/server/chunks/ssr/{[root-of-the-server]__977b1a68._.js → [root-of-the-server]__0841f0b5._.js} +2 -2
  128. package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__1f5fc489._.js +9 -0
  129. package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__2f64210a._.js +3 -0
  130. package/dist/web/.next/server/chunks/ssr/{[root-of-the-server]__d57c94e0._.js → [root-of-the-server]__92021d33._.js} +2 -2
  131. package/dist/web/.next/server/chunks/ssr/{[root-of-the-server]__e0032864._.js → [root-of-the-server]__9ac6ea25._.js} +2 -2
  132. package/dist/web/.next/server/chunks/ssr/{[root-of-the-server]__677b7660._.js → [root-of-the-server]__bcbe4bf2._.js} +2 -2
  133. package/dist/web/.next/server/chunks/ssr/{[root-of-the-server]__6db27fe1._.js → [root-of-the-server]__dfe2728c._.js} +2 -2
  134. package/dist/web/.next/server/chunks/ssr/src_components_Dashboard_tsx_efc4dc27._.js +1 -1
  135. package/dist/web/.next/server/middleware-build-manifest.js +2 -2
  136. package/dist/web/.next/server/pages/404.html +1 -1
  137. package/dist/web/.next/server/pages/500.html +2 -2
  138. package/dist/web/.next/static/chunks/0bab8c7c82cef548.js +4 -0
  139. package/dist/web/.next/static/chunks/{b694f741bb34ac84.js → 1b46e836090e130b.js} +1 -1
  140. package/dist/web/.next/static/chunks/{ef0dc6461e9250d8.js → cf910fc299170b7d.js} +2 -2
  141. package/dist/web/.next/static/chunks/de965b463aff71a9.js +1 -0
  142. package/dist/web/.next/static/chunks/f3d7065d54a0b9ac.css +1 -0
  143. package/dist/web/.next/static/chunks/{turbopack-4964158913ad5e72.js → turbopack-563e27abda63930f.js} +2 -2
  144. package/dist/web/package-lock.json +1589 -155
  145. package/dist/web/package.json +1 -0
  146. package/dist/web/src/app/api/transcribe/route.ts +96 -1
  147. package/dist/web/src/components/AddProjectModal.tsx +67 -35
  148. package/dist/web/tsconfig.tsbuildinfo +1 -1
  149. package/package.json +1 -1
  150. package/templates/kanban-seed.json +1 -1
  151. package/dist/data/scaffold-templates/seed-cards.json +0 -20
  152. package/dist/web/.next/server/chunks/[externals]__65615fd8._.js +0 -3
  153. package/dist/web/.next/server/chunks/[root-of-the-server]__884d73e4._.js +0 -3
  154. package/dist/web/.next/server/chunks/[root-of-the-server]__8ab096a3._.js +0 -3
  155. package/dist/web/.next/server/chunks/[root-of-the-server]__9058a007._.js +0 -3
  156. package/dist/web/.next/server/chunks/[root-of-the-server]__f4124388._.js +0 -3
  157. package/dist/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_ffc6c790.js +0 -11
  158. package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__73cfc6bb._.js +0 -3
  159. package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__79c0188d._.js +0 -9
  160. package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__bb807bcf._.js +0 -3
  161. package/dist/web/.next/static/chunks/7660448872d80e27.js +0 -4
  162. package/dist/web/.next/static/chunks/f4999889f2006fea.css +0 -1
  163. package/dist/web/.next/static/chunks/fbbdd316640f1e15.js +0 -1
  164. /package/dist/web/.next/static/{sQ8knGS1B967Ng9HqMKOG → 2EjhdcgXP_G4Dc2aTnMhk}/_buildManifest.js +0 -0
  165. /package/dist/web/.next/static/{sQ8knGS1B967Ng9HqMKOG → 2EjhdcgXP_G4Dc2aTnMhk}/_clientMiddlewareManifest.json +0 -0
  166. /package/dist/web/.next/static/{sQ8knGS1B967Ng9HqMKOG → 2EjhdcgXP_G4Dc2aTnMhk}/_ssgManifest.js +0 -0
@@ -11,6 +11,7 @@
11
11
  "lint": "eslint"
12
12
  },
13
13
  "dependencies": {
14
+ "@aws-sdk/client-transcribe-streaming": "^3.1012.0",
14
15
  "@tailwindcss/typography": "^0.5.19",
15
16
  "@xterm/addon-fit": "^0.11.0",
16
17
  "@xterm/addon-web-links": "^0.12.0",
@@ -4,8 +4,16 @@ import { promises as fs } from 'fs';
4
4
  import path from 'path';
5
5
  import { execFile } from 'child_process';
6
6
  import { getSlycodeRoot } from '@/lib/paths';
7
+ import {
8
+ TranscribeStreamingClient,
9
+ StartStreamTranscriptionCommand,
10
+ type AudioStream,
11
+ type LanguageCode,
12
+ type MediaEncoding,
13
+ } from '@aws-sdk/client-transcribe-streaming';
7
14
 
8
15
  let openaiClient: OpenAI | null = null;
16
+ let transcribeClient: TranscribeStreamingClient | null = null;
9
17
  let envLoaded = false;
10
18
  let envCache: Record<string, string> = {};
11
19
 
@@ -41,6 +49,13 @@ async function getClient(): Promise<OpenAI> {
41
49
  return openaiClient;
42
50
  }
43
51
 
52
+ function getTranscribeClient(region?: string): TranscribeStreamingClient {
53
+ if (!transcribeClient) {
54
+ transcribeClient = new TranscribeStreamingClient(region ? { region } : {});
55
+ }
56
+ return transcribeClient;
57
+ }
58
+
44
59
  function execAsync(cmd: string, args: string[], options: Record<string, unknown>): Promise<string> {
45
60
  return new Promise((resolve, reject) => {
46
61
  execFile(cmd, args, { ...options, windowsHide: true }, (error, stdout, stderr) => {
@@ -76,6 +91,78 @@ async function transcribeLocal(audioBuffer: Buffer, ext: string, cliPath: string
76
91
  }
77
92
  }
78
93
 
94
+ async function transcribeAwsStreaming(audioBuffer: Buffer, ext: string, region: string, language: string): Promise<string> {
95
+ const client = getTranscribeClient(region || undefined);
96
+
97
+ const isOgg = ext === 'ogg' || ext === 'oga';
98
+
99
+ let streamBuffer: Buffer;
100
+ let mediaEncoding: MediaEncoding;
101
+ let sampleRate: number;
102
+ let pcmPath: string | null = null;
103
+
104
+ if (isOgg) {
105
+ // OGG/Opus can be sent directly
106
+ streamBuffer = audioBuffer;
107
+ mediaEncoding = 'ogg-opus';
108
+ sampleRate = 48000;
109
+ } else {
110
+ // Convert WebM/MP4 to PCM via ffmpeg
111
+ const tmpDir = process.env.TMPDIR || '/tmp';
112
+ const tmpFile = path.join(tmpDir, `transcribe_${Date.now()}.${ext}`);
113
+ pcmPath = tmpFile.replace(/\.[^.]+$/, '.pcm');
114
+
115
+ try {
116
+ await fs.writeFile(tmpFile, audioBuffer);
117
+ await execAsync('ffmpeg', [
118
+ '-i', tmpFile, '-ar', '16000', '-ac', '1', '-f', 's16le', '-y', pcmPath,
119
+ ], { timeout: 30_000 });
120
+ streamBuffer = await fs.readFile(pcmPath);
121
+ await fs.unlink(tmpFile).catch(() => {});
122
+ } catch (err) {
123
+ await fs.unlink(tmpFile).catch(() => {});
124
+ throw new Error(`ffmpeg conversion to PCM failed: ${(err as Error).message}`);
125
+ }
126
+ mediaEncoding = 'pcm';
127
+ sampleRate = 16000;
128
+ }
129
+
130
+ const chunkSize = 4096;
131
+ async function* audioStream(): AsyncGenerator<AudioStream> {
132
+ for (let offset = 0; offset < streamBuffer.length; offset += chunkSize) {
133
+ yield { AudioEvent: { AudioChunk: streamBuffer.subarray(offset, offset + chunkSize) } };
134
+ }
135
+ }
136
+
137
+ try {
138
+ const response = await client.send(new StartStreamTranscriptionCommand({
139
+ LanguageCode: language as LanguageCode,
140
+ MediaEncoding: mediaEncoding,
141
+ MediaSampleRateHertz: sampleRate,
142
+ AudioStream: audioStream(),
143
+ }));
144
+
145
+ const parts: string[] = [];
146
+ if (response.TranscriptResultStream) {
147
+ for await (const event of response.TranscriptResultStream) {
148
+ if (event.TranscriptEvent?.Transcript?.Results) {
149
+ for (const result of event.TranscriptEvent.Transcript.Results) {
150
+ if (!result.IsPartial && result.Alternatives?.[0]?.Transcript) {
151
+ parts.push(result.Alternatives[0].Transcript);
152
+ }
153
+ }
154
+ }
155
+ }
156
+ }
157
+
158
+ return parts.join(' ').trim();
159
+ } finally {
160
+ if (pcmPath) {
161
+ await fs.unlink(pcmPath).catch(() => {});
162
+ }
163
+ }
164
+ }
165
+
79
166
  export async function POST(request: Request) {
80
167
  try {
81
168
  const formData = await request.formData();
@@ -102,7 +189,15 @@ export async function POST(request: Request) {
102
189
  return NextResponse.json({ text });
103
190
  }
104
191
 
105
- // OpenAI API path
192
+ if (backend === 'aws-transcribe') {
193
+ const region = process.env.AWS_TRANSCRIBE_REGION || env.AWS_TRANSCRIBE_REGION || '';
194
+ const language = process.env.AWS_TRANSCRIBE_LANGUAGE || env.AWS_TRANSCRIBE_LANGUAGE || 'en-AU';
195
+
196
+ const text = await transcribeAwsStreaming(buffer, ext, region, language);
197
+ return NextResponse.json({ text });
198
+ }
199
+
200
+ // OpenAI API path (default)
106
201
  let client: OpenAI;
107
202
  try {
108
203
  client = await getClient();
@@ -15,6 +15,7 @@ interface AnalysisItem {
15
15
  details?: { count?: number };
16
16
  group?: string;
17
17
  provider?: string;
18
+ essential?: boolean;
18
19
  }
19
20
 
20
21
  interface AnalysisGroup {
@@ -598,7 +599,7 @@ export function AddProjectModal({ open, onClose, onCreated }: AddProjectModalPro
598
599
  {/* Expanded items */}
599
600
  {expanded && (
600
601
  <div className="border-t border-void-700/50">
601
- {group.items.map((item) => {
602
+ {[...group.items].sort((a, b) => (b.essential ? 1 : 0) - (a.essential ? 1 : 0)).map((item) => {
602
603
  const action = itemActions[item.path] || 'skip';
603
604
  const isMissing = item.status === 'missing';
604
605
  const isPresent = item.status === 'present';
@@ -608,45 +609,64 @@ export function AddProjectModal({ open, onClose, onCreated }: AddProjectModalPro
608
609
  return (
609
610
  <div
610
611
  key={item.path}
611
- className="flex items-center justify-between border-b border-void-700/30 px-3 py-1.5 last:border-0"
612
+ className={`flex flex-col border-b border-void-700/30 px-3 py-1.5 last:border-0 ${item.essential ? 'bg-amber-900/10' : ''}`}
612
613
  >
613
- <div className="flex min-w-0 items-center gap-2">
614
- {isMissing && (
615
- <span
616
- className="flex-shrink-0 text-blue-400"
617
- title="Will be created"
618
- >
619
- +
614
+ <div className="flex items-center justify-between">
615
+ <div className="flex min-w-0 items-center gap-2">
616
+ {isMissing && (
617
+ <span
618
+ className="flex-shrink-0 text-blue-400"
619
+ title="Will be created"
620
+ >
621
+ +
622
+ </span>
623
+ )}
624
+ {isPresent && (
625
+ <span
626
+ className="flex-shrink-0 text-green-400"
627
+ title={hasDifferentVersion ? `Local: ${item.localVersion}, Store: ${item.masterVersion}` : 'Up to date'}
628
+ >
629
+ &#x2713;
630
+ </span>
631
+ )}
632
+ <span className="truncate font-mono text-xs text-void-300">
633
+ {item.path}
620
634
  </span>
621
- )}
622
- {isPresent && (
623
- <span
624
- className="flex-shrink-0 text-green-400"
625
- title={hasDifferentVersion ? `Local: ${item.localVersion}, Store: ${item.masterVersion}` : 'Up to date'}
635
+ {item.essential && (
636
+ <span className="flex-shrink-0 rounded-full bg-amber-500/20 px-1.5 py-0.5 text-[10px] font-medium text-amber-400">
637
+ Required
638
+ </span>
639
+ )}
640
+ {hasDifferentVersion && (
641
+ <span className="flex-shrink-0 text-xs text-void-600">
642
+ v{item.localVersion}
643
+ </span>
644
+ )}
645
+ </div>
646
+ {isMissing && (
647
+ <button
648
+ onClick={() => toggleAction(item.path)}
649
+ className={`flex-shrink-0 rounded px-2 py-0.5 text-xs font-medium ${
650
+ action === 'skip'
651
+ ? 'bg-void-700 text-void-400'
652
+ : 'bg-blue-600/20 text-blue-400'
653
+ }`}
626
654
  >
627
- &#x2713;
628
- </span>
629
- )}
630
- <span className="truncate font-mono text-xs text-void-300">
631
- {item.path}
632
- </span>
633
- {hasDifferentVersion && (
634
- <span className="flex-shrink-0 text-xs text-void-600">
635
- v{item.localVersion}
636
- </span>
655
+ {action === 'skip' ? 'Skip' : 'Create'}
656
+ </button>
637
657
  )}
638
658
  </div>
639
- {isMissing && (
640
- <button
641
- onClick={() => toggleAction(item.path)}
642
- className={`flex-shrink-0 rounded px-2 py-0.5 text-xs font-medium ${
643
- action === 'skip'
644
- ? 'bg-void-700 text-void-400'
645
- : 'bg-blue-600/20 text-blue-400'
646
- }`}
647
- >
648
- {action === 'skip' ? 'Skip' : 'Create'}
649
- </button>
659
+ {/* Warning when essential skill is skipped */}
660
+ {item.essential && isMissing && action === 'skip' && (
661
+ <p className="mt-1 text-[11px] text-amber-400/80">
662
+ SlyCode requires this skill to function properly.
663
+ </p>
664
+ )}
665
+ {/* Warning when essential skill already exists (may be overwritten by updates) */}
666
+ {item.essential && isPresent && (
667
+ <p className="mt-1 text-[11px] text-void-500">
668
+ Exists will be kept. Updates are managed via CLI Assets.
669
+ </p>
650
670
  )}
651
671
  </div>
652
672
  );
@@ -658,6 +678,18 @@ export function AddProjectModal({ open, onClose, onCreated }: AddProjectModalPro
658
678
  })}
659
679
  </div>
660
680
 
681
+ {/* Warning if essential skills are being skipped */}
682
+ {analysis.items.some(i => i.essential && i.status === 'missing' && itemActions[i.path] === 'skip') && (
683
+ <div className="rounded border border-amber-500/30 bg-amber-500/10 px-3 py-2">
684
+ <p className="text-xs font-medium text-amber-400">
685
+ Essential skills skipped
686
+ </p>
687
+ <p className="mt-0.5 text-[11px] text-amber-400/70">
688
+ Kanban and messaging skills are required for SlyCode&apos;s core functionality (card management, Telegram integration). The project will scaffold but some features won&apos;t work.
689
+ </p>
690
+ </div>
691
+ )}
692
+
661
693
  <div className="flex justify-between pt-2">
662
694
  <button
663
695
  onClick={() => setPhase('providers')}