@remix-run/cli 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +0 -3
  2. package/bootstrap/.agents/skills/remix/SKILL.md +501 -0
  3. package/bootstrap/.agents/skills/remix/references/animate-elements.md +195 -0
  4. package/bootstrap/.agents/skills/remix/references/assets-and-browser-modules.md +122 -0
  5. package/bootstrap/.agents/skills/remix/references/auth-and-sessions.md +420 -0
  6. package/bootstrap/.agents/skills/remix/references/component-model.md +282 -0
  7. package/bootstrap/.agents/skills/remix/references/create-mixins.md +158 -0
  8. package/bootstrap/.agents/skills/remix/references/data-and-validation.md +363 -0
  9. package/bootstrap/.agents/skills/remix/references/hydration-frames-navigation.md +297 -0
  10. package/bootstrap/.agents/skills/remix/references/middleware-and-server.md +243 -0
  11. package/bootstrap/.agents/skills/remix/references/mixins-styling-events.md +213 -0
  12. package/bootstrap/.agents/skills/remix/references/routing-and-controllers.md +324 -0
  13. package/bootstrap/.agents/skills/remix/references/testing-patterns.md +156 -0
  14. package/bootstrap/AGENTS.md +4 -0
  15. package/bootstrap/app/assets/entry.ts +19 -0
  16. package/bootstrap/app/assets.ts +18 -0
  17. package/bootstrap/app/controllers/auth.tsx +2 -2
  18. package/bootstrap/app/controllers/home.tsx +3 -18
  19. package/bootstrap/app/router.ts +6 -0
  20. package/bootstrap/app/routes.ts +2 -1
  21. package/bootstrap/app/ui/document.tsx +6 -1
  22. package/bootstrap/app/ui/prompt-button.tsx +162 -0
  23. package/bootstrap/app/ui/scaffold-home-page.tsx +526 -0
  24. package/bootstrap/app/utils/render.tsx +22 -3
  25. package/bootstrap/server.ts +13 -13
  26. package/bootstrap/tsconfig.json +0 -1
  27. package/dist/lib/cli.d.ts.map +1 -1
  28. package/dist/lib/cli.js +7 -10
  29. package/dist/lib/commands/help.d.ts.map +1 -1
  30. package/dist/lib/commands/help.js +9 -33
  31. package/dist/lib/commands/test.d.ts +1 -1
  32. package/dist/lib/commands/test.d.ts.map +1 -1
  33. package/dist/lib/commands/test.js +8 -4
  34. package/dist/lib/completion.d.ts.map +1 -1
  35. package/dist/lib/completion.js +3 -101
  36. package/dist/lib/errors.d.ts +0 -6
  37. package/dist/lib/errors.d.ts.map +1 -1
  38. package/dist/lib/errors.js +0 -11
  39. package/package.json +3 -4
  40. package/src/lib/cli.ts +7 -11
  41. package/src/lib/commands/help.ts +9 -43
  42. package/src/lib/commands/test.ts +10 -4
  43. package/src/lib/completion.ts +3 -146
  44. package/src/lib/errors.ts +0 -12
  45. package/dist/lib/commands/skills.d.ts +0 -6
  46. package/dist/lib/commands/skills.d.ts.map +0 -1
  47. package/dist/lib/commands/skills.js +0 -222
  48. package/dist/lib/skills-cache.d.ts +0 -19
  49. package/dist/lib/skills-cache.d.ts.map +0 -1
  50. package/dist/lib/skills-cache.js +0 -89
  51. package/dist/lib/skills.d.ts +0 -30
  52. package/dist/lib/skills.d.ts.map +0 -1
  53. package/dist/lib/skills.js +0 -441
  54. package/src/lib/commands/skills.ts +0 -306
  55. package/src/lib/skills-cache.ts +0 -140
  56. package/src/lib/skills.ts +0 -706
@@ -4,27 +4,8 @@ export interface CompletionResult {
4
4
  }
5
5
 
6
6
  const COMPLETION_SHELLS = ['bash', 'zsh'] as const
