@jacebenson/jsn 3.0.6 → 3.1.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 CHANGED
@@ -1,270 +1,113 @@
1
- # JSN - ServiceNow CLI
1
+ # JSN — ServiceNow CLI
2
2
 
3
- A command-line interface for ServiceNow that follows the Unix philosophy: simple, composable, and scriptable.
3
+ A command-line interface for ServiceNow. Type `jsn` and start working — no web UI needed.
4
4
 
5
- ## Installation
5
+ Works standalone or with AI agents (Claude Code, OpenCode, Hermes, Cursor, Copilot).
6
6
 
7
- ### npm (recommended — cross-platform)
7
+ ## Install
8
8
 
9
9
  ```bash
10
10
  npm install -g @jacebenson/jsn
11
11
  ```
12
12
 
13
- No compilation needed. Works on macOS, Linux, and Windows with Node.js 18+.
13
+ Node.js 18+. macOS, Linux, Windows.
14
14
 
15
- > **Note:** As of v0.1.0, the `latest` dist-tag points to the Node.js implementation. This is the version you get with `npm install -g @jacebenson/jsn`.
15
+ The install also copies an AI agent skill file to `~/.agents/skills/servicenow/SKILL.md`.
16
16
 
17
- ### Quick Start
17
+ ## Quick Start
18
18
 
19
19
  ```bash
20
- # Interactive setup
21
- jsn setup
22
-
23
- # Check auth status
24
- jsn auth status
20
+ jsn setup # Interactive first-time setup
21
+ jsn auth login https://dev12345.service-now.com
22
+ jsn incidents list --query "priority=1" # Critical incidents
23
+ ```
25
24
 
26
- # List incidents
27
- jsn incidents
25
+ ## What It Does
28
26
 
29
- # Check for updates
30
- jsn version --check
31
- ```
27
+ jsn talks to the ServiceNow REST API. Read, create, update, delete — tickets, users, groups, records on any table. Inspect flows and business rules. Export update sets. Run background scripts.
32
28
 
33
- ## What's New in v0.1.0
34
-
35
- Full feature parity — 128 tests, lint clean:
36
-
37
- | Feature | Status |
38
- |---------|--------|
39
- | Incident management (CRUD) | ✅ |
40
- | Change request management (CRUD) | ✅ |
41
- | Service catalog requests (with attachments + variables) | ✅ |
42
- | Catalog tasks, generic tickets | ✅ |
43
- | User management (CRUD) | ✅ |
44
- | Group management (CRUD) | ✅ |
45
- | Group memberships and roles | ✅ |
46
- | Generic Table API (`jsn records`) | ✅ |
47
- | OAuth PKCE with keychain (shared auth store) | ✅ |
48
- | Bot/CI/CD auth flags (`--code`, `--print-url`) | ✅ |
49
- | Dev commands: flows, actions, includes, rules, ACLs, etc. | ✅ |
50
- | Script execution (`jsn dev eval`) via OAuth session flow | ✅ |
51
- | Interactive search-as-you-type pickers | ✅ |
52
- | Output formats: JSON, styled, markdown, quiet | ✅ |
53
- | Enriched `requests show` with attachments + variables | ✅ |
54
- | Categorized help system (CORE, DATA, DEV, CONFIG) | ✅ |
55
-
56
- ### New Commands
57
-
58
- - **`jsn version --check`** — Check npm registry for newer versions
59
- - **`jsn skill`** — Manage the jsn AI agent skill file (for Hermes, Claude Code, etc.)
60
- - `jsn skill show` — Display the bundled skill
61
- - `jsn skill fetch` — Download latest skill from GitHub
62
- - `jsn skill path` — Show skill file locations
63
- - `jsn skill install` — Download + save to Hermes skills directory
64
-
65
- ## Configuration
66
-
67
- JSN uses a layered configuration system:
68
-
69
- | Source | Priority | Description |
70
- |--------|----------|-------------|
71
- | Flags | Highest | `--instance`, `--profile`, `--format` |
72
- | Environment | High | `SERVICENOW_INSTANCE_URL`, `SERVICENOW_FORMAT` |
73
- | Local config | Medium | `./.servicenow/config.json` |
74
- | Global config | Low | `~/.config/servicenow/config.json` |
75
- | Defaults | Lowest | Built-in defaults |
76
-
77
- ### Profiles
29
+ Everything outputs JSON when piped, styled tables in a terminal.
78
30
 
79
31
  ```bash
