@solcreek/cli 0.4.13 → 0.4.14

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/README.md CHANGED
@@ -67,6 +67,9 @@ creek deploy https://github.com/user/repo/tree/main/packages/app
67
67
  | `creek deploy --dry-run` | Show the deploy plan without executing (agent-safe) |
68
68
  | `creek projects` | List your projects |
69
69
  | `creek deployments` | List deployments for a project |
70
+ | `creek logs` | Read recent log entries (R2 archive) |
71
+ | `creek logs --follow` | Live tail via WebSocket until Ctrl+C |
72
+ | `creek logs --outcome exception` | Filter by tail outcome (or `--deployment`, `--branch`, `--level`, `--search`) |
70
73
  | `creek status` | Show current project status |
71
74
  | `creek login` | Authenticate with Creek |
72
75
  | `creek login --token <key>` | Authenticate in CI/CD (non-interactive) |
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `creek doctor` — pre-deploy sanity check.
3
+ *
4
+ * Runs the SDK rule engine against the current project, reports
5
+ * findings. Exits 0 if ok, 1 if any error-severity finding fires.
6
+ *
7
+ * This is the one command designed for LLM agents to invoke before
8
+ * `creek deploy`. With `--json` the output is ndjson-adjacent (pretty
9
+ * JSON, but parseable), letting an agent look up fixes by stable
10
+ * CK-* codes and apply them without re-reading the source.
11
+ */
12
+ export declare const doctorCommand: import("citty").CommandDef<{
13
+ json: {
14
+ type: "boolean";
15
+ description: string;
16
+ default: boolean;
17
+ };
18
+ yes: {
19
+ type: "boolean";
20
+ description: string;
21
+ default: boolean;
22
+ };
23
+ path: {
24
+ type: "positional";
25
+ description: string;
26
+ required: false;
27
+ };
28
+ }>;
29
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1,174 @@
1
+ import { defineCommand } from "citty";
2
+ import consola from "consola";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { join, resolve } from "node:path";
5
+ import { runDoctor, resolveConfig, ConfigNotFoundError, } from "@solcreek/sdk";
6
+ import { globalArgs, resolveJsonMode, jsonOutput, } from "../utils/output.js";
7
+ /**
8
+ * `creek doctor` — pre-deploy sanity check.
9
+ *
10
+ * Runs the SDK rule engine against the current project, reports
11
+ * findings. Exits 0 if ok, 1 if any error-severity finding fires.
12
+ *
13
+ * This is the one command designed for LLM agents to invoke before
14
+ * `creek deploy`. With `--json` the output is ndjson-adjacent (pretty
15
+ * JSON, but parseable), letting an agent look up fixes by stable
16
+ * CK-* codes and apply them without re-reading the source.
17
+ */
18
+ export const doctorCommand = defineCommand({
19
+ meta: {
20
+ name: "doctor",
21
+ description: "Analyze the project for pre-deploy issues — missing build output, deprecated config keys, Workers-incompatible deps, portability leaks.",
22
+ },
23
+ args: {
24
+ path: {
25
+ type: "positional",
26
+ description: "Project directory to analyze. Defaults to cwd.",
27
+ required: false,
28
+ },
29
+ ...globalArgs,
30
+ },
31
+ async run({ args }) {
32
+ const cwd = resolve(args.path ?? process.cwd());
33
+ const jsonMode = resolveJsonMode(args);
34
+ const ctx = buildContext(cwd);
35
+ const report = runDoctor(ctx);
36
+ if (jsonMode) {
37
+ jsonOutput({
38
+ ok: report.ok,
39
+ cwd,
40
+ archetype: report.archetype,
41
+ summary: report.summary,
42
+ findings: report.findings,
43
+ }, report.ok ? 0 : 1);
44
+ return;
45
+ }
46
+ printHuman(cwd, report);
47
+ if (!report.ok)
48
+ process.exit(1);
49
+ },
50
+ });
51
+ function buildContext(cwd) {
52
+ const fileExists = (relPath) => existsSync(join(cwd, relPath));
53
+ const creekTomlPath = join(cwd, "creek.toml");
54
+ const creekTomlRaw = existsSync(creekTomlPath)
55
+ ? safeRead(creekTomlPath)
56
+ : null;
57
+ const pkgPath = join(cwd, "package.json");
58
+ const packageJson = existsSync(pkgPath)
59
+ ? safeParseJson(pkgPath)
60
+ : null;
61
+ const resolved = resolveConfigSafely(cwd);
62
+ const allDeps = {
63
+ ...(packageJson?.dependencies ?? {}),
64
+ ...(packageJson?.devDependencies ?? {}),
65
+ };
66
+ return { cwd, resolved, packageJson, creekTomlRaw, fileExists, allDeps };
67
+ }
68
+ function resolveConfigSafely(cwd) {
69
+ try {
70
+ return resolveConfig(cwd);
71
+ }
72
+ catch (err) {
73
+ if (err instanceof ConfigNotFoundError)
74
+ return null;
75
+ // Other errors (parse failures) bubble as null — the rules will
76
+ // still pick up partial info from creekTomlRaw + packageJson.
77
+ return null;
78
+ }
79
+ }
80
+ function safeRead(path) {
81
+ try {
82
+ return readFileSync(path, "utf8");
83
+ }
84
+ catch {
85
+ return null;
86
+ }
87
+ }
88
+ function safeParseJson(path) {
89
+ const raw = safeRead(path);
90
+ if (raw === null)
91
+ return null;
92
+ try {
93
+ return JSON.parse(raw);
94
+ }
95
+ catch {
96
+ return null;
97
+ }
98
+ }
99
+ // ─── Human output ───────────────────────────────────────────────────────
100
+ const COLOR = {
101
+ reset: "\x1b[0m",
102
+ bold: "\x1b[1m",
103
+ dim: "\x1b[2m",
104
+ red: "\x1b[31m",
105
+ yellow: "\x1b[33m",
106
+ green: "\x1b[32m",
107
+ cyan: "\x1b[36m",
108
+ gray: "\x1b[90m",
109
+ };
110
+ function tty() {
111
+ return process.stdout.isTTY ?? false;
112
+ }
113
+ function c(s, color) {
114
+ return tty() ? `${COLOR[color]}${s}${COLOR.reset}` : s;
115
+ }
116
+ function printHuman(cwd, report) {
117
+ consola.log("");
118
+ consola.log(` ${c("⬡ creek doctor", "bold")} ${c(cwd, "dim")}`);
119
+ consola.log(` ${c("archetype:", "dim")} ${report.archetype ?? "unknown"}`);
120
+ consola.log("");
121
+ if (report.findings.length === 0) {
122
+ consola.log(` ${c("✓", "green")} No issues detected. Deploy is good to go.`);
123
+ consola.log("");
124
+ return;
125
+ }
126
+ const order = ["error", "warn", "info"];
127
+ const grouped = groupBy(report.findings, (f) => f.severity);
128
+ for (const sev of order) {
129
+ const bucket = grouped.get(sev) ?? [];
130
+ for (const f of bucket) {
131
+ printFinding(f);
132
+ }
133
+ }
134
+ const parts = [];
135
+ if (report.summary.error)
136
+ parts.push(c(`${report.summary.error} error${s(report.summary.error)}`, "red"));
137
+ if (report.summary.warn)
138
+ parts.push(c(`${report.summary.warn} warning${s(report.summary.warn)}`, "yellow"));
139
+ if (report.summary.info)
140
+ parts.push(c(`${report.summary.info} info`, "cyan"));
141
+ consola.log(` Summary: ${parts.join(", ")}`);
142
+ consola.log("");
143
+ }
144
+ function printFinding(f) {
145
+ const icon = f.severity === "error" ? c("✗", "red")
146
+ : f.severity === "warn" ? c("⚠", "yellow")
147
+ : c("ℹ", "cyan");
148
+ consola.log(` ${icon} ${c(f.title, "bold")} ${c(`[${f.code}]`, "gray")}`);
149
+ for (const line of f.detail.split("\n")) {
150
+ consola.log(` ${c(line, "dim")}`);
151
+ }
152
+ consola.log(` ${c("→ fix:", "cyan")}`);
153
+ for (const line of f.fix.split("\n")) {
154
+ consola.log(` ${line}`);
155
+ }
156
+ if (f.references?.length) {
157
+ consola.log(` ${c("→ refs:", "dim")} ${f.references.join(", ")}`);
158
+ }
159
+ consola.log("");
160
+ }
161
+ function groupBy(arr, key) {
162
+ const out = new Map();
163
+ for (const v of arr) {
164
+ const k = key(v);
165
+ const bucket = out.get(k) ?? [];
166
+ bucket.push(v);
167
+ out.set(k, bucket);
168
+ }
169
+ return out;
170
+ }
171
+ function s(n) {
172
+ return n === 1 ? "" : "s";
173
+ }
174
+ //# sourceMappingURL=doctor.js.map
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ import { rollbackCommand } from "./commands/rollback.js";
19
19
  import { opsCommand } from "./commands/ops.js";
20
20
  import { queueCommand } from "./commands/queue.js";
21
21
  import { logsCommand } from "./commands/logs.js";
22
+ import { doctorCommand } from "./commands/doctor.js";
22
23
  const __dirname = dirname(fileURLToPath(import.meta.url));
23
24
  const cliPkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
24
25
  // Read version from the "creek" facade package (what users install),
@@ -45,6 +46,7 @@ const main = defineCommand({
45
46
  projects: projectsCommand,
46
47
  deployments: deploymentsCommand,
47
48
  logs: logsCommand,
49
+ doctor: doctorCommand,
48
50
  login: loginCommand,
49
51
  whoami: whoamiCommand,
50
52
  init: initCommand,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solcreek/cli",
3
- "version": "0.4.13",
3
+ "version": "0.4.14",
4
4
  "description": "CLI for the Creek deployment platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -33,7 +33,7 @@
33
33
  "esbuild": "^0.25.0",
34
34
  "smol-toml": "^1.3.1",
35
35
  "ws": "^8.20.0",
36
- "@solcreek/sdk": "0.4.5"
36
+ "@solcreek/sdk": "0.4.6"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@testing-library/dom": "^10.4.1",