@remix-run/cli 0.0.0 → 0.1.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 (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -3
  3. package/bootstrap/AGENTS.md +35 -0
  4. package/bootstrap/README.md +27 -0
  5. package/bootstrap/app/controllers/auth.tsx +21 -0
  6. package/bootstrap/app/controllers/home.tsx +26 -0
  7. package/bootstrap/app/router.ts +10 -0
  8. package/bootstrap/app/routes.ts +6 -0
  9. package/bootstrap/app/ui/document.tsx +21 -0
  10. package/bootstrap/app/ui/layout.tsx +22 -0
  11. package/bootstrap/app/utils/render.tsx +7 -0
  12. package/bootstrap/package.json +22 -0
  13. package/bootstrap/server.ts +37 -0
  14. package/bootstrap/tsconfig.json +19 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +1 -0
  18. package/dist/lib/bootstrap-project.d.ts +17 -0
  19. package/dist/lib/bootstrap-project.d.ts.map +1 -0
  20. package/dist/lib/bootstrap-project.js +137 -0
  21. package/dist/lib/cli-context.d.ts +11 -0
  22. package/dist/lib/cli-context.d.ts.map +1 -0
  23. package/dist/lib/cli-context.js +19 -0
  24. package/dist/lib/cli.d.ts +6 -0
  25. package/dist/lib/cli.d.ts.map +1 -0
  26. package/dist/lib/cli.js +87 -0
  27. package/dist/lib/commands/completion.d.ts +3 -0
  28. package/dist/lib/commands/completion.d.ts.map +1 -0
  29. package/dist/lib/commands/completion.js +51 -0
  30. package/dist/lib/commands/doctor.d.ts +4 -0
  31. package/dist/lib/commands/doctor.d.ts.map +1 -0
  32. package/dist/lib/commands/doctor.js +293 -0
  33. package/dist/lib/commands/help.d.ts +4 -0
  34. package/dist/lib/commands/help.d.ts.map +1 -0
  35. package/dist/lib/commands/help.js +124 -0
  36. package/dist/lib/commands/new.d.ts +4 -0
  37. package/dist/lib/commands/new.d.ts.map +1 -0
  38. package/dist/lib/commands/new.js +70 -0
  39. package/dist/lib/commands/routes.d.ts +4 -0
  40. package/dist/lib/commands/routes.d.ts.map +1 -0
  41. package/dist/lib/commands/routes.js +161 -0
  42. package/dist/lib/commands/skills.d.ts +6 -0
  43. package/dist/lib/commands/skills.d.ts.map +1 -0
  44. package/dist/lib/commands/skills.js +222 -0
  45. package/dist/lib/commands/test.d.ts +4 -0
  46. package/dist/lib/commands/test.d.ts.map +1 -0
  47. package/dist/lib/commands/test.js +19 -0
  48. package/dist/lib/commands/version.d.ts +4 -0
  49. package/dist/lib/commands/version.d.ts.map +1 -0
  50. package/dist/lib/commands/version.js +29 -0
  51. package/dist/lib/completion.d.ts +12 -0
  52. package/dist/lib/completion.d.ts.map +1 -0
  53. package/dist/lib/completion.js +408 -0
  54. package/dist/lib/contained-path.d.ts +2 -0
  55. package/dist/lib/contained-path.d.ts.map +1 -0
  56. package/dist/lib/contained-path.js +10 -0
  57. package/dist/lib/controller-files.d.ts +14 -0
  58. package/dist/lib/controller-files.d.ts.map +1 -0
  59. package/dist/lib/controller-files.js +53 -0
  60. package/dist/lib/controller-ownership.d.ts +41 -0
  61. package/dist/lib/controller-ownership.d.ts.map +1 -0
  62. package/dist/lib/controller-ownership.js +224 -0
  63. package/dist/lib/display-path.d.ts +2 -0
  64. package/dist/lib/display-path.d.ts.map +1 -0
  65. package/dist/lib/display-path.js +12 -0
  66. package/dist/lib/doctor/controller-findings.d.ts +4 -0
  67. package/dist/lib/doctor/controller-findings.d.ts.map +1 -0
  68. package/dist/lib/doctor/controller-findings.js +132 -0
  69. package/dist/lib/doctor/controller-fix-plans.d.ts +4 -0
  70. package/dist/lib/doctor/controller-fix-plans.d.ts.map +1 -0
  71. package/dist/lib/doctor/controller-fix-plans.js +130 -0
  72. package/dist/lib/doctor/controller-placeholders.d.ts +4 -0
  73. package/dist/lib/doctor/controller-placeholders.d.ts.map +1 -0
  74. package/dist/lib/doctor/controller-placeholders.js +183 -0
  75. package/dist/lib/doctor/controllers.d.ts +8 -0
  76. package/dist/lib/doctor/controllers.d.ts.map +1 -0
  77. package/dist/lib/doctor/controllers.js +13 -0
  78. package/dist/lib/doctor/environment.d.ts +17 -0
  79. package/dist/lib/doctor/environment.d.ts.map +1 -0
  80. package/dist/lib/doctor/environment.js +226 -0
  81. package/dist/lib/doctor/fixes.d.ts +3 -0
  82. package/dist/lib/doctor/fixes.d.ts.map +1 -0
  83. package/dist/lib/doctor/fixes.js +65 -0
  84. package/dist/lib/doctor/project.d.ts +10 -0
  85. package/dist/lib/doctor/project.d.ts.map +1 -0
  86. package/dist/lib/doctor/project.js +371 -0
  87. package/dist/lib/doctor/types.d.ts +48 -0
  88. package/dist/lib/doctor/types.d.ts.map +1 -0
  89. package/dist/lib/doctor/types.js +15 -0
  90. package/dist/lib/errors.d.ts +173 -0
  91. package/dist/lib/errors.d.ts.map +1 -0
  92. package/dist/lib/errors.js +318 -0
  93. package/dist/lib/help-text.d.ts +13 -0
  94. package/dist/lib/help-text.d.ts.map +1 -0
  95. package/dist/lib/help-text.js +43 -0
  96. package/dist/lib/load-route-map-worker.d.ts +2 -0
  97. package/dist/lib/load-route-map-worker.d.ts.map +1 -0
  98. package/dist/lib/load-route-map-worker.js +92 -0
  99. package/dist/lib/parse-args.d.ts +22 -0
  100. package/dist/lib/parse-args.d.ts.map +1 -0
  101. package/dist/lib/parse-args.js +35 -0
  102. package/dist/lib/remix-version.d.ts +2 -0
  103. package/dist/lib/remix-version.d.ts.map +1 -0
  104. package/dist/lib/remix-version.js +58 -0
  105. package/dist/lib/reporter.d.ts +56 -0
  106. package/dist/lib/reporter.d.ts.map +1 -0
  107. package/dist/lib/reporter.js +357 -0
  108. package/dist/lib/route-map.d.ts +38 -0
  109. package/dist/lib/route-map.d.ts.map +1 -0
  110. package/dist/lib/route-map.js +195 -0
  111. package/dist/lib/skills-cache.d.ts +19 -0
  112. package/dist/lib/skills-cache.d.ts.map +1 -0
  113. package/dist/lib/skills-cache.js +89 -0
  114. package/dist/lib/skills.d.ts +30 -0
  115. package/dist/lib/skills.d.ts.map +1 -0
  116. package/dist/lib/skills.js +441 -0
  117. package/dist/lib/terminal.d.ts +17 -0
  118. package/dist/lib/terminal.d.ts.map +1 -0
  119. package/dist/lib/terminal.js +66 -0
  120. package/package.json +48 -5
  121. package/src/index.ts +1 -0
  122. package/src/lib/bootstrap-project.ts +222 -0
  123. package/src/lib/cli-context.ts +38 -0
  124. package/src/lib/cli.ts +120 -0
  125. package/src/lib/commands/completion.ts +74 -0
  126. package/src/lib/commands/doctor.ts +393 -0
  127. package/src/lib/commands/help.ts +157 -0
  128. package/src/lib/commands/new.ts +104 -0
  129. package/src/lib/commands/routes.ts +251 -0
  130. package/src/lib/commands/skills.ts +306 -0
  131. package/src/lib/commands/test.ts +25 -0
  132. package/src/lib/commands/version.ts +39 -0
  133. package/src/lib/completion.ts +561 -0
  134. package/src/lib/contained-path.ts +13 -0
  135. package/src/lib/controller-files.ts +79 -0
  136. package/src/lib/controller-ownership.ts +397 -0
  137. package/src/lib/display-path.ts +16 -0
  138. package/src/lib/doctor/controller-findings.ts +157 -0
  139. package/src/lib/doctor/controller-fix-plans.ts +184 -0
  140. package/src/lib/doctor/controller-placeholders.ts +242 -0
  141. package/src/lib/doctor/controllers.ts +23 -0
  142. package/src/lib/doctor/environment.ts +289 -0
  143. package/src/lib/doctor/fixes.ts +84 -0
  144. package/src/lib/doctor/project.ts +435 -0
  145. package/src/lib/doctor/types.ts +95 -0
  146. package/src/lib/errors.ts +379 -0
  147. package/src/lib/help-text.ts +88 -0
  148. package/src/lib/load-route-map-worker.ts +110 -0
  149. package/src/lib/parse-args.ts +82 -0
  150. package/src/lib/remix-version.ts +80 -0
  151. package/src/lib/reporter.ts +535 -0
  152. package/src/lib/route-map.ts +303 -0
  153. package/src/lib/skills-cache.ts +140 -0
  154. package/src/lib/skills.ts +706 -0
  155. package/src/lib/terminal.ts +86 -0
@@ -0,0 +1,161 @@
1
+ import * as process from 'node:process';
2
+ import { invalidFlagCombination, renderCliError, toCliError } from "../errors.js";
3
+ import { formatHelpText } from "../help-text.js";
4
+ import { parseArgs } from "../parse-args.js";
5
+ import { createCommandReporter } from "../reporter.js";
6
+ import { loadRouteMap } from "../route-map.js";
7
+ import { lightRed } from "../terminal.js";
8
+ const CONTROLLERS_PATH_PREFIX = 'app/controllers/';
9
+ export async function runRoutesCommand(argv, context) {
10
+ if (argv.includes('-h') || argv.includes('--help')) {
11
+ process.stdout.write(getRoutesCommandHelpText());
12
+ return 0;
13
+ }
14
+ try {
15
+ let options = parseRoutesCommandArgs(argv);
16
+ let routeMap = await loadRouteMap(context.cwd);
17
+ if (options.json) {
18
+ process.stdout.write(`${JSON.stringify(routeMap, null, 2)}\n`);
19
+ }
20
+ else {
21
+ let reporter = createCommandReporter({ remixVersion: context.remixVersion });
22
+ writeRouteMap(reporter.out, routeMap, options);
23
+ reporter.finish();
24
+ }
25
+ return 0;
26
+ }
27
+ catch (error) {
28
+ process.stderr.write(renderCliError(toCliError(error), { helpText: getRoutesCommandHelpText(process.stderr) }));
29
+ return 1;
30
+ }
31
+ }
32
+ export function getRoutesCommandHelpText(target = process.stdout) {
33
+ return formatHelpText({
34
+ description: 'Show the Remix route tree for the current app.',
35
+ examples: [
36
+ 'remix routes',
37
+ 'remix routes --table',
38
+ 'remix routes --table --no-headers',
39
+ 'remix routes --verbose',
40
+ 'remix routes --json',
41
+ ],
42
+ options: [
43
+ { description: 'Print the normalized route tree as JSON', label: '--json' },
44
+ { description: 'Print routes as a flat table', label: '--table' },
45
+ {
46
+ description: 'Omit the table header row when using --table',
47
+ label: '--no-headers',
48
+ },
49
+ { description: 'Show full owner paths in tree or table output', label: '--verbose' },
50
+ ],
51
+ usage: ['remix routes [--json | --table] [--no-headers] [--verbose] [--no-color]'],
52
+ }, target);
53
+ }
54
+ function parseRoutesCommandArgs(argv) {
55
+ let parsed = parseArgs(argv, {
56
+ json: { flag: '--json', type: 'boolean' },
57
+ noHeaders: { flag: '--no-headers', type: 'boolean' },
58
+ table: { flag: '--table', type: 'boolean' },
59
+ verbose: { flag: '--verbose', type: 'boolean' },
60
+ }, { maxPositionals: 0 });
61
+ let json = parsed.options.json;
62
+ let noHeaders = parsed.options.noHeaders;
63
+ let table = parsed.options.table;
64
+ let verbose = parsed.options.verbose;
65
+ if (json && table) {
66
+ throw invalidFlagCombination('Cannot combine --json with --table.');
67
+ }
68
+ if (json && verbose) {
69
+ throw invalidFlagCombination('Cannot combine --json with --verbose.');
70
+ }
71
+ if (noHeaders && !table) {
72
+ throw invalidFlagCombination('Cannot use --no-headers without --table.');
73
+ }
74
+ return { json, noHeaders, table, verbose };
75
+ }
76
+ function writeRouteMap(out, routeMap, options) {
77
+ if (routeMap.tree.length === 0) {
78
+ out.line('No routes.');
79
+ return;
80
+ }
81
+ if (options.table) {
82
+ writeRouteTable(out, routeMap, options);
83
+ return;
84
+ }
85
+ let lines = [];
86
+ renderRouteNodes(lines, routeMap.tree, '', true, null, options);
87
+ for (let line of lines) {
88
+ out.line(line);
89
+ }
90
+ }
91
+ function renderRouteNodes(lines, nodes, prefix, isRoot, parentOwnerPath, options) {
92
+ let leafKeyWidth = getLeafKeyWidth(nodes);
93
+ nodes.forEach((node, index) => {
94
+ renderRouteNode(lines, node, prefix, index === nodes.length - 1, isRoot, parentOwnerPath, leafKeyWidth, options);
95
+ });
96
+ }
97
+ function renderRouteNode(lines, node, prefix, isLast, isRoot, parentOwnerPath, leafKeyWidth, options) {
98
+ let branch = isRoot ? '' : isLast ? '└─ ' : '├─ ';
99
+ let line = `${prefix}${branch}${formatRouteNode(node, parentOwnerPath, leafKeyWidth, options)}`;
100
+ lines.push(colorRouteLine(line, node));
101
+ if (node.kind !== 'group') {
102
+ return;
103
+ }
104
+ let childPrefix = isRoot ? '' : `${prefix}${isLast ? ' ' : '│ '}`;
105
+ renderRouteNodes(lines, node.children, childPrefix, false, node.owner.path, options);
106
+ }
107
+ function getLeafKeyWidth(nodes) {
108
+ let leafNodes = nodes.filter((node) => node.kind === 'route');
109
+ return leafNodes.reduce((width, node) => Math.max(width, node.key.length), 0);
110
+ }
111
+ function formatRouteNode(node, parentOwnerPath, leafKeyWidth, options) {
112
+ let owner = formatOwner(node, options);
113
+ if (node.kind === 'group') {
114
+ return `${node.key} -> ${owner}`;
115
+ }
116
+ let leaf = `${node.key.padEnd(leafKeyWidth)} ${node.method.padEnd(6)} ${node.pattern}`;
117
+ if (options.verbose || parentOwnerPath == null || parentOwnerPath !== node.owner.path) {
118
+ return `${leaf} -> ${owner}`;
119
+ }
120
+ return leaf;
121
+ }
122
+ function writeRouteTable(out, routeMap, options) {
123
+ let rows = flattenRoutes(routeMap.tree).map((node) => ({
124
+ method: node.method,
125
+ node,
126
+ owner: formatOwner(node, options),
127
+ path: node.pattern,
128
+ route: node.name,
129
+ }));
130
+ out.table({
131
+ formatRow(line, rowIndex) {
132
+ return colorRouteLine(line, rows[rowIndex].node);
133
+ },
134
+ headers: ['Route', 'Method', 'Path', 'Owner'],
135
+ noHeaders: options.noHeaders,
136
+ rows: rows.map((row) => [row.route, row.method, row.path, row.owner]),
137
+ });
138
+ }
139
+ function flattenRoutes(nodes, routes = []) {
140
+ for (let node of nodes) {
141
+ if (node.kind === 'route') {
142
+ routes.push(node);
143
+ continue;
144
+ }
145
+ flattenRoutes(node.children, routes);
146
+ }
147
+ return routes;
148
+ }
149
+ function formatOwner(node, options) {
150
+ let ownerPath = options.verbose ? node.owner.path : getCompactOwnerPath(node.owner.path);
151
+ return `${ownerPath}${node.owner.exists ? '' : ' [missing]'}`;
152
+ }
153
+ function colorRouteLine(line, node) {
154
+ return node.owner.exists ? line : lightRed(line);
155
+ }
156
+ function getCompactOwnerPath(ownerPath) {
157
+ if (ownerPath.startsWith(CONTROLLERS_PATH_PREFIX)) {
158
+ return ownerPath.slice(CONTROLLERS_PATH_PREFIX.length);
159
+ }
160
+ return ownerPath;
161
+ }
@@ -0,0 +1,6 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,222 @@
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
+ }
@@ -0,0 +1,4 @@
1
+ import type { CliContext } from '../cli-context.ts';
2
+ export declare function runTestCommand(argv: string[], context: CliContext): Promise<number>;
3
+ export declare function getTestCommandHelpText(target?: NodeJS.WriteStream): string;
4
+ //# sourceMappingURL=test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../../src/lib/commands/test.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAGnD,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAczF;AAED,wBAAgB,sBAAsB,CAAC,MAAM,GAAE,MAAM,CAAC,WAA4B,GAAG,MAAM,CAE1F"}
@@ -0,0 +1,19 @@
1
+ import { getRemixTestHelpText, runRemixTest } from '@remix-run/test/cli';
2
+ import * as process from 'node:process';
3
+ import { renderCliError, toCliError } from "../errors.js";
4
+ export async function runTestCommand(argv, context) {
5
+ if (argv.includes('-h') || argv.includes('--help')) {
6
+ process.stdout.write(`${getTestCommandHelpText()}\n`);
7
+ return 0;
8
+ }
9
+ try {
10
+ return await runRemixTest({ argv, cwd: context.cwd });
11
+ }
12
+ catch (error) {
13
+ process.stderr.write(renderCliError(toCliError(error), { helpText: getTestCommandHelpText(process.stderr) }));
14
+ return 1;
15
+ }
16
+ }
17
+ export function getTestCommandHelpText(target = process.stdout) {
18
+ return getRemixTestHelpText(target);
19
+ }
@@ -0,0 +1,4 @@
1
+ import type { CliContext } from '../cli-context.ts';
2
+ export declare function runVersionCommand(argv: string[], context: CliContext): Promise<number>;
3
+ export declare function getVersionCommandHelpText(target?: NodeJS.WriteStream): string;
4
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../src/lib/commands/version.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAKnD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAoB5F;AAED,wBAAgB,yBAAyB,CAAC,MAAM,GAAE,MAAM,CAAC,WAA4B,GAAG,MAAM,CAS7F"}
@@ -0,0 +1,29 @@
1
+ import * as process from 'node:process';
2
+ import { remixVersionUnavailable, renderCliError, toCliError } from "../errors.js";
3
+ import { formatHelpText } from "../help-text.js";
4
+ import { parseArgs } from "../parse-args.js";
5
+ export async function runVersionCommand(argv, context) {
6
+ if (argv.includes('-h') || argv.includes('--help')) {
7
+ process.stdout.write(getVersionCommandHelpText());
8
+ return 0;
9
+ }
10
+ try {
11
+ parseArgs(argv, {}, { maxPositionals: 0 });
12
+ if (context.remixVersion == null) {
13
+ throw remixVersionUnavailable();
14
+ }
15
+ process.stdout.write(`${context.remixVersion}\n`);
16
+ return 0;
17
+ }
18
+ catch (error) {
19
+ process.stderr.write(renderCliError(toCliError(error), { helpText: getVersionCommandHelpText(process.stderr) }));
20
+ return 1;
21
+ }
22
+ }
23
+ export function getVersionCommandHelpText(target = process.stdout) {
24
+ return formatHelpText({
25
+ description: 'Show the current Remix version.',
26
+ examples: ['remix version', 'remix --version'],
27
+ usage: ['remix version'],
28
+ }, target);
29
+ }
@@ -0,0 +1,12 @@
1
+ export interface CompletionResult {
2
+ mode: 'files' | 'none' | 'values';
3
+ values?: string[];
4
+ }
5
+ declare const COMPLETION_SHELLS: readonly ["bash", "zsh"];
6
+ export type CompletionShell = (typeof COMPLETION_SHELLS)[number];
7
+ export declare function isCompletionShell(value: string): value is CompletionShell;
8
+ export declare function getCompletionResult(words: string[], currentIndex: number): CompletionResult;
9
+ export declare function renderCompletionResult(result: CompletionResult): string;
10
+ export declare function getCompletionScript(): string;
11
+ export {};
12
+ //# sourceMappingURL=completion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../../src/lib/completion.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;IACjC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,QAAA,MAAM,iBAAiB,0BAA2B,CAAA;AAuBlD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEhE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,eAAe,CAEzE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,YAAY,EAAE,MAAM,GAAG,gBAAgB,CAM3F;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAQvE;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CA4E5C"}