80
- # Login to a new instance
81
- jsn auth login https://dev12345.service-now.com
32
+ # Core ticket work
33
+ jsn incidents list --query "active=true^priority=1"
34
+ jsn incidents INC0010001
35
+ jsn incidents create --description "Server down" --priority 1
82
36
 
83
- # List all profiles
84
- jsn profiles list
37
+ # Admin tasks
38
+ jsn flows list # Interactive picker with pagination
39
+ jsn flows show "Assign Task" # Full flow detail
40
+ jsn rules list --query "collection=incident"
41
+ jsn updatesets set "My Feature"
85
42
 
86
- # Switch to a different profile
87
- jsn profiles use dev12345
43
+ # Generic table access
44
+ jsn records list --table incident --limit 50 --json | jq '.[].number'
45
+ jsn records create --table incident --data '{"short_description":"test"}'
88
46
 
89
- # Show current profile
90
- jsn profiles show
47
+ # Script execution
48
+ jsn eval "gs.info('Hello World')"
91
49
  ```
92
50
 
93
51
  ## Commands
94
52
 
95
- ### CORE
96
-
97
- | Command | Aliases | Description |
98
- |---------|---------|-------------|
99
- | `incidents` | `incident`, `inc` | Manage IT incidents |
100
- | `changes` | `change`, `chg` | Manage change requests |
101
- | `requests` | `request`, `req`, `ritm` | Manage service catalog requests |
102
- | `tasks` | `task`, `sctask` | Manage service catalog tasks |
103
- | `tickets` | `ticket` | Query generic tickets |
104
-
105
- ### DATA & ADMIN
106
-
107
- | Command | Aliases | Description |
108
- |---------|---------|-------------|
109
- | `records` | - | Generic Table API for any table |
110
- | `users` | `user` | Manage users |
111
- | `groups` | `group` | Manage user groups |
112
- | `groupmembers` | `gm` | Manage group memberships |
113
- | `grouproles` | `gr` | Manage group roles |
114
-
115
- ### DEVELOPMENT
116
-
117
- | Command | Description |
118
- |---------|-------------|
119
- | `dev flows` | Manage Flow Designer flows |
120
- | `dev actions` | Manage action definitions |
121
- | `dev includes` | Manage script includes |
122
- | `dev rules` | Manage business rules |
123
- | `dev clientscripts` | Manage client scripts |
124
- | `dev uiactions` | Manage UI actions |
125
- | `dev uipolicies` | Manage UI policies |
126
- | `dev tables` | View table definitions |
127
- | `dev columns` | Manage column definitions |
128
- | `dev import` | Manage import sets |
129
- | `dev acls` | Manage access controls |
130
- | `dev roles` | Manage roles |
131
- | `dev updatesets` | Manage update sets |
132
- | `dev scopes` | Manage application scopes |
133
- | `dev properties` | Manage system properties |
134
- | `dev logs` | Query system logs |
135
- | `dev rest` | Raw REST API calls |
136
- | `dev eval` | Execute background scripts |
137
-
138
- ### CONFIGURATION
139
-
140
- | Command | Description |
141
- |---------|-------------|
142
- | `setup` | Interactive first-time setup |
143
- | `auth` | Manage OAuth authentication |
144
- | `profiles` | Manage instance profiles |
145
-
146
- ### UTILITY
147
-
148
- | Command | Description |
149
- |---------|-------------|
150
- | `version` | Show version (use `--check` for update check) |
151
- | `skill` | Manage the jsn AI agent skill file |
152
-
153
- ## Usage Examples
154
-
155
- ### Incidents
53
+ Run `jsn` for the full grouped list. Commands are organized by ServiceNow domain:
156
54
 
157
- ```bash
158
- jsn incidents # List all
159
- jsn incidents list --query "priority=1" # Critical only
160
- jsn incidents INC0010001 # Show specific
161
- jsn incidents create --description "Server down" --priority 1
162
- jsn incidents update INC0010001 --data '{"state": "6"}'
163
- ```
55
+ **Core** — `incidents`, `changes`, `requests`, `tasks`, `tickets`
164
56
 
165
- ### Requests (with enriched details)
57
+ **Automation** — `flows`, `actions`, `rules`, `scrapi`, `updatesets`, `eval`, `rest`
166
58
 
167
- ```bash
168
- jsn requests show RITM0010001
169
- # Output includes attachments + catalog variables:
170
- # ─ Attachments ─
171
- # onboarding_form.pdf (by John Smith, 2026-05-15 14:23:10)
172
- # ─ Catalog Variables ─
173
- # Department: Engineering
174
- # Urgency: High
175
- ```
59
+ **Access** — `acls`, `roles`, `scopes`, `properties`, `privileges`
176
60
 
177
- ### Development
61
+ **User Experience** — `forms`, `lists`, `clientscripts`, `uipolicies`, `uiactions`
178
62
 
179
- ```bash
180
- jsn dev flows # List flows
181
- jsn dev includes MyScriptInclude # Show a script include
182
- jsn dev rules # List business rules
183
- jsn dev updatesets set "My Update Set" # Set current updateset
184
- jsn dev eval "gs.info('Hello World')" # Run background script
185
- ```
63
+ **Data** — `records`, `tables`, `columns`, `includes`, `import`, `logs`, `users`, `groups`
186
64
 
187
- ### Generic Table API
65
+ Every command supports `--json`, `--query`, and `--help`.
66
+
67
+ ## Profiles
68
+
69
+ Switch between instances without re-authenticating.
188
70
 
189
71
  ```bash
