deploylog 0.1.0 → 0.2.1
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 +138 -0
- package/dist/api.d.ts +22 -0
- package/dist/api.js +6 -0
- package/dist/git.d.ts +38 -0
- package/dist/git.js +92 -0
- package/dist/index.js +91 -8
- package/package.json +16 -5
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.d.ts
CHANGED
|
@@ -30,3 +30,25 @@ export interface CreateEntryInput {
|
|
|
30
30
|
publish?: boolean;
|
|
31
31
|
}
|
|
32
32
|
export declare function createEntry(projectSlug: string, input: CreateEntryInput): Promise<Entry>;
|
|
33
|
+
export interface SummarizeInput {
|
|
34
|
+
project_slug?: string;
|
|
35
|
+
commits?: string[];
|
|
36
|
+
release_notes?: string;
|
|
37
|
+
version?: string;
|
|
38
|
+
}
|
|
39
|
+
export type EntryType = 'feature' | 'fix' | 'improvement' | 'breaking' | 'announcement';
|
|
40
|
+
export interface AiSummary {
|
|
41
|
+
title: string;
|
|
42
|
+
entry_type: EntryType;
|
|
43
|
+
body_markdown: string;
|
|
44
|
+
}
|
|
45
|
+
export interface SummarizeResponse {
|
|
46
|
+
summary: AiSummary;
|
|
47
|
+
model: string;
|
|
48
|
+
usage: {
|
|
49
|
+
used: number;
|
|
50
|
+
limit: number | null;
|
|
51
|
+
month_key: string;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export declare function summarize(input: SummarizeInput): Promise<SummarizeResponse>;
|
package/dist/api.js
CHANGED
package/dist/git.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare class GitError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Thin shell-out wrapper. Exported for test injection.
|
|
6
|
+
* Returns stdout with trailing newline trimmed, or null on non-zero exit.
|
|
7
|
+
*/
|
|
8
|
+
export declare function runGit(args: string[], cwd?: string): string | null;
|
|
9
|
+
export interface GitRunner {
|
|
10
|
+
(args: string[]): string | null;
|
|
11
|
+
}
|
|
12
|
+
export declare function isGitRepo(run?: GitRunner): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Most recent annotated or lightweight tag reachable from HEAD, or null.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getLastTag(run?: GitRunner): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* If HEAD is on a tag that looks like semver (v1.2.3 or 1.2.3), return the
|
|
19
|
+
* bare semver string (without the 'v'). Otherwise null.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getHeadVersion(run?: GitRunner): string | null;
|
|
22
|
+
export interface CommitSummary {
|
|
23
|
+
hash: string;
|
|
24
|
+
subject: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Commit subjects (and short hashes) between `ref` and HEAD, oldest-first.
|
|
28
|
+
* If `ref` is null, returns the most recent `limit` commits on HEAD.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getCommitsSince(ref: string | null, limit?: number, run?: GitRunner): CommitSummary[];
|
|
31
|
+
/**
|
|
32
|
+
* Derive a sensible default title from the latest tag (or fallback).
|
|
33
|
+
*/
|
|
34
|
+
export declare function defaultTitleFromGit(version: string | null, lastTag: string | null): string;
|
|
35
|
+
/**
|
|
36
|
+
* Format a commit list as a markdown bullet body.
|
|
37
|
+
*/
|
|
38
|
+
export declare function formatCommitsAsMarkdown(commits: CommitSummary[]): string;
|
package/dist/git.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
export class GitError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = 'GitError';
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Thin shell-out wrapper. Exported for test injection.
|
|
10
|
+
* Returns stdout with trailing newline trimmed, or null on non-zero exit.
|
|
11
|
+
*/
|
|
12
|
+
export function runGit(args, cwd = process.cwd()) {
|
|
13
|
+
try {
|
|
14
|
+
return execFileSync('git', args, {
|
|
15
|
+
cwd,
|
|
16
|
+
encoding: 'utf8',
|
|
17
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
18
|
+
}).replace(/\n$/, '');
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function isGitRepo(run = runGit) {
|
|
25
|
+
return run(['rev-parse', '--is-inside-work-tree']) === 'true';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Most recent annotated or lightweight tag reachable from HEAD, or null.
|
|
29
|
+
*/
|
|
30
|
+
export function getLastTag(run = runGit) {
|
|
31
|
+
const out = run(['describe', '--tags', '--abbrev=0']);
|
|
32
|
+
return out && out.length > 0 ? out : null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* If HEAD is on a tag that looks like semver (v1.2.3 or 1.2.3), return the
|
|
36
|
+
* bare semver string (without the 'v'). Otherwise null.
|
|
37
|
+
*/
|
|
38
|
+
export function getHeadVersion(run = runGit) {
|
|
39
|
+
const exact = run(['tag', '--points-at', 'HEAD']);
|
|
40
|
+
if (!exact)
|
|
41
|
+
return null;
|
|
42
|
+
for (const line of exact.split('\n')) {
|
|
43
|
+
const m = line.match(/^v?(\d+\.\d+\.\d+)$/);
|
|
44
|
+
if (m?.[1])
|
|
45
|
+
return m[1];
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Commit subjects (and short hashes) between `ref` and HEAD, oldest-first.
|
|
51
|
+
* If `ref` is null, returns the most recent `limit` commits on HEAD.
|
|
52
|
+
*/
|
|
53
|
+
export function getCommitsSince(ref, limit = 200, run = runGit) {
|
|
54
|
+
const range = ref ? `${ref}..HEAD` : 'HEAD';
|
|
55
|
+
const args = ['log', range, `--pretty=format:%h\t%s`, '--no-merges', `-${limit}`, '--reverse'];
|
|
56
|
+
const out = run(args);
|
|
57
|
+
if (out === null) {
|
|
58
|
+
// `ref..HEAD` with an unknown ref (or an empty repo) returns null via our wrapper.
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
if (out.length === 0)
|
|
62
|
+
return [];
|
|
63
|
+
const commits = [];
|
|
64
|
+
for (const line of out.split('\n')) {
|
|
65
|
+
const tab = line.indexOf('\t');
|
|
66
|
+
if (tab < 0)
|
|
67
|
+
continue;
|
|
68
|
+
const hash = line.slice(0, tab);
|
|
69
|
+
const subject = line.slice(tab + 1).trim();
|
|
70
|
+
if (subject.length > 0)
|
|
71
|
+
commits.push({ hash, subject });
|
|
72
|
+
}
|
|
73
|
+
return commits;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Derive a sensible default title from the latest tag (or fallback).
|
|
77
|
+
*/
|
|
78
|
+
export function defaultTitleFromGit(version, lastTag) {
|
|
79
|
+
if (version)
|
|
80
|
+
return `Release v${version}`;
|
|
81
|
+
if (lastTag)
|
|
82
|
+
return `Changes since ${lastTag}`;
|
|
83
|
+
return 'Recent changes';
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Format a commit list as a markdown bullet body.
|
|
87
|
+
*/
|
|
88
|
+
export function formatCommitsAsMarkdown(commits) {
|
|
89
|
+
if (commits.length === 0)
|
|
90
|
+
return '_No new commits since last tag._';
|
|
91
|
+
return commits.map((c) => `- ${c.subject}`).join('\n');
|
|
92
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { setApiKey, setApiUrl, getConfigPath, clearConfig } from './config.js';
|
|
5
|
-
import { listProjects, listEntries, createEntry, ApiError } from './api.js';
|
|
5
|
+
import { listProjects, listEntries, createEntry, summarize, ApiError, } from './api.js';
|
|
6
6
|
import { readProjectConfig } from './project-config.js';
|
|
7
|
+
import { isGitRepo, getLastTag, getHeadVersion, getCommitsSince, defaultTitleFromGit, formatCommitsAsMarkdown, } from './git.js';
|
|
7
8
|
const program = new Command();
|
|
8
9
|
program
|
|
9
10
|
.name('deploylog')
|
|
10
11
|
.description('Push changelog entries from the terminal')
|
|
11
|
-
.version('0.
|
|
12
|
+
.version('0.2.0');
|
|
12
13
|
// ─── login ──────────────────────────────────────────────────────────────────
|
|
13
14
|
program
|
|
14
15
|
.command('login')
|
|
@@ -108,22 +109,82 @@ program
|
|
|
108
109
|
program
|
|
109
110
|
.command('push')
|
|
110
111
|
.description('Create a new changelog entry')
|
|
111
|
-
.
|
|
112
|
-
.
|
|
112
|
+
.option('-t, --title <title>', 'Entry title (required unless --from-git or --ai-summarize)')
|
|
113
|
+
.option('-b, --body <markdown>', 'Entry body (Markdown)')
|
|
113
114
|
.option('-p, --project <slug>', 'Project slug (or set in .deploylog.yml)')
|
|
114
115
|
.option('--type <type>', 'Entry type: feature, fix, improvement, breaking, announcement')
|
|
115
116
|
.option('--version <version>', 'Semver version (e.g. 1.2.3)')
|
|
116
117
|
.option('--publish', 'Publish immediately (default: draft)')
|
|
117
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)')
|
|
121
|
+
.option('-y, --yes', 'Skip interactive confirmation for AI-generated content')
|
|
118
122
|
.action(async (opts) => {
|
|
119
123
|
try {
|
|
120
124
|
const slug = resolveProject(opts.project);
|
|
121
125
|
const projectConfig = readProjectConfig();
|
|
126
|
+
// Gather source material (commits + version) if --from-git.
|
|
127
|
+
let commits = [];
|
|
128
|
+
let gitVersion = null;
|
|
129
|
+
let gitTitle = null;
|
|
130
|
+
let gitBody = null;
|
|
131
|
+
if (opts.fromGit) {
|
|
132
|
+
if (!isGitRepo()) {
|
|
133
|
+
console.error(chalk.red('Not in a git repository. Remove --from-git or cd to a repo.'));
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
const lastTag = getLastTag();
|
|
137
|
+
commits = getCommitsSince(lastTag);
|
|
138
|
+
gitVersion = getHeadVersion();
|
|
139
|
+
gitTitle = defaultTitleFromGit(gitVersion, lastTag);
|
|
140
|
+
gitBody = formatCommitsAsMarkdown(commits);
|
|
141
|
+
if (commits.length === 0 && !opts.body && !opts.aiSummarize) {
|
|
142
|
+
console.error(chalk.yellow(lastTag
|
|
143
|
+
? `No commits since tag ${lastTag}. Nothing to summarize.`
|
|
144
|
+
: 'No commits found on HEAD.'));
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Build the entry (with optional AI rewrite).
|
|
149
|
+
let title = opts.title ?? gitTitle ?? '';
|
|
150
|
+
let body = opts.body ?? gitBody ?? '';
|
|
151
|
+
let entryType = opts.type ?? projectConfig?.default_type ?? null;
|
|
152
|
+
const version = opts.version ?? gitVersion ?? undefined;
|
|
153
|
+
if (opts.aiSummarize) {
|
|
154
|
+
const hasSource = commits.length > 0 || (opts.body && opts.body.trim().length > 0);
|
|
155
|
+
if (!hasSource) {
|
|
156
|
+
console.error(chalk.red('--ai-summarize needs source material. Pass --from-git or provide --body as raw notes.'));
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
process.stdout.write(chalk.dim('Summarizing with Claude Haiku... '));
|
|
160
|
+
const res = await summarize({
|
|
161
|
+
project_slug: slug,
|
|
162
|
+
commits: commits.map((c) => c.subject),
|
|
163
|
+
release_notes: opts.body,
|
|
164
|
+
version,
|
|
165
|
+
});
|
|
166
|
+
process.stdout.write(chalk.green('done\n'));
|
|
167
|
+
title = opts.title ?? res.summary.title;
|
|
168
|
+
body = res.summary.body_markdown;
|
|
169
|
+
entryType = opts.type ?? res.summary.entry_type;
|
|
170
|
+
printAiPreview(res.summary, res.usage);
|
|
171
|
+
if (!opts.yes && process.stdin.isTTY) {
|
|
172
|
+
const ok = await confirm('Publish this entry?');
|
|
173
|
+
if (!ok) {
|
|
174
|
+
console.log(chalk.yellow('Cancelled. No entry was created.'));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (!title || !body) {
|
|
180
|
+
console.error(chalk.red('Entry requires --title and --body (or --from-git / --ai-summarize to derive them).'));
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
122
183
|
const entry = await createEntry(slug, {
|
|
123
|
-
title
|
|
124
|
-
body_markdown:
|
|
125
|
-
entry_type:
|
|
126
|
-
version
|
|
184
|
+
title,
|
|
185
|
+
body_markdown: body,
|
|
186
|
+
entry_type: entryType,
|
|
187
|
+
version,
|
|
127
188
|
publish: opts.publish && !opts.draft,
|
|
128
189
|
});
|
|
129
190
|
const status = entry.published
|
|
@@ -140,6 +201,28 @@ program
|
|
|
140
201
|
}
|
|
141
202
|
});
|
|
142
203
|
// ─── helpers ────────────────────────────────────────────────────────────────
|
|
204
|
+
function printAiPreview(summary, usage) {
|
|
205
|
+
console.log();
|
|
206
|
+
console.log(chalk.bold('AI-generated entry:'));
|
|
207
|
+
console.log(` ${chalk.dim('Title:')} ${summary.title}`);
|
|
208
|
+
console.log(` ${chalk.dim('Type:')} ${summary.entry_type}`);
|
|
209
|
+
console.log(chalk.dim(' Body:'));
|
|
210
|
+
for (const line of summary.body_markdown.split('\n')) {
|
|
211
|
+
console.log(` ${line}`);
|
|
212
|
+
}
|
|
213
|
+
const limitLabel = usage.limit === null ? '∞' : String(usage.limit);
|
|
214
|
+
console.log(chalk.dim(` Usage: ${usage.used}/${limitLabel} this month (${usage.month_key})`));
|
|
215
|
+
console.log();
|
|
216
|
+
}
|
|
217
|
+
async function confirm(question) {
|
|
218
|
+
const { createInterface } = await import('node:readline');
|
|
219
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
220
|
+
const answer = await new Promise((resolve) => {
|
|
221
|
+
rl.question(`${question} [y/N] `, resolve);
|
|
222
|
+
});
|
|
223
|
+
rl.close();
|
|
224
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
225
|
+
}
|
|
143
226
|
function resolveProject(cliArg) {
|
|
144
227
|
if (cliArg)
|
|
145
228
|
return cliArg;
|
package/package.json
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deploylog",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Push changelog entries from the terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"deploylog": "./dist/index.js"
|
|
8
8
|
},
|
|
9
|
-
"files": [
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
10
12
|
"scripts": {
|
|
11
13
|
"build": "tsc",
|
|
12
14
|
"dev": "tsc --watch",
|
|
13
15
|
"prepublishOnly": "tsc",
|
|
14
|
-
"start": "node dist/index.js"
|
|
16
|
+
"start": "node dist/index.js",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:watch": "vitest"
|
|
15
19
|
},
|
|
16
|
-
"keywords": [
|
|
20
|
+
"keywords": [
|
|
21
|
+
"changelog",
|
|
22
|
+
"deploylog",
|
|
23
|
+
"cli",
|
|
24
|
+
"release-notes",
|
|
25
|
+
"devtools"
|
|
26
|
+
],
|
|
17
27
|
"license": "MIT",
|
|
18
28
|
"repository": {
|
|
19
29
|
"type": "git",
|
|
@@ -28,6 +38,7 @@
|
|
|
28
38
|
},
|
|
29
39
|
"devDependencies": {
|
|
30
40
|
"@types/node": "^22.15.3",
|
|
31
|
-
"typescript": "^5.8.3"
|
|
41
|
+
"typescript": "^5.8.3",
|
|
42
|
+
"vitest": "^4.1.4"
|
|
32
43
|
}
|
|
33
44
|
}
|