@catladder/cli 0.0.0-new-cli-81ed6546 → 0.0.0-new-cli-52525b58

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 (93) hide show
  1. package/README.md +2 -2
  2. package/dist/bundles/catenv/index.js +1 -1
  3. package/dist/bundles/cli/index.js +128 -81
  4. package/dist/cli/src/cli.js +24 -5
  5. package/dist/cli/src/cli.js.map +1 -1
  6. package/dist/cli/src/commands/cloudSQL/commandRestoreDb.js +1 -1
  7. package/dist/cli/src/commands/cloudSQL/commandRestoreDb.js.map +1 -1
  8. package/dist/cli/src/commands/fun/commandDadjoke.js +1 -1
  9. package/dist/cli/src/commands/fun/commandDadjoke.js.map +1 -1
  10. package/dist/cli/src/commands/fun/commandStarwars.js +1 -1
  11. package/dist/cli/src/commands/fun/commandStarwars.js.map +1 -1
  12. package/dist/cli/src/commands/general/commandKubeCurrentContext.js +1 -1
  13. package/dist/cli/src/commands/general/commandKubeGetShell.js +1 -1
  14. package/dist/cli/src/commands/general/commandKubeListNamespaces.js +1 -1
  15. package/dist/cli/src/commands/general/commandKubeListPods.js +1 -1
  16. package/dist/cli/src/commands/general/commandKubeListSecrets.js +1 -1
  17. package/dist/cli/src/commands/general/commandKubePortForward.js +1 -1
  18. package/dist/cli/src/commands/general/commandKubeStopPortforward.js +1 -1
  19. package/dist/cli/src/commands/mongodb/commandMongoDestroyMember.js +1 -1
  20. package/dist/cli/src/commands/mongodb/commandMongoGetShell.js +1 -1
  21. package/dist/cli/src/commands/mongodb/commandMongoPortForward.js +1 -1
  22. package/dist/cli/src/commands/project/commandCloudSqlProxy.js +1 -1
  23. package/dist/cli/src/commands/project/commandCloudSqlProxy.js.map +1 -1
  24. package/dist/cli/src/commands/project/commandConfigSecrets.js +1 -1
  25. package/dist/cli/src/commands/project/commandDeletePods.js +1 -1
  26. package/dist/cli/src/commands/project/commandDeleteProject.js +1 -1
  27. package/dist/cli/src/commands/project/commandEnvVars.js +1 -1
  28. package/dist/cli/src/commands/project/commandGetMyTotalWorktime.js +1 -1
  29. package/dist/cli/src/commands/project/commandGetMyTotalWorktime.js.map +1 -1
  30. package/dist/cli/src/commands/project/commandGetShell.js +1 -1
  31. package/dist/cli/src/commands/project/commandGitlabCi.js +2 -2
  32. package/dist/cli/src/commands/project/commandListPods.js +1 -1
  33. package/dist/cli/src/commands/project/commandNamespace.js +1 -1
  34. package/dist/cli/src/commands/project/commandOpenEnv.js +1 -1
  35. package/dist/cli/src/commands/project/commandOpenGit.js +1 -1
  36. package/dist/cli/src/commands/project/commandOpenLogs.js +1 -1
  37. package/dist/cli/src/commands/project/commandPauseProject.js +1 -1
  38. package/dist/cli/src/commands/project/commandPortForward.js +1 -1
  39. package/dist/cli/src/commands/project/commandProjectRestoreDb.js +1 -1
  40. package/dist/cli/src/commands/project/commandProjectRestoreDb.js.map +1 -1
  41. package/dist/cli/src/commands/project/commandRenewToken.js +1 -1
  42. package/dist/cli/src/commands/project/commandSecretsClearBackups.js +1 -1
  43. package/dist/cli/src/commands/project/commandSecurityEvaluate.js +1 -1
  44. package/dist/cli/src/commands/project/commandSetup.js +1 -1
  45. package/dist/cli/src/commands/project/commandTriggerCronjob.js +2 -2
  46. package/dist/cli/src/commands/project/commandTriggerCronjob.js.map +1 -1
  47. package/dist/cli/src/commands/security/commandSecurityAuditCiJob.js +1 -1
  48. package/dist/cli/src/commands/security/commandSecurityAuditCreate.js +1 -1
  49. package/dist/cli/src/commands/security/commandSecurityAuditEvaluate.js +1 -1
  50. package/dist/cli/src/completion.js +64 -36
  51. package/dist/cli/src/completion.js.map +1 -1
  52. package/dist/pipeline/src/constants.js +1 -1
  53. package/dist/tsconfig.tsbuildinfo +1 -1
  54. package/package.json +1 -1
  55. package/src/cli.ts +29 -5
  56. package/src/commands/cloudSQL/commandRestoreDb.ts +1 -1
  57. package/src/commands/fun/commandDadjoke.ts +1 -1
  58. package/src/commands/fun/commandStarwars.ts +1 -1
  59. package/src/commands/general/commandKubeCurrentContext.ts +1 -1
  60. package/src/commands/general/commandKubeGetShell.ts +1 -1
  61. package/src/commands/general/commandKubeListNamespaces.ts +1 -1
  62. package/src/commands/general/commandKubeListPods.ts +1 -1
  63. package/src/commands/general/commandKubeListSecrets.ts +1 -1
  64. package/src/commands/general/commandKubePortForward.ts +1 -1
  65. package/src/commands/general/commandKubeStopPortforward.ts +1 -1
  66. package/src/commands/mongodb/commandMongoDestroyMember.ts +1 -1
  67. package/src/commands/mongodb/commandMongoGetShell.ts +1 -1
  68. package/src/commands/mongodb/commandMongoPortForward.ts +1 -1
  69. package/src/commands/project/commandCloudSqlProxy.ts +1 -1
  70. package/src/commands/project/commandConfigSecrets.ts +1 -1
  71. package/src/commands/project/commandDeletePods.ts +1 -1
  72. package/src/commands/project/commandDeleteProject.ts +1 -1
  73. package/src/commands/project/commandEnvVars.ts +1 -1
  74. package/src/commands/project/commandGetMyTotalWorktime.ts +1 -1
  75. package/src/commands/project/commandGetShell.ts +1 -1
  76. package/src/commands/project/commandGitlabCi.ts +2 -2
  77. package/src/commands/project/commandListPods.ts +1 -1
  78. package/src/commands/project/commandNamespace.ts +1 -1
  79. package/src/commands/project/commandOpenEnv.ts +1 -1
  80. package/src/commands/project/commandOpenGit.ts +1 -1
  81. package/src/commands/project/commandOpenLogs.ts +1 -1
  82. package/src/commands/project/commandPauseProject.ts +1 -1
  83. package/src/commands/project/commandPortForward.ts +1 -1
  84. package/src/commands/project/commandProjectRestoreDb.ts +1 -1
  85. package/src/commands/project/commandRenewToken.ts +1 -1
  86. package/src/commands/project/commandSecretsClearBackups.ts +1 -1
  87. package/src/commands/project/commandSecurityEvaluate.ts +1 -1
  88. package/src/commands/project/commandSetup.ts +1 -1
  89. package/src/commands/project/commandTriggerCronjob.ts +2 -2
  90. package/src/commands/security/commandSecurityAuditCiJob.ts +1 -1
  91. package/src/commands/security/commandSecurityAuditCreate.ts +1 -1
  92. package/src/commands/security/commandSecurityAuditEvaluate.ts +1 -1
  93. package/src/completion.ts +63 -42