190
- jsn records list --table incident --limit 10
191
- jsn records list --table incident --query "priority=1^active=true"
192
- jsn records get --table incident --sys-id abc123
193
- jsn records create --table incident --data '{"short_description": "Test"}'
72
+ jsn auth login https://dev12345.service-now.com
73
+ jsn profiles list
74
+ jsn profiles use dev12345
75
+ jsn profiles show
194
76
  ```
195
77
 
196
78
  ## Output Formats
197
79
 
198
- | Format | Flag | Description |
199
- |--------|------|-------------|
200
- | Auto (default) | `--format=auto` | JSON for pipes, styled for TTY |
201
- | JSON | `--json` or `--format=json` | Machine-readable JSON |
202
- | Styled | `--styled` | ANSI-styled tables |
203
- | Markdown | `--markdown` | Markdown tables |
204
- | Quiet | `--quiet` or `-q` | Data only, no envelope |
205
-
206
- ```bash
207
- jsn incidents --json | jq '.[].number'
208
- jsn incidents --styled
209
- jsn incidents -q | jq '.[].number'
210
- ```
80
+ | Flag | When to use |
81
+ |------|-------------|
82
+ | (default) | Terminal — styled tables |
83
+ | `--json` | Pipelines, scripts |
84
+ | `--markdown` | Documentation |
85
+ | `--quiet` / `-q` | Data only, no envelope |
211
86
 
212
87
  ## Authentication
213
88
 
214
- OAuth 2.0 with PKCE. Credentials stored in `~/.config/servicenow/credentials/`.
89
+ OAuth 2.0 with PKCE. Credentials in `~/.config/servicenow/credentials/`.
215
90
 
216
91
  ```bash
217
92
  jsn auth login https://dev12345.service-now.com
218
-
219
- # Bot-friendly: print auth URL instead of opening a browser
220
- jsn auth login https://dev12345.service-now.com --print-url
221
-
222
- # Paste code from browser
223
- jsn auth login https://dev12345.service-now.com --code ABC123
224
93
  ```
225
94
 
226
- ### CI/CD Integration
95
+ For CI/CD, set environment variables:
227
96
 
228
97
  ```bash
