alphamilk 0.0.3 → 0.0.5

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 (3) hide show
  1. package/README.md +146 -0
  2. package/dist/cli.js +247 -75
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # alphamilk
2
+
3
+ AI-powered SEO research from your terminal. A thin CLI proxy to the Alpha Milk Worker API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # Run directly without installing
9
+ npx alphamilk --help
10
+
11
+ # Or install globally
12
+ npm install -g alphamilk
13
+ ```
14
+
15
+ ## Authentication
16
+
17
+ All commands (except `login` and `logout`) require an active session. Authenticate with your access token:
18
+
19
+ ```bash
20
+ alphamilk login --token <your-token-id>
21
+ ```
22
+
23
+ This creates a session stored at `~/.config/alphamilk/session.json`. Sessions expire after 24 hours by default.
24
+
25
+ Check your current session:
26
+
27
+ ```bash
28
+ alphamilk session
29
+ ```
30
+
31
+ Clear your session:
32
+
33
+ ```bash
34
+ alphamilk logout
35
+ ```
36
+
37
+ ## Commands
38
+
39
+ ### `login` -- Authenticate with your access token
40
+
41
+ ```bash
42
+ alphamilk login --token <tokenId>
43
+ alphamilk login -t <tokenId>
44
+ ```
45
+
46
+ Calls `GET /milk/create-session/<tokenId>` and stores the returned session locally.
47
+
48
+ ### `logout` -- Clear your session
49
+
50
+ ```bash
51
+ alphamilk logout
52
+ ```
53
+
54
+ Removes the local session file.
55
+
56
+ ### `session` -- Show current session info
57
+
58
+ ```bash
59
+ alphamilk session
60
+ ```
61
+
62
+ Displays the session ID, token (truncated), base URL, and expiry.
63
+
64
+ ### `playbooks` -- List available research playbooks
65
+
66
+ ```bash
67
+ alphamilk playbooks
68
+ ```
69
+
70
+ ### `playbook` -- Open a playbook or its report template
71
+
72
+ ```bash
73
+ # Get playbook content
74
+ alphamilk playbook competitor-discovery
75
+
76
+ # Get the report template for a playbook
77
+ alphamilk playbook competitor-discovery --report
78
+ alphamilk playbook competitor-discovery -r
79
+ ```
80
+
81
+ ### `probe` -- Execute a research probe
82
+
83
+ ```bash
84
+ alphamilk probe serp-google -p keyword=seo -p location_code=2840
85
+ alphamilk probe ranked-keywords --param target=example.com
86
+ ```
87
+
88
+ Parameters are passed as repeatable `--param key=value` (or `-p key=value`) pairs. The probe is executed via `POST /milk/probe/<sessionId>/<slug>` with parameters sent as a JSON body.
89
+
90
+ ### `report` -- Save and retrieve research reports
91
+
92
+ ```bash
93
+ # Save a report from a file (recommended)
94
+ alphamilk report save --name "Competitor Analysis" --file ./report.md
95
+
96
+ # Save a report with inline content
97
+ alphamilk report save --name "Quick Note" --content "# Summary\n\nKey findings..."
98
+
99
+ # Retrieve a saved report
100
+ alphamilk report get <reportId>
101
+ ```
102
+
103
+ You must provide exactly one of `--file` or `--content` when saving.
104
+
105
+ ### `artifact` -- Save and retrieve research artifacts
106
+
107
+ ```bash
108
+ # Save an artifact
109
+ alphamilk artifact save --type domains --tags "competitors,direct" --data "example.com,other.com"
110
+
111
+ # List artifacts with optional filters
112
+ alphamilk artifact list
113
+ alphamilk artifact list --type domains
114
+ alphamilk artifact list --tag competitors
115
+
116
+ # Retrieve a specific artifact
117
+ alphamilk artifact get <artifactId>
118
+ ```
119
+
120
+ Artifact types are `domains` or `keywords`. Tags and data are comma-separated.
121
+
122
+ ## How It Works
123
+
124
+ The CLI is a thin HTTP proxy. Every command translates to a GET or POST request to the Alpha Milk Worker API at `https://alphamilk.ai/milk/*`. Responses (typically markdown) are printed directly to stdout.
125
+
126
+ All requests include two custom headers for server-side identification:
127
+ - `X-AlphaMilk-Client: cli`
128
+ - `X-AlphaMilk-CLI-Version: <version>`
129
+
130
+ ## Build
131
+
132
+ ```bash
133
+ # Build the single-file bundle
134
+ pnpm build
135
+
136
+ # Run in development mode (via tsx)
137
+ pnpm dev
138
+
139
+ # Type check
140
+ pnpm check
141
+
142
+ # Run tests
143
+ pnpm test
144
+ ```
145
+
146
+ The build uses esbuild to bundle all source into a single `dist/cli.js` file with a Node.js shebang prepended.
package/dist/cli.js CHANGED
@@ -44,7 +44,7 @@ function clearSession() {
44
44
  // src/api.ts
45
45
  import { Effect, Data } from "effect";
46
46
  import { HttpClient, HttpClientRequest } from "@effect/platform";
47
- var CLI_VERSION = "0.0.1";
47
+ var CLI_VERSION = "0.0.3";
48
48
  var ApiError = class extends Data.TaggedError("ApiError") {
49
49
  };
50
50
  var apiGet = (baseUrl, path3, params) => Effect.gen(function* () {
@@ -68,6 +68,21 @@ var apiGet = (baseUrl, path3, params) => Effect.gen(function* () {
68
68
  }
69
69
  return body;
70
70
  });
71
+ var apiPut = (baseUrl, path3, body) => Effect.gen(function* () {
72
+ const client = yield* HttpClient.HttpClient;
73
+ const url = new URL(path3, baseUrl);
74
+ const request = HttpClientRequest.put(url.toString()).pipe(
75
+ HttpClientRequest.setHeader("X-AlphaMilk-Client", "cli"),
76
+ HttpClientRequest.setHeader("X-AlphaMilk-CLI-Version", CLI_VERSION),
77
+ HttpClientRequest.bodyUnsafeJson(body)
78
+ );
79
+ const response = yield* client.execute(request).pipe(Effect.scoped);
80
+ const responseBody = yield* response.text;
81
+ if (response.status >= 400) {
82
+ return yield* Effect.fail(new ApiError({ statusCode: response.status, message: responseBody }));
83
+ }
84
+ return responseBody;
85
+ });
71
86
  var apiPost = (baseUrl, path3, body) => Effect.gen(function* () {
72
87
  const client = yield* HttpClient.HttpClient;
73
88
  const url = new URL(path3, baseUrl);
@@ -217,18 +232,30 @@ var playbookReport = Options.boolean("report").pipe(
217
232
  Options.withDefault(false),
218
233
  Options.withDescription("Get the report template instead of playbook content")
219
234
  );
235
+ var playbookStep = Options.text("step").pipe(
236
+ Options.withAlias("s"),
237
+ Options.withDescription("Get a single step's instructions by step ID"),
238
+ Options.optional
239
+ );
220
240
  var playbookCommand = Command.make(
221
241
  "playbook",
222
- { slug: playbookSlug, report: playbookReport },
223
- ({ slug, report }) => Effect3.gen(function* () {
242
+ { slug: playbookSlug, report: playbookReport, step: playbookStep },
243
+ ({ slug, report, step }) => Effect3.gen(function* () {
224
244
  const session = yield* requireSession;
225
- const urlPath = report ? `/milk/playbook/${session.sessionId}/${slug}/report` : `/milk/playbook/${session.sessionId}/${slug}`;
245
+ let urlPath;
246
+ if (report) {
247
+ urlPath = `/milk/playbook/${session.sessionId}/${slug}/report`;
248
+ } else if (step._tag === "Some") {
249
+ urlPath = `/milk/playbook/${session.sessionId}/${slug}?step=${encodeURIComponent(step.value)}`;
250
+ } else {
251
+ urlPath = `/milk/playbook/${session.sessionId}/${slug}`;
252
+ }
226
253
  const response = yield* apiGet(session.baseUrl, urlPath).pipe(
227
254
  handleApiError("Failed to get playbook")
228
255
  );
229
256
  yield* Console2.log(response);
230
257
  })
231
- ).pipe(Command.withDescription("Open a playbook or its report template"));
258
+ ).pipe(Command.withDescription("Open a playbook, a specific step, or its report template"));
232
259
  var probeSlug = Args.text({ name: "slug" }).pipe(
233
260
  Args.withDescription("Probe slug (e.g., serp-google, ranked-keywords)")
234
261
  );
@@ -252,99 +279,153 @@ var probeCommand = Command.make(
252
279
  session.baseUrl,
253
280
  `/milk/probe/${session.sessionId}/${slug}`,
254
281
  queryParams
255
- ).pipe(handleApiError("Skill execution failed"));
282
+ ).pipe(handleApiError("Probe execution failed"));
256
283
  yield* Console2.log(response);
257
284
  })
258
285
  ).pipe(Command.withDescription("Execute a research probe"));
259
- var reportSaveName = Options.text("name").pipe(
286
+ var artifactSaveType = Options.choice("type", ["domains", "keywords", "document", "report", "metrics"]).pipe(
287
+ Options.withDescription("Artifact type")
288
+ );
289
+ var artifactSaveTitle = Options.text("title").pipe(
260
290
  Options.withAlias("n"),
261
- Options.withDescription("Report title/name")
291
+ Options.withDescription("Artifact title (required for document/report)"),
292
+ Options.optional
262
293
  );
263
- var reportSaveContent = Options.text("content").pipe(
264
- Options.withAlias("c"),
265
- Options.withDescription("Full markdown report content (inline). Use --file instead for file-based input."),
294
+ var artifactSaveTags = Options.text("tags").pipe(
295
+ Options.withDescription("Comma-separated tags (e.g., competitors,direct)")
296
+ );
297
+ var artifactSaveData = Options.text("data").pipe(
298
+ Options.withAlias("d"),
299
+ Options.withDescription("Comma-separated data items (for domains/keywords)"),
266
300
  Options.optional
267
301
  );
268
- var reportSaveFile = Options.text("file").pipe(
302
+ var artifactSaveFile = Options.text("file").pipe(
269
303
  Options.withAlias("f"),
270
- Options.withDescription("Path to a markdown file containing the report content"),
304
+ Options.withDescription("Path to a file (markdown for document, JSON for report/metrics)"),
305
+ Options.optional
306
+ );
307
+ var artifactSaveContent = Options.text("content").pipe(
308
+ Options.withAlias("c"),
309
+ Options.withDescription("Inline content string (for document type)"),
271
310
  Options.optional
272
311
  );
273
- var reportSaveCommand = Command.make(
312
+ var artifactSaveCommand = Command.make(
274
313
  "save",
275
- { name: reportSaveName, content: reportSaveContent, file: reportSaveFile },
276
- ({ name, content, file }) => Effect3.gen(function* () {
314
+ { type: artifactSaveType, title: artifactSaveTitle, tags: artifactSaveTags, data: artifactSaveData, file: artifactSaveFile, content: artifactSaveContent },
315
+ ({ type, title, tags, data, file, content }) => Effect3.gen(function* () {
277
316
  const session = yield* requireSession;
278
- let resolvedContent;
279
- if (content._tag === "Some" && file._tag === "Some") {
280
- return yield* exitWithError(
281
- 'Cannot use both --content and --file. Provide one or the other.\n\n --file report.md Read content from a markdown file\n --content "# ..." Pass content inline as a string'
282
- );
283
- }
284
- if (content._tag === "None" && file._tag === "None") {
285
- return yield* exitWithError(
286
- 'Missing report content. Provide one of:\n\n --file report.md Read content from a markdown file (recommended)\n --content "# ..." Pass content inline as a string\n\nExample:\n alphamilk report save --name "My Report" --file ./report.md'
287
- );
317
+ const body = {
318
+ type,
319
+ tags: tags.split(",").map((t) => t.trim())
320
+ };
321
+ if (title._tag === "Some") {
322
+ body.title = title.value;
288
323
  }
289
- if (file._tag === "Some") {
290
- resolvedContent = yield* readReportFile(file.value);
291
- } else {
292
- resolvedContent = content.value;
324
+ if (type === "domains" || type === "keywords") {
325
+ if (data._tag === "None") {
326
+ return yield* exitWithError(
327
+ `--data is required for ${type} artifacts.
328
+
329
+ Example: alphamilk artifact save --type ${type} --tags step:discovery --data "example.com,other.com"`
330
+ );
331
+ }
332
+ body.data = data.value.split(",").map((d) => d.trim());
333
+ } else if (type === "document") {
334
+ if (file._tag === "None" && content._tag === "None") {
335
+ return yield* exitWithError(
336
+ 'Document artifacts require --file or --content.\n\nExample:\n alphamilk artifact save --type document --title "Analysis" --tags step:analysis --file ./analysis.md\n alphamilk artifact save --type document --title "Notes" --tags notes --content "# Notes\\n..."'
337
+ );
338
+ }
339
+ if (file._tag === "Some" && content._tag === "Some") {
340
+ return yield* exitWithError("Cannot use both --file and --content. Provide one or the other.");
341
+ }
342
+ if (file._tag === "Some") {
343
+ body.content = yield* readReportFile(file.value);
344
+ } else {
345
+ body.content = content.value;
346
+ }
347
+ } else if (type === "report") {
348
+ if (file._tag === "None") {
349
+ return yield* exitWithError(
350
+ 'Report artifacts require --file with a JSON manifest.\n\nExample: alphamilk artifact save --type report --title "Final Report" --tags report --file ./manifest.json'
351
+ );
352
+ }
353
+ body.content = yield* readReportFile(file.value);
354
+ } else if (type === "metrics") {
355
+ if (file._tag === "Some") {
356
+ body.content = yield* readReportFile(file.value);
357
+ } else if (data._tag === "Some") {
358
+ const rows = data.value.split(",").map((pair) => {
359
+ const [label, rest] = pair.trim().split(":");
360
+ const value = parseFloat(rest ?? "0");
361
+ return { label: label?.trim() ?? "", value: isNaN(value) ? 0 : value };
362
+ });
363
+ body.content = JSON.stringify(rows);
364
+ } else {
365
+ return yield* exitWithError(
366
+ 'Metrics artifacts require --file (JSON) or --data (label:value pairs).\n\nExample:\n alphamilk artifact save --type metrics --tags traffic --file ./metrics.json\n alphamilk artifact save --type metrics --tags traffic --data "example.com:45000,other.com:12000"'
367
+ );
368
+ }
293
369
  }
294
370
  const response = yield* apiPost(
295
371
  session.baseUrl,
296
- `/milk/report/${session.sessionId}`,
297
- { name, content: resolvedContent }
298
- ).pipe(handleApiError("Failed to save report"));
372
+ `/milk/artifact/${session.sessionId}`,
373
+ body
374
+ ).pipe(handleApiError("Failed to save artifact"));
299
375
  yield* Console2.log(response);
300
376
  })
301
- ).pipe(Command.withDescription("Save a research report"));
302
- var reportGetId = Args.text({ name: "reportId" }).pipe(
303
- Args.withDescription("Report ID")
377
+ ).pipe(Command.withDescription("Save a research artifact"));
378
+ var artifactUpdateId = Args.text({ name: "artifactId" }).pipe(
379
+ Args.withDescription("Artifact ID to update")
304
380
  );
305
- var reportGetCommand = Command.make(
306
- "get",
307
- { reportId: reportGetId },
308
- ({ reportId }) => Effect3.gen(function* () {
309
- const session = yield* requireSession;
310
- const response = yield* apiGet(
311
- session.baseUrl,
312
- `/milk/report/${session.sessionId}/${reportId}`
313
- ).pipe(handleApiError("Failed to get report"));
314
- yield* Console2.log(response);
315
- })
316
- ).pipe(Command.withDescription("Retrieve a saved report"));
317
- var reportCommand = Command.make("report").pipe(
318
- Command.withDescription("Save and retrieve research reports"),
319
- Command.withSubcommands([reportSaveCommand, reportGetCommand])
381
+ var artifactUpdateTitle = Options.text("title").pipe(
382
+ Options.withAlias("n"),
383
+ Options.withDescription("Updated title"),
384
+ Options.optional
320
385
  );
321
- var artifactSaveType = Options.choice("type", ["domains", "keywords"]).pipe(
322
- Options.withDescription("Artifact type")
386
+ var artifactUpdateFile = Options.text("file").pipe(
387
+ Options.withAlias("f"),
388
+ Options.withDescription("Path to updated content file"),
389
+ Options.optional
323
390
  );
324
- var artifactSaveTags = Options.text("tags").pipe(
325
- Options.withDescription("Comma-separated tags (e.g., competitors,direct)")
391
+ var artifactUpdateContent = Options.text("content").pipe(
392
+ Options.withAlias("c"),
393
+ Options.withDescription("Updated inline content"),
394
+ Options.optional
326
395
  );
327
- var artifactSaveData = Options.text("data").pipe(
396
+ var artifactUpdateData = Options.text("data").pipe(
328
397
  Options.withAlias("d"),
329
- Options.withDescription("Comma-separated data items (e.g., example.com,other.com)")
398
+ Options.withDescription("Updated comma-separated data items"),
399
+ Options.optional
330
400
  );
331
- var artifactSaveCommand = Command.make(
332
- "save",
333
- { type: artifactSaveType, tags: artifactSaveTags, data: artifactSaveData },
334
- ({ type, tags, data }) => Effect3.gen(function* () {
401
+ var artifactUpdateCommand = Command.make(
402
+ "update",
403
+ { artifactId: artifactUpdateId, title: artifactUpdateTitle, file: artifactUpdateFile, content: artifactUpdateContent, data: artifactUpdateData },
404
+ ({ artifactId, title, file, content, data }) => Effect3.gen(function* () {
335
405
  const session = yield* requireSession;
336
- const response = yield* apiPost(
406
+ const body = {};
407
+ if (title._tag === "Some") body.title = title.value;
408
+ if (file._tag === "Some") {
409
+ body.content = yield* readReportFile(file.value);
410
+ } else if (content._tag === "Some") {
411
+ body.content = content.value;
412
+ }
413
+ if (data._tag === "Some") {
414
+ body.data = data.value.split(",").map((d) => d.trim());
415
+ }
416
+ if (Object.keys(body).length === 0) {
417
+ return yield* exitWithError(
418
+ "Nothing to update. Provide at least one of: --title, --file, --content, --data"
419
+ );
420
+ }
421
+ const response = yield* apiPut(
337
422
  session.baseUrl,
338
- `/milk/artifact/${session.sessionId}`,
339
- {
340
- type,
341
- tags: tags.split(",").map((t) => t.trim()),
342
- data: data.split(",").map((d) => d.trim())
343
- }
344
- ).pipe(handleApiError("Failed to save artifact"));
423
+ `/milk/artifact/${session.sessionId}/${artifactId}`,
424
+ body
425
+ ).pipe(handleApiError("Failed to update artifact"));
345
426
  yield* Console2.log(response);
346
427
  })
347
- ).pipe(Command.withDescription("Save a research artifact"));
428
+ ).pipe(Command.withDescription("Update an existing artifact"));
348
429
  var artifactGetId = Args.text({ name: "artifactId" }).pipe(
349
430
  Args.withDescription("Artifact ID")
350
431
  );
@@ -360,6 +441,21 @@ var artifactGetCommand = Command.make(
360
441
  yield* Console2.log(response);
361
442
  })
362
443
  ).pipe(Command.withDescription("Retrieve a saved artifact"));
444
+ var artifactRenderId = Args.text({ name: "artifactId" }).pipe(
445
+ Args.withDescription("Artifact ID to render")
446
+ );
447
+ var artifactRenderCommand = Command.make(
448
+ "render",
449
+ { artifactId: artifactRenderId },
450
+ ({ artifactId }) => Effect3.gen(function* () {
451
+ const session = yield* requireSession;
452
+ const response = yield* apiGet(
453
+ session.baseUrl,
454
+ `/milk/artifact/${session.sessionId}/${artifactId}/render`
455
+ ).pipe(handleApiError("Failed to render artifact"));
456
+ yield* Console2.log(response);
457
+ })
458
+ ).pipe(Command.withDescription("Render an artifact (assembles reports from referenced artifacts)"));
363
459
  var artifactListType = Options.text("type").pipe(
364
460
  Options.withDescription("Filter by artifact type"),
365
461
  Options.optional
@@ -385,8 +481,84 @@ var artifactListCommand = Command.make(
385
481
  })
386
482
  ).pipe(Command.withDescription("List saved artifacts"));
387
483
  var artifactCommand = Command.make("artifact").pipe(
388
- Command.withDescription("Save and retrieve research artifacts"),
389
- Command.withSubcommands([artifactSaveCommand, artifactGetCommand, artifactListCommand])
484
+ Command.withDescription("Save, update, and retrieve research artifacts"),
485
+ Command.withSubcommands([artifactSaveCommand, artifactUpdateCommand, artifactGetCommand, artifactRenderCommand, artifactListCommand])
486
+ );
487
+ var planSaveFile = Options.text("file").pipe(
488
+ Options.withAlias("f"),
489
+ Options.withDescription("Path to an XML file containing the plan")
490
+ );
491
+ var planSaveCommand = Command.make(
492
+ "save",
493
+ { file: planSaveFile },
494
+ ({ file }) => Effect3.gen(function* () {
495
+ const session = yield* requireSession;
496
+ const xmlContent = yield* readReportFile(file);
497
+ const response = yield* apiPost(
498
+ session.baseUrl,
499
+ `/milk/plan/${session.sessionId}/save`,
500
+ { xml: xmlContent }
501
+ ).pipe(handleApiError("Failed to save plan"));
502
+ yield* Console2.log(response);
503
+ })
504
+ ).pipe(Command.withDescription("Save a plan from an XML file"));
505
+ var planStatusCommand = Command.make(
506
+ "status",
507
+ {},
508
+ () => Effect3.gen(function* () {
509
+ const session = yield* requireSession;
510
+ const response = yield* apiPost(
511
+ session.baseUrl,
512
+ `/milk/plan/${session.sessionId}/status`,
513
+ {}
514
+ ).pipe(handleApiError("Failed to get plan status"));
515
+ yield* Console2.log(response);
516
+ })
517
+ ).pipe(Command.withDescription("View current plan status"));
518
+ var planCheckStepId = Args.text({ name: "stepId" }).pipe(
519
+ Args.withDescription("Step ID to mark as done (e.g., broad-discovery)")
520
+ );
521
+ var planCheckCommand = Command.make(
522
+ "check",
523
+ { stepId: planCheckStepId },
524
+ ({ stepId }) => Effect3.gen(function* () {
525
+ const session = yield* requireSession;
526
+ const response = yield* apiPost(
527
+ session.baseUrl,
528
+ `/milk/plan/${session.sessionId}/check/${stepId}`,
529
+ {}
530
+ ).pipe(handleApiError("Failed to check step"));
531
+ yield* Console2.log(response);
532
+ })
533
+ ).pipe(Command.withDescription("Mark a plan step as done"));
534
+ var planSkipStepId = Args.text({ name: "stepId" }).pipe(
535
+ Args.withDescription("Step ID to skip (e.g., validate-gaps)")
536
+ );
537
+ var planSkipReason = Options.text("reason").pipe(
538
+ Options.withAlias("r"),
539
+ Options.withDescription("Reason for skipping this step"),
540
+ Options.optional
541
+ );
542
+ var planSkipCommand = Command.make(
543
+ "skip",
544
+ { stepId: planSkipStepId, reason: planSkipReason },
545
+ ({ stepId, reason }) => Effect3.gen(function* () {
546
+ const session = yield* requireSession;
547
+ const body = {};
548
+ if (reason._tag === "Some") {
549
+ body.reason = reason.value;
550
+ }
551
+ const response = yield* apiPost(
552
+ session.baseUrl,
553
+ `/milk/plan/${session.sessionId}/skip/${stepId}`,
554
+ body
555
+ ).pipe(handleApiError("Failed to skip step"));
556
+ yield* Console2.log(response);
557
+ })
558
+ ).pipe(Command.withDescription("Skip a plan step with an optional reason"));
559
+ var planCommand = Command.make("plan").pipe(
560
+ Command.withDescription("Save and manage research plans"),
561
+ Command.withSubcommands([planSaveCommand, planStatusCommand, planCheckCommand, planSkipCommand])
390
562
  );
391
563
  var logoutCommand = Command.make(
392
564
  "logout",
@@ -405,13 +577,13 @@ var root = Command.make("alphamilk").pipe(
405
577
  playbooksCommand,
406
578
  playbookCommand,
407
579
  probeCommand,
408
- reportCommand,
409
- artifactCommand
580
+ artifactCommand,
581
+ planCommand
410
582
  ])
411
583
  );
412
584
  var cli = Command.run(root, {
413
585
  name: "alphamilk",
414
- version: "0.0.1"
586
+ version: "0.0.3"
415
587
  });
416
588
  var MainLayer = Layer.mergeAll(
417
589
  NodeContext.layer,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alphamilk",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Alpha Milk CLI - AI-powered SEO research from your terminal",