create-next-pro-cli 0.1.31 → 0.1.33

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.
package/dist/bin.bun.js CHANGED
@@ -4956,14 +4956,120 @@ import path10 from "path";
4956
4956
  // src/cli/completion.ts
4957
4957
  import path2 from "path";
4958
4958
 
4959
+ // src/core/contracts.ts
4960
+ class CliError extends Error {
4961
+ exitCode;
4962
+ code;
4963
+ hint;
4964
+ scope;
4965
+ path;
4966
+ constructor(message, options = {}) {
4967
+ super(message);
4968
+ this.name = "CliError";
4969
+ if (typeof options === "number") {
4970
+ this.exitCode = options;
4971
+ this.code = "FILESYSTEM_ERROR";
4972
+ } else {
4973
+ this.exitCode = options.exitCode ?? 1;
4974
+ this.code = options.code ?? "FILESYSTEM_ERROR";
4975
+ this.hint = options.hint;
4976
+ this.scope = options.scope;
4977
+ this.path = options.path;
4978
+ }
4979
+ }
4980
+ }
4981
+
4982
+ // src/core/page-area.ts
4983
+ var PAGE_AREAS = ["public", "user"];
4984
+ function isPageArea(value) {
4985
+ return PAGE_AREAS.includes(value);
4986
+ }
4987
+ function parseAreaOption(args) {
4988
+ const remaining = args.length > 0 ? [args[0]] : [];
4989
+ let area;
4990
+ for (let index = 1;index < args.length; index += 1) {
4991
+ const argument = args[index];
4992
+ if (argument.startsWith("--area=")) {
4993
+ throw new CliError("The --area option must use a separate public or user value.", {
4994
+ code: "INVALID_ARGUMENT",
4995
+ hint: "Use --area public or --area user."
4996
+ });
4997
+ }
4998
+ if (argument !== "--area") {
4999
+ remaining.push(argument);
5000
+ continue;
5001
+ }
5002
+ if (area) {
5003
+ throw new CliError("The --area option can only be provided once.", {
5004
+ code: "INVALID_ARGUMENT"
5005
+ });
5006
+ }
5007
+ const value = args[index + 1];
5008
+ if (!value || value.startsWith("-")) {
5009
+ throw new CliError("The --area option requires public or user.", {
5010
+ code: "INVALID_ARGUMENT",
5011
+ hint: "Use --area public or --area user."
5012
+ });
5013
+ }
5014
+ if (!isPageArea(value)) {
5015
+ throw new CliError(`Unsupported page area: ${value}.`, {
5016
+ code: "INVALID_ARGUMENT",
5017
+ hint: "Use --area public or --area user."
5018
+ });
5019
+ }
5020
+ area = value;
5021
+ index += 1;
5022
+ }
5023
+ return { area, args: remaining };
5024
+ }
5025
+ function requirePageArea(area, command) {
5026
+ if (area)
5027
+ return area;
5028
+ throw new CliError(`The ${command} command requires --area public or user.`, {
5029
+ code: "INVALID_ARGUMENT",
5030
+ hint: `Run ${command} with --area public or --area user.`
5031
+ });
5032
+ }
5033
+ function areaRouteGroup(area) {
5034
+ return `(${area})`;
5035
+ }
5036
+
4959
5037
  // src/core/page-catalog.ts
4960
5038
  import path from "path";
4961
5039
  function isRouteGroup(segment) {
4962
5040
  return segment.startsWith("(") && segment.endsWith(")");
4963
5041
  }
