@lotics/cli 0.29.0 → 0.30.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.
@@ -0,0 +1,32 @@
1
+ /**
2
+ * CLI argument parser.
3
+ *
4
+ * Splits `process.argv.slice(2)` into a command / subcommand / positional
5
+ * (`toolArgs`) / remaining-positionals (`restArgs`) shape plus typed `flags`.
6
+ * Value-taking flags (`-m`, `--api-key`, …) consume the next token; boolean
7
+ * flags (`--force-workflow-sync`, `--json`, …) toggle. Anything not matching a
8
+ * known flag is positional.
9
+ *
10
+ * Lives apart from `cli.ts` so it can be unit-tested — `cli.ts` runs `main()`
11
+ * on import, so importing the parser from there would execute the CLI.
12
+ */
13
+ export declare function parseArgs(argv: string[]): {
14
+ command?: string;
15
+ subcommand?: string;
16
+ toolArgs?: string;
17
+ restArgs: string[];
18
+ flags: {
19
+ json: boolean;
20
+ timeout?: number;
21
+ output?: string;
22
+ as?: string;
23
+ apiKey?: string;
24
+ name?: string;
25
+ timezone?: string;
26
+ message?: string;
27
+ forceWorkflowSync: boolean;
28
+ local: boolean;
29
+ version: boolean;
30
+ help: boolean;
31
+ };
32
+ };
@@ -0,0 +1,94 @@
1
+ /**
2
+ * CLI argument parser.
3
+ *
4
+ * Splits `process.argv.slice(2)` into a command / subcommand / positional
5
+ * (`toolArgs`) / remaining-positionals (`restArgs`) shape plus typed `flags`.
6
+ * Value-taking flags (`-m`, `--api-key`, …) consume the next token; boolean
7
+ * flags (`--force-workflow-sync`, `--json`, …) toggle. Anything not matching a
8
+ * known flag is positional.
9
+ *
10
+ * Lives apart from `cli.ts` so it can be unit-tested — `cli.ts` runs `main()`
11
+ * on import, so importing the parser from there would execute the CLI.
12
+ */
13
+ export function parseArgs(argv) {
14
+ const flags = {
15
+ json: false,
16
+ timeout: undefined,
17
+ output: undefined,
18
+ as: undefined,
19
+ apiKey: undefined,
20
+ name: undefined,
21
+ timezone: undefined,
22
+ message: undefined,
23
+ forceWorkflowSync: false,
24
+ local: false,
25
+ version: false,
26
+ help: false,
27
+ };
28
+ let command;
29
+ let subcommand;
30
+ let toolArgs;
31
+ const restArgs = [];
32
+ let i = 0;
33
+ while (i < argv.length) {
34
+ const arg = argv[i];
35
+ switch (arg) {
36
+ case "--json":
37
+ flags.json = true;
38
+ break;
39
+ case "--timeout":
40
+ flags.timeout = parseInt(argv[++i], 10);
41
+ break;
42
+ case "--output":
43
+ case "-o":
44
+ flags.output = argv[++i];
45
+ break;
46
+ case "--as":
47
+ flags.as = argv[++i];
48
+ break;
49
+ case "--api-key":
50
+ flags.apiKey = argv[++i];
51
+ break;
52
+ case "--name":
53
+ flags.name = argv[++i];
54
+ break;
55
+ case "--timezone":
56
+ flags.timezone = argv[++i];
57
+ break;
58
+ case "-m":
59
+ case "--message":
60
+ flags.message = argv[++i];
61
+ break;
62
+ case "--force-workflow-sync":
63
+ flags.forceWorkflowSync = true;
64
+ break;
65
+ case "--local":
66
+ flags.local = true;
67
+ break;
68
+ case "--version":
69
+ case "-v":
70
+ flags.version = true;
71
+ break;
72
+ case "--help":
73
+ case "-h":
74
+ flags.help = true;
75
+ break;
76
+ default:
77
+ if (!command) {
78
+ command = arg;
79
+ }
80
+ else if (!subcommand) {
81
+ subcommand = arg;
82
+ }
83
+ else if (!toolArgs) {
84
+ toolArgs = arg;
85
+ }
86
+ else {
87
+ restArgs.push(arg);
88
+ }
89
+ break;
90
+ }
91
+ i++;
92
+ }
93
+ return { command, subcommand, toolArgs, restArgs, flags };
94
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { parseArgs } from "./args.js";
3
+ describe("parseArgs", () => {
4
+ it("splits command / subcommand / positional", () => {
5
+ const r = parseArgs(["app", "deploy", "my message"]);
6
+ expect(r.command).toBe("app");
7
+ expect(r.subcommand).toBe("deploy");
8
+ expect(r.toolArgs).toBe("my message");
9
+ expect(r.restArgs).toEqual([]);
10
+ });
11
+ // `app deploy` regression: `-m` and `--force-workflow-sync` must be parsed
12
+ // as flags, not silently consumed as the positional message.
13
+ it("parses -m as the message flag", () => {
14
+ const r = parseArgs(["app", "deploy", "-m", "a message"]);
15
+ expect(r.flags.message).toBe("a message");
16
+ expect(r.toolArgs).toBeUndefined();
17
+ });
18
+ it("parses --message as the message flag", () => {
19
+ const r = parseArgs(["app", "deploy", "--message", "a message"]);
20
+ expect(r.flags.message).toBe("a message");
21
+ });
22
+ it("parses --force-workflow-sync as a boolean flag", () => {
23
+ const r = parseArgs(["app", "deploy", "msg", "--force-workflow-sync"]);
24
+ expect(r.flags.forceWorkflowSync).toBe(true);
25
+ expect(r.toolArgs).toBe("msg");
26
+ });
27
+ it("parses --force-workflow-sync before -m without eating the message", () => {
28
+ const r = parseArgs(["app", "deploy", "--force-workflow-sync", "-m", "msg"]);
29
+ expect(r.flags.forceWorkflowSync).toBe(true);
30
+ expect(r.flags.message).toBe("msg");
31
+ });
32
+ it("defaults forceWorkflowSync to false and message to undefined", () => {
33
+ const r = parseArgs(["app", "deploy"]);
34
+ expect(r.flags.forceWorkflowSync).toBe(false);
35
+ expect(r.flags.message).toBeUndefined();
36
+ });
37
+ });
package/dist/src/cli.js CHANGED
@@ -6,6 +6,7 @@ import { LoticsClient, API_BASE_URL } from "./client.js";
6
6
  import { resolveAuth, loadConfig, saveConfig, deleteConfig, getConfigPath, checkForUpdate } from "./config.js";
7
7
  import { VERSION } from "./version.js";
8
8
  import { appCreate, appPull, appDeploy, appDev } from "./app_commands.js";
9
+ import { parseArgs } from "./args.js";
9
10
  function printHelp() {
10
11
  console.log(`Lotics CLI v${VERSION} — AI agent interface for Lotics
11
12
 
@@ -114,79 +115,6 @@ else ~/.lotics/config.json. A per-directory config pins a project or worktree to
114
115
  its own account and workspace; --local creates one. Note: an exported
115
116
  LOTICS_API_KEY env var overrides the config file's key.`);
116
117
  }
117
- function parseArgs(argv) {
118
- const flags = {
119
- json: false,
120
- timeout: undefined,
121
- output: undefined,
122
- as: undefined,
123
- apiKey: undefined,
124
- name: undefined,
125
- timezone: undefined,
126
- local: false,
127
- version: false,
128
- help: false,
129
- };
130
- let command;
131
- let subcommand;
132
- let toolArgs;
133
- const restArgs = [];
134
- let i = 0;
135
- while (i < argv.length) {
136
- const arg = argv[i];
137
- switch (arg) {
138
- case "--json":
139
- flags.json = true;
140
- break;
141
- case "--timeout":
142
- flags.timeout = parseInt(argv[++i], 10);
143
- break;
144
- case "--output":
145
- case "-o":
146
- flags.output = argv[++i];
147
- break;
148
- case "--as":
149
- flags.as = argv[++i];
150
- break;
151
- case "--api-key":
152
- flags.apiKey = argv[++i];
153
- break;
154
- case "--name":
155
- flags.name = argv[++i];
156
- break;
157
- case "--timezone":
158
- flags.timezone = argv[++i];
159
- break;
160
- case "--local":
161
- flags.local = true;
162
- break;
163
- case "--version":
164
- case "-v":
165
- flags.version = true;
166
- break;
167
- case "--help":
168
- case "-h":
169
- flags.help = true;
170
- break;
171
- default:
172
- if (!command) {
173
- command = arg;
174
- }
175
- else if (!subcommand) {
176
- subcommand = arg;
177
- }
178
- else if (!toolArgs) {
179
- toolArgs = arg;
180
- }
181
- else {
182
- restArgs.push(arg);
183
- }
184
- break;
185
- }
186
- i++;
187
- }
188
- return { command, subcommand, toolArgs, restArgs, flags };
189
- }
190
118
  function readStdin() {
191
119
  return new Promise((resolve, reject) => {
192
120
  const chunks = [];
@@ -546,13 +474,11 @@ async function main() {
546
474
  return;
547
475
  }
548
476
  if (subcommand === "deploy") {
549
- // -m / --message can be passed via toolArgs or after a flag-like delimiter.
550
- // Keep it simple: any positional arg after `deploy` is treated as the message.
551
- // --force-workflow-sync is a separate flag captured upstream via restArgs
552
- // because it has no value (boolean toggle).
553
- const message = toolArgs;
554
- const forceWorkflowSync = restArgs.includes("--force-workflow-sync");
555
- await appDeploy(client, { message, forceWorkflowSync });
477
+ // The message is either `-m <message>` or a bare positional arg after
478
+ // `deploy`. `--force-workflow-sync` is a parsed boolean flag, valid in
479
+ // any position.
480
+ const message = flags.message ?? toolArgs;
481
+ await appDeploy(client, { message, forceWorkflowSync: flags.forceWorkflowSync });
556
482
  return;
557
483
  }
558
484
  if (subcommand === "dev") {
@@ -111,6 +111,8 @@ function inputDeclToTsType(decl) {
111
111
  }
112
112
  case "date_range":
113
113
  return "{ start: string; end: string }";
114
+ case "file":
115
+ return "string";
114
116
  case "json":
115
117
  return "unknown";
116
118
  default:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/cli",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {