deploylog 0.2.1 → 0.3.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Monolithiq LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/Idzuo32/deploylog-cli/main/.github/assets/logo.png" width="120" alt="DeployLog" />
2
+ <img src="https://raw.githubusercontent.com/marko-builds/deploylog-cli/main/.github/assets/logo.png" width="120" alt="DeployLog" />
3
3
  </p>
4
4
 
5
5
  <h1 align="center">deploylog</h1>
@@ -23,7 +23,7 @@
23
23
  npm i -g deploylog
24
24
  ```
25
25
 
26
- Node 18+ required.
26
+ Node 18+ required. Installs two equivalent commands: `deploylog` and the short alias `dpl`.
27
27
 
28
28
  ## Authenticate
29
29
 
@@ -94,12 +94,12 @@ Create a new changelog entry.
94
94
  -t, --title <title> Entry title
95
95
  -b, --body <markdown> Entry body (Markdown)
96
96
  -p, --project <slug> Project slug (or set in .deploylog.yml)
97
- --type <type> feature | fix | improvement | breaking | announcement
97
+ -T, --type <type> feature | fix | improvement | breaking | announcement
98
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
99
+ -P, --publish Publish immediately
100
+ -D, --draft Save as draft (default)
101
+ -g, --from-git Derive title/body from commits since the last tag (alias: --git)
102
+ -a, --ai-summarize Rewrite the entry with Claude Haiku (alias: --ai)
103
103
  -y, --yes Skip interactive confirmation for AI-generated content
104
104
  ```
105
105
 
@@ -117,6 +117,8 @@ Collects commits since the last git tag, formats them as a Markdown list, and op
117
117
 
118
118
  ```bash
119
119
  deploylog push --from-git --ai-summarize --version 1.4.0 --publish
120
+ # or, with short flags and the dpl alias:
121
+ dpl push -g -a --version 1.4.0 --publish
120
122
  ```
121
123
 
122
124
  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.
@@ -125,13 +127,13 @@ Uses Claude Haiku to rewrite your raw commits into user-friendly release notes.
125
127
 
126
128
  For CI workflows, prefer the official Action:
127
129
 