229
98
  export SERVICENOW_INSTANCE_URL="https://dev12345.service-now.com"
230
99
  export SERVICENOW_OAUTH_TOKEN="***"
231
- jsn incidents list # No interactive auth needed
100
+ jsn incidents list
232
101
  ```
233
102
 
234
- ## AI Agent Integration
103
+ ## AI Agents
235
104
 
236
- JSN includes a built-in skill file for AI agents:
105
+ jsn ships with a skill file that tells AI agents how to use it. Installed automatically to `~/.agents/skills/servicenow/SKILL.md`.
237
106
 
238
107
  ```bash
239
- # View the skill file
240
- jsn skill show
241
-
242
- # Download the latest skill from GitHub
243
- jsn skill fetch | head -30
244
-
245
- # Install to Hermes skills directory
246
- jsn skill install
247
-
248
- # Install to a custom location
249
- jsn skill install /path/to/skills/
250
- ```
251
-
252
- ## Environment Variables
253
-
254
- | Variable | Description |
255
- |----------|-------------|
256
- | `SERVICENOW_INSTANCE_URL` | Default instance URL |
257
- | `SERVICENOW_FORMAT` | Default output format |
258
- | `SERVICENOW_OAUTH_TOKEN` | OAuth token (for CI/CD) |
259
- | `JSN_NO_HEADER` | Suppress context header |
260
-
261
- ## Getting Help
262
-
263
- ```bash
264
- jsn # Categorized help with command groups
265
- jsn incidents --help # Command details
266
- jsn version # Show version
267
- jsn version --check # Check for npm updates
108
+ jsn skill show # View the skill
109
+ jsn skill install # Install to Hermes
110
+ jsn skill install --target all # All supported agents
268
111
  ```
269
112
 
270
113
  ## Development
@@ -272,80 +115,12 @@ jsn version --check # Check for npm updates
272
115
  ```bash
273
116
  git clone https://github.com/jacebenson/jsn.git
274
117
  cd jsn
275
- git checkout nodejs
276
118
  npm install
