@jacebenson/jsn 3.0.6 → 3.1.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/README.md +60 -285
- package/package.json +3 -1
- package/scripts/postinstall.mjs +80 -0
- package/scripts/pre-commit-check.sh +61 -0
- package/scripts/release.js +62 -0
- package/scripts/tag-reminder.js +30 -0
- package/skills/servicenow/SKILL.md +41 -4
- package/src/app.js +5 -5
- package/src/cli.js +60 -0
- package/src/commands/_ticket.js +1 -1
- package/src/commands/dev/_generic.js +13 -13
- package/src/commands/dev/eval.js +1 -1
- package/src/commands/dev/flows.js +31 -8
- package/src/commands/dev/forms.js +16 -6
- package/src/commands/dev/lists.js +16 -6
- package/src/commands/dev/logs.js +11 -1
- package/src/commands/dev/scopes.js +4 -3
- package/src/commands/dev/scrapi.js +13 -13
- package/src/commands/dev/updatesets.js +6 -5
- package/src/commands/records.js +3 -3
- package/src/help.js +52 -9
- package/src/helpers.js +36 -33
- package/src/paginated-search.js +178 -0
package/README.md
CHANGED
|
@@ -1,270 +1,113 @@
|
|
|
1
|
-
# JSN
|
|
1
|
+
# JSN — ServiceNow CLI
|
|
2
2
|
|
|
3
|
-
A command-line interface for ServiceNow
|
|
3
|
+
A command-line interface for ServiceNow. Type `jsn` and start working — no web UI needed.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Works standalone or with AI agents (Claude Code, OpenCode, Hermes, Cursor, Copilot).
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Install
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npm install -g @jacebenson/jsn
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Node.js 18+. macOS, Linux, Windows.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
The install also copies an AI agent skill file to `~/.agents/skills/servicenow/SKILL.md`.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
## Quick Start
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
# Interactive setup
|
|
21
|
-
jsn
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
27
|
-
jsn incidents
|
|
25
|
+
## What It Does
|
|
28
26
|
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
81
|
-
jsn
|
|
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
|
-
#
|
|
84
|
-
jsn
|
|
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
|
-
#
|
|
87
|
-
jsn
|
|
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
|
-
#
|
|
90
|
-
jsn
|
|
47
|
+
# Script execution
|
|
48
|
+
jsn eval "gs.info('Hello World')"
|
|
91
49
|
```
|
|
92
50
|
|
|
93
51
|
## Commands
|
|
94
52
|
|
|
95
|
-
|
|
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
|
-
|
|
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
|
-
|
|
57
|
+
**Automation** — `flows`, `actions`, `rules`, `scrapi`, `updatesets`, `eval`, `rest`
|
|
166
58
|
|
|
167
|
-
|
|
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
|
-
|
|
61
|
+
**User Experience** — `forms`, `lists`, `clientscripts`, `uipolicies`, `uiactions`
|
|
178
62
|
|
|
179
|
-
|
|
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
|
-
|
|
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
|
|
191
|
-
jsn
|
|
192
|
-
jsn
|
|
193
|
-
jsn
|
|
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
|
-
|
|
|
199
|
-
|
|
200
|
-
|
|
|
201
|
-
|
|
|
202
|
-
|
|
|
203
|
-
|
|
|
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
|
|
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
|
-
|
|
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
|
|
100
|
+
jsn incidents list
|
|
232
101
|
```
|
|
233
102
|
|
|
234
|
-
## AI
|
|
103
|
+
## AI Agents
|
|
235
104
|
|
|
236
|
-
|
|
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
|
|
240
|
-
jsn skill
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
3
|
+
"version": "3.1.0",
|
|
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"
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"bin/jsn.js",
|
|
41
42
|
"src/",
|
|
42
43
|
"skills/",
|
|
44
|
+
"scripts/",
|
|
43
45
|
"README.md",
|
|
44
46
|
"LICENSE"
|
|
45
47
|
]
|
|
@@ -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('');
|