7
- const HELP_COMMANDS = [
8
- 'completion',
9
- 'doctor',
10
- 'help',
11
- 'new',
12
- 'routes',
13
- 'skills',
14
- 'test',
15
- 'version',
16
- ] as const
17
- const ROOT_COMMANDS = [
18
- 'completion',
19
- 'doctor',
20
- 'help',
21
- 'new',
22
- 'routes',
23
- 'skills',
24
- 'test',
25
- 'version',
26
- ] as const
27
- const SKILLS_COMMANDS = ['install', 'list'] as const
7
+ const HELP_COMMANDS = ['completion', 'doctor', 'help', 'new', 'routes', 'test', 'version'] as const
8
+ const ROOT_COMMANDS = ['completion', 'doctor', 'help', 'new', 'routes', 'test', 'version'] as const
28
9
 
29
10
  export type CompletionShell = (typeof COMPLETION_SHELLS)[number]
30
11
 
@@ -183,10 +164,6 @@ function completeCommand(
183
164
  return completeRoutes(tokens, currentWord, usedGlobalFlags)
184
165
  }
185
166
 
186
- if (command === 'skills') {
187
- return completeSkills(tokens, currentWord, usedGlobalFlags)
188
- }
189
-
190
167
  if (command === 'version') {
191
168
  return completeSimpleFlags(tokens, currentWord, usedGlobalFlags, [])
192
169
  }
@@ -220,12 +197,8 @@ function completeHelp(
220
197
  return completeValues(withHelpFlags([...HELP_COMMANDS], usedGlobalFlags), currentWord)
221
198
  }
222
199
 
223
- let [topic, ...rest] = filteredTokens
200
+ let [, ...rest] = filteredTokens
224
201
  if (rest.length === 0) {
225
- if (topic === 'skills') {
226
- return completeValues(withHelpFlags([...SKILLS_COMMANDS], usedGlobalFlags), currentWord)
227
- }
228
-
229
202
  return completeValues([], currentWord)
230
203
  }
231
204
 
@@ -344,122 +317,6 @@ function completeRoutes(
344
317
  return completeValues(flags, currentWord)
345
318
  }
346
319
 
347
- function completeSkills(
348
- tokens: string[],
349
- currentWord: string,
350
- usedGlobalFlags: Set<string>,
351
- ): CompletionResult {
352
- let filteredTokens = filterGlobalCommandTokens(tokens, usedGlobalFlags)
353
- if (filteredTokens == null) {
354
- return completeValues([], currentWord)
355
- }
356
-
357
- if (filteredTokens.length === 0) {
358
- return completeValues(withHelpFlags([...SKILLS_COMMANDS], usedGlobalFlags), currentWord)
359
- }
360
-
361
- let [subcommand, ...rest] = filteredTokens
362
- if (subcommand === 'install') {
363
- return completeSkillsInstall(rest, currentWord, usedGlobalFlags)
364
- }
365
-
366
- if (subcommand === 'list') {
367
- return completeSkillsList(rest, currentWord, usedGlobalFlags)
368
- }
369
-
370
- return completeValues([], currentWord)
371
- }
372
-
373
- function completeSkillsInstall(
374
- tokens: string[],
375
- currentWord: string,
376
- usedGlobalFlags: Set<string>,
377
- ): CompletionResult {
378
- let filteredTokens = filterGlobalCommandTokens(tokens, usedGlobalFlags)
379
- if (filteredTokens == null) {
380
- return completeValues([], currentWord)
381
- }
382
-
383
- let hasDir = false
384
- let expectsDir = false
385
-
386
- for (let token of filteredTokens) {
387
- if (expectsDir) {
388
- expectsDir = false
389
- continue
390
- }
391
-
392
- if (token === '--dir') {
393
- hasDir = true
394
- expectsDir = true
395
- continue
396
- }
397
-
398
- return completeValues([], currentWord)
399
- }
400
-
401
- if (expectsDir) {
402
- return { mode: 'files' }
403
- }
404
-
405
- return completeValues(withHelpFlags(!hasDir ? ['--dir'] : [], usedGlobalFlags), currentWord)
406
- }
407
-
408
- function completeSkillsList(
409
- tokens: string[],
410
- currentWord: string,
411
- usedGlobalFlags: Set<string>,
412
- ): CompletionResult {
413
- return completeSkillsDirectoryCommand(tokens, currentWord, usedGlobalFlags)
414
- }
415
-
416
- function completeSkillsDirectoryCommand(
417
- tokens: string[],
418
- currentWord: string,
419
- usedGlobalFlags: Set<string>,
420
- ): CompletionResult {
421
- let filteredTokens = filterGlobalCommandTokens(tokens, usedGlobalFlags)
422
- if (filteredTokens == null) {
423
- return completeValues([], currentWord)
424
- }
425
-
426
- let hasDir = false
427
- let hasJson = false
428
- let expectsDir = false
429
-
430
- for (let token of filteredTokens) {
431
- if (expectsDir) {
432
- expectsDir = false
433
- continue
434
- }
435
-
436
- if (token === '--dir') {
437
- hasDir = true
438
- expectsDir = true
439
- continue
440
- }
441
-
442
- if (token === '--json') {
443
- hasJson = true
444
- continue
445
- }
446
-
447
- return completeValues([], currentWord)
448
- }
449
-
450
- if (expectsDir) {
451
- return { mode: 'files' }
452
- }
453
-
454
- return completeValues(
455
- withHelpFlags(
456
- [...(!hasDir ? ['--dir'] : []), ...(!hasJson ? ['--json'] : [])],
457
- usedGlobalFlags,
458
- ),
459
- currentWord,
460
- )
461
- }
462
-
463
320
  function completeCompletionCommand(
464
321
  tokens: string[],
465
322
  currentWord: string,
package/src/lib/errors.ts CHANGED
@@ -133,11 +133,6 @@ export const CLI_ERROR_DEFINITIONS = {
133
133
  title: 'Unknown help topic',
134
134
  fix: 'Run `remix help` to see available commands and help topics.',
135
135
  },
136
- unknownSkillsCommand: {
137
- code: 'RMX_UNKNOWN_SKILLS_COMMAND',
138
- title: 'Unknown skills command',
139
- fix: 'Run `remix skills --help` to see available skills commands.',
140
- },
141
136
  } satisfies Record<string, CliErrorDefinition>