277
- npm test # 128+ tests
278
- npm run lint # ESLint
279
- npm run start # Run CLI locally
280
- ```
281
-
282
- ### Project Structure
283
-
284
- ```
285
- ├── src/
286
- │ ├── cli.js # Root CLI (yargs setup)
287
- │ ├── app.js # App context
288
- │ ├── auth.js # OAuth PKCE manager
289
- │ ├── sdk.js # ServiceNow REST API client
290
- │ ├── output.js # Output formatting
291
- │ ├── config.js # Configuration loader
292
- │ ├── context.js # Runtime context
293
- │ ├── errors.js # Structured error types
294
- │ ├── helpers.js # Shared utilities
295
- │ ├── help.js # Custom grouped help renderer
296
- │ └── commands/ # CLI command modules
297
- ├── skills/
298
- │ └── servicenow/
299
- │ └── SKILL.md # AI agent skill file
300
- ├── test/ # Test suite
301
- └── package.json
302
- ```
303
-
304
- ### Releasing
305
-
306
- ```bash
307
- npm run release -- patch # or minor, major
308
- # Creates node-v* tag, pushes to GitHub, publishes to npm
309
- ```
310
-
311
- ## Troubleshooting
312
-
313
- ### Not authenticated
314
-
315
- ```
316
- ⚠️ Not authenticated to https://dev12345.service-now.com
317
-
318
- To get started, run:
319
- jsn setup
320
- jsn auth login
321
- ```
322
-
323
- ### Instance URL required
324
-
325
- ```
326
- Error (usage): Instance URL required. Set via --instance flag,
327
- SERVICENOW_INSTANCE_URL env, or config file.
119
+ npm test
328
120
  ```
329
121
 
330
- ### Outdated version
331
-
332
- ```bash
333
- jsn version --check
334
- # → jsn 0.0.10 — newer version 0.1.0 available
335
- npm install -g @jacebenson/jsn
336
- ```
122
+ Releases: `npm run release -- patch` (or `minor`, `major`). Tags, pushes, and publishes to npm via GitHub Actions.
337
123
 
338
124
  ## License
339
125
 
340
- MIT License — see LICENSE file for details
341
-
342
- ## Acknowledgments
343
-
344
- This project follows architectural patterns from [basecamp-cli](https://github.com/basecamp/basecamp-cli).
345
-
346
- | Project | What we learned |
347
- |---------|----------------|
348
- | [Abey's `sn` CLI](https://github.com/tehubersheezy/servicenow-cli) (Rust) | Table API patterns, aggregate stats, raw REST passthrough |
349
- | [ServiceNow `now-sdk`](https://www.npmjs.com/package/@servicenow/sdk) | OAuth session flow for UI pages |
350
- | [ServiceNow VS Code Extension](https://marketplace.visualstudio.com/items?itemName=ServiceNow.now-vscode) | `sys.scripts.do` CSRF extraction |
351
- | [Getting Real](https://basecamp.com/gettingreal) by Basecamp | Build less, start with no, embrace constraints |
126
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jacebenson/jsn",
3
- "version": "3.0.6",
3
+ "version": "3.1.1",
4
4
  "description": "A command-line interface for ServiceNow that follows the Unix philosophy: simple, composable, and scriptable.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,6 +10,7 @@
10
10
  "scripts": {
11
11
  "test": "node --test $(find test -name '*.test.js')",
12
12
  "lint": "npx eslint src/ bin/ test/",
13
+ "postinstall": "node scripts/postinstall.mjs",
13
14
  "start": "node bin/jsn.js",
14
15
  "tag-reminder": "node scripts/tag-reminder.js",
15
16
  "release": "node scripts/release.js"
@@ -26,6 +27,7 @@
26
27
  "node": ">=18.0.0"
27
28
  },
28
29
  "dependencies": {
30
+ "@inquirer/core": "^10.1.0",
29
31
  "@inquirer/prompts": "^7.5.1",
30
32
  "chalk": "^5.4.1",
31
33
  "cli-table3": "^0.6.5",
@@ -33,13 +35,14 @@
33
35
  },
34
36
  "devDependencies": {
35
37
  "@eslint/js": "^10.0.1",
36
- "@inquirer/search": "^4.2.0",
38
+ "@inquirer/search": "^4.2.1",
37
39
  "globals": "^17.6.0"
38
40
  },
39
41
  "files": [
40
42
  "bin/jsn.js",
41
43
  "src/",
42
44
  "skills/",
45
+ "scripts/",
43
46
  "README.md",
44
47
  "LICENSE"
45
48
  ]
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * postinstall.mjs — Copies the bundled ServiceNow SKILL.md to agent skill directories
4
+ * on npm global install. Mirrors what `basecamp`'s installer does for their skill.
5
+ *
6
+ * Only runs when `npm install -g @jacebenson/jsn`.
7
+ * On `npm install` (local dependency), it's a no-op.
8
+ */
9
+
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import os from 'node:os';
13
+ import { fileURLToPath } from 'node:url';
14
+
15
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
+ const SKILL_SOURCE = path.resolve(__dirname, '..', 'skills', 'servicenow', 'SKILL.md');
17
+ const AGENTS_DIR = path.join(os.homedir(), '.agents', 'skills', 'servicenow');
18
+ const AGENTS_SKILL = path.join(AGENTS_DIR, 'SKILL.md');
19
+ const CLAUDE_SKILL_DIR = path.join(os.homedir(), '.claude', 'skills');
20
+ const CLAUDE_SYMLINK = path.join(CLAUDE_SKILL_DIR, 'servicenow');
21
+
22
+ function isGlobalInstall() {
23
+ return (
24
+ process.env.npm_config_global === 'true' ||
25
+ process.env.npm_lifecycle_event === 'postinstall'
26
+ );
27
+ }
28
+
29
+ function installSkill() {
30
+ // Check if running in a non-interactive/CI environment where .agents
31
+ // doesn't make sense. Skip silently.
32
+ if (!AGENTS_DIR.startsWith(os.homedir())) {
33
+ return;
34
+ }
35
+
36
+ if (!fs.existsSync(SKILL_SOURCE)) {
37
+ console.warn(' ⚠ jsn: ServiceNow SKILL.md not found at', SKILL_SOURCE);
38
+ return;
39
+ }
40
+
41
+ // Create ~/.agents/skills/servicenow/
42
+ try {
43
+ fs.mkdirSync(AGENTS_DIR, { recursive: true });
44
+ } catch {
45
+ // user's home might not be writable in container/CI
46
+ return;
47
+ }
48
+
49
+ // Copy the bundled skill file
50
+ try {
51
+ fs.copyFileSync(SKILL_SOURCE, AGENTS_SKILL);
52
+ console.log(' ✓ jsn: ServiceNow skill installed to ' + AGENTS_SKILL);
53
+ } catch (err) {
54
+ console.warn(' ⚠ jsn: Could not copy skill to ' + AGENTS_SKILL, err.message);
55
+ return;
56
+ }
57
+
58
+ // Create symlink at ~/.claude/skills/servicenow → ../../.agents/skills/servicenow
59
+ // This is the same pattern basecamp's installer uses: ~/.claude/skills/basecamp → ../../.agents/skills/basecamp
60
+ try {
61
+ fs.mkdirSync(CLAUDE_SKILL_DIR, { recursive: true });
62
+
63
+ // Remove stale symlink or directory if it exists
64
+ try {
65
+ fs.unlinkSync(CLAUDE_SYMLINK);
66
+ } catch {
67
+ // doesn't exist — fine
68
+ }
69
+
70
+ // Relative symlink so it survives user home dir moves
71
+ const relativeTarget = path.relative(CLAUDE_SKILL_DIR, AGENTS_DIR);
72
+ fs.symlinkSync(relativeTarget, CLAUDE_SYMLINK, 'dir');
73
+ console.log(' ✓ jsn: Claude Code skill link at ' + CLAUDE_SYMLINK);
74
+ } catch (err) {
75
+ // Claude Code skill dir might not be expected — non-fatal
76
+ console.warn(' ⚠ jsn: Could not create Claude Code symlink:', err.message);
77
+ }
78
+ }
79
+
80
+ installSkill();
@@ -0,0 +1,61 @@
1
+ #!/bin/bash
2
+ # pre-commit-check.sh - Run this before committing to check architecture compliance
3
+
4
+ echo "🔍 Checking architecture patterns..."
5
+
6
+ # Check for forbidden SDK patterns
7
+ FORBIDDEN_PATTERNS=(
8
+ "func.*Client.*ListForm"
9
+ "func.*Client.*ListList"
10
+ "func.*Client.*GetSP"
11
+ "func.*Client.*ListSP"
12
+ )
13
+
14
+ ERRORS=0
15
+
16
+ for pattern in "${FORBIDDEN_PATTERNS[@]}"; do
17
+ matches=$(grep -r "$pattern" internal/sdk/*.go 2>/dev/null | grep -v "_test.go" | grep -v "^Binary")
18
+ if [ ! -z "$matches" ]; then
19
+ echo "❌ Found forbidden SDK pattern: $pattern"
20
+ echo "$matches"
21
+ ERRORS=$((ERRORS + 1))
22
+ fi
23
+ done
24
+
25
+ # Check that commands don't import old SDK types
26
+ BAD_IMPORTS=(
27
+ "sdk.FormSection"
28
+ "sdk.FormElement"
29
+ "sdk.ListLayout"
30
+ "sdk.ListElement"
31
+ "sdk.SPPage"
32
+ "sdk.SPWidgetInstance"
33
+ )
34
+
35
+ for pattern in "${BAD_IMPORTS[@]}"; do
36
+ matches=$(grep -r "$pattern" internal/commands/**/*.go 2>/dev/null)
37
+ if [ ! -z "$matches" ]; then
38
+ echo "❌ Command using SDK type instead of local type: $pattern"
39
+ echo "$matches"
40
+ ERRORS=$((ERRORS + 1))
41
+ fi
42
+ done
43
+
44
+ # Run architecture tests
45
+ echo "🧪 Running architecture tests..."
46
+ if ! go test ./internal/sdk -run TestNoSDKHelperMethods -run TestCommandsUseDirectSDKList > /dev/null 2>&1; then
47
+ echo "❌ Architecture tests failed"
48
+ go test ./internal/sdk -v -run "TestNoSDKHelperMethods|TestCommandsUseDirectSDKList"
49
+ ERRORS=$((ERRORS + 1))
50
+ fi
51
+
52
+ if [ $ERRORS -eq 0 ]; then
53
+ echo "✅ All architecture checks passed!"
54
+ exit 0
55
+ else
56
+ echo ""
57
+ echo "⚠️ Architecture violations found!"
58
+ echo " Remember: Commands should call app.SDK.List() directly with local types."
59
+ echo " See internal/commands/dev/forms.go for the correct pattern."
60
+ exit 1
61
+ fi
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ import { execSync } from 'child_process';
3
+ import { readFileSync } from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, join } from 'path';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
9
+
10
+ const type = process.argv[2];
11
+ if (!type || !['patch', 'minor', 'major'].includes(type)) {
12
+ console.error('Usage: npm run release -- <patch|minor|major>');
13
+ process.exit(1);
14
+ }
15
+
16
+ let branch;
17
+ try {
18
+ branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim();
19
+ } catch {
20
+ console.error('Error: Not in a git repository');
21
+ process.exit(1);
22
+ }
23
+
24
+ if (branch !== 'main') {
25
+ console.error(`Error: Releases can only be made from 'main' branch (currently on '${branch}')`);
26
+ process.exit(1);
27
+ }
28
+
29
+ const prefix = 'v';
30
+
31
+ console.log('');
32
+ console.log(`🚀 Releasing from '${branch}'`);
33
+ console.log(` Bump: ${type}`);
34
+ console.log('');
35
+
36
+ // Run tests
37
+ console.log('⏳ Running tests...');
38
+ try {
39
+ execSync('npm test', { stdio: 'inherit' });
40
+ } catch {
41
+ console.error('');
42
+ console.error('❌ Tests failed. Release aborted.');
43
+ process.exit(1);
44
+ }
45
+
46
+ // Bump version with correct tag prefix
47
+ console.log('');
48
+ console.log(`⏳ Bumping version (${type})...`);
49
+ execSync(`npm version ${type} --tag-version-prefix=${prefix}`, { stdio: 'inherit' });
50
+
51
+ // Push commits and tags
52
+ console.log('');
53
+ console.log('⏳ Pushing to origin...');
54
+ execSync('git push && git push --tags', { stdio: 'inherit' });
55
+
56
+ // Show final state
57
+ const newPkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
58
+ console.log('');
59
+ console.log('✅ Release complete!');
60
+ console.log(` Version: ${newPkg.version}`);
61
+ console.log(` Tag: ${prefix}${newPkg.version}`);
62
+ console.log('');
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ import { execSync } from 'child_process';
3
+ import { readFileSync } from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, join } from 'path';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
9
+
10
+ let branch;
11
+ try {
12
+ branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim();
13
+ } catch {
14
+ branch = 'unknown';
15
+ }
16
+
17
+ const version = pkg.version;
18
+ const prefix = 'v';
19
+
20
+ console.log('');
21
+ console.log('📦 Release Reminder');
22
+ console.log('');
23
+ console.log(` Current version: ${version}`);
24
+ console.log(` Current branch: ${branch}`);
25
+ console.log('');
26
+ console.log(' To create a release tag, run:');
27
+ console.log('');
28
+ console.log(` git tag ${prefix}${version}`);
29
+ console.log(' git push origin --tags');
30
+ console.log('');