@jacobsandersen/glab-axi 0.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/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/bin/glab-axi.d.ts +2 -0
- package/dist/bin/glab-axi.js +4 -0
- package/dist/bin/glab-axi.js.map +1 -0
- package/dist/src/args.d.ts +8 -0
- package/dist/src/args.js +84 -0
- package/dist/src/args.js.map +1 -0
- package/dist/src/body.d.ts +15 -0
- package/dist/src/body.js +132 -0
- package/dist/src/body.js.map +1 -0
- package/dist/src/cli.d.ts +9 -0
- package/dist/src/cli.js +124 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/commands/api.d.ts +3 -0
- package/dist/src/commands/api.js +155 -0
- package/dist/src/commands/api.js.map +1 -0
- package/dist/src/commands/home.d.ts +3 -0
- package/dist/src/commands/home.js +57 -0
- package/dist/src/commands/home.js.map +1 -0
- package/dist/src/commands/issue.d.ts +3 -0
- package/dist/src/commands/issue.js +364 -0
- package/dist/src/commands/issue.js.map +1 -0
- package/dist/src/commands/job.d.ts +3 -0
- package/dist/src/commands/job.js +119 -0
- package/dist/src/commands/job.js.map +1 -0
- package/dist/src/commands/label.d.ts +3 -0
- package/dist/src/commands/label.js +117 -0
- package/dist/src/commands/label.js.map +1 -0
- package/dist/src/commands/mr.d.ts +3 -0
- package/dist/src/commands/mr.js +365 -0
- package/dist/src/commands/mr.js.map +1 -0
- package/dist/src/commands/pipeline.d.ts +3 -0
- package/dist/src/commands/pipeline.js +168 -0
- package/dist/src/commands/pipeline.js.map +1 -0
- package/dist/src/commands/project.d.ts +3 -0
- package/dist/src/commands/project.js +138 -0
- package/dist/src/commands/project.js.map +1 -0
- package/dist/src/commands/release.d.ts +3 -0
- package/dist/src/commands/release.js +179 -0
- package/dist/src/commands/release.js.map +1 -0
- package/dist/src/commands/search.d.ts +3 -0
- package/dist/src/commands/search.js +266 -0
- package/dist/src/commands/search.js.map +1 -0
- package/dist/src/commands/setup.d.ts +2 -0
- package/dist/src/commands/setup.js +23 -0
- package/dist/src/commands/setup.js.map +1 -0
- package/dist/src/commands/variable.d.ts +3 -0
- package/dist/src/commands/variable.js +92 -0
- package/dist/src/commands/variable.js.map +1 -0
- package/dist/src/context.d.ts +7 -0
- package/dist/src/context.js +42 -0
- package/dist/src/context.js.map +1 -0
- package/dist/src/errors.d.ts +5 -0
- package/dist/src/errors.js +81 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/fields.d.ts +10 -0
- package/dist/src/fields.js +26 -0
- package/dist/src/fields.js.map +1 -0
- package/dist/src/format.d.ts +8 -0
- package/dist/src/format.js +17 -0
- package/dist/src/format.js.map +1 -0
- package/dist/src/glab.d.ts +10 -0
- package/dist/src/glab.js +73 -0
- package/dist/src/glab.js.map +1 -0
- package/dist/src/secretValue.d.ts +1 -0
- package/dist/src/secretValue.js +33 -0
- package/dist/src/secretValue.js.map +1 -0
- package/dist/src/skill.d.ts +6 -0
- package/dist/src/skill.js +79 -0
- package/dist/src/skill.js.map +1 -0
- package/dist/src/stdin.d.ts +2 -0
- package/dist/src/stdin.js +15 -0
- package/dist/src/stdin.js.map +1 -0
- package/dist/src/suggestions.d.ts +11 -0
- package/dist/src/suggestions.js +262 -0
- package/dist/src/suggestions.js.map +1 -0
- package/dist/src/toon.d.ts +57 -0
- package/dist/src/toon.js +150 -0
- package/dist/src/toon.js.map +1 -0
- package/package.json +59 -0
- package/skills/glab-axi/SKILL.md +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jacob Andersen
|
|
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
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# glab-axi
|
|
2
|
+
|
|
3
|
+
GitLab CLI for agents — designed with [AXI](https://github.com/kunchenguid/axi) (Agent eXperience Interface).
|
|
4
|
+
|
|
5
|
+
Wraps the official [`glab`](https://gitlab.com/gitlab-org/cli) CLI with token-efficient [TOON](https://toonformat.dev) output, contextual next-step suggestions, and structured error handling. Built for autonomous agents that interact with GitLab via shell execution.
|
|
6
|
+
|
|
7
|
+
If you're an AI coding agent (Claude Code, Codex, OpenCode, etc.) working with GitLab, glab-axi gives you a clean, structured interface to issues, merge requests, pipelines, jobs, and more — no JSON parsing, no interactive prompts, just token-efficient output with actionable next-step hints.
|
|
8
|
+
|
|
9
|
+
## Hit the ground running
|
|
10
|
+
|
|
11
|
+
**Prerequisites:**
|
|
12
|
+
|
|
13
|
+
1. [Node.js](https://nodejs.org/) 20+
|
|
14
|
+
2. [`glab`](https://gitlab.com/gitlab-org/cli) CLI installed and authenticated:
|
|
15
|
+
```sh
|
|
16
|
+
glab auth login
|
|
17
|
+
```
|
|
18
|
+
3. Be inside a Git repository with a GitLab remote (so glab-axi can detect the project)
|
|
19
|
+
|
|
20
|
+
**Try it:**
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
npx -y @jacobsandersen/glab-axi # dashboard — open issues, MRs, next steps
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
That's it. No install needed — npx fetches it on demand.
|
|
27
|
+
|
|
28
|
+
**Common first commands:**
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npx -y @jacobsandersen/glab-axi issue list # list issues in current project
|
|
32
|
+
npx -y @jacobsandersen/glab-axi mr view 42 # view merge request #42
|
|
33
|
+
npx -y @jacobsandersen/glab-axi pipeline list # list CI/CD pipelines
|
|
34
|
+
npx -y @jacobsandersen/glab-axi job log 12345 # view job logs
|
|
35
|
+
npx -y @jacobsandersen/glab-axi search issues "bug" # search issues
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Every response ends with a `help:` block suggesting what to do next — follow those hints.
|
|
39
|
+
|
|
40
|
+
**Target a different project:**
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
npx -y @jacobsandersen/glab-axi issue list --repo owner/name
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Session hooks (ambient context)
|
|
47
|
+
|
|
48
|
+
By default, you invoke glab-axi on demand. But if you want your agent to **automatically see GitLab state** (open issues, merge requests) every time a session starts — without being asked — install the session hooks:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
npm install -g @jacobsandersen/glab-axi
|
|
52
|
+
glab-axi setup hooks
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This installs `SessionStart` hooks into your agent (Claude Code, Codex, or OpenCode). On each new session, glab-axi runs automatically and injects a summary of your project's open issues and MRs into the agent's context. Restart your agent session after installing.
|
|
56
|
+
|
|
57
|
+
This is optional but recommended — it means your agent already knows about open issues and in-flight MRs before you ask anything.
|
|
58
|
+
|
|
59
|
+
## Built with
|
|
60
|
+
|
|
61
|
+
- [`axi-sdk-js`](https://github.com/kunchenguid/axi) — shared AXI runtime (command dispatch, structured errors, hook installation, self-update)
|
|
62
|
+
- [`@toon-format/toon`](https://toonformat.dev) — Token-Oriented Object Notation for ~40% fewer tokens than JSON
|
|
63
|
+
- [`glab`](https://gitlab.com/gitlab-org/cli) — the official GitLab CLI, used under the hood
|
|
64
|
+
|
|
65
|
+
## How it works
|
|
66
|
+
|
|
67
|
+
glab-axi spawns `glab` under the hood, parses its JSON output, and re-encodes it as [TOON](https://toonformat.dev) — a compact format that uses ~40% fewer tokens than JSON while remaining human-readable. Every response includes structured error handling and a `help:` block with contextual next-step suggestions, so agents can chain operations without guessing.
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Agent ──▶ glab-axi ──▶ glab ──▶ GitLab API
|
|
71
|
+
│
|
|
72
|
+
├── TOON-encoded output (token-efficient)
|
|
73
|
+
├── Structured errors (no interactive prompts)
|
|
74
|
+
└── Contextual next-step hints
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Tips
|
|
78
|
+
|
|
79
|
+
- **Output is TOON-encoded** — compact and token-efficient. Pipe through `grep`/`head` only when a list is very long.
|
|
80
|
+
- **Mutations are idempotent** — re-running a failed `issue create` or `mr create` is safe; it reports what changed.
|
|
81
|
+
- **Multi-line bodies** — write the text to a UTF-8 file and pass `--body-file <path>` instead of escaping shell quotes.
|
|
82
|
+
- **Raw API** — use `api` for anything the dedicated commands don't cover, e.g. `npx -y @jacobsandersen/glab-axi api projects/{id}/variables`.
|
|
83
|
+
|
|
84
|
+
## Commands
|
|
85
|
+
|
|
86
|
+
| Command | Description |
|
|
87
|
+
| -------- | ---------------------------------------------------------------------------------------------- |
|
|
88
|
+
| issue | Issues — list, view, create, edit, close, reopen, note, subscribe, unsubscribe |
|
|
89
|
+
| mr | Merge requests — list, view, create, edit, close, reopen, merge, approve, note, diff, checkout |
|
|
90
|
+
| pipeline | Pipelines — list, view, retry, cancel, delete |
|
|
91
|
+
| job | Jobs — list, view, log, retry |
|
|
92
|
+
| release | Releases — list, view, create, delete, upload |
|
|
93
|
+
| project | Projects — view, list, create, fork |
|
|
94
|
+
| label | Labels — list, create, edit, delete |
|
|
95
|
+
| variable | CI/CD variables — list, set, delete |
|
|
96
|
+
| search | Search issues, MRs, projects, blobs |
|
|
97
|
+
| api | Raw GitLab API access |
|
|
98
|
+
| setup | Install optional agent session hooks |
|
|
99
|
+
|
|
100
|
+
## Global flags
|
|
101
|
+
|
|
102
|
+
- `--help` — show help for any command
|
|
103
|
+
- `-v`, `-V`, `--version` — show the installed glab-axi version
|
|
104
|
+
- `-R owner/name`, `--repo owner/name` — target a specific project
|
|
105
|
+
|
|
106
|
+
## Related tools
|
|
107
|
+
|
|
108
|
+
| Tool | Description |
|
|
109
|
+
| --------------------------------------------------------- | ----------------------------------------------------------- |
|
|
110
|
+
| [axi](https://github.com/kunchenguid/axi) | The AXI monorepo — SDK, spec, and reference implementations |
|
|
111
|
+
| [gh-axi](https://github.com/kunchenguid/gh-axi) | GitHub equivalent of glab-axi, same AXI pattern |
|
|
112
|
+
| [toon](https://toonformat.dev) | Token-Oriented Object Notation spec and libraries |
|
|
113
|
+
| [glab](https://gitlab.com/gitlab-org/cli) | Official GitLab CLI that glab-axi wraps |
|
|
114
|
+
| [no-mistakes](https://github.com/kunchenguid/no-mistakes) | Validation gate for agent code changes |
|
|
115
|
+
|
|
116
|
+
## Development
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
npm run build # Compile TypeScript to dist/
|
|
120
|
+
npm run build:skill # Regenerate skills/glab-axi/SKILL.md
|
|
121
|
+
npm run dev # Run CLI directly with tsx
|
|
122
|
+
npm test # Run tests with vitest
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Release
|
|
126
|
+
|
|
127
|
+
Publishing to npm is automated via GitHub Actions. Creating a GitHub Release (or triggering the workflow manually) runs CI, builds, tests, and publishes to npm.
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
npm version patch # or minor / major — bumps version and creates a git tag
|
|
131
|
+
git push --follow-tags
|
|
132
|
+
# then create a GitHub Release for the new tag
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The `NPM_TOKEN` secret must be configured in the repository's GitHub Actions secrets.
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glab-axi.js","sourceRoot":"","sources":["../../bin/glab-axi.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAErC,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function getFlag(args: string[], name: string): string | undefined;
|
|
2
|
+
export declare function takeFlag(args: string[], flag: string): string | undefined;
|
|
3
|
+
export declare function hasFlag(args: string[], flag: string): boolean;
|
|
4
|
+
export declare function takeBoolFlag(args: string[], flag: string): boolean;
|
|
5
|
+
export declare function getAllFlags(args: string[], flag: string): string[];
|
|
6
|
+
export declare function getPositional(args: string[], startIndex: number): string | undefined;
|
|
7
|
+
export declare function requireNumber(raw: string | undefined, label: string): number;
|
|
8
|
+
export declare function takeNumber(args: string[], label: string): number;
|
package/dist/src/args.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { AxiError } from "./errors.js";
|
|
2
|
+
function flagEqualsPrefix(flag) {
|
|
3
|
+
return `${flag}=`;
|
|
4
|
+
}
|
|
5
|
+
export function getFlag(args, name) {
|
|
6
|
+
const equalsPrefix = flagEqualsPrefix(name);
|
|
7
|
+
for (let i = 0; i < args.length; i++) {
|
|
8
|
+
const arg = args[i];
|
|
9
|
+
if (arg === name) {
|
|
10
|
+
if (i + 1 >= args.length)
|
|
11
|
+
return undefined;
|
|
12
|
+
return args[i + 1];
|
|
13
|
+
}
|
|
14
|
+
if (arg.startsWith(equalsPrefix)) {
|
|
15
|
+
return arg.slice(equalsPrefix.length);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
export function takeFlag(args, flag) {
|
|
21
|
+
const equalsPrefix = flagEqualsPrefix(flag);
|
|
22
|
+
for (let i = 0; i < args.length; i++) {
|
|
23
|
+
const arg = args[i];
|
|
24
|
+
if (arg === flag) {
|
|
25
|
+
const val = args[i + 1];
|
|
26
|
+
args.splice(i, 2);
|
|
27
|
+
return val;
|
|
28
|
+
}
|
|
29
|
+
if (arg.startsWith(equalsPrefix)) {
|
|
30
|
+
const val = arg.slice(equalsPrefix.length);
|
|
31
|
+
args.splice(i, 1);
|
|
32
|
+
return val;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
export function hasFlag(args, flag) {
|
|
38
|
+
return args.includes(flag);
|
|
39
|
+
}
|
|
40
|
+
export function takeBoolFlag(args, flag) {
|
|
41
|
+
const idx = args.indexOf(flag);
|
|
42
|
+
if (idx === -1)
|
|
43
|
+
return false;
|
|
44
|
+
args.splice(idx, 1);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
export function getAllFlags(args, flag) {
|
|
48
|
+
const result = [];
|
|
49
|
+
const equalsPrefix = flagEqualsPrefix(flag);
|
|
50
|
+
for (let i = 0; i < args.length; i++) {
|
|
51
|
+
const arg = args[i];
|
|
52
|
+
if (arg === flag && i + 1 < args.length) {
|
|
53
|
+
result.push(args[i + 1]);
|
|
54
|
+
i++;
|
|
55
|
+
}
|
|
56
|
+
else if (arg.startsWith(equalsPrefix)) {
|
|
57
|
+
result.push(arg.slice(equalsPrefix.length));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
export function getPositional(args, startIndex) {
|
|
63
|
+
for (let i = startIndex; i < args.length; i++) {
|
|
64
|
+
if (!args[i].startsWith("--"))
|
|
65
|
+
return args[i];
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
export function requireNumber(raw, label) {
|
|
70
|
+
if (!raw)
|
|
71
|
+
throw new AxiError(`Missing ${label} number`, "VALIDATION_ERROR");
|
|
72
|
+
const n = parseInt(raw, 10);
|
|
73
|
+
if (isNaN(n))
|
|
74
|
+
throw new AxiError(`Invalid ${label} number: ${raw}`, "VALIDATION_ERROR");
|
|
75
|
+
return n;
|
|
76
|
+
}
|
|
77
|
+
export function takeNumber(args, label) {
|
|
78
|
+
const raw = args.find((a) => /^\d+$/.test(a));
|
|
79
|
+
if (!raw)
|
|
80
|
+
throw new AxiError(`Missing ${label} number`, "VALIDATION_ERROR");
|
|
81
|
+
args.splice(args.indexOf(raw), 1);
|
|
82
|
+
return Number(raw);
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=args.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/args.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface TakeBodyOptions {
|
|
2
|
+
required?: boolean;
|
|
3
|
+
inlineFlags?: string[];
|
|
4
|
+
fileFlags?: string[];
|
|
5
|
+
valueBoundaryFlags?: string[];
|
|
6
|
+
label?: string;
|
|
7
|
+
suggestions?: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function takeBody(args: string[], options: TakeBodyOptions & {
|
|
10
|
+
required: true;
|
|
11
|
+
}): string;
|
|
12
|
+
export declare function takeBody(args: string[], options?: TakeBodyOptions): string | undefined;
|
|
13
|
+
export declare function cleanBody(text: string): string;
|
|
14
|
+
export declare function truncateBody(body: unknown, maxLen?: number): string;
|
|
15
|
+
export {};
|
package/dist/src/body.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { AxiError } from "./errors.js";
|
|
3
|
+
function defaultSuggestions(label) {
|
|
4
|
+
return [
|
|
5
|
+
`Use --body "..." for inline ${label}, or --body-file <path> for markdown from a file`,
|
|
6
|
+
];
|
|
7
|
+
}
|
|
8
|
+
function isMissingValue(value) {
|
|
9
|
+
return value === undefined || value.trim() === "";
|
|
10
|
+
}
|
|
11
|
+
function isValueBoundary(arg, flags) {
|
|
12
|
+
if (arg === undefined)
|
|
13
|
+
return false;
|
|
14
|
+
return flags.some((flag) => arg === flag || arg.startsWith(`${flag}=`));
|
|
15
|
+
}
|
|
16
|
+
function takeFlagMatches(args, flags, valueBoundaryFlags) {
|
|
17
|
+
const matches = [];
|
|
18
|
+
for (let index = 0; index < args.length; index++) {
|
|
19
|
+
const arg = args[index];
|
|
20
|
+
let matched = false;
|
|
21
|
+
for (const flag of flags) {
|
|
22
|
+
const equalsPrefix = `${flag}=`;
|
|
23
|
+
if (arg === flag) {
|
|
24
|
+
const next = args[index + 1];
|
|
25
|
+
const value = next !== undefined && !isValueBoundary(next, valueBoundaryFlags)
|
|
26
|
+
? next
|
|
27
|
+
: undefined;
|
|
28
|
+
const consumeCount = value === undefined ? 1 : 2;
|
|
29
|
+
args.splice(index, consumeCount);
|
|
30
|
+
index--;
|
|
31
|
+
matches.push({ flag, value });
|
|
32
|
+
matched = true;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
if (arg.startsWith(equalsPrefix)) {
|
|
36
|
+
args.splice(index, 1);
|
|
37
|
+
index--;
|
|
38
|
+
matches.push({ flag, value: arg.slice(equalsPrefix.length) });
|
|
39
|
+
matched = true;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (matched)
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
return matches;
|
|
47
|
+
}
|
|
48
|
+
function readBodyFile(flag, path, suggestions) {
|
|
49
|
+
try {
|
|
50
|
+
return readFileSync(path, "utf8");
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
const code = error && typeof error === "object" && "code" in error
|
|
54
|
+
? String(error.code)
|
|
55
|
+
: "UNKNOWN";
|
|
56
|
+
if (code === "ENOENT") {
|
|
57
|
+
throw new AxiError(`${flag} path not found: ${path}`, "VALIDATION_ERROR", suggestions);
|
|
58
|
+
}
|
|
59
|
+
if (code === "EISDIR") {
|
|
60
|
+
throw new AxiError(`${flag} must point to a readable UTF-8 file, not a directory: ${path}`, "VALIDATION_ERROR", suggestions);
|
|
61
|
+
}
|
|
62
|
+
throw new AxiError(`Could not read ${flag} path: ${path} (${code})`, "VALIDATION_ERROR", suggestions);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function takeBody(args, options = {}) {
|
|
66
|
+
const inlineFlags = options.inlineFlags ?? ["--body"];
|
|
67
|
+
const fileFlags = options.fileFlags ?? ["--body-file"];
|
|
68
|
+
const valueBoundaryFlags = [
|
|
69
|
+
...new Set([
|
|
70
|
+
...inlineFlags,
|
|
71
|
+
...fileFlags,
|
|
72
|
+
...(options.valueBoundaryFlags ?? []),
|
|
73
|
+
]),
|
|
74
|
+
];
|
|
75
|
+
const label = options.label ?? "body";
|
|
76
|
+
const suggestions = options.suggestions ?? defaultSuggestions(label);
|
|
77
|
+
const inlineMatches = takeFlagMatches(args, inlineFlags, valueBoundaryFlags);
|
|
78
|
+
const fileMatches = takeFlagMatches(args, fileFlags, valueBoundaryFlags);
|
|
79
|
+
const matches = [...inlineMatches, ...fileMatches];
|
|
80
|
+
if (matches.length === 0) {
|
|
81
|
+
if (options.required) {
|
|
82
|
+
throw new AxiError(`${inlineFlags[0]} or ${fileFlags[0]} is required`, "VALIDATION_ERROR", suggestions);
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
if (matches.length > 1) {
|
|
87
|
+
throw new AxiError(`Use only one ${label} source: ${matches.map((m) => m.flag).join(", ")} were provided`, "VALIDATION_ERROR", suggestions);
|
|
88
|
+
}
|
|
89
|
+
const match = matches[0];
|
|
90
|
+
const value = match.value;
|
|
91
|
+
if (isMissingValue(value)) {
|
|
92
|
+
const noun = fileFlags.includes(match.flag) ? "path" : "text";
|
|
93
|
+
throw new AxiError(`${match.flag} requires ${noun}`, "VALIDATION_ERROR", suggestions);
|
|
94
|
+
}
|
|
95
|
+
const resolvedValue = value ?? "";
|
|
96
|
+
if (fileFlags.includes(match.flag)) {
|
|
97
|
+
return readBodyFile(match.flag, resolvedValue, suggestions);
|
|
98
|
+
}
|
|
99
|
+
return resolvedValue;
|
|
100
|
+
}
|
|
101
|
+
export function cleanBody(text) {
|
|
102
|
+
let s = text.replace(/\[([^\]]+)\]\(https:\/\/gitlab\.com\/[^/]+\/[^/]+\/-\/merge_requests\/(\d+)\)/g, "[$1](MR#$2)");
|
|
103
|
+
s = s.replace(/\[([^\]]+)\]\(https:\/\/gitlab\.com\/[^/]+\/[^/]+\/-\/issues\/(\d+)\)/g, "[$1](Issue#$2)");
|
|
104
|
+
s = s.replace(/(?<!\()https:\/\/gitlab\.com\/[^/]+\/[^/]+\/-\/merge_requests\/(\d+)/g, "MR#$1");
|
|
105
|
+
s = s.replace(/(?<!\()https:\/\/gitlab\.com\/[^/]+\/[^/]+\/-\/issues\/(\d+)/g, "Issue#$1");
|
|
106
|
+
s = s.replace(/!\[([^\]]*)\]\([^)]+\)/g, (_m, alt) => alt ? `[image: ${alt}]` : "[image]");
|
|
107
|
+
s = s.replace(/\[([^\]]+)\]\(([^)]{80,})\)/g, "[$1]");
|
|
108
|
+
s = s.replace(/(?<!\()https?:\/\/\S{100,}/g, "[long URL removed]");
|
|
109
|
+
s = s.replace(/(^|\n)(>\s?[^\n]*\n?){3,}/gm, "$1[quoted text removed]\n");
|
|
110
|
+
return s;
|
|
111
|
+
}
|
|
112
|
+
export function truncateBody(body, maxLen = 500) {
|
|
113
|
+
if (typeof body !== "string" || !body)
|
|
114
|
+
return "";
|
|
115
|
+
if (body.length <= maxLen)
|
|
116
|
+
return body;
|
|
117
|
+
const cleaned = cleanBody(body);
|
|
118
|
+
if (cleaned.length <= maxLen) {
|
|
119
|
+
if (cleaned !== body) {
|
|
120
|
+
return (cleaned +
|
|
121
|
+
"\n(cleaned, " +
|
|
122
|
+
body.length +
|
|
123
|
+
" chars original — use --full to see original)");
|
|
124
|
+
}
|
|
125
|
+
return cleaned;
|
|
126
|
+
}
|
|
127
|
+
return (cleaned.slice(0, maxLen) +
|
|
128
|
+
"\n... (truncated, " +
|
|
129
|
+
cleaned.length +
|
|
130
|
+
" chars total — use --full to see complete body)");
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=body.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body.js","sourceRoot":"","sources":["../../src/body.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAgBvC,SAAS,kBAAkB,CAAC,KAAa;IACvC,OAAO;QACL,+BAA+B,KAAK,kDAAkD;KACvF,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,KAAyB;IAC/C,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AACpD,CAAC;AAED,SAAS,eAAe,CAAC,GAAuB,EAAE,KAAe;IAC/D,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACpC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;AAC1E,CAAC;AAED,SAAS,eAAe,CACtB,IAAc,EACd,KAAe,EACf,kBAA4B;IAE5B,MAAM,OAAO,GAAoB,EAAE,CAAC;IAEpC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACjD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,YAAY,GAAG,GAAG,IAAI,GAAG,CAAC;YAChC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;gBACjB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;gBAC7B,MAAM,KAAK,GACT,IAAI,KAAK,SAAS,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,kBAAkB,CAAC;oBAC9D,CAAC,CAAC,IAAI;oBACN,CAAC,CAAC,SAAS,CAAC;gBAChB,MAAM,YAAY,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACjD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;gBACjC,KAAK,EAAE,CAAC;gBACR,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC9B,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM;YACR,CAAC;YAED,IAAI,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;gBACjC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;gBACtB,KAAK,EAAE,CAAC;gBACR,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC9D,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM;YACR,CAAC;QACH,CAAC;QAED,IAAI,OAAO;YAAE,SAAS;IACxB,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,YAAY,CACnB,IAAY,EACZ,IAAY,EACZ,WAAqB;IAErB,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,GACR,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK;YACnD,CAAC,CAAC,MAAM,CAAE,KAA+B,CAAC,IAAI,CAAC;YAC/C,CAAC,CAAC,SAAS,CAAC;QAChB,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,MAAM,IAAI,QAAQ,CAChB,GAAG,IAAI,oBAAoB,IAAI,EAAE,EACjC,kBAAkB,EAClB,WAAW,CACZ,CAAC;QACJ,CAAC;QACD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,MAAM,IAAI,QAAQ,CAChB,GAAG,IAAI,0DAA0D,IAAI,EAAE,EACvE,kBAAkB,EAClB,WAAW,CACZ,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,QAAQ,CAChB,kBAAkB,IAAI,UAAU,IAAI,KAAK,IAAI,GAAG,EAChD,kBAAkB,EAClB,WAAW,CACZ,CAAC;IACJ,CAAC;AACH,CAAC;AAUD,MAAM,UAAU,QAAQ,CACtB,IAAc,EACd,UAA2B,EAAE;IAE7B,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,aAAa,CAAC,CAAC;IACvD,MAAM,kBAAkB,GAAG;QACzB,GAAG,IAAI,GAAG,CAAC;YACT,GAAG,WAAW;YACd,GAAG,SAAS;YACZ,GAAG,CAAC,OAAO,CAAC,kBAAkB,IAAI,EAAE,CAAC;SACtC,CAAC;KACH,CAAC;IACF,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC;IACtC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACrE,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,EAAE,WAAW,EAAE,kBAAkB,CAAC,CAAC;IAC7E,MAAM,WAAW,GAAG,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,kBAAkB,CAAC,CAAC;IACzE,MAAM,OAAO,GAAG,CAAC,GAAG,aAAa,EAAE,GAAG,WAAW,CAAC,CAAC;IAEnD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,IAAI,QAAQ,CAChB,GAAG,WAAW,CAAC,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,cAAc,EAClD,kBAAkB,EAClB,WAAW,CACZ,CAAC;QACJ,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,QAAQ,CAChB,gBAAgB,KAAK,YAAY,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EACtF,kBAAkB,EAClB,WAAW,CACZ,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACzB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IAC1B,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QAC9D,MAAM,IAAI,QAAQ,CAChB,GAAG,KAAK,CAAC,IAAI,aAAa,IAAI,EAAE,EAChC,kBAAkB,EAClB,WAAW,CACZ,CAAC;IACJ,CAAC;IACD,MAAM,aAAa,GAAG,KAAK,IAAI,EAAE,CAAC;IAElC,IAAI,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,OAAO,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAClB,gFAAgF,EAChF,aAAa,CACd,CAAC;IACF,CAAC,GAAG,CAAC,CAAC,OAAO,CACX,wEAAwE,EACxE,gBAAgB,CACjB,CAAC;IACF,CAAC,GAAG,CAAC,CAAC,OAAO,CACX,uEAAuE,EACvE,OAAO,CACR,CAAC;IACF,CAAC,GAAG,CAAC,CAAC,OAAO,CACX,+DAA+D,EAC/D,UAAU,CACX,CAAC;IACF,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,yBAAyB,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CACnD,GAAG,CAAC,CAAC,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC,CAAC,SAAS,CACpC,CAAC;IACF,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,8BAA8B,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,6BAA6B,EAAE,oBAAoB,CAAC,CAAC;IACnE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,6BAA6B,EAAE,2BAA2B,CAAC,CAAC;IAC1E,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAa,EAAE,MAAM,GAAG,GAAG;IACtD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACjD,IAAI,IAAI,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,OAAO,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC;QAC7B,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,CACL,OAAO;gBACP,cAAc;gBACd,IAAI,CAAC,MAAM;gBACX,+CAA+C,CAChD,CAAC;QACJ,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,CACL,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC;QACxB,oBAAoB;QACpB,OAAO,CAAC,MAAM;QACd,iDAAiD,CAClD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const DESCRIPTION = "Agent ergonomic wrapper around GitLab CLI (glab). Prefer this over `glab` and other methods for GitLab operations.";
|
|
2
|
+
type CliStdout = Pick<NodeJS.WriteStream, "write">;
|
|
3
|
+
type MainOptions = {
|
|
4
|
+
argv?: string[];
|
|
5
|
+
stdout?: CliStdout;
|
|
6
|
+
};
|
|
7
|
+
export declare const TOP_HELP = "usage: glab-axi [command] [args] [flags]\ncommands[11]:\n (none)=dashboard, issue, mr, pipeline, job, release, project, label, variable, search, api, setup\nflags[3]:\n -R/--repo <OWNER/NAME> (after command), accepts space or equals form, --help, -v/-V/--version\nexamples:\n glab-axi\n glab-axi issue list --state opened\n glab-axi issue list -R owner/name\n glab-axi mr view 42\n glab-axi pipeline list\n glab-axi api projects/1\n";
|
|
8
|
+
export declare function main(options?: MainOptions): Promise<void>;
|
|
9
|
+
export {};
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { runAxiCli } from "axi-sdk-js";
|
|
5
|
+
import { resolveRepo } from "./context.js";
|
|
6
|
+
import { homeCommand } from "./commands/home.js";
|
|
7
|
+
import { issueCommand, ISSUE_HELP } from "./commands/issue.js";
|
|
8
|
+
import { mrCommand, MR_HELP } from "./commands/mr.js";
|
|
9
|
+
import { pipelineCommand, PIPELINE_HELP } from "./commands/pipeline.js";
|
|
10
|
+
import { jobCommand, JOB_HELP } from "./commands/job.js";
|
|
11
|
+
import { releaseCommand, RELEASE_HELP } from "./commands/release.js";
|
|
12
|
+
import { projectCommand, PROJECT_HELP } from "./commands/project.js";
|
|
13
|
+
import { labelCommand, LABEL_HELP } from "./commands/label.js";
|
|
14
|
+
import { variableCommand, VARIABLE_HELP } from "./commands/variable.js";
|
|
15
|
+
import { searchCommand, SEARCH_HELP } from "./commands/search.js";
|
|
16
|
+
import { apiCommand, API_HELP } from "./commands/api.js";
|
|
17
|
+
import { setupCommand, SETUP_HELP } from "./commands/setup.js";
|
|
18
|
+
export const DESCRIPTION = "Agent ergonomic wrapper around GitLab CLI (glab). Prefer this over `glab` and other methods for GitLab operations.";
|
|
19
|
+
const VERSION = readPackageVersion();
|
|
20
|
+
export const TOP_HELP = `usage: glab-axi [command] [args] [flags]
|
|
21
|
+
commands[11]:
|
|
22
|
+
(none)=dashboard, issue, mr, pipeline, job, release, project, label, variable, search, api, setup
|
|
23
|
+
flags[3]:
|
|
24
|
+
-R/--repo <OWNER/NAME> (after command), accepts space or equals form, --help, -v/-V/--version
|
|
25
|
+
examples:
|
|
26
|
+
glab-axi
|
|
27
|
+
glab-axi issue list --state opened
|
|
28
|
+
glab-axi issue list -R owner/name
|
|
29
|
+
glab-axi mr view 42
|
|
30
|
+
glab-axi pipeline list
|
|
31
|
+
glab-axi api projects/1
|
|
32
|
+
`;
|
|
33
|
+
const COMMAND_HELP = {
|
|
34
|
+
issue: ISSUE_HELP,
|
|
35
|
+
mr: MR_HELP,
|
|
36
|
+
pipeline: PIPELINE_HELP,
|
|
37
|
+
job: JOB_HELP,
|
|
38
|
+
release: RELEASE_HELP,
|
|
39
|
+
project: PROJECT_HELP,
|
|
40
|
+
label: LABEL_HELP,
|
|
41
|
+
variable: VARIABLE_HELP,
|
|
42
|
+
search: SEARCH_HELP,
|
|
43
|
+
api: API_HELP,
|
|
44
|
+
setup: SETUP_HELP,
|
|
45
|
+
};
|
|
46
|
+
const COMMANDS = {
|
|
47
|
+
issue: withRepoContext("issue", issueCommand),
|
|
48
|
+
mr: withRepoContext("mr", mrCommand),
|
|
49
|
+
pipeline: withRepoContext("pipeline", pipelineCommand),
|
|
50
|
+
job: withRepoContext("job", jobCommand),
|
|
51
|
+
release: withRepoContext("release", releaseCommand),
|
|
52
|
+
project: withRepoContext("project", projectCommand),
|
|
53
|
+
label: withRepoContext("label", labelCommand),
|
|
54
|
+
variable: withRepoContext("variable", variableCommand),
|
|
55
|
+
search: withRepoContext("search", searchCommand),
|
|
56
|
+
api: withRepoContext("api", apiCommand),
|
|
57
|
+
setup: setupCommand,
|
|
58
|
+
};
|
|
59
|
+
export async function main(options = {}) {
|
|
60
|
+
await runAxiCli({
|
|
61
|
+
...(options.argv ? { argv: options.argv } : {}),
|
|
62
|
+
description: DESCRIPTION,
|
|
63
|
+
version: VERSION,
|
|
64
|
+
topLevelHelp: TOP_HELP,
|
|
65
|
+
...(options.stdout ? { stdout: options.stdout } : {}),
|
|
66
|
+
home: withRepoContext(undefined, homeCommand),
|
|
67
|
+
commands: COMMANDS,
|
|
68
|
+
getCommandHelp: (command) => COMMAND_HELP[command],
|
|
69
|
+
resolveContext: ({ command, args }) => resolveRepo(parseRepoContextArgs(command, args).repoFlag),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function readPackageVersion() {
|
|
73
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
74
|
+
for (const candidate of [
|
|
75
|
+
join(here, "..", "package.json"),
|
|
76
|
+
join(here, "..", "..", "package.json"),
|
|
77
|
+
]) {
|
|
78
|
+
if (!existsSync(candidate))
|
|
79
|
+
continue;
|
|
80
|
+
const parsed = JSON.parse(readFileSync(candidate, "utf-8"));
|
|
81
|
+
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
82
|
+
return parsed.version;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
throw new Error("Could not determine glab-axi package version");
|
|
86
|
+
}
|
|
87
|
+
function withRepoContext(command, handler) {
|
|
88
|
+
return (args, ctx) => handler(parseRepoContextArgs(command, args).strippedArgs, ctx);
|
|
89
|
+
}
|
|
90
|
+
function parseRepoContextArgs(command, args) {
|
|
91
|
+
const stripped = [];
|
|
92
|
+
let repoFlag;
|
|
93
|
+
for (let index = 0; index < args.length; index++) {
|
|
94
|
+
const arg = args[index];
|
|
95
|
+
if (arg === "-R" && index + 1 < args.length) {
|
|
96
|
+
repoFlag = args[index + 1];
|
|
97
|
+
index++;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (arg.startsWith("-R=") && arg.length > 3) {
|
|
101
|
+
repoFlag = arg.slice(3);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (arg === "--repo" && index + 1 < args.length) {
|
|
105
|
+
const value = args[index + 1];
|
|
106
|
+
repoFlag = value;
|
|
107
|
+
if (command === "search") {
|
|
108
|
+
stripped.push(arg, value);
|
|
109
|
+
}
|
|
110
|
+
index++;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (arg.startsWith("--repo=") && arg.length > "--repo=".length) {
|
|
114
|
+
repoFlag = arg.slice("--repo=".length);
|
|
115
|
+
if (command === "search") {
|
|
116
|
+
stripped.push(arg);
|
|
117
|
+
}
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
stripped.push(arg);
|
|
121
|
+
}
|
|
122
|
+
return { repoFlag, strippedArgs: stripped };
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { RepoContext } from "../context.js";
|
|
2
|
+
export declare const API_HELP = "usage: glab-axi api [<method>] <path>\ndescription: Make an authenticated GitLab API request. Defaults to GET if no method specified.\nmethods[6]:\n GET, POST, PUT, PATCH, DELETE\nflags[2]:\n --field <key=value> (repeatable), --header <key:value> (repeatable)\nexamples:\n glab-axi api /projects/{owner}%2F{repo}\n glab-axi api POST /projects/{id}/issues --field title=\"Bug report\"\n glab-axi api GET /projects/{id}/merge_requests";
|
|
3
|
+
export declare function apiCommand(args: string[], ctx?: RepoContext): Promise<string>;
|