142
137
 
143
138
  export class CliError extends Error {
@@ -321,13 +316,6 @@ export function unknownHelpTopic(topic: string): UsageError {
321
316
  })
322
317
  }
323
318
 
324
- export function unknownSkillsCommand(command: string): UsageError {
325
- return createUsageError(CLI_ERROR_DEFINITIONS.unknownSkillsCommand, {
326
- context: { command },
327
- message: `Unknown skills command: ${command}`,
328
- })
329
- }
330
-
331
319
  export function unexpectedExtraArgument(argument: string): UsageError {
332
320
  return createUsageError(CLI_ERROR_DEFINITIONS.unexpectedExtraArgument, {
333
321
  context: { argument },
@@ -1,6 +0,0 @@
1
- import type { CliContext } from '../cli-context.ts';
2
- export declare function runSkillsCommand(argv: string[], context: CliContext): Promise<number>;
3
- export declare function getSkillsCommandHelpText(target?: NodeJS.WriteStream): string;
4
- export declare function getSkillsInstallCommandHelpText(target?: NodeJS.WriteStream): string;
5
- export declare function getSkillsListCommandHelpText(target?: NodeJS.WriteStream): string;
6
- //# sourceMappingURL=skills.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../../src/lib/commands/skills.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAsBnD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAwB3F;AAED,wBAAgB,wBAAwB,CAAC,MAAM,GAAE,MAAM,CAAC,WAA4B,GAAG,MAAM,CAwB5F;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,GAAE,MAAM,CAAC,WAA4B,GAC1C,MAAM,CAeR;AAED,wBAAgB,4BAA4B,CAAC,MAAM,GAAE,MAAM,CAAC,WAA4B,GAAG,MAAM,CAqBhG"}
@@ -1,222 +0,0 @@
1
- import * as process from 'node:process';
2
- import { getDisplayPath } from "../display-path.js";
3
- import { renderCliError, toCliError, unknownSkillsCommand } from "../errors.js";
4
- import { formatHelpText } from "../help-text.js";
5
- import { parseArgs } from "../parse-args.js";
6
- import { getSkillsOverview, installRemixSkills } from "../skills.js";
7
- import { createCommandReporter, createStepProgressReporter, } from "../reporter.js";
8
- const SKILLS_PROGRESS_LABELS = {
9
- 'compare-local-skills': 'Compare local skills',
10
- 'download-remix-skills-archive': 'Download Remix skills archive',
11
- 'fetch-remix-skills-metadata': 'Fetch Remix skills metadata from GitHub',
12
- 'read-local-skills-cache': 'Read local skills cache',
13
- 'resolve-project-root': 'Resolve project root',
14
- 'write-updated-skills': 'Write updated skills',
15
- };
16
- export async function runSkillsCommand(argv, context) {
17
- if (argv.length === 0 || argv[0] === '-h' || argv[0] === '--help') {
18
- process.stdout.write(getSkillsCommandHelpText());
19
- return 0;
20
- }
21
- let [subcommand, ...rest] = argv;
22
- try {
23
- if (subcommand === 'install') {
24
- return await runSkillsInstallCommand(rest, context);
25
- }
26
- if (subcommand === 'list') {
27
- return await runSkillsListCommand(rest, context);
28
- }
29
- throw unknownSkillsCommand(subcommand);
30
- }
31
- catch (error) {
32
- process.stderr.write(renderCliError(toCliError(error), { helpText: getSkillsCommandHelpText(process.stderr) }));
33
- return 1;
34
- }
35
- }
36
- export function getSkillsCommandHelpText(target = process.stdout) {
37
- return formatHelpText({
38
- commands: [
39
- {
40
- description: 'Install Remix skills into a local directory',
41
- label: 'install [--dir <path>]',
42
- },
43
- {
44
- description: 'List Remix skills and local status',
45
- label: 'list [--dir <path>] [--json]',
46
- },
47
- ],
48
- description: 'Manage Remix skills for the current project.',
49
- examples: [
50
- 'remix skills install',
51
- 'remix skills install --dir custom/skills',
52
- 'remix skills list --dir custom/skills',
53
- 'remix skills list --json',
54
- ],
55
- usage: ['remix skills <command>'],
56
- }, target);
57
- }
58
- export function getSkillsInstallCommandHelpText(target = process.stdout) {
59
- return formatHelpText({
60
- description: 'Install or refresh Remix skills in .agents/skills for the current project.',
61
- examples: ['remix skills install', 'remix skills install --dir custom/skills'],
62
- options: [
63
- {
64
- description: 'Install skills into a custom directory relative to the project root',
65
- label: '--dir <path>',
66
- },
67
- ],
68
- usage: ['remix skills install [--dir <path>]'],
69
- }, target);
70
- }
71
- export function getSkillsListCommandHelpText(target = process.stdout) {
72
- return formatHelpText({
73
- description: 'List Remix skills and show whether each one is installed, outdated, or missing locally.',
74
- examples: [
75
- 'remix skills list',
76
- 'remix skills list --dir custom/skills',
77
- 'remix skills list --json',
78
- ],
79
- options: [
80
- {
81
- description: 'Read local skills from a custom directory relative to the project root',
82
- label: '--dir <path>',
83
- },
84
- { description: 'Print skill state as JSON', label: '--json' },
85
- ],
86
- usage: ['remix skills list [--dir <path>] [--json]'],
87
- }, target);
88
- }
89
- async function runSkillsInstallCommand(argv, context) {
90
- if (argv.includes('-h') || argv.includes('--help')) {
91
- process.stdout.write(getSkillsInstallCommandHelpText());
92
- return 0;
93
- }
94
- let reporter = null;
95
- let progress = null;
96
- try {
97
- let options = parseSkillsInstallCommandArgs(argv);
98
- reporter = createCommandReporter({ remixVersion: context.remixVersion });
99
- progress = createSkillsProgressReporter(reporter);
100
- let cwd = context.cwd;
101
- await reporter.status.commandHeader('skills install');
102
- let result = await installRemixSkills(cwd, globalThis.fetch, {
103
- progress,
104
- skillsDir: options.dir ?? undefined,
105
- });
106
- let skillsDir = getDisplayPath(result.skillsDir, cwd);
107
- progress.writeSummaryGap();
108
- if (result.appliedChanges.length === 0) {
109
- reporter.out.line(`No changes. ${skillsDir} is up to date.`);
110
- reporter.finish();
111
- return 0;
112
- }
113
- reporter.out.line(`Synced Remix skills into ${skillsDir}:`);
114
- reporter.out.bullets(formatAppliedChanges(result.appliedChanges));
115
- reporter.finish();
116
- return 0;
117
- }
118
- catch (error) {
119
- progress?.writeSummaryGap();
120
- process.stderr.write(renderCliError(toCliError(error), {
121
- helpText: getSkillsInstallCommandHelpText(process.stderr),
122
- }));
123
- reporter?.finish();
124
- return 1;
125
- }
126
- }
127
- async function runSkillsListCommand(argv, context) {
128
- if (argv.includes('-h') || argv.includes('--help')) {
129
- process.stdout.write(getSkillsListCommandHelpText());
130
- return 0;
131
- }
132
- let reporter = null;
133
- let progress = null;
134
- try {
135
- let options = parseSkillsDirArgs(argv, { allowJson: true });
136
- let cwd = context.cwd;
137
- reporter = options.json ? null : createCommandReporter({ remixVersion: context.remixVersion });
138
- if (reporter != null) {
139
- progress = createSkillsProgressReporter(reporter);
140
- await reporter.status.commandHeader('skills list');
141
- }
142
- let result = await getSkillsOverview(cwd, globalThis.fetch, {
143
- progress: progress ?? undefined,
144
- skillsDir: options.dir ?? undefined,
145
- });
146
- if (options.json) {
147
- process.stdout.write(`${JSON.stringify({
148
- entries: result.entries,
149
- projectRoot: result.projectRoot,
150
- skillsDir: result.skillsDir,
151
- }, null, 2)}\n`);
152
- return 0;
153
- }
154
- progress?.writeSummaryGap();
155
- if (reporter == null) {
156
- return 0;
157
- }
158
- let listReporter = reporter;
159
- listReporter.out.line(formatSkillsListSummary(result.entries, getDisplayPath(result.skillsDir, cwd)));
160
- listReporter.out.bullets(result.entries.map((entry) => formatSkillListEntry(listReporter, entry)));
161
- listReporter.finish();
162
- return 0;
163
- }
164
- catch (error) {
165
- progress?.writeSummaryGap();
166
- process.stderr.write(renderCliError(toCliError(error), { helpText: getSkillsListCommandHelpText(process.stderr) }));
167
- reporter?.finish();
168
- return 1;
169
- }
170
- }
171
- function createSkillsProgressReporter(reporter) {
172
- return createStepProgressReporter(reporter.status, SKILLS_PROGRESS_LABELS);
173
- }
174
- function formatAppliedChanges(changes) {
175
- let showAction = changes.some((change) => change.action !== 'add');
176
- return changes.map((change) => showAction ? `${toPastTense(change.action)} ${change.name}` : change.name);
177
- }
178
- function formatSkillListEntry(reporter, entry) {
179
- if (entry.state === 'installed') {
180
- return entry.name;
181
- }
182
- let tone = entry.state === 'missing' ? 'error' : 'warn';
183
- return `${entry.name} ${reporter.out.label(entry.state, '', { tone })}`;
184
- }
185
- function formatSkillsListSummary(entries, skillsDir) {
186
- let installedCount = entries.filter((entry) => entry.state === 'installed').length;
187
- let outdatedCount = entries.filter((entry) => entry.state === 'outdated').length;
188
- let missingCount = entries.filter((entry) => entry.state === 'missing').length;
189
- let detailParts = [
190
- installedCount > 0 ? `${installedCount} installed` : null,
191
- outdatedCount > 0 ? `${outdatedCount} outdated` : null,
192
- missingCount > 0 ? `${missingCount} missing` : null,
193
- ].filter((part) => part != null);
194
- let detail = detailParts.length === 0 ? '0 installed' : detailParts.join(', ');
195
- return `Checked Remix skills against ${skillsDir}: ${detail}.`;
196
- }
197
- function parseSkillsInstallCommandArgs(argv) {
198
- let { dir } = parseSkillsDirArgs(argv);
199
- return { dir };
200
- }
201
- function parseSkillsDirArgs(argv, options = {}) {
202
- if (options.allowJson) {
203
- let parsed = parseArgs(argv, {
204
- dir: { flag: '--dir', type: 'string' },
205
- json: { flag: '--json', type: 'boolean' },
206
- }, { maxPositionals: 0 });
207
- return {
208
- dir: parsed.options.dir ?? null,
209
- json: parsed.options.json,
210
- };
211
- }
212
- let parsed = parseArgs(argv, {
213
- dir: { flag: '--dir', type: 'string' },
214
- }, { maxPositionals: 0 });
215
- return {
216
- dir: parsed.options.dir ?? null,
217
- json: false,
218
- };
219
- }
220
- function toPastTense(action) {
221
- return action === 'add' ? 'added' : 'replaced';
222
- }
@@ -1,19 +0,0 @@
1
- export interface SkillsCacheFileEntry {
2
- localHash: string;
3
- remoteSha: string;
4
- }
5
- export interface SkillsCacheSkillEntry {
6
- files: Record<string, SkillsCacheFileEntry>;
7
- }
8
- export interface SkillsCacheManifest {
9
- ref: string;
10
- repo: string;
11
- skills: Record<string, SkillsCacheSkillEntry>;
12
- skillsDir: string;
13
- version: number;
14
- }
15
- export declare function createSkillsCacheManifest(skillsDir: string, skills: Record<string, SkillsCacheSkillEntry>): SkillsCacheManifest;
16
- export declare function getSkillsCacheFilePath(skillsDir: string): string;
17
- export declare function readSkillsCache(skillsDir: string): Promise<SkillsCacheManifest | null>;
18
- export declare function writeSkillsCache(skillsDir: string, manifest: SkillsCacheManifest): Promise<void>;
19
- //# sourceMappingURL=skills-cache.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skills-cache.d.ts","sourceRoot":"","sources":["../../src/lib/skills-cache.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAA;CAC5C;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAA;IAC7C,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,GAC5C,mBAAmB,CAQrB;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAGhE;AAED,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAiB5F;AAED,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAOf"}
@@ -1,89 +0,0 @@
1
- import * as crypto from 'node:crypto';
2
- import * as fs from 'node:fs/promises';
3
- import * as os from 'node:os';
4
- import * as path from 'node:path';
5
- import * as process from 'node:process';
6
- const REMIX_SKILLS_CACHE_VERSION = 1;
7
- const REMIX_SKILLS_CACHE_REPO = 'remix-run/remix';
8
- const REMIX_SKILLS_CACHE_REF = 'main';
9
- export function createSkillsCacheManifest(skillsDir, skills) {
10
- return {
11
- ref: REMIX_SKILLS_CACHE_REF,
12
- repo: REMIX_SKILLS_CACHE_REPO,
13
- skills,
14
- skillsDir: path.resolve(skillsDir),
15
- version: REMIX_SKILLS_CACHE_VERSION,
16
- };
17
- }
18
- export function getSkillsCacheFilePath(skillsDir) {
19
- let cacheKey = crypto.createHash('sha256').update(path.resolve(skillsDir)).digest('hex');
20
- return path.join(getSkillsCacheDir(), `${cacheKey}.json`);
21
- }
22
- export async function readSkillsCache(skillsDir) {
23
- try {
24
- let filePath = getSkillsCacheFilePath(skillsDir);
25
- let raw = JSON.parse(await fs.readFile(filePath, 'utf8'));
26
- return isSkillsCacheManifest(raw, skillsDir) ? raw : null;
27
- }
28
- catch (error) {
29
- if (error instanceof SyntaxError) {
30
- return null;
31
- }
32
- let nodeError = error;
33
- if (nodeError.code === 'ENOENT') {
34
- return null;
35
- }
36
- throw error;
37
- }
38
- }
39
- export async function writeSkillsCache(skillsDir, manifest) {
40
- let filePath = getSkillsCacheFilePath(skillsDir);
41
- let tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
42
- await fs.mkdir(path.dirname(filePath), { recursive: true });
43
- await fs.writeFile(tempPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
44
- await fs.rename(tempPath, filePath);
45
- }
46
- function getSkillsCacheDir() {
47
- if (process.platform === 'win32') {
48
- let localAppData = process.env.LOCALAPPDATA ?? path.join(os.homedir(), 'AppData', 'Local');
49
- return path.join(localAppData, 'remix', 'Cache', 'skills');
50
- }
51
- if (process.platform === 'darwin') {
52
- return path.join(process.env.HOME ?? os.homedir(), 'Library', 'Caches', 'remix', 'cli', 'skills');
53
- }
54
- let xdgCacheHome = process.env.XDG_CACHE_HOME ?? path.join(os.homedir(), '.cache');
55
- return path.join(xdgCacheHome, 'remix', 'cli', 'skills');
56
- }
57
- function isSkillsCacheManifest(value, skillsDir) {
58
- if (typeof value !== 'object' || value == null) {
59
- return false;
60
- }
61
- if (Reflect.get(value, 'version') !== REMIX_SKILLS_CACHE_VERSION ||
62
- Reflect.get(value, 'repo') !== REMIX_SKILLS_CACHE_REPO ||
63
- Reflect.get(value, 'ref') !== REMIX_SKILLS_CACHE_REF ||
64
- Reflect.get(value, 'skillsDir') !== path.resolve(skillsDir)) {
65
- return false;
66
- }
67
- let skills = Reflect.get(value, 'skills');
68
- if (typeof skills !== 'object' || skills == null) {
69
- return false;
70
- }
71
- for (let skillEntry of Object.values(skills)) {
72
- if (typeof skillEntry !== 'object' || skillEntry == null) {
73
- return false;
74
- }
75
- let files = Reflect.get(skillEntry, 'files');
76
- if (typeof files !== 'object' || files == null) {
77
- return false;
78
- }
79
- for (let fileEntry of Object.values(files)) {
80
- if (typeof fileEntry !== 'object' ||
81
- fileEntry == null ||
82
- typeof Reflect.get(fileEntry, 'localHash') !== 'string' ||
83
- typeof Reflect.get(fileEntry, 'remoteSha') !== 'string') {
84
- return false;
85
- }
86
- }
87
- }
88
- return true;
89
- }
@@ -1,30 +0,0 @@
1
- import { type StepProgressReporter } from './reporter.ts';
2
- export interface SkillChange {
3
- action: 'add' | 'replace';
4
- name: string;
5
- }
6
- export interface SkillStatusEntry {
7
- name: string;
8
- state: 'installed' | 'missing' | 'outdated';
9
- }
10
- interface SkillsResult {
11
- entries: SkillStatusEntry[];
12
- projectRoot: string;
13
- skillsDir: string;
14
- }
15
- export interface SkillsOverview extends SkillsResult {
16
- }
17
- export interface SkillsInstallResult extends SkillsResult {
18
- appliedChanges: SkillChange[];
19
- }
20
- export interface SkillsOptions {
21
- progress?: SkillsProgressReporter;
22
- skillsDir?: string;
23
- }
24
- export type SkillsInstallPhase = 'resolve-project-root' | 'fetch-remix-skills-metadata' | 'read-local-skills-cache' | 'compare-local-skills' | 'download-remix-skills-archive' | 'write-updated-skills';
25
- export type SkillsProgressReporter = StepProgressReporter<SkillsInstallPhase>;
26
- type FetchImpl = typeof fetch;
27
- export declare function getSkillsOverview(cwd?: string, fetchImpl?: FetchImpl, options?: SkillsOptions): Promise<SkillsOverview>;
28
- export declare function installRemixSkills(cwd?: string, fetchImpl?: FetchImpl, options?: SkillsOptions): Promise<SkillsInstallResult>;
29
- export {};
30
- //# sourceMappingURL=skills.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/lib/skills.ts"],"names":[],"mappings":"AASA,OAAO,EAAmB,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAe1E,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,KAAK,GAAG,SAAS,CAAA;IACzB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,WAAW,GAAG,SAAS,GAAG,UAAU,CAAA;CAC5C;AAED,UAAU,YAAY;IACpB,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,cAAe,SAAQ,YAAY;CAAG;AAEvD,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,cAAc,EAAE,WAAW,EAAE,CAAA;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,sBAAsB,CAAA;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,MAAM,kBAAkB,GAC1B,sBAAsB,GACtB,6BAA6B,GAC7B,yBAAyB,GACzB,sBAAsB,GACtB,+BAA+B,GAC/B,sBAAsB,CAAA;AAE1B,MAAM,MAAM,sBAAsB,GAAG,oBAAoB,CAAC,kBAAkB,CAAC,CAAA;AA4C7E,KAAK,SAAS,GAAG,OAAO,KAAK,CAAA;AAE7B,wBAAsB,iBAAiB,CACrC,GAAG,GAAE,MAAsB,EAC3B,SAAS,GAAE,SAA4B,EACvC,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,cAAc,CAAC,CAOzB;AAED,wBAAsB,kBAAkB,CACtC,GAAG,GAAE,MAAsB,EAC3B,SAAS,GAAE,SAA4B,EACvC,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CA4C9B"}