128
- - [`deploylogdev/action`](https://github.com/marketplace/actions/deploylog) on the GitHub Marketplace
130
+ - [`deploylogdev/action`](https://github.com/marketplace/actions/publish-to-deploylog) on the GitHub Marketplace
129
131
 
130
132
  ## Related
131
133
 
132
134
  - **Dashboard** — [deploylog.dev](https://deploylog.dev)
133
135
  - **Widget** — embeddable changelog widget at `cdn.deploylog.dev`
134
- - **GitHub Action** — [`deploylogdev/action@v1`](https://github.com/marketplace/actions/deploylog)
136
+ - **GitHub Action** — [`deploylogdev/action@v1`](https://github.com/marketplace/actions/publish-to-deploylog)
135
137
 
136
138
  ## License
137
139
 
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
@@ -9,7 +9,7 @@ const program = new Command();
9
9
  program
10
10
  .name('deploylog')
11
11
  .description('Push changelog entries from the terminal')
12
- .version('0.2.0');
12
+ .version('0.3.0');
13
13
  // ─── login ──────────────────────────────────────────────────────────────────
14
14
  program
15
15
  .command('login')
@@ -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) {
@@ -112,15 +126,20 @@ program
112
126
  .option('-t, --title <title>', 'Entry title (required unless --from-git or --ai-summarize)')
113
127
  .option('-b, --body <markdown>', 'Entry body (Markdown)')
114
128
  .option('-p, --project <slug>', 'Project slug (or set in .deploylog.yml)')
115
- .option('--type <type>', 'Entry type: feature, fix, improvement, breaking, announcement')
129
+ .option('-T, --type <type>', 'Entry type: feature, fix, improvement, breaking, announcement')
116
130
  .option('--version <version>', 'Semver version (e.g. 1.2.3)')
117
- .option('--publish', 'Publish immediately (default: draft)')
118
- .option('--draft', 'Save as draft (default)')
119
- .option('--from-git', 'Derive title/body from commits since the last tag')
120
- .option('--ai-summarize', 'Rewrite the entry with Claude Haiku (user-friendly release notes)')
131
+ .option('-P, --publish', 'Publish immediately (default: draft)')
132
+ .option('-D, --draft', 'Save as draft (default)')
133
+ .option('-g, --from-git', 'Derive title/body from commits since the last tag')
134
+ .option('--git', 'Alias of --from-git')
135
+ .option('-a, --ai-summarize', 'Rewrite the entry with Claude Haiku (user-friendly release notes)')
136
+ .option('--ai', 'Alias of --ai-summarize')
121
137
  .option('-y, --yes', 'Skip interactive confirmation for AI-generated content')
122
138
  .action(async (opts) => {
123
139
  try {
140
+ // Reconcile each flag with its alias (-g/--git, -a/--ai).
141
+ const fromGit = opts.fromGit || opts.git;
142
+ const aiSummarize = opts.aiSummarize || opts.ai;
124
143
  const slug = resolveProject(opts.project);
125
144
  const projectConfig = readProjectConfig();
126
145
  // Gather source material (commits + version) if --from-git.
@@ -128,7 +147,7 @@ program
128
147
  let gitVersion = null;
129
148
  let gitTitle = null;
130
149
  let gitBody = null;
131
- if (opts.fromGit) {
150
+ if (fromGit) {
132
151
  if (!isGitRepo()) {
133
152
  console.error(chalk.red('Not in a git repository. Remove --from-git or cd to a repo.'));
134
153
  process.exit(1);
@@ -138,7 +157,7 @@ program
138
157
  gitVersion = getHeadVersion();
139
158
  gitTitle = defaultTitleFromGit(gitVersion, lastTag);
140
159
  gitBody = formatCommitsAsMarkdown(commits);
141
- if (commits.length === 0 && !opts.body && !opts.aiSummarize) {
160
+ if (commits.length === 0 && !opts.body && !aiSummarize) {
142
161
  console.error(chalk.yellow(lastTag
143
162
  ? `No commits since tag ${lastTag}. Nothing to summarize.`
144
163
  : 'No commits found on HEAD.'));
@@ -150,7 +169,7 @@ program
150
169
  let body = opts.body ?? gitBody ?? '';
151
170
  let entryType = opts.type ?? projectConfig?.default_type ?? null;
152
171
  const version = opts.version ?? gitVersion ?? undefined;
153
- if (opts.aiSummarize) {
172
+ if (aiSummarize) {
154
173
  const hasSource = commits.length > 0 || (opts.body && opts.body.trim().length > 0);
155
174
  if (!hasSource) {
156
175
  console.error(chalk.red('--ai-summarize needs source material. Pass --from-git or provide --body as raw notes.'));
@@ -175,11 +194,19 @@ program
175
194
  return;
176
195
  }
177
196
  }
197
+ else if (!opts.yes) {
198
+ // Non-interactive shell (CI, piped stdin): there's no prompt to show,
199
+ // so make the unreviewed auto-proceed explicit. (BUG-019)
200
+ console.log(chalk.yellow('Non-interactive shell: proceeding with the AI-generated entry without confirmation.'));
201
+ }
178
202
  }
179
203
  if (!title || !body) {
180
204
  console.error(chalk.red('Entry requires --title and --body (or --from-git / --ai-summarize to derive them).'));
181
205
  process.exit(1);
182
206
  }
207
+ if (opts.publish && opts.draft) {
208
+ console.log(chalk.yellow('Both --publish and --draft passed; saving as a draft.'));
209
+ }
183
210
  const entry = await createEntry(slug, {
184
211
  title,
185
212
  body_markdown: body,
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "deploylog",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Push changelog entries from the terminal",
5
5
  "type": "module",
6
6
  "bin": {
7
- "deploylog": "./dist/index.js"
7
+ "deploylog": "./dist/index.js",
8
+ "dpl": "./dist/index.js"
8
9
  },
9
10
  "files": [
10
11
  "dist"
@@ -27,7 +28,7 @@
27
28
  "license": "MIT",
28
29
  "repository": {
29
30
  "type": "git",
30
- "url": "https://github.com/Idzuo32/deploylog-cli.git"
31
+ "url": "https://github.com/marko-builds/deploylog-cli.git"
31
32
  },
32
33
  "homepage": "https://deploylog.dev",
33
34
  "dependencies": {