@gilbertwong1996/ado 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/package.json +10 -6
- package/scripts/postinstall.js +336 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gilbertwong1996/ado",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "AI-native Azure DevOps CLI — structured JSON output, embedded skills for LLM agents, single-file cross-platform binary.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/gilbertwong96/ado_cli",
|
|
@@ -23,8 +23,12 @@
|
|
|
23
23
|
"bin": {
|
|
24
24
|
"ado": "bin/ado"
|
|
25
25
|
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"postinstall": "node scripts/postinstall.js"
|
|
28
|
+
},
|
|
26
29
|
"files": [
|
|
27
30
|
"bin/ado",
|
|
31
|
+
"scripts/postinstall.js",
|
|
28
32
|
"README.md",
|
|
29
33
|
"LICENSE"
|
|
30
34
|
],
|
|
@@ -32,10 +36,10 @@
|
|
|
32
36
|
"node": ">=18"
|
|
33
37
|
},
|
|
34
38
|
"optionalDependencies": {
|
|
35
|
-
"@gilbertwong1996/ado-darwin-arm64": "0.1
|
|
36
|
-
"@gilbertwong1996/ado-darwin-x64": "0.1
|
|
37
|
-
"@gilbertwong1996/ado-linux-arm64": "0.1
|
|
38
|
-
"@gilbertwong1996/ado-linux-x64": "0.1
|
|
39
|
-
"@gilbertwong1996/ado-win32-x64": "0.1
|
|
39
|
+
"@gilbertwong1996/ado-darwin-arm64": "0.2.1",
|
|
40
|
+
"@gilbertwong1996/ado-darwin-x64": "0.2.1",
|
|
41
|
+
"@gilbertwong1996/ado-linux-arm64": "0.2.1",
|
|
42
|
+
"@gilbertwong1996/ado-linux-x64": "0.2.1",
|
|
43
|
+
"@gilbertwong1996/ado-win32-x64": "0.2.1"
|
|
40
44
|
}
|
|
41
45
|
}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ado npm postinstall — auto-installs shell completion.
|
|
3
|
+
//
|
|
4
|
+
// Runs after `npm install -g @gilbertwong1996/ado`. Detects the
|
|
5
|
+
// user's shell, generates the right completion script, and
|
|
6
|
+
// installs it to the standard auto-load location for that shell
|
|
7
|
+
// (e.g. ~/.config/fish/completions/ado.fish, ~/.zsh/completions/_ado,
|
|
8
|
+
// ~/.local/share/bash-completion/completions/ado). For shells that
|
|
9
|
+
// need an explicit fpath/source line in the user's shell config
|
|
10
|
+
// (zsh, powershell), appends the line to the config file.
|
|
11
|
+
//
|
|
12
|
+
// Honors the ADO_NO_COMPLETION env var to opt out:
|
|
13
|
+
// ADO_NO_COMPLETION=1 npm install -g @gilbertwong1996/ado
|
|
14
|
+
//
|
|
15
|
+
// Idempotent: re-running just refreshes the completion script.
|
|
16
|
+
// Skips re-appending the config line if it's already there.
|
|
17
|
+
//
|
|
18
|
+
// Note: stdout from the postinstall is shown by npm to the user
|
|
19
|
+
// (unless --silent). We use stdout for the success messages and
|
|
20
|
+
// stderr for warnings/errors, so the user always sees what was done.
|
|
21
|
+
|
|
22
|
+
'use strict';
|
|
23
|
+
|
|
24
|
+
const { execFileSync } = require('child_process');
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
const os = require('os');
|
|
27
|
+
const path = require('path');
|
|
28
|
+
|
|
29
|
+
// ── Configuration ────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
// Standard auto-load locations for completion scripts per shell.
|
|
32
|
+
// All paths are XDG-friendly and don't require sudo.
|
|
33
|
+
const HOME = os.homedir();
|
|
34
|
+
const PLATFORM = process.platform;
|
|
35
|
+
|
|
36
|
+
const SHELL_CONFIG = {
|
|
37
|
+
bash: {
|
|
38
|
+
installPath: path.join(
|
|
39
|
+
HOME,
|
|
40
|
+
'.local',
|
|
41
|
+
'share',
|
|
42
|
+
'bash-completion',
|
|
43
|
+
'completions',
|
|
44
|
+
'ado'
|
|
45
|
+
),
|
|
46
|
+
needsConfigEdit: false,
|
|
47
|
+
// bash-completion's standard location. The bash-completion
|
|
48
|
+
// package (https://github.com/scop/bash-completion) auto-loads
|
|
49
|
+
// any file in this directory. Most package managers install
|
|
50
|
+
// bash-completion by default. If it's not installed, the file
|
|
51
|
+
// just sits there harmlessly.
|
|
52
|
+
autoLoadNote:
|
|
53
|
+
'Works automatically with the bash-completion package. ' +
|
|
54
|
+
'If <TAB> does nothing, install it via your package manager ' +
|
|
55
|
+
'(e.g. `brew install bash-completion`, `apt install bash-completion`).'
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
zsh: {
|
|
59
|
+
installPath: path.join(HOME, '.zsh', 'completions', '_ado'),
|
|
60
|
+
needsConfigEdit: true,
|
|
61
|
+
configPath: path.join(HOME, '.zshrc'),
|
|
62
|
+
configMarker: '# ado shell completion (added by npm postinstall)',
|
|
63
|
+
configLines: [
|
|
64
|
+
'# ado shell completion (added by npm postinstall)',
|
|
65
|
+
'fpath=($HOME/.zsh/completions $fpath)',
|
|
66
|
+
'autoload -U compinit && compinit'
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
fish: {
|
|
71
|
+
installPath: path.join(
|
|
72
|
+
HOME,
|
|
73
|
+
'.config',
|
|
74
|
+
'fish',
|
|
75
|
+
'completions',
|
|
76
|
+
'ado.fish'
|
|
77
|
+
),
|
|
78
|
+
needsConfigEdit: false,
|
|
79
|
+
// Fish auto-loads every file in this directory. Zero config.
|
|
80
|
+
autoLoadNote: 'Works automatically. Restart your fish shell.'
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
powershell: {
|
|
84
|
+
installPath:
|
|
85
|
+
PLATFORM === 'win32'
|
|
86
|
+
? path.join(HOME, 'Documents', 'PowerShell', 'ado-completion.ps1')
|
|
87
|
+
: path.join(HOME, '.config', 'powershell', 'ado-completion.ps1'),
|
|
88
|
+
needsConfigEdit: true,
|
|
89
|
+
configPath:
|
|
90
|
+
PLATFORM === 'win32'
|
|
91
|
+
? path.join(
|
|
92
|
+
HOME,
|
|
93
|
+
'Documents',
|
|
94
|
+
'PowerShell',
|
|
95
|
+
'Microsoft.PowerShell_profile.ps1'
|
|
96
|
+
)
|
|
97
|
+
: path.join(
|
|
98
|
+
HOME,
|
|
99
|
+
'.config',
|
|
100
|
+
'powershell',
|
|
101
|
+
'Microsoft.PowerShell_profile.ps1'
|
|
102
|
+
),
|
|
103
|
+
configMarker: '# ado shell completion (added by npm postinstall)',
|
|
104
|
+
configLines: []
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// ── Shell detection ───────────────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
function detectShell() {
|
|
111
|
+
// On Windows, default to PowerShell.
|
|
112
|
+
if (PLATFORM === 'win32') return 'powershell';
|
|
113
|
+
|
|
114
|
+
// On Unix, use $SHELL.
|
|
115
|
+
const sh = (process.env.SHELL || '').toLowerCase();
|
|
116
|
+
if (sh.endsWith('/bash') || sh.endsWith('/sh')) return 'bash';
|
|
117
|
+
if (sh.endsWith('/zsh')) return 'zsh';
|
|
118
|
+
if (sh.endsWith('/fish')) return 'fish';
|
|
119
|
+
if (sh.includes('pwsh') || sh.includes('powershell')) return 'powershell';
|
|
120
|
+
|
|
121
|
+
// Default to bash if undetectable.
|
|
122
|
+
return 'bash';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ── Completion script generation ─────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
function findAdoWrapper() {
|
|
128
|
+
// The Node.js wrapper that spawns the platform-specific binary.
|
|
129
|
+
// We try multiple paths because the postinstall runs in
|
|
130
|
+
// different cwd contexts:
|
|
131
|
+
// 1. In an installed npm package:
|
|
132
|
+
// node_modules/@gilbertwong1996/ado/scripts/postinstall.js
|
|
133
|
+
// -> bin/ado is at ../bin/ado
|
|
134
|
+
// 2. In the project source tree (for local testing):
|
|
135
|
+
// scripts/postinstall.js
|
|
136
|
+
// -> bin/ado is at npm/@gilbertwong1996-ado/bin/ado
|
|
137
|
+
//
|
|
138
|
+
// 3. For end-to-end testing, callers can set ADO_BIN env var
|
|
139
|
+
// to point at a real binary (e.g. the dev escript at ./ado)
|
|
140
|
+
// to bypass the wrapper entirely.
|
|
141
|
+
|
|
142
|
+
if (process.env.ADO_BIN && fs.existsSync(process.env.ADO_BIN)) {
|
|
143
|
+
return process.env.ADO_BIN;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Try the npm-resolvable path first (works when installed)
|
|
147
|
+
try {
|
|
148
|
+
return require.resolve('@gilbertwong1996/ado/bin/ado');
|
|
149
|
+
} catch {
|
|
150
|
+
// Fall through to local source paths
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Try the local source path (for testing)
|
|
154
|
+
const localPath = path.join(
|
|
155
|
+
__dirname,
|
|
156
|
+
'..',
|
|
157
|
+
'npm',
|
|
158
|
+
'@gilbertwong1996-ado',
|
|
159
|
+
'bin',
|
|
160
|
+
'ado'
|
|
161
|
+
);
|
|
162
|
+
if (fs.existsSync(localPath)) return localPath;
|
|
163
|
+
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function generateCompletion(shell) {
|
|
168
|
+
const wrapper = findAdoWrapper();
|
|
169
|
+
if (!wrapper) {
|
|
170
|
+
return {
|
|
171
|
+
ok: false,
|
|
172
|
+
error:
|
|
173
|
+
'Could not find the ado binary. This usually means a ' +
|
|
174
|
+
'broken npm install. Try: npm install -g @gilbertwong1996/ado --force'
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const stdout = execFileSync(wrapper, ['completion', shell], {
|
|
180
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
181
|
+
encoding: 'utf8'
|
|
182
|
+
});
|
|
183
|
+
return { ok: true, script: stdout };
|
|
184
|
+
} catch (err) {
|
|
185
|
+
return {
|
|
186
|
+
ok: false,
|
|
187
|
+
error: err.stderr || err.message,
|
|
188
|
+
status: err.status
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ── Config file editing ──────────────────────────────────────────────
|
|
194
|
+
|
|
195
|
+
function ensureConfigLine(shell, script, cfg) {
|
|
196
|
+
if (!cfg.needsConfigEdit) {
|
|
197
|
+
return { skipped: true, reason: 'auto-load' };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const configPath = cfg.configPath;
|
|
201
|
+
|
|
202
|
+
if (shell === 'powershell') {
|
|
203
|
+
// For PowerShell, write a separate .ps1 file and source it
|
|
204
|
+
// from $PROFILE. This way updating ado (which rewrites the
|
|
205
|
+
// .ps1) doesn't touch the user's $PROFILE.
|
|
206
|
+
const ps1Path = cfg.installPath;
|
|
207
|
+
fs.mkdirSync(path.dirname(ps1Path), { recursive: true });
|
|
208
|
+
// The .ps1 content IS the completion script (Register-ArgumentCompleter
|
|
209
|
+
// call). The $PROFILE just . -sources it.
|
|
210
|
+
fs.writeFileSync(
|
|
211
|
+
ps1Path,
|
|
212
|
+
"# ado shell completion (auto-generated, do not edit)\n" +
|
|
213
|
+
"# Sourced by $PROFILE on shell startup.\n" +
|
|
214
|
+
"# Re-generated by `npm install -g @gilbertwong1996/ado`.\n" +
|
|
215
|
+
"\n" +
|
|
216
|
+
script
|
|
217
|
+
);
|
|
218
|
+
return appendLine(configPath, `. '${ps1Path.replace(/'/g, "''")}'`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (shell === 'zsh') {
|
|
222
|
+
// For zsh, append the fpath + compinit lines to .zshrc.
|
|
223
|
+
return appendLines(configPath, cfg.configLines);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return { skipped: true, reason: 'unknown shell' };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function appendLine(path, line) {
|
|
230
|
+
return appendLines(path, [line]);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function appendLines(filePath, lines) {
|
|
234
|
+
const marker = lines[0]; // First line is the marker
|
|
235
|
+
const content = fs.existsSync(filePath)
|
|
236
|
+
? fs.readFileSync(filePath, 'utf8')
|
|
237
|
+
: '';
|
|
238
|
+
|
|
239
|
+
if (content.includes(marker)) {
|
|
240
|
+
return { skipped: true, reason: 'already configured' };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
244
|
+
fs.appendFileSync(
|
|
245
|
+
filePath,
|
|
246
|
+
(content.endsWith('\n') || content === '' ? '' : '\n') +
|
|
247
|
+
'\n' +
|
|
248
|
+
lines.join('\n') +
|
|
249
|
+
'\n'
|
|
250
|
+
);
|
|
251
|
+
return { added: true, path: filePath };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ── Main ─────────────────────────────────────────────────────────────
|
|
255
|
+
|
|
256
|
+
function main() {
|
|
257
|
+
// Opt-out
|
|
258
|
+
if (process.env.ADO_NO_COMPLETION === '1') {
|
|
259
|
+
console.log(
|
|
260
|
+
'ado: ADO_NO_COMPLETION=1 set, skipping shell completion install.'
|
|
261
|
+
);
|
|
262
|
+
console.log(' To install later, run: ado completion <shell>');
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const shell = detectShell();
|
|
267
|
+
const cfg = SHELL_CONFIG[shell];
|
|
268
|
+
|
|
269
|
+
if (!cfg) {
|
|
270
|
+
console.error(`ado: unsupported shell '${shell}', skipping completion install.`);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
console.log(`ado: detected shell '${shell}'`);
|
|
275
|
+
|
|
276
|
+
// Generate the completion script
|
|
277
|
+
const result = generateCompletion(shell);
|
|
278
|
+
if (!result.ok) {
|
|
279
|
+
console.error(
|
|
280
|
+
`ado: failed to generate completion script: ${result.error}`
|
|
281
|
+
);
|
|
282
|
+
console.error(
|
|
283
|
+
' You can install manually later with: ado completion ' + shell
|
|
284
|
+
);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Write the script to the install path
|
|
289
|
+
try {
|
|
290
|
+
fs.mkdirSync(path.dirname(cfg.installPath), { recursive: true });
|
|
291
|
+
fs.writeFileSync(cfg.installPath, result.script, 'utf8');
|
|
292
|
+
console.log(`ado: wrote completion script to ${cfg.installPath}`);
|
|
293
|
+
} catch (err) {
|
|
294
|
+
console.error(
|
|
295
|
+
`ado: failed to write completion script to ${cfg.installPath}: ${err.message}`
|
|
296
|
+
);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Edit the user's shell config if needed. PowerShell needs
|
|
301
|
+
// the generated script content too (to write it to a separate
|
|
302
|
+
// .ps1 file that gets sourced from $PROFILE), so we pass the
|
|
303
|
+
// script along.
|
|
304
|
+
if (cfg.needsConfigEdit) {
|
|
305
|
+
const configResult = ensureConfigLine(shell, result.script, cfg);
|
|
306
|
+
if (configResult.added) {
|
|
307
|
+
console.log(
|
|
308
|
+
`ado: added completion loader line to ${configResult.path}`
|
|
309
|
+
);
|
|
310
|
+
} else if (configResult.skipped) {
|
|
311
|
+
console.log(
|
|
312
|
+
`ado: shell config already has completion loader (${configResult.reason})`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Final hint
|
|
318
|
+
console.log('');
|
|
319
|
+
console.log('ado: shell completion installed!');
|
|
320
|
+
if (cfg.autoLoadNote) {
|
|
321
|
+
console.log(' ' + cfg.autoLoadNote);
|
|
322
|
+
} else {
|
|
323
|
+
console.log(' Restart your shell, or: source ' + cfg.configPath);
|
|
324
|
+
}
|
|
325
|
+
console.log(' Then press <TAB> after typing `ado ` to see it in action.');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
main();
|
|
330
|
+
} catch (err) {
|
|
331
|
+
console.error(`ado: postinstall failed: ${err.message}`);
|
|
332
|
+
// Don't fail the install just because completion setup failed.
|
|
333
|
+
// The binary still works; users can run `ado completion <shell>`
|
|
334
|
+
// manually if they want.
|
|
335
|
+
process.exit(0);
|
|
336
|
+
}
|