deploylog 0.2.0 → 0.2.2

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 ADDED
@@ -0,0 +1,138 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/Idzuo32/deploylog-cli/main/.github/assets/logo.png" width="120" alt="DeployLog" />
3
+ </p>
4
+
5
+ <h1 align="center">deploylog</h1>
6
+
7
+ <p align="center">
8
+ Push changelog entries from the terminal. Generate release notes from git with one flag. Rewrite them with Claude Haiku with another.
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/deploylog"><img src="https://img.shields.io/npm/v/deploylog.svg" alt="npm"></a>
13
+ <a href="https://www.npmjs.com/package/deploylog"><img src="https://img.shields.io/npm/dm/deploylog.svg" alt="downloads"></a>
14
+ <img src="https://img.shields.io/node/v/deploylog.svg" alt="node">
15
+ <img src="https://img.shields.io/npm/l/deploylog.svg" alt="license">
16
+ </p>
17
+
18
+ ---
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ npm i -g deploylog
24
+ ```
25
+
26
+ Node 18+ required.
27
+
28
+ ## Authenticate
29
+
30
+ Create an API key in your dashboard at [deploylog.dev/dashboard/api-keys](https://deploylog.dev/dashboard/api-keys), then:
31
+
32
+ ```bash
33
+ deploylog login --key dk_xxx
34
+ ```
35
+
36
+ Credentials are stored with [`conf`](https://github.com/sindresorhus/conf) in your OS's standard config directory.
37
+
38
+ ## Quick start
39
+
40
+ ```bash
41
+ # List your projects
42
+ deploylog projects
43
+
44
+ # Publish an entry
45
+ deploylog push \
46
+ --project my-app \
47
+ --title "Dark mode" \
48
+ --body "Auto-detects system preference." \
49
+ --type feature \
50
+ --version 1.4.0 \
51
+ --publish
52
+ ```
53
+
54
+ ## Project config
55
+
56
+ Create a `.deploylog.yml` at your repo root so you don't have to pass `--project` every time:
57
+
58
+ ```yaml
59
+ project: my-app
60
+ ```
61
+
62
+ ## Commands
63
+
64
+ ### `deploylog login`
65
+
66
+ Authenticate with an API key.
67
+
68
+ ```
69
+ --key <key> API key (starts with dk_)
70
+ --api-url <url> API base URL (default: https://deploylog.dev)
71
+ ```
72
+
73
+ ### `deploylog logout`
74
+
75
+ Remove stored credentials.
76
+
77
+ ### `deploylog projects`
78
+
79
+ List projects in your organization.
80
+
81
+ ### `deploylog list`
82
+
83
+ List recent entries for a project.
84
+
85
+ ```
86
+ -p, --project <slug> Project slug (or set in .deploylog.yml)
87
+ ```
88
+
89
+ ### `deploylog push`
90
+
91
+ Create a new changelog entry.
92
+
93
+ ```
94
+ -t, --title <title> Entry title
95
+ -b, --body <markdown> Entry body (Markdown)
96
+ -p, --project <slug> Project slug (or set in .deploylog.yml)
97
+ --type <type> feature | fix | improvement | breaking | announcement
98
+ --version <version> Semver (e.g. 1.2.3)
99
+ --publish Publish immediately
100
+ --draft Save as draft (default)
101
+ --from-git Derive title/body from commits since the last tag
102
+ --ai-summarize Rewrite the entry with Claude Haiku
103
+ -y, --yes Skip interactive confirmation for AI-generated content
104
+ ```
105
+
106
+ ## Recipes
107
+
108
+ **Draft from recent commits:**
109
+
110
+ ```bash
111
+ deploylog push --from-git
112
+ ```
113
+
114
+ Collects commits since the last git tag, formats them as a Markdown list, and opens the entry as a draft.
115
+
116
+ **AI-polished release notes:**
117
+
118
+ ```bash
119
+ deploylog push --from-git --ai-summarize --version 1.4.0 --publish
120
+ ```
121
+
122
+ Uses Claude Haiku to rewrite your raw commits into user-friendly release notes. Free plan includes 5 AI summaries per month; paid plans are unlimited.
123
+
124
+ **CI / GitHub Actions:**
125
+
126
+ For CI workflows, prefer the official Action:
127
+
128
+ - [`deploylogdev/action`](https://github.com/marketplace/actions/deploylog) on the GitHub Marketplace
129
+
130
+ ## Related
131
+
132
+ - **Dashboard** — [deploylog.dev](https://deploylog.dev)
133
+ - **Widget** — embeddable changelog widget at `cdn.deploylog.dev`
134
+ - **GitHub Action** — [`deploylogdev/action@v1`](https://github.com/marketplace/actions/deploylog)
135
+
136
+ ## License
137
+
138
+ MIT © DeployLog
package/dist/api.js CHANGED
@@ -23,11 +23,25 @@ async function request(path, options = {}) {
23
23
  ...options.headers,
24
24
  },
25
25
  });
26
- const json = await res.json();
26
+ const raw = await res.text();
27
+ let body = null;
28
+ if (raw) {
29
+ try {
30
+ body = JSON.parse(raw);
31
+ }
32
+ catch {
33
+ body = null;
34
+ }
35
+ }
27
36
  if (!res.ok) {
28
- throw new ApiError(res.status, json.error?.code ?? 'UNKNOWN', json.error?.message ?? `Request failed (${res.status})`);
37
+ throw new ApiError(res.status, body?.error?.code ?? 'UNKNOWN', body?.error?.message ?? `Request failed (${res.status})`);
38
+ }
39
+ // Reject non-JSON bodies and well-formed JSON that's missing `data` — both
40
+ // violate the response contract and would otherwise return undefined.
41
+ if (!body || body.data === undefined) {
42
+ throw new ApiError(res.status, 'INVALID_RESPONSE', `Server returned an unexpected response (${res.status})`);
29
43
  }
30
- return json.data;
44
+ return body.data;
31
45
  }
32
46
  export async function listProjects() {
33
47
  return request('/projects');
package/dist/config.js CHANGED
@@ -13,10 +13,12 @@ export function setApiKey(key) {
13
13
  config.set('apiKey', key);
14
14
  }
15
15
  export function getApiUrl() {
16
- return config.get('apiUrl') ?? 'https://deploylog.dev';
16
+ return (config.get('apiUrl') ?? 'https://deploylog.dev').replace(/\/+$/, '');
17
17
  }
18
18
  export function setApiUrl(url) {
19
- config.set('apiUrl', url);
19
+ // Normalize so request building (`${apiUrl}/api/cli${path}`) never produces a
20
+ // double slash from a trailing-slash paste. (BUG-012)
21
+ config.set('apiUrl', url.trim().replace(/\/+$/, ''));
20
22
  }
21
23
  export function clearConfig() {
22
24
  config.clear();
package/dist/index.js CHANGED
@@ -18,6 +18,20 @@ program
18
18
  .option('--api-url <url>', 'API base URL (default: https://deploylog.dev)')
19
19
  .action(async (opts) => {
20
20
  if (opts.apiUrl) {
21
+ let parsed;
22
+ try {
23
+ parsed = new URL(opts.apiUrl.trim());
24
+ }
25
+ catch {
26
+ console.error(chalk.red('Invalid API URL. Provide a valid absolute URL.'));
27
+ process.exit(1);
28
+ }
29
+ // Require http(s) and a real host — a scheme-only value like "https://"
30
+ // parses but would break request URL construction.
31
+ if (!/^https?:$/.test(parsed.protocol) || !parsed.hostname) {
32
+ console.error(chalk.red('Invalid API URL. Must use http(s) and include a host.'));
33
+ process.exit(1);
34
+ }
21
35
  setApiUrl(opts.apiUrl);
22
36
  }
23
37
  if (opts.key) {
@@ -175,11 +189,19 @@ program
175
189
  return;
176
190
  }
177
191
  }
192
+ else if (!opts.yes) {
193
+ // Non-interactive shell (CI, piped stdin): there's no prompt to show,
194
+ // so make the unreviewed auto-proceed explicit. (BUG-019)
195
+ console.log(chalk.yellow('Non-interactive shell: proceeding with the AI-generated entry without confirmation.'));
196
+ }
178
197
  }
179
198
  if (!title || !body) {
180
199
  console.error(chalk.red('Entry requires --title and --body (or --from-git / --ai-summarize to derive them).'));
181
200
  process.exit(1);
182
201
  }
202
+ if (opts.publish && opts.draft) {
203
+ console.log(chalk.yellow('Both --publish and --draft passed; saving as a draft.'));
204
+ }
183
205
  const entry = await createEntry(slug, {
184
206
  title,
185
207
  body_markdown: body,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deploylog",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Push changelog entries from the terminal",
5
5
  "type": "module",
6
6
  "bin": {