4964
- async function discoverPages(projectRoot, fs) {
4965
- const appRoot = path.join(projectRoot, "src", "app", "[locale]");
4966
- const candidates = [];
5042
+ function logicalSegments(relative) {
5043
+ return relative.filter((segment) => !isRouteGroup(segment));
5044
+ }
5045
+ function issueForRoute(projectRoot, directory, relative) {
5046
+ const segments = logicalSegments(relative);
5047
+ if (segments.length === 0 || segments.some((segment) => segment.startsWith("_") || segment.startsWith("["))) {
5048
+ return;
5049
+ }
5050
+ const first = relative[0];
5051
+ if (!first || !isRouteGroup(first)) {
5052
+ return {
5053
+ logicalName: segments.join("."),
5054
+ reason: "ungrouped",
5055
+ routeDirectories: [path.relative(projectRoot, directory)]
5056
+ };
5057
+ }
5058
+ const groupName = first.slice(1, -1);
5059
+ if (!isPageArea(groupName) || relative.slice(1).some(isRouteGroup)) {
5060
+ return {
5061
+ logicalName: segments.join("."),
5062
+ reason: "unsupported-route-group",
5063
+ routeDirectories: [path.relative(projectRoot, directory)]
5064
+ };
5065
+ }
5066
+ return;
5067
+ }
5068
+ async function discoverPageCatalog(projectRoot, fs) {
5069
+ const localizedRoot = path.join(projectRoot, "src", "app", "[locale]");
5070
+ const appRoot = fs.exists(localizedRoot) ? localizedRoot : path.join(projectRoot, "src", "app");
5071
+ const rawCandidates = [];
5072
+ const issues = [];
4967
5073
  async function visit(directory, relative = []) {
4968
5074
  let entries;
4969
5075
  try {
@@ -4972,17 +5078,25 @@ async function discoverPages(projectRoot, fs) {
4972
5078
  return;
4973
5079
  }
4974
5080
  if (entries.some((entry) => entry.isFile && entry.name === "page.tsx")) {
4975
- const routeSegments = relative.filter((segment) => !isRouteGroup(segment));
4976
- if (routeSegments.length > 0 && !routeSegments.some((segment) => segment.startsWith("_") || segment.startsWith("["))) {
4977
- const logicalName = routeSegments.join(".");
4978
- candidates.push({
4979
- logicalName,
4980
- routeSegments,
4981
- routeDirectory: directory,
4982
- uiDirectory: path.join(projectRoot, "src", "ui", ...routeSegments),
4983
- messageFile: path.join(projectRoot, "messages", "{locale}", `${routeSegments[0]}.json`),
4984
- messageKey: routeSegments.length > 1 ? routeSegments.at(-1) : undefined
4985
- });
5081
+ const issue = issueForRoute(projectRoot, directory, relative);
5082
+ if (issue) {
5083
+ issues.push(issue);
5084
+ } else if (relative.length > 1) {
5085
+ const area = relative[0].slice(1, -1);
5086
+ const routeSegments = relative.slice(1);
5087
+ if (routeSegments.length > 0 && !routeSegments.some((segment) => segment.startsWith("_") || segment.startsWith("["))) {
5088
+ const logicalName = routeSegments.join(".");
5089
+ rawCandidates.push({
5090
+ id: `${area}:${logicalName}`,
5091
+ area,
5092
+ logicalName,
5093
+ routeSegments,
5094
+ routeDirectory: directory,
5095
+ uiDirectory: path.join(projectRoot, "src", "ui", ...routeSegments),
5096
+ messageFile: path.join(projectRoot, "messages", "{locale}", `${routeSegments[0]}.json`),
5097
+ messageKey: routeSegments.length > 1 ? routeSegments.at(-1) : undefined
5098
+ });
5099
+ }
4986
5100
  }
4987
5101
  }
4988
5102
  for (const entry of entries) {
@@ -4995,7 +5109,52 @@ async function discoverPages(projectRoot, fs) {
4995
5109
  }
4996
5110
  }
4997
5111
  await visit(appRoot);
4998
- return candidates.sort((left, right) => left.logicalName.localeCompare(right.logicalName));
5112
+ const candidatesByName = new Map;
5113
+ for (const candidate of rawCandidates) {
5114
+ const entries = candidatesByName.get(candidate.logicalName) ?? [];
5115
+ entries.push(candidate);
5116
+ candidatesByName.set(candidate.logicalName, entries);
5117
+ }
5118
+ for (const [logicalName, candidates2] of candidatesByName) {
5119
+ if (candidates2.length < 2)
5120
+ continue;
5121
+ issues.push({
5122
+ logicalName,
5123
+ reason: "duplicate-logical-route",
5124
+ routeDirectories: candidates2.map((candidate) => path.relative(projectRoot, candidate.routeDirectory))
5125
+ });
5126
+ }
5127
+ const invalidNames = new Set(issues.map((issue) => issue.logicalName));
5128
+ const candidates = rawCandidates.filter((candidate) => !invalidNames.has(candidate.logicalName)).sort((left, right) => left.area.localeCompare(right.area) || left.logicalName.localeCompare(right.logicalName));
5129
+ issues.sort((left, right) => left.logicalName.localeCompare(right.logicalName));
5130
+ return { candidates, issues };
5131
+ }
5132
+ function routeIssue(catalog, logicalName) {
5133
+ return catalog.issues.find((issue) => issue.logicalName === logicalName);
5134
+ }
5135
+ function assertConsistentLogicalRoute(catalog, logicalName) {
5136
+ const issue = routeIssue(catalog, logicalName);
5137
+ if (!issue)
5138
+ return;
5139
+ throw new CliError(`Page route "${logicalName}" is inconsistent.`, {
5140
+ code: "INCONSISTENT_ROUTE",
5141
+ scope: "project",
5142
+ path: issue.routeDirectories.join(", "),
5143
+ hint: "Move the route into exactly one of the (public) or (user) areas before retrying."
5144
+ });
5145
+ }
5146
+ function resolvePageCandidate(catalog, logicalName, area) {
5147
+ assertConsistentLogicalRoute(catalog, logicalName);
5148
+ const candidate = catalog.candidates.find((entry) => entry.logicalName === logicalName && entry.area === area);
5149
+ if (candidate)
5150
+ return candidate;
5151
+ const otherArea = catalog.candidates.find((entry) => entry.logicalName === logicalName);
5152
+ throw new CliError(`Page not found in the ${area} area: ${logicalName}.`, {
5153
+ code: "TARGET_NOT_FOUND",
5154
+ scope: "project",
5155
+ path: logicalName.replaceAll(".", "/"),
5156
+ hint: otherArea ? `The page exists in the ${otherArea.area} area.` : undefined
5157
+ });
4999
5158
  }
5000
5159
 
5001
5160
  // src/cli/completion.ts
@@ -5014,6 +5173,7 @@ var PUBLIC_COMMANDS = [
5014
5173
  ];
5015
5174
  var OPTIONS = {
5016
5175
  addpage: [
5176
+ "--area",
5017
5177
  "--layout",
5018
5178
  "--page",
5019
5179
  "--loading",
@@ -5033,21 +5193,51 @@ async function directories(root, context) {
5033
5193
  return [];
5034
5194
  }
5035
5195
  }
5036
- async function completionCandidates(command, context) {
5037
- if (!command)
5196
+ function selectedArea(args) {
5197
+ const index = args.lastIndexOf("--area");
5198
+ if (index < 0)
5199
+ return;
5200
+ const value = args[index + 1];
5201
+ return value && isPageArea(value) ? value : undefined;
5202
+ }
5203
+ function uniqueSorted(candidates) {
5204
+ return [...new Set(candidates)].sort((left, right) => left.localeCompare(right));
5205
+ }
5206
+ async function completionCandidates(words, context) {
5207
+ if (words.length === 0)
5038
5208
  return [...PUBLIC_COMMANDS];
5209
+ const [command, ...args] = words;
5210
+ const previous = args.at(-1);
5211
+ if (previous === "--area")
5212
+ return [...PAGE_AREAS];
5213
+ const area = selectedArea(args);
5214
+ const hasArea = args.includes("--area");
5039
5215
  if (command === "rmpage") {
5040
- return (await discoverPages(context.cwd, context.fs)).map((candidate) => candidate.logicalName);
5216
+ if (!area)
5217
+ return hasArea ? [] : ["--area"];
5218
+ const catalog = await discoverPageCatalog(context.cwd, context.fs);
5219
+ return uniqueSorted(catalog.candidates.filter((candidate) => candidate.area === area).map((candidate) => candidate.logicalName));
5220
+ }
5221
+ if (command === "addcomponent") {
5222
+ const pageOptionIndex = args.findIndex((argument) => argument === "--page" || argument === "-P");
5223
+ if (previous === "--page" || previous === "-P") {
5224
+ const catalog = await discoverPageCatalog(context.cwd, context.fs);
5225
+ return uniqueSorted(catalog.candidates.filter((candidate) => !area || candidate.area === area).map((candidate) => candidate.logicalName));
5226
+ }
5227
+ return uniqueSorted([
5228
+ ...OPTIONS.addcomponent ?? [],
5229
+ ...pageOptionIndex >= 0 && !hasArea ? ["--area"] : []
5230
+ ]);
5041
5231
  }
5042
- if (command === "addcomponent" || command === "addpage") {
5043
- return [
5044
- ...OPTIONS[command] ?? [],
5232
+ if (command === "addpage") {
5233
+ return uniqueSorted([
5234
+ ...(OPTIONS.addpage ?? []).filter((candidate) => candidate !== "--area" || !hasArea),
5045
5235
  ...await directories(path2.join(context.cwd, "src", "ui"), context)
5046
- ];
5236
+ ]);
5047
5237
  }
5048
5238
  if (command === "addlanguage")
5049
5239
  return ["de", "en", "es", "fr", "it", "ja", "pt"];
5050
- return OPTIONS[command] ?? [];
5240
+ return uniqueSorted(OPTIONS[command] ?? []);
5051
5241
  }
5052
5242
 
5053
5243
  // src/cli/onboarding.ts
@@ -5055,31 +5245,6 @@ import path4 from "path";
5055
5245
 
5056
5246
  // src/core/operations.ts
5057
5247
  import path3 from "path";
5058
-
5059
- // src/core/contracts.ts
5060
- class CliError extends Error {
5061
- exitCode;
5062
- code;
5063
- hint;
5064
- scope;
5065
- path;
5066
- constructor(message, options = {}) {
5067
- super(message);
5068
- this.name = "CliError";
5069
- if (typeof options === "number") {
5070
- this.exitCode = options;
5071
- this.code = "FILESYSTEM_ERROR";
5072
- } else {
5073
- this.exitCode = options.exitCode ?? 1;
5074
- this.code = options.code ?? "FILESYSTEM_ERROR";
5075
- this.hint = options.hint;
5076
- this.scope = options.scope;
5077
- this.path = options.path;
5078
- }
5079
- }
5080
- }
5081
-
5082
- // src/core/operations.ts
5083
5248
  class OperationJournal {
5084
5249
  #events = [];
5085
5250
  record(event) {
@@ -5635,16 +5800,58 @@ var addApi = async (args, context) => {
5635
5800
 
5636
5801
  // src/lib/addComponent.ts
5637
5802
  import { join as join3 } from "path";
5638
- var addComponent = async (args, context) => {
5639
- let componentName = args[1];
5640
- const pageIndex = args.findIndex((argument) => argument === "-P" || argument === "--page");
5641
- const pageScope = pageIndex >= 0 ? args[pageIndex + 1] : undefined;
5642
- if (pageIndex >= 0 && !pageScope) {
5643
- throw new CliError("The --page option requires a page name.", {
5644
- code: "INVALID_ARGUMENT"
5645
- });
5803
+ function formatGeneratedTranslations(content) {
5804
+ return content.replace(/^(\s*)(<(?:h2|p)\b[^>]*>)\{t\("([^"]+)"\)\}(<\/(?:h2|p)>)$/gm, (line, indent, opening, key, closing) => line.length <= 80 ? line : `${indent}${opening}
5805
+ ${indent} {t("${key}")}
5806
+ ${indent}${closing}`);
5807
+ }
5808
+ function parseAddComponentArguments(args) {
5809
+ const parsedArea = parseAreaOption(args);
5810
+ let componentName;
5811
+ let pageScope;
5812
+ for (let index = 1;index < parsedArea.args.length; index += 1) {
5813
+ const argument = parsedArea.args[index];
5814
+ if (argument === "-P" || argument === "--page") {
5815
+ if (pageScope) {
5816
+ throw new CliError("The --page option can only be provided once.", {
5817
+ code: "INVALID_ARGUMENT"
5818
+ });
5819
+ }
5820
+ const value = parsedArea.args[index + 1];
5821
+ if (!value || value.startsWith("-")) {
5822
+ throw new CliError("The --page option requires a page name.", {
5823
+ code: "INVALID_ARGUMENT"
5824
+ });
5825
+ }
5826
+ pageScope = value;
5827
+ index += 1;
5828
+ continue;
5829
+ }
5830
+ if (argument.startsWith("-")) {
5831
+ throw new CliError(`Unknown addcomponent option: ${argument}.`, {
5832
+ code: "INVALID_ARGUMENT"
5833
+ });
5834
+ }
5835
+ if (componentName) {
5836
+ throw new CliError(`Unexpected addcomponent argument: ${argument}.`, {
5837
+ code: "INVALID_ARGUMENT"
5838
+ });
5839
+ }
5840
+ componentName = argument;
5841
+ }
5842
+ if (pageScope && !parsedArea.area) {
5843
+ requirePageArea(parsedArea.area, "addcomponent --page");
5646
5844
  }
5647
- if (!componentName || componentName.startsWith("-")) {
5845
+ if (!pageScope && parsedArea.area) {
5846
+ throw new CliError("The --area option is only valid with addcomponent --page.", { code: "INVALID_ARGUMENT" });
5847
+ }
5848
+ return { area: parsedArea.area, componentName, pageScope };
5849
+ }
5850
+ var addComponent = async (args, context) => {
5851
+ const parsed = parseAddComponentArguments(args);
5852
+ let { componentName } = parsed;
5853
+ const { area, pageScope } = parsed;
5854
+ if (!componentName) {
5648
5855
  if (context.outputMode === "json") {
5649
5856
  throw new CliError("Component name is required in JSON mode.", {
5650
5857
  code: "INTERACTIVE_INPUT_REQUIRED",
@@ -5688,6 +5895,10 @@ var addComponent = async (args, context) => {
5688
5895
  hint: "Run this command from the generated project root."
5689
5896
  });
5690
5897
  }
5898
+ if (pageScope) {
5899
+ const catalog = await discoverPageCatalog(context.cwd, context.fs);
5900
+ resolvePageCandidate(catalog, pageScope, area);
5901
+ }
5691
5902
  const componentNameUpper = capitalize(toIdentifier(componentName));
5692
5903
  const templateRoot = join3(context.packageRoot, "templates", "Component");
5693
5904
  const componentTemplate = join3(templateRoot, "Component.tsx");
@@ -5703,7 +5914,7 @@ var addComponent = async (args, context) => {
5703
5914
  await assertSafeTarget(context.cwd, targetDirectory, context.fs);
5704
5915
  const componentFile = join3(targetDirectory, `${componentNameUpper}.tsx`);
5705
5916
  const translationNamespace = pageScope ?? "_global_ui";
5706
- const componentContent = (await context.fs.readText(componentTemplate)).replace(/Component/g, componentNameUpper).replace(/componentPage/g, translationNamespace);
5917
+ const componentContent = formatGeneratedTranslations((await context.fs.readText(componentTemplate)).replace(/Component/g, componentNameUpper).replace(/componentPage/g, translationNamespace));
5707
5918
  const preparedMessages = [];
5708
5919
  if (config.useI18n) {
5709
5920
  const messagesRoot = join3(context.cwd, "messages");
@@ -5714,6 +5925,7 @@ var addComponent = async (args, context) => {
5714
5925
  path: "messages"
5715
5926
  });
5716
5927
  }
5928
+ await assertSafeTarget(context.cwd, messagesRoot, context.fs);
5717
5929
  const templateMessages = JSON.parse(await context.fs.readText(messagesTemplate));
5718
5930
  const locales = (await context.fs.list(messagesRoot)).filter((entry) => entry.isDirectory).map((entry) => entry.name).sort();
5719
5931
  if (locales.length === 0) {
@@ -5726,6 +5938,7 @@ var addComponent = async (args, context) => {
5726
5938
  for (const locale of locales) {
5727
5939
  const messageFile = pageScope ? pageSegments[0] : "_global_ui";
5728
5940
  const target = join3(messagesRoot, locale, `${messageFile}.json`);
5941
+ await assertSafeTarget(context.cwd, target, context.fs);
5729
5942
  let data = {};
5730
5943
  if (context.fs.exists(target)) {
5731
5944
  try {
@@ -5768,17 +5981,20 @@ var addComponent = async (args, context) => {
5768
5981
  const gateway = new MutationGateway(context, context.cwd);
5769
5982
  await gateway.mkdir(targetDirectory, {
5770
5983
  role: "component-directory",
5771
- resource: "directory"
5984
+ resource: "directory",
5985
+ detail: area ? { area } : undefined
5772
5986
  });
5773
5987
  await gateway.write(componentFile, componentContent, {
5774
5988
  role: "ui-component",
5775
- preserveExisting: true
5989
+ preserveExisting: true,
5990
+ detail: area ? { area } : undefined
5776
5991
  });
5777
5992
  for (const item of preparedMessages) {
5778
5993
  if (item.exists) {
5779
5994
  gateway.unchanged(item.target, {
5780
5995
  role: "translation-messages",
5781
5996
  detail: {
5997
+ ...area ? { area } : {},
5782
5998
  locale: item.locale,
5783
5999
  key: `${translationNamespace}.${componentNameUpper}`
5784
6000
  }
@@ -5787,6 +6003,7 @@ var addComponent = async (args, context) => {
5787
6003
  await gateway.write(item.target, item.content, {
5788
6004
  role: "translation-messages",
5789
6005
  detail: {
6006
+ ...area ? { area } : {},
5790
6007
  locale: item.locale,
5791
6008
  key: `${translationNamespace}.${componentNameUpper}`
5792
6009
  }
@@ -5796,8 +6013,9 @@ var addComponent = async (args, context) => {
5796
6013
  const mutated = context.operations.snapshot().some((event) => event.action === "created" || event.action === "updated");
5797
6014
  return commandResult(context, {
5798
6015
  command: "addcomponent",
5799
- summary: mutated ? `Added component "${componentNameUpper}" ${pageScope ? `to page "${pageScope}"` : "globally"}.` : `Component "${componentNameUpper}" already exists and was preserved.`,
6016
+ summary: mutated ? `Added component "${componentNameUpper}" ${pageScope ? `to ${area} page "${pageScope}"` : "globally"}.` : pageScope ? `Component "${componentNameUpper}" already exists on ${area} page "${pageScope}" and was preserved.` : `Component "${componentNameUpper}" already exists and was preserved.`,
5800
6017
  projectRoot: context.cwd,
6018
+ data: pageScope ? { area, componentName: componentNameUpper, page: pageScope } : { componentName: componentNameUpper },
5801
6019
  nextSteps: mutated ? [
5802
6020
  {
5803
6021
  kind: "review",
@@ -6239,8 +6457,6 @@ var SHORT_FLAGS = {
6239
6457
  };
6240
6458
  function registerMessagesFile(content, locale, fileName) {
6241
6459
  const importPath = `./${locale}/${fileName}.json`;
6242
- if (content.includes(importPath))
6243
- return content;
6244
6460
  const declarationIndex = content.indexOf("const messages =");
6245
6461
  const registryMatch = content.match(/const messages = \{([\s\S]*?)\n\};/);
6246
6462
  if (declarationIndex < 0 || !registryMatch) {
@@ -6251,16 +6467,28 @@ function registerMessagesFile(content, locale, fileName) {
6251
6467
  });
6252
6468
  }
6253
6469
  const identifier = toIdentifier(fileName);
6470
+ const property = identifier === fileName ? ` ${identifier},` : ` ${JSON.stringify(fileName)}: ${identifier},`;
6471
+ if (content.includes(importPath)) {
6472
+ if (registryMatch[1].split(/\r?\n/).includes(property))
6473
+ return content;
6474
+ const legacyProperty = new RegExp(`^\\s*${identifier},\\s*$`, "m");
6475
+ if (legacyProperty.test(registryMatch[1])) {
6476
+ return content.replace(legacyProperty, property);
6477
+ }
6478
+ const nextRegistry2 = registryMatch[0].replace(/\n\};$/, `
6479
+ ${property}
6480
+ };`);
6481
+ return content.replace(registryMatch[0], nextRegistry2);
6482
+ }
6254
6483
  const importStatement = `import ${identifier} from "${importPath}";
6255
6484
  `;
6256
6485
  const nextRegistry = registryMatch[0].replace(/\n\};$/, `
6257
- ${identifier},
6486
+ ${property}
6258
6487
  };`);
6259
6488
  return content.slice(0, declarationIndex) + importStatement + content.slice(declarationIndex).replace(registryMatch[0], nextRegistry);
6260
6489
  }
6261
- function selectedFlags(args) {
6490
+ function selectedFlags(optionArguments) {
6262
6491
  const selected = new Set;
6263
- const optionArguments = args.slice(2).filter((argument) => argument.startsWith("-"));
6264
6492
  if (optionArguments.length === 0)
6265
6493
  return new Set(["layout", "page", "loading"]);
6266
6494
  for (const argument of optionArguments) {
@@ -6286,23 +6514,61 @@ function selectedFlags(args) {
6286
6514
  }
6287
6515
  return selected;
6288
6516
  }
6517
+ function parseAddPageArguments(args) {
6518
+ const parsedArea = parseAreaOption(args);
6519
+ let logicalName;
6520
+ const optionArguments = [];
6521
+ for (const argument of parsedArea.args.slice(1)) {
6522
+ if (argument.startsWith("-")) {
6523
+ optionArguments.push(argument);
6524
+ continue;
6525
+ }
6526
+ if (logicalName) {
6527
+ throw new CliError(`Unexpected addpage argument: ${argument}.`, {
6528
+ code: "INVALID_ARGUMENT"
6529
+ });
6530
+ }
6531
+ logicalName = argument;
6532
+ }
6533
+ return {
6534
+ area: parsedArea.area,
6535
+ logicalName,
6536
+ flags: selectedFlags(optionArguments)
6537
+ };
6538
+ }
6289
6539
  var addPage = async (args, context) => {
6290
- let logicalName = args[1];
6291
- if (!logicalName || logicalName.startsWith("-")) {
6540
+ const parsed = parseAddPageArguments(args);
6541
+ let { area, logicalName } = parsed;
6542
+ if (!logicalName) {
6292
6543
  if (context.outputMode === "json") {
6293
6544
  throw new CliError("Page name is required in JSON mode.", {
6294
6545
  code: "INTERACTIVE_INPUT_REQUIRED",
6295
6546
  hint: "Pass a simple or Parent.Child page name after addpage."
6296
6547
  });
6297
6548
  }
6298
- const response = await context.prompt({
6299
- type: "text",
6300
- name: "pageName",
6301
- message: "Page name to add:",
6302
- validate: (name) => name ? true : "Page name is required"
6303
- });
6549
+ const questions = [
6550
+ {
6551
+ type: "text",
6552
+ name: "pageName",
6553
+ message: "Page name to add:",
6554
+ validate: (name) => name ? true : "Page name is required"
6555
+ },
6556
+ ...!area ? [
6557
+ {
6558
+ type: "select",
6559
+ name: "area",
6560
+ message: "Page area:",
6561
+ choices: [
6562
+ { title: "public", value: "public" },
6563
+ { title: "user", value: "user" }
6564
+ ]
6565
+ }
6566
+ ] : []
6567
+ ];
6568
+ const response = await context.prompt(questions);
6304
6569
  logicalName = String(response.pageName ?? "");
6305
- if (!logicalName) {
6570
+ area = area ?? response.area;
6571
+ if (!logicalName || !area) {
6306
6572
  context.operations.record({
6307
6573
  action: "cancelled",
6308
6574
  resource: "command",
@@ -6317,14 +6583,17 @@ var addPage = async (args, context) => {
6317
6583
  status: "cancelled"
6318
6584
  });
6319
6585
  }
6586
+ } else {
6587
+ area = requirePageArea(area, "addpage");
6320
6588
  }
6589
+ area = requirePageArea(area, "addpage");
6321
6590
  const pageSegments = parseLogicalName(logicalName, "page name");
6322
6591
  if (pageSegments.length > 2) {
6323
6592
  throw new CliError("Nested pages support exactly Parent.Child.", {
6324
6593
  code: "INVALID_ARGUMENT"
6325
6594
  });
6326
6595
  }
6327
- const flags = selectedFlags(args);
6596
+ const flags = parsed.flags;
6328
6597
  const config = await loadConfig(context);
6329
6598
  if (!config) {
6330
6599
  throw new CliError("Configuration file cnp.config.json was not found.", {
@@ -6342,16 +6611,45 @@ var addPage = async (args, context) => {
6342
6611
  path: useI18n ? "src/app/[locale]" : "src/app"
6343
6612
  });
6344
6613
  }
6614
+ const areaRoot = join6(appRoot, areaRouteGroup(area));
6615
+ if (!context.fs.exists(areaRoot)) {
6616
+ throw new CliError(`The ${area} page area was not found.`, {
6617
+ code: "CONFIG_NOT_FOUND",
6618
+ scope: "project",
6619
+ path: join6(useI18n ? "src/app/[locale]" : "src/app", areaRouteGroup(area))
6620
+ });
6621
+ }
6345
6622
  const [parentName, childName] = pageSegments.length === 2 ? pageSegments : [undefined, undefined];
6346
6623
  const leafName = childName ?? pageSegments[0];
6347
6624
  const pageIdentifier = capitalize(toIdentifier(leafName));
6348
6625
  const jsonFileName = parentName ?? leafName;
6349
6626
  const uiDirectory = join6(context.cwd, "src", "ui", ...pageSegments);
6350
- const routeDirectory = join6(appRoot, ...pageSegments);
6627
+ const routeDirectory = join6(areaRoot, ...pageSegments);
6628
+ const catalog = await discoverPageCatalog(context.cwd, context.fs);
6629
+ assertConsistentLogicalRoute(catalog, logicalName);
6630
+ const existingOtherArea = catalog.candidates.find((candidate) => candidate.logicalName === logicalName && candidate.area !== area);
6631
+ const otherArea = area === "public" ? "user" : "public";
6632
+ const otherAreaDirectory = join6(appRoot, areaRouteGroup(otherArea), ...pageSegments);
6633
+ if (existingOtherArea || context.fs.exists(otherAreaDirectory)) {
6634
+ throw new CliError(`Page "${logicalName}" already belongs to the ${otherArea} area.`, {
6635
+ code: "TARGET_EXISTS",
6636
+ scope: "project",
6637
+ path: join6(useI18n ? "src/app/[locale]" : "src/app", areaRouteGroup(otherArea), ...pageSegments)
6638
+ });
6639
+ }
6640
+ const legacyDirectory = join6(appRoot, ...pageSegments);
6641
+ if (context.fs.exists(legacyDirectory)) {
6642
+ throw new CliError(`Page route "${logicalName}" is ungrouped.`, {
6643
+ code: "INCONSISTENT_ROUTE",
6644
+ scope: "project",
6645
+ path: join6(useI18n ? "src/app/[locale]" : "src/app", ...pageSegments),
6646
+ hint: "Move the route into exactly one of the (public) or (user) areas before retrying."
6647
+ });
6648
+ }
6351
6649
  await assertSafeTarget(context.cwd, uiDirectory, context.fs);
6352
6650
  await assertSafeTarget(context.cwd, routeDirectory, context.fs);
6353
6651
  const templateRoot = join6(context.packageRoot, "templates", "Page");
6354
- const uiTemplate = join6(templateRoot, "page-ui.tsx");
6652
+ const uiTemplate = join6(templateRoot, area === "user" ? "page-ui.user.tsx" : "page-ui.tsx");
6355
6653
  const routeTemplates = [...flags].map((flag) => ({
6356
6654
  flag,
6357
6655
  source: join6(templateRoot, toFileName(flag))
@@ -6383,6 +6681,7 @@ var addPage = async (args, context) => {
6383
6681
  path: "messages"
6384
6682
  });
6385
6683
  }
6684
+ await assertSafeTarget(context.cwd, messagesRoot, context.fs);
6386
6685
  const locales = (await context.fs.list(messagesRoot)).filter((entry) => entry.isDirectory).map((entry) => entry.name).sort();
6387
6686
  if (locales.length === 0) {
6388
6687
  throw new CliError("No locale directories were found.", {
@@ -6394,6 +6693,8 @@ var addPage = async (args, context) => {
6394
6693
  for (const locale of locales) {
6395
6694
  const target = join6(messagesRoot, locale, `${jsonFileName}.json`);
6396
6695
  const aggregator = join6(messagesRoot, `${locale}.ts`);
6696
+ await assertSafeTarget(context.cwd, target, context.fs);
6697
+ await assertSafeTarget(context.cwd, aggregator, context.fs);
6397
6698
  if (!context.fs.exists(aggregator)) {
6398
6699
  throw new CliError(`Locale aggregator messages/${locale}.ts was not found.`, {
6399
6700
  code: "CONFIG_NOT_FOUND",
@@ -6436,17 +6737,20 @@ var addPage = async (args, context) => {
6436
6737
  const gateway = new MutationGateway(context, context.cwd);
6437
6738
  await gateway.mkdir(uiDirectory, {
6438
6739
  role: "page-ui-directory",
6439
- resource: "directory"
6740
+ resource: "directory",
6741
+ detail: { area }
6440
6742
  });
6441
6743
  const uiFile = join6(uiDirectory, "page-ui.tsx");
6442
6744
  const uiContent = (await context.fs.readText(uiTemplate)).replace('useTranslations("template")', `useTranslations("${translationNamespace}")`).replaceAll('from "@/', `from "${aliasPrefix}/`).replace(/template/g, pageIdentifier).replace(/Template/g, pageIdentifier);
6443
6745
  await gateway.write(uiFile, uiContent, {
6444
6746
  role: "page-ui",
6445
- preserveExisting: true
6747
+ preserveExisting: true,
6748
+ detail: { area }
6446
6749
  });
6447
6750
  await gateway.mkdir(routeDirectory, {
6448
6751
  role: "page-route-directory",
6449
- resource: "directory"
6752
+ resource: "directory",
6753
+ detail: { area }
6450
6754
  });
6451
6755
  for (const template of routeTemplates) {
6452
6756
  const filename = toFileName(template.flag);
@@ -6455,7 +6759,8 @@ var addPage = async (args, context) => {
6455
6759
  const content = (await context.fs.readText(template.source)).replace("@/ui/template/", `@/ui/${uiImportPath}/`).replaceAll('from "@/', `from "${aliasPrefix}/`).replace(/template/g, pageIdentifier).replace(/Template/g, pageIdentifier);
6456
6760
  await gateway.write(target, content, {
6457
6761
  role: `page-${template.flag}`,
6458
- preserveExisting: true
6762
+ preserveExisting: true,
6763
+ detail: { area }
6459
6764
  });
6460
6765
  }
6461
6766
  if (useI18n) {
@@ -6463,23 +6768,32 @@ var addPage = async (args, context) => {
6463
6768
  if (item.messageExists) {
6464
6769
  gateway.unchanged(item.target, {
6465
6770
  role: "translation-messages",
6466
- detail: { locale: item.locale, namespace: translationNamespace }
6771
+ detail: {
6772
+ area,
6773
+ locale: item.locale,
6774
+ namespace: translationNamespace
6775
+ }
6467
6776
  });
6468
6777
  } else {
6469
6778
  await gateway.write(item.target, item.targetContent, {
6470
6779
  role: "translation-messages",
6471
- detail: { locale: item.locale, namespace: translationNamespace }
6780
+ detail: {
6781
+ area,
6782
+ locale: item.locale,
6783
+ namespace: translationNamespace
6784
+ }
6472
6785
  });
6473
6786
  }
6474
6787
  await gateway.write(item.aggregator, item.aggregatorContent, {
6475
- role: "locale-aggregator"
6788
+ role: "locale-aggregator",
6789
+ detail: { area }
6476
6790
  });
6477
6791
  }
6478
6792
  } else {
6479
6793
  gateway.skipped(join6(context.cwd, "messages"), {
6480
6794
  role: "translation-messages",
6481
6795
  resource: "directory",
6482
- detail: { reason: "Internationalization is disabled." }
6796
+ detail: { area, reason: "Internationalization is disabled." }
6483
6797
  });
6484
6798
  }
6485
6799
  const mutated = context.operations.snapshot().some((event) => ["created", "updated", "copied", "deleted"].includes(event.action));
@@ -6496,8 +6810,9 @@ var addPage = async (args, context) => {
6496
6810
  ];
6497
6811
  return commandResult(context, {
6498
6812
  command: "addpage",
6499
- summary: mutated ? `Added page "${logicalName}" and its missing resources.` : `Page "${logicalName}" already exists and was preserved.`,
6813
+ summary: mutated ? `Added ${area} page "${logicalName}" and its missing resources.` : `${area[0].toUpperCase()}${area.slice(1)} page "${logicalName}" already exists and was preserved.`,
6500
6814
  projectRoot: context.cwd,
6815
+ data: { area, logicalName },
6501
6816
  nextSteps: mutated ? [
6502
6817
  {
6503
6818
  kind: "review",
@@ -6655,9 +6970,11 @@ function escapeRegExp(value) {
6655
6970
  function unregisterMessagesFile(content, locale, fileName) {
6656
6971
  const identifier = toIdentifier(fileName);
6657
6972
  const escapedIdentifier = escapeRegExp(identifier);
6973
+ const escapedFileName = escapeRegExp(fileName);
6658
6974
  const escapedPath = escapeRegExp(`./${locale}/${fileName}.json`);
6659
6975
  const importPattern = new RegExp(`^import\\s+${escapedIdentifier}\\s+from\\s+["']${escapedPath}["'];?\\r?\\n?`, "m");
6660
- const propertyPattern = new RegExp(`^\\s*${escapedIdentifier},\\s*\\r?\\n?`, "m");
6976
+ const propertyExpression = identifier === fileName ? escapedIdentifier : `(?:["']${escapedFileName}["']\\s*:\\s*)?${escapedIdentifier}`;
6977
+ const propertyPattern = new RegExp(`^\\s*${propertyExpression},\\s*\\r?\\n?`, "m");
6661
6978
  const hasImport = importPattern.test(content);
6662
6979
  const hasProperty = propertyPattern.test(content);
6663
6980
  if (hasImport !== hasProperty) {
@@ -6674,24 +6991,57 @@ function unregisterMessagesFile(content, locale, fileName) {
6674
6991
  changed: true
6675
6992
  };
6676
6993
  }
6994
+ function parseRmPageArguments(args) {
6995
+ const parsedArea = parseAreaOption(args);
6996
+ let logicalName;
6997
+ for (const argument of parsedArea.args.slice(1)) {
6998
+ if (argument.startsWith("-")) {
6999
+ throw new CliError(`Unknown rmpage option: ${argument}.`, {
7000
+ code: "INVALID_ARGUMENT"
7001
+ });
7002
+ }
7003
+ if (logicalName) {
7004
+ throw new CliError(`Unexpected rmpage argument: ${argument}.`, {
7005
+ code: "INVALID_ARGUMENT"
7006
+ });
7007
+ }
7008
+ logicalName = argument;
7009
+ }
7010
+ return { area: parsedArea.area, logicalName };
7011
+ }
6677
7012
  var rmPage = async (args, context) => {
6678
- const candidates = await discoverPages(context.cwd, context.fs);
6679
- let logicalName = args[1];
6680
- if (!logicalName || logicalName.startsWith("-")) {
7013
+ const parsed = parseRmPageArguments(args);
7014
+ const catalog = await discoverPageCatalog(context.cwd, context.fs);
7015
+ let { area, logicalName } = parsed;
7016
+ let candidate;
7017
+ if (!logicalName) {
6681
7018
  if (context.outputMode === "json") {
6682
7019
  throw new CliError("Page name is required in JSON mode.", {
6683
7020
  code: "INTERACTIVE_INPUT_REQUIRED",
6684
7021
  hint: "Pass a page name returned by completion after rmpage."
6685
7022
  });
6686
7023
  }
7024
+ const candidates = area ? catalog.candidates.filter((entry) => entry.area === area) : catalog.candidates;
7025
+ if (candidates.length === 0) {
7026
+ const issue = catalog.issues[0];
7027
+ if (issue) {
7028
+ throw new CliError(`Page route "${issue.logicalName}" is inconsistent.`, {
7029
+ code: "INCONSISTENT_ROUTE",
7030
+ scope: "project",
7031
+ path: issue.routeDirectories.join(", "),
7032
+ hint: "Move the route into exactly one of the (public) or (user) areas before retrying."
7033
+ });
7034
+ }
7035
+ throw new CliError(area ? `No pages were found in the ${area} area.` : "No removable pages were found.", { code: "TARGET_NOT_FOUND", scope: "project", path: "." });
7036
+ }
6687
7037
  const selected = await context.prompt([
6688
7038
  {
6689
7039
  type: "autocomplete",
6690
7040
  name: "page",
6691
7041
  message: "Page to remove:",
6692
7042
  choices: candidates.map((candidate2) => ({
6693
- title: candidate2.logicalName.replaceAll(".", " > "),
6694
- value: candidate2.logicalName
7043
+ title: `${candidate2.area[0].toUpperCase()}${candidate2.area.slice(1)} > ${candidate2.logicalName.replaceAll(".", " > ")}`,
7044
+ value: candidate2.id
6695
7045
  }))
6696
7046
  },
6697
7047
  {
@@ -6701,41 +7051,56 @@ var rmPage = async (args, context) => {
6701
7051
  initial: false
6702
7052
  }
6703
7053
  ]);
7054
+ const selectedId = String(selected.page ?? "");
7055
+ const selectedCandidate = candidates.find((entry) => entry.id === selectedId);
6704
7056
  if (!selected.confirm) {
6705
7057
  context.operations.record({
6706
7058
  action: "cancelled",
6707
7059
  resource: "command",
6708
7060
  role: "page-removal",
6709
7061
  scope: "project",
6710
- path: "."
7062
+ path: ".",
7063
+ detail: selectedCandidate ? { area: selectedCandidate.area } : area ? { area } : undefined
6711
7064
  });
6712
7065
  return commandResult(context, {
6713
7066
  command: "rmpage",
6714
- summary: "Page deletion was cancelled.",
7067
+ summary: selectedCandidate ? `Deletion of ${selectedCandidate.area} page "${selectedCandidate.logicalName}" was cancelled.` : area ? `Page deletion in the ${area} area was cancelled.` : "Page deletion was cancelled.",
6715
7068
  projectRoot: context.cwd,
6716
- status: "cancelled"
7069
+ status: "cancelled",
7070
+ data: selectedCandidate ? {
7071
+ area: selectedCandidate.area,
7072
+ logicalName: selectedCandidate.logicalName
7073
+ } : area ? { area } : undefined
6717
7074
  });
6718
7075
  }
6719
- logicalName = String(selected.page ?? "");
6720
- }
6721
- parseLogicalName(logicalName, "page name");
6722
- const candidate = candidates.find((entry) => entry.logicalName === logicalName);
6723
- if (!candidate) {
6724
- throw new CliError(`Page not found: ${logicalName}.`, {
6725
- code: "TARGET_NOT_FOUND",
6726
- scope: "project",
6727
- path: logicalName.replaceAll(".", "/")
6728
- });
7076
+ if (!selectedCandidate) {
7077
+ throw new CliError("The selected page is not in the page catalog.", {
7078
+ code: "INVALID_ARGUMENT"
7079
+ });
7080
+ }
7081
+ candidate = selectedCandidate;
7082
+ logicalName = candidate.logicalName;
7083
+ area = candidate.area;
7084
+ } else {
7085
+ area = requirePageArea(area, "rmpage");
7086
+ parseLogicalName(logicalName, "page name");
7087
+ candidate = resolvePageCandidate(catalog, logicalName, area);
6729
7088
  }
7089
+ area = requirePageArea(area, "rmpage");
6730
7090
  const messagesRoot = resolveInside(context.cwd, "messages");
7091
+ await assertSafeTarget(context.cwd, candidate.uiDirectory, context.fs);
7092
+ await assertSafeTarget(context.cwd, candidate.routeDirectory, context.fs);
6731
7093
  const preparedMessages = [];
6732
7094
  const preparedAggregators = [];
6733
7095
  if (context.fs.exists(messagesRoot)) {
7096
+ await assertSafeTarget(context.cwd, messagesRoot, context.fs);
6734
7097
  const locales = (await context.fs.list(messagesRoot)).filter((entry) => entry.isDirectory).map((entry) => entry.name).sort();
6735
7098
  for (const locale of locales) {
6736
7099
  const target = resolveInside(context.cwd, "messages", locale, `${candidate.routeSegments[0]}.json`);
7100
+ await assertSafeTarget(context.cwd, target, context.fs);
6737
7101
  if (!candidate.messageKey) {
6738
7102
  const aggregator = resolveInside(context.cwd, "messages", `${locale}.ts`);
7103
+ await assertSafeTarget(context.cwd, aggregator, context.fs);
6739
7104
  if (!context.fs.exists(aggregator)) {
6740
7105
  throw new CliError(`Locale aggregator messages/${locale}.ts was not found.`, {
6741
7106
  code: "CONFIG_NOT_FOUND",
@@ -6780,41 +7145,51 @@ var rmPage = async (args, context) => {
6780
7145
  const gateway = new MutationGateway(context, context.cwd);
6781
7146
  for (const prepared of preparedMessages) {
6782
7147
  if (!candidate.messageKey) {
6783
- await gateway.remove(prepared.target, { role: "translation-messages" });
7148
+ await gateway.remove(prepared.target, {
7149
+ role: "translation-messages",
7150
+ detail: { area }
7151
+ });
6784
7152
  } else if (prepared.keyPresent) {
6785
7153
  await gateway.write(prepared.target, prepared.content, {
6786
7154
  role: "translation-messages",
6787
- detail: { removedKey: candidate.messageKey }
7155
+ detail: { area, removedKey: candidate.messageKey }
6788
7156
  });
6789
7157
  } else {
6790
7158
  gateway.unchanged(prepared.target, {
6791
7159
  role: "translation-messages",
6792
- detail: { missingKey: candidate.messageKey }
7160
+ detail: { area, missingKey: candidate.messageKey }
6793
7161
  });
6794
7162
  }
6795
7163
  }
6796
7164
  for (const prepared of preparedAggregators) {
6797
7165
  if (prepared.changed) {
6798
7166
  await gateway.write(prepared.target, prepared.content, {
6799
- role: "locale-aggregator"
7167
+ role: "locale-aggregator",
7168
+ detail: { area }
6800
7169
  });
6801
7170
  } else {
6802
- gateway.unchanged(prepared.target, { role: "locale-aggregator" });
7171
+ gateway.unchanged(prepared.target, {
7172
+ role: "locale-aggregator",
7173
+ detail: { area }
7174
+ });
6803
7175
  }
6804
7176
  }
6805
7177
  await gateway.remove(candidate.uiDirectory, {
6806
7178
  role: "page-ui",
6807
- resource: "directory"
7179
+ resource: "directory",
7180
+ detail: { area }
6808
7181
  }, { recursive: true, force: false });
6809
7182
  await gateway.remove(candidate.routeDirectory, {
6810
7183
  role: "page-route",
6811
- resource: "directory"
7184
+ resource: "directory",
7185
+ detail: { area }
6812
7186
  }, { recursive: true, force: false });
6813
7187
  const mutated = context.operations.snapshot().some((event) => event.action === "deleted" || event.action === "updated");
6814
7188
  return commandResult(context, {
6815
7189
  command: "rmpage",
6816
- summary: mutated ? `Deleted page "${logicalName}" and its associated resources.` : `No resources remained for page "${logicalName}".`,
7190
+ summary: mutated ? `Deleted ${area} page "${logicalName}" and its associated resources.` : `No resources remained for ${area} page "${logicalName}".`,
6817
7191
  projectRoot: context.cwd,
7192
+ data: { area, logicalName },
6818
7193
  nextSteps: mutated ? [
6819
7194
  {
6820
7195
  kind: "run-checks",
@@ -6920,6 +7295,7 @@ async function validateScaffoldTemplate(root, fs) {
6920
7295
  "bun.lock",
6921
7296
  "pnpm-workspace.yaml",
6922
7297
  "vitest.config.ts",
7298
+ path8.join("scripts", "audit-policy.ts"),
6923
7299
  path8.join("scripts", "audit.ts"),
6924
7300
  path8.join("scripts", "package-manager.ts"),
6925
7301
  path8.join("tests", "consumer", "validate-template.ts"),
@@ -7305,18 +7681,19 @@ var HELP_TEXT = `create-next-pro
7305
7681
 
7306
7682
  Usage:
7307
7683
  create-next-pro <project-name> [--force] [--json]
7308
- create-next-pro addpage [options] [--json]
7309
- create-next-pro addcomponent [options] [--json]
7684
+ create-next-pro addpage [name] --area <public|user> [options] [--json]
7685
+ create-next-pro addcomponent [name] [--page <page> --area <public|user>] [--json]
7310
7686
  create-next-pro addlib [name] [--json]
7311
7687
  create-next-pro addapi [name] [--json]
7312
7688
  create-next-pro addlanguage [locale] [--json]
7313
7689
  create-next-pro addtext <path> [text] [--json]
7314
- create-next-pro rmpage [options] [--json]
7690
+ create-next-pro rmpage [name] [--area <public|user>] [--json]
7315
7691
 
7316
7692
  Options:
7317
7693
  --help Show this help message
7318
7694
  --version Show the CLI version
7319
7695
  --json Emit one deterministic JSON document
7696
+ --area Select the public or user area for page operations
7320
7697
  --reconfigure Run the configuration assistant again
7321
7698
  `;
7322
7699
  function parseOptions(args) {
@@ -7396,7 +7773,7 @@ async function executeCli(context) {
7396
7773
  }
7397
7774
  async function runCompletion(context, args) {
7398
7775
  try {
7399
- for (const candidate of await completionCandidates(args[1], context)) {
7776
+ for (const candidate of await completionCandidates(args.slice(1), context)) {
7400
7777
  context.terminal.log(candidate);
7401
7778
  }
7402
7779
  return 0;