package/package.json CHANGED
@@ -53,7 +53,7 @@
53
53
  }
54
54
  ],
55
55
  "license": "MIT",
56
- "version": "0.0.0-new-cli-81ed6546",
56
+ "version": "0.0.0-new-cli-52525b58",
57
57
  "scripts": {
58
58
  "lint": "eslint \"src/**/*.ts\"",
59
59
  "lint:fix": "eslint \"src/**/*.ts\" --fix",
package/src/cli.ts CHANGED
@@ -24,8 +24,28 @@ program
24
24
  // Collect all command defs for completion
25
25
  const allCommandDefs: CommandDef[] = [];
26
26
 
27
+ // Cache for intermediate subcommand groups (e.g., "project", "project cloudsql")
28
+ const subcommandCache = new Map<string, Command>();
29
+
30
+ /**
31
+ * Get or create a chain of nested subcommands.
32
+ * E.g., for ["project", "cloudsql"], ensures program.command("project").command("cloudsql") exists.
33
+ */
34
+ function getOrCreateParent(parts: string[]): Command {
35
+ if (parts.length === 0) return program;
36
+
37
+ const key = parts.join(" ");
38
+ if (subcommandCache.has(key)) return subcommandCache.get(key)!;
39
+
40
+ const parent = getOrCreateParent(parts.slice(0, -1));
41
+ const cmd = parent.command(parts[parts.length - 1]);
42
+ subcommandCache.set(key, cmd);
43
+ return cmd;
44
+ }
45
+
27
46
  /**
28
47
  * Register a CommandDef with Commander.js.
48
+ * - Command name can contain spaces for nesting (e.g., "project cloudsql proxy")
29
49
  * - Inputs with `positional: true` become Commander positional args
30
50
  * - Other inputs become optional --flags
31
51
  * - --inputs accepts JSON for programmatic use
@@ -45,15 +65,19 @@ function registerCommand(def: CommandDef): void {
45
65
  }
46
66
  }
47
67
 
48
- // Build the command string with positional args.
49
- // All positionals are registered as optional in Commander so that
50
- // ctx.get() can prompt for them interactively when missing.
51
- let cmdStr = def.name;
68
+ // Split name into parent groups + leaf command
69
+ const nameParts = def.name.split(" ");
70
+ const parentParts = nameParts.slice(0, -1);
71
+ const leafName = nameParts[nameParts.length - 1];
72
+
73
+ // Build leaf command string with positional args
74
+ let cmdStr = leafName;
52
75
  for (const [name] of positionalInputs) {
53
76
  cmdStr += ` [${name}]`;
54
77
  }
55
78
 
56
- const cmd = program.command(cmdStr).description(def.description);
79
+ const parent = getOrCreateParent(parentParts);
80
+ const cmd = parent.command(cmdStr).description(def.description);
57
81
 
58
82
  // Add --flag for each non-positional input
59
83
  for (const [name, inputDef] of flagInputs) {
@@ -4,7 +4,7 @@ import type { CloudSqlBackgroundProxy } from "../../gcloud/cloudSql/startProxy";
4
4
  import { startCloudSqlProxyInBackground } from "../../gcloud/cloudSql/startProxy";
5
5
 
6
6
  export const commandRestoreDb = defineCommand({
7
- name: "cloud-sql-restore-db",
7
+ name: "cloudsql restore-db",
8
8
  description: "restore a db from one source to another target",
9
9
  group: "cloudSQL",
10
10
  inputs: {
@@ -2,7 +2,7 @@ import fetch from "node-fetch";
2
2
  import { defineCommand } from "../../core/defineCommand";
3
3
 
4
4
  export const commandDadjoke = defineCommand({
5
- name: "dadjoke",
5
+ name: "fun dadjoke",
6
6
  description: "something for jonas.",
7
7
  group: "fun",
8
8
  inputs: {},
@@ -2,7 +2,7 @@ import { spawn } from "child-process-promise";
2
2
  import { defineCommand } from "../../core/defineCommand";
3
3
 
4
4
  export const commandStarwars = defineCommand({
5
- name: "starwars",
5
+ name: "fun starwars",
6
6
  description: "Long time ago... in a galaxy far far away...",
7
7
  group: "fun",
8
8
  inputs: {},
@@ -2,7 +2,7 @@ import { defineCommand } from "../../core/defineCommand";
2
2
  import { getCurrentContext } from "../../utils/cluster";
3
3
 
4
4
  export const commandKubeCurrentContext = defineCommand({
5
- name: "kube-current-context",
5
+ name: "kube current-context",
6
6
  description: "show current kubernetes context",
7
7
  group: "general",
8
8
  inputs: {},
@@ -4,7 +4,7 @@ import { getk8sApi } from "../../k8sApi";
4
4
  import { getShell } from "../../utils/shell";
5
5
 
6
6
  export const commandKubeGetShell = defineCommand({
7
- name: "kube-get-shell",
7
+ name: "kube get-shell",
8
8
  description: "get a shell to a pod in the environment",
9
9
  group: "general",
10
10
  inputs: {
@@ -2,7 +2,7 @@ import { defineCommand } from "../../core/defineCommand";
2
2
  import { getk8sApi } from "../../k8sApi";
3
3
 
4
4
  export const commandKubeListNamespaces = defineCommand({
5
- name: "kube-list-namespaces",
5
+ name: "kube list-namespaces",
6
6
  description: "list all namespaces",
7
7
  group: "general",
8
8
  inputs: {},
@@ -2,7 +2,7 @@ import { defineCommand } from "../../core/defineCommand";
2
2
  import { getk8sApi } from "../../k8sApi";
3
3
 
4
4
  export const commandKubeListPods = defineCommand({
5
- name: "kube-list-pods",
5
+ name: "kube list-pods",
6
6
  description: "list all pods of namespace",
7
7
  group: "general",
8
8
  inputs: {
@@ -2,7 +2,7 @@ import { defineCommand } from "../../core/defineCommand";
2
2
  import { getk8sApi } from "../../k8sApi";
3
3
 
4
4
  export const commandKubeListSecrets = defineCommand({
5
- name: "kube-list-secrets",
5
+ name: "kube list-secrets",
6
6
  description: "show secrets",
7
7
  group: "general",
8
8
  inputs: {
@@ -3,7 +3,7 @@ import { getk8sApi } from "../../k8sApi";
3
3
  import { startKubePortForward } from "../../kubernetes/portForward";
4
4
 
5
5
  export const commandKubePortForward = defineCommand({
6
- name: "kube-port-forward",
6
+ name: "kube port-forward",
7
7
  description: "start port-forwarding",
8
8
  group: "general",
9
9
  inputs: {
@@ -2,7 +2,7 @@ import { defineCommand } from "../../core/defineCommand";
2
2
  import { stopPortForward } from "../../utils/portForwards";
3
3
 
4
4
  export const commandKubeStopPortforward = defineCommand({
5
- name: "kube-stop-portforward",
5
+ name: "kube stop-portforward",
6
6
  description: "stop a running port forward",
7
7
  group: "general",
8
8
  inputs: {
@@ -34,7 +34,7 @@ const removeFinalizerAndDelete = async (
34
34
  };
35
35
 
36
36
  export const commandMongoDestroyMember = defineCommand({
37
- name: "project-mongo-destroy-member",
37
+ name: "project mongo destroy-member",
38
38
  description: "DESTROY a member of a replicaset in order to reinitialize it",
39
39
  group: "mongodb",
40
40
  inputs: {
@@ -9,7 +9,7 @@ import {
9
9
  import { envAndComponents } from "../../apps/cli/commands/project/utils/autocompletions";
10
10
 
11
11
  export const commandMongoGetShell = defineCommand({
12
- name: "project-mongo-get-shell",
12
+ name: "project mongo get-shell",
13
13
  description: "get a shell to a mongodb in the environment",
14
14
  group: "mongodb",
15
15
  inputs: {
@@ -9,7 +9,7 @@ import clipboard from "clipboardy";
9
9
  import { envAndComponents } from "../../apps/cli/commands/project/utils/autocompletions";
10
10
 
11
11
  export const commandMongoPortForward = defineCommand({
12
- name: "project-mongo-port-forward",
12
+ name: "project mongo port-forward",
13
13
  description: "port forward to a mongodb",
14
14
  group: "mongodb",
15
15
  inputs: {
@@ -91,7 +91,7 @@ const getJdbcUrl = (
91
91
  `DATABASE_JDBC_URL="jdbc:postgresql://localhost:${localPort}/${DB_NAME}?schema=public&user=${DB_USER}&password=${DB_PASSWORD}"`;
92
92
 
93
93
  export const commandCloudSqlProxy = defineCommand({
94
- name: "project-cloud-sql-proxy",
94
+ name: "project cloudsql proxy",
95
95
  description: "proxy to cloud sql db",
96
96
  group: "project",
97
97
  inputs: {
@@ -7,7 +7,7 @@ import { envAndComponents } from "../../apps/cli/commands/project/utils/autocomp
7
7
  // Full migration to CommandIO would require reworking the editor flow,
8
8
  // so for now we bridge it.
9
9
  export const commandConfigSecrets = defineCommand({
10
- name: "project-config-secrets",
10
+ name: "project config-secrets",
11
11
  description: "setup/update secrets stored in pass",
12
12
  group: "project",
13
13
  inputs: {
@@ -7,7 +7,7 @@ import { ensureCluster } from "../../apps/cli/commands/project/utils/ensureClust
7
7
  import { envAndComponents } from "../../apps/cli/commands/project/utils/autocompletions";
8
8
 
9
9
  export const commandDeletePods = defineCommand({
10
- name: "project-delete-pods",
10
+ name: "project delete-pods",
11
11
  description: "delete / restart pods",
12
12
  group: "project",
13
13
 
@@ -4,7 +4,7 @@ import { getProjectNamespace } from "../../utils/projects";
4
4
  import { envAndComponents } from "../../apps/cli/commands/project/utils/autocompletions";
5
5
 
6
6
  export const commandDeleteProject = defineCommand({
7
- name: "project-delete",
7
+ name: "project delete",
8
8
  description: "deletes a environment of a project (it deletes the namespace)",
9
9
  group: "project",
10
10
  inputs: {
@@ -3,7 +3,7 @@ import { getEnvVarsResolved, parseChoice } from "../../config/getProjectConfig";
3
3
  import { envAndComponents } from "../../apps/cli/commands/project/utils/autocompletions";
4
4
 
5
5
  export const commandEnvVars = defineCommand({
6
- name: "project-env-vars",
6
+ name: "project env-vars",
7
7
  description: "list env vars",
8
8
  group: "project",
9
9
  inputs: {
@@ -2,7 +2,7 @@ import { spawn } from "child-process-promise";
2
2
  import { defineCommand } from "../../core/defineCommand";
3
3
 
4
4
  export const commandGetMyTotalWorktime = defineCommand({
5
- name: "project-get-my-total-worktime",
5
+ name: "project worktime",
6
6
  description: "show the total worktime that you spent on a project",
7
7
  group: "project",
8
8
  inputs: {},
@@ -7,7 +7,7 @@ import { ensureCluster } from "../../apps/cli/commands/project/utils/ensureClust
7
7
  import { envAndComponents } from "../../apps/cli/commands/project/utils/autocompletions";
8
8
 
9
9
  export const commandGetShell = defineCommand({
10
- name: "project-get-shell",
10
+ name: "project get-shell",
11
11
  description: "get a shell to a pod in the environment",
12
12
  group: "project",
13
13
  inputs: {
@@ -61,7 +61,7 @@ const findJobById = async (io: any, jobName: string) => {
61
61
  };
62
62
 
63
63
  export const commandCiJobOpen = defineCommand({
64
- name: "project-ci-job-open",
64
+ name: "project ci job-open",
65
65
  description: "Open a Job",
66
66
  group: "project",
67
67
  inputs: {
@@ -79,7 +79,7 @@ export const commandCiJobOpen = defineCommand({
79
79
  });
80
80
 
81
81
  export const commandCiJobLog = defineCommand({
82
- name: "project-ci-job-log",
82
+ name: "project ci job-log",
83
83
  description: "Show a job's log",
84
84
  group: "project",
85
85
  inputs: {
@@ -6,7 +6,7 @@ import { ensureCluster } from "../../apps/cli/commands/project/utils/ensureClust
6
6
  import { envAndComponents } from "../../apps/cli/commands/project/utils/autocompletions";
7
7
 
8
8
  export const commandListPods = defineCommand({
9
- name: "project-list-pods",
9
+ name: "project list-pods",
10
10
  description: "list pods of local project",
11
11
  group: "project",
12
12
  inputs: {
@@ -3,7 +3,7 @@ import { getProjectNamespace } from "../../utils/projects";
3
3
  import { envAndComponents } from "../../apps/cli/commands/project/utils/autocompletions";
4
4
 
5
5
  export const commandNamespace = defineCommand({
6
- name: "project-namespace",
6
+ name: "project namespace",
7
7
  description: "show namespace of local project",
8
8
  group: "project",
9
9
  inputs: {
@@ -5,7 +5,7 @@ import { ensureCluster } from "../../apps/cli/commands/project/utils/ensureClust
5
5
  import { envAndComponents } from "../../apps/cli/commands/project/utils/autocompletions";
6
6
 
7
7
  export const commandOpenEnv = defineCommand({
8
- name: "project-open-env",
8
+ name: "project open env",
9
9
  description: "open the live environment",
10
10
  group: "project",
11
11
  inputs: {
@@ -2,7 +2,7 @@ import { exec } from "child-process-promise";
2
2
  import { defineCommand } from "../../core/defineCommand";
3
3
 
4
4
  export const commandOpenGit = defineCommand({
5
- name: "project-open-git",
5
+ name: "project open git",
6
6
  description: "open the repo on gitlab / github in your browser",
7
7
  group: "project",
8
8
  inputs: {},
@@ -7,7 +7,7 @@ import { openGoogleCloudLogs } from "../../kubernetes/openKubernetesDashboards";
7
7
  import { envAndComponents } from "../../apps/cli/commands/project/utils/autocompletions";
8
8
 
9
9
  export const commandOpenLogs = defineCommand({
10
- name: "project-open-logs",
10
+ name: "project open logs",
11
11
  description: "open google cloud logs (stackdriver logs)",
12
12
  group: "project",
13
13
  inputs: {
@@ -4,7 +4,7 @@ import { getProjectNamespace } from "../../utils/projects";
4
4
  import { envAndComponents } from "../../apps/cli/commands/project/utils/autocompletions";
5
5
 
6
6
  export const commandPauseProject = defineCommand({
7
- name: "project-pause",
7
+ name: "project pause",
8
8
  description: "halts all running pods (scales to 0)",
9
9
  group: "project",
10
10
  inputs: {
@@ -12,7 +12,7 @@ import open from "open";
12
12
  import { envAndComponents } from "../../apps/cli/commands/project/utils/autocompletions";
13
13
 
14
14
  export const commandPortForward = defineCommand({
15
- name: "project-port-forward",
15
+ name: "project port-forward",
16
16
  description: "start port-forwarding",
17
17
  group: "project",
18
18
  inputs: {
@@ -23,7 +23,7 @@ const hasCloudSql = (context: ComponentContext) => {
23
23
  };
24
24
 
25
25
  export const commandProjectRestoreDb = defineCommand({
26
- name: "project-cloud-sql-restore-db",
26
+ name: "project cloudsql restore-db",
27
27
  description: "restores a project db from one source to another target",
28
28
  group: "project",
29
29
  inputs: {
@@ -2,7 +2,7 @@ import { defineCommand } from "../../core/defineCommand";
2
2
  import { setupAccessTokens } from "../../apps/cli/commands/project/setup/setupAccessTokens";
3
3
 
4
4
  export const commandRenewToken = defineCommand({
5
- name: "project-renew-token",
5
+ name: "project renew-token",
6
6
  description: "Configures the project access token for semantic release.",
7
7
  group: "project",
8
8
  inputs: {},
@@ -2,7 +2,7 @@ import { defineCommand } from "../../core/defineCommand";
2
2
  import { clearBackups } from "../../utils/gitlab";
3
3
 
4
4
  export const commandSecretsClearBackups = defineCommand({
5
- name: "project-secrets-clear-backups",
5
+ name: "project secrets-clear-backups",
6
6
  description: "clears all backups",
7
7
  group: "project",
8
8
  inputs: {
@@ -6,7 +6,7 @@ import {
6
6
  import { getGitRoot } from "../../utils/projects";
7
7
 
8
8
  export const commandSecurityEvaluate = defineCommand({
9
- name: "project-security-evaluate",
9
+ name: "project security-evaluate",
10
10
  description: "evaluate project's security audit document",
11
11
  group: "project",
12
12
  inputs: {},
@@ -2,7 +2,7 @@ import { defineCommand } from "../../core/defineCommand";
2
2
  import { setupProject } from "../../apps/cli/commands/project/setup";
3
3
 
4
4
  export const commandSetup = defineCommand({
5
- name: "project-setup",
5
+ name: "project setup",
6
6
  description:
7
7
  "Initializes all environments and creates required resources, service accounts, etc.",
8
8
  group: "project",
@@ -46,7 +46,7 @@ const runCronjob = async (
46
46
  };
47
47
 
48
48
  export const commandTriggerCronjobGeneral = defineCommand({
49
- name: "trigger-cronjob",
49
+ name: "kube trigger-cronjob",
50
50
  description: "trigger cronjob",
51
51
  group: "general",
52
52
  inputs: {
@@ -69,7 +69,7 @@ export const commandTriggerCronjobGeneral = defineCommand({
69
69
  });
70
70
 
71
71
  export const commandTriggerCronjobProject = defineCommand({
72
- name: "project-trigger-cronjob",
72
+ name: "project trigger-cronjob",
73
73
  description: "trigger cronjob",
74
74
  group: "project",
75
75
  inputs: {
@@ -12,7 +12,7 @@ import {
12
12
  const GITLAB_HOST = "https://git.panter.ch";
13
13
 
14
14
  export const commandSecurityAuditCiJob = defineCommand({
15
- name: "security-audit-ci-job",
15
+ name: "security audit ci-job",
16
16
  description:
17
17
  "Evaluates security audit document. Creates MR with template if missing.",
18
18
  group: "security",
@@ -5,7 +5,7 @@ import { createSecurityAuditMergeRequest } from "../../security/createSecurityAu
5
5
  const GITLAB_HOST = "https://git.panter.ch";
6
6
 
7
7
  export const commandSecurityAuditCreate = defineCommand({
8
- name: "security-audit-create",
8
+ name: "security audit create",
9
9
  description: "Creates a MR with the latest security audit template document",
10
10
  group: "security",
11
11
  inputs: {
@@ -6,7 +6,7 @@ import {
6
6
  import { SECURITY_AUDIT_FILE_NAME } from "../../security/createSecurityAuditMergeRequest";
7
7
 
8
8
  export const commandSecurityAuditEvaluate = defineCommand({
9
- name: "security-audit-evaluate",
9
+ name: "security audit evaluate",
10
10
  description: "Evaluates security audit document in given path",
11
11
  group: "security",
12
12
  inputs: {
package/src/completion.ts CHANGED
@@ -16,60 +16,81 @@ export async function getCompletions(
16
16
  words: string[],
17
17
  cursorWord: string,
18
18
  ): Promise<string[]> {
19
- // If no command yet, complete command names
20
- if (words.length === 0) {
21
- return allCommands
22
- .map((c) => c.name)
23
- .filter((name) => name.startsWith(cursorWord));
24
- }
19
+ // Try to find a command by matching the longest prefix of words.
20
+ // E.g., for words ["project", "cloudsql", "restore-db", "--source"],
21
+ // we match "project cloudsql restore-db" and treat ["--source"] as args.
22
+ const matched = findCommand(allCommands, words);
25
23
 
26
- const commandName = words[0];
27
- const command = allCommands.find((c) => c.name === commandName);
24
+ if (matched) {
25
+ const { command, argsAfterCommand } = matched;
28
26
 
29
- // If the first word doesn't match a command, complete command names
30
- if (!command) {
31
- return allCommands
32
- .map((c) => c.name)
33
- .filter((name) => name.startsWith(commandName));
34
- }
27
+ // Check if cursor is a --flag name
28
+ if (cursorWord.startsWith("--")) {
29
+ return getFlagCompletions(command, cursorWord);
30
+ }
35
31
 
36
- // We have a valid command. Figure out what to complete.
37
- const argsAfterCommand = words.slice(1);
38
-
39
- // Check if we're completing a --flag value
40
- const prevWord = argsAfterCommand[argsAfterCommand.length - 1];
41
- if (prevWord?.startsWith("--")) {
42
- // Previous word is a flag, complete its values
43
- const flagName = prevWord.replace(/^--/, "");
44
- const inputName = flagNameToInputName(flagName);
45
- const inputDef = command.inputs[inputName];
46
- if (inputDef && "choices" in inputDef && inputDef.choices) {
47
- return resolveChoicesForCompletion(inputDef, cursorWord);
32
+ // Check if previous word was a --flag (complete its value)
33
+ const prevWord = argsAfterCommand[argsAfterCommand.length - 1];
34
+ if (prevWord?.startsWith("--")) {
35
+ const flagName = prevWord.replace(/^--/, "");
36
+ const inputName = flagNameToInputName(flagName);
37
+ const inputDef = command.inputs[inputName];
38
+ if (inputDef && "choices" in inputDef && inputDef.choices) {
39
+ return resolveChoicesForCompletion(inputDef, cursorWord);
40
+ }
41
+ return [];
42
+ }
43
+
44
+ // Complete positional inputs
45
+ const positionalInputs = getPositionalInputs(command);
46
+ const positionalArgs = argsAfterCommand.filter((w) => !w.startsWith("--"));
47
+ const positionalIndex = positionalArgs.length;
48
+ const targetInput = positionalInputs[positionalIndex];
49
+
50
+ if (targetInput) {
51
+ const [, inputDef] = targetInput;
52
+ if ("choices" in inputDef && inputDef.choices) {
53
+ return resolveChoicesForCompletion(inputDef, cursorWord);
54
+ }
48
55
  }
49
56
  return [];
50
57
  }
51
58
 
52
- // Check if we're typing a --flag name
53
- if (cursorWord.startsWith("--")) {
54
- return getFlagCompletions(command, cursorWord);
59
+ // No command matched. Complete subcommand names at the current level.
60
+ const typed = words.join(" ");
61
+ const prefix = typed ? typed + " " : "";
62
+
63
+ const nextParts = new Set<string>();
64
+ for (const cmd of allCommands) {
65
+ if (cmd.name.startsWith(prefix)) {
66
+ const rest = cmd.name.slice(prefix.length);
67
+ const nextPart = rest.split(" ")[0];
68
+ if (nextPart && nextPart.startsWith(cursorWord)) {
69
+ nextParts.add(nextPart);
70
+ }
71
+ }
55
72
  }
56
73
 
57
- // Otherwise, complete positional inputs
58
- const positionalInputs = getPositionalInputs(command);
59
- const positionalArgs = argsAfterCommand.filter((w) => !w.startsWith("--"));
60
-
61
- // Which positional index are we on?
62
- const positionalIndex = positionalArgs.length;
63
- const targetInput = positionalInputs[positionalIndex];
74
+ return [...nextParts].sort();
75
+ }
64
76
 
65
- if (targetInput) {
66
- const [, inputDef] = targetInput;
67
- if ("choices" in inputDef && inputDef.choices) {
68
- return resolveChoicesForCompletion(inputDef, cursorWord);
77
+ /**
78
+ * Find a command by matching the longest prefix of words against command names.
79
+ * Returns the matched command and the remaining words (args/flags after the command name).
80
+ */
81
+ function findCommand(
82
+ allCommands: CommandDef[],
83
+ words: string[],
84
+ ): { command: CommandDef; argsAfterCommand: string[] } | null {
85
+ // Try longest match first
86
+ for (let i = words.length; i >= 1; i--) {
87
+ const candidate = words.slice(0, i).join(" ");
88
+ const command = allCommands.find((c) => c.name === candidate);
89
+ if (command) {
90
+ return { command, argsAfterCommand: words.slice(i) };
69
91
  }
70
92
  }
71
-
72
- return [];
93
+ return null;
73
94
  }
74
95
 
75
96
  function getPositionalInputs(command: CommandDef): [string, InputDef][] {