Skip to content

Commit 08eb262

Browse files
facundofariasclaude
andcommitted
v0.2.0: Agent DX, new commands, structured errors, global options
New commands: - dm update — push content revisions from file or stdin - dm react — add emoji reactions - dm list — show all docs in .draftmark.json - dm browse — open doc in default browser - dm config — show resolved config from all sources Agent DX: - --agent flag on create, auto-inherited by comment/review - --meta <json> for arbitrary metadata on create - stdin support (dm create -, dm update -) - Structured JSON errors ({ error, code, details }) on all failures - --quiet / -q suppresses stderr, stdout-only for piping - --format (table/json/minimal) on status and comments - --since date filter on comments Global options: - --base-url overrides DM_BASE_URL without env var - Standardized exit codes (0 ok, 1 error, 2 auth, 3 not found, 4 conflict) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0de5263 commit 08eb262

5 files changed

Lines changed: 500 additions & 68 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "draftmark",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "CLI for Draftmark",
55
"type": "module",
66
"bin": {

src/api.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,48 @@ interface RequestOptions {
88
params?: Record<string, string>;
99
}
1010

11+
export interface ApiError {
12+
error: string;
13+
code: string;
14+
details?: unknown;
15+
}
16+
1117
export interface ApiResponse<T = unknown> {
1218
ok: boolean;
1319
status: number;
1420
data: T;
1521
}
1622

23+
const STATUS_CODES: Record<number, string> = {
24+
400: "bad_request",
25+
401: "unauthorized",
26+
403: "forbidden",
27+
404: "not_found",
28+
409: "conflict",
29+
422: "unprocessable_entity",
30+
429: "rate_limited",
31+
500: "internal_error",
32+
};
33+
34+
function normalizeError(status: number, data: unknown): ApiError {
35+
const code = STATUS_CODES[status] || `http_${status}`;
36+
37+
if (data && typeof data === "object" && "error" in data) {
38+
const raw = data as Record<string, unknown>;
39+
return {
40+
error: String(raw.error),
41+
code: typeof raw.code === "string" ? raw.code : code,
42+
details: raw.details,
43+
};
44+
}
45+
46+
if (typeof data === "string" && data.length > 0) {
47+
return { error: data, code };
48+
}
49+
50+
return { error: `Request failed with status ${status}`, code };
51+
}
52+
1753
export async function api<T = unknown>(
1854
path: string,
1955
opts: RequestOptions = {}
@@ -49,8 +85,16 @@ export async function api<T = unknown>(
4985
? await response.json()
5086
: await response.text();
5187

88+
if (!response.ok) {
89+
return {
90+
ok: false,
91+
status: response.status,
92+
data: normalizeError(response.status, data) as T,
93+
};
94+
}
95+
5296
return {
53-
ok: response.ok,
97+
ok: true,
5498
status: response.status,
5599
data: data as T,
56100
};

0 commit comments

Comments
 (0)