gitdog 0.0.0-stage → 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/dist/ui.js ADDED
@@ -0,0 +1,155 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { stripVTControlCharacters } from "node:util";
3
+ export const VERSION = "0.1.0";
4
+ export const DOG = String.raw `
5
+ / \__
6
+ ( @\___
7
+ / O
8
+ / (_____/
9
+ /_____/ U
10
+ `;
11
+ export function redactSecrets(value) {
12
+ return value
13
+ .replace(/\beyJ[\w-]+\.[\w-]+\.[\w-]+\b/g, "[redacted token]")
14
+ .replace(/\bgdk_[a-f0-9]{64}\b/g, "[redacted API key]");
15
+ }
16
+ export function clean(value) {
17
+ return redactSecrets(stripVTControlCharacters(value).replace(/[\x00-\x08\x0b-\x1f\x7f]/g, ""));
18
+ }
19
+ export class Ui {
20
+ json;
21
+ constructor(json = false) {
22
+ this.json = json;
23
+ }
24
+ color(text, code = 36) {
25
+ return process.stdout.isTTY &&
26
+ !process.env.NO_COLOR &&
27
+ process.env.TERM !== "dumb"
28
+ ? `\x1b[${code}m${text}\x1b[0m`
29
+ : text;
30
+ }
31
+ banner() {
32
+ if (this.json || process.env.GITDOG_NO_BANNER === "1")
33
+ return;
34
+ process.stdout.write(this.color(DOG) + this.color(` G I T D O G / ${VERSION}\n`, 1));
35
+ process.stdout.write(" Your repositories. A very good assistant.\n\n");
36
+ }
37
+ info(message) {
38
+ if (!this.json)
39
+ process.stderr.write(`${clean(message)}\n`);
40
+ }
41
+ output(value) {
42
+ if (this.json) {
43
+ process.stdout.write(`${redactSecrets(JSON.stringify(value ?? null, null, 2))}\n`);
44
+ return;
45
+ }
46
+ if (typeof value === "string") {
47
+ process.stdout.write(`${clean(value)}\n`);
48
+ return;
49
+ }
50
+ const record = asRecord(value);
51
+ const rows = Array.isArray(value)
52
+ ? value
53
+ : Array.isArray(record?.items)
54
+ ? record.items
55
+ : undefined;
56
+ if (rows) {
57
+ if (rows.length === 0) {
58
+ process.stdout.write("No results.\n");
59
+ }
60
+ else {
61
+ const preferred = [
62
+ "path",
63
+ "kind",
64
+ "githubRepo",
65
+ "fullName",
66
+ "githubNumber",
67
+ "githubTitle",
68
+ "name",
69
+ "prefix",
70
+ "scopes",
71
+ "expiresAt",
72
+ "revokedAt",
73
+ "type",
74
+ "githubUsername",
75
+ "title",
76
+ "message",
77
+ "status",
78
+ "indexStatus",
79
+ "role",
80
+ "userRole",
81
+ "_id",
82
+ ];
83
+ const keys = preferred.filter((key) => rows.some((row) => asRecord(row)?.[key] !== undefined));
84
+ if (keys.length > 0)
85
+ this.table(rows.map((row) => asRecord(row) ?? {}), keys);
86
+ else
87
+ process.stdout.write(`${clean(JSON.stringify(value, null, 2))}\n`);
88
+ }
89
+ if (typeof record?.total === "number")
90
+ this.info(`Showing ${rows.length} of ${record.total}. Use --offset and --limit for more.`);
91
+ if (record?.nextCursor !== undefined && record.nextCursor !== null) {
92
+ this.info(`Next page: --cursor ${JSON.stringify(record.nextCursor)}`);
93
+ }
94
+ if (record?.hasMore === true && record.nextCursor == null)
95
+ this.info("More results are available.");
96
+ return;
97
+ }
98
+ process.stdout.write(`${clean(JSON.stringify(value, null, 2) ?? "null")}\n`);
99
+ }
100
+ table(rows, keys) {
101
+ const cell = (value) => clean(String(value ?? "—"))
102
+ .replace(/\s+/g, " ")
103
+ .slice(0, 90);
104
+ const widths = keys.map((key) => Math.max(key.length, ...rows.map((row) => cell(row[key]).length)));
105
+ const line = (row) => keys
106
+ .map((key, i) => cell(row[key]).padEnd(widths[i]))
107
+ .join(" ")
108
+ .trimEnd();
109
+ process.stdout.write(this.color(line(Object.fromEntries(keys.map((key) => [key, key]))), 1) +
110
+ "\n");
111
+ rows.forEach((row) => process.stdout.write(`${line(row)}\n`));
112
+ }
113
+ async ask(question, fallback) {
114
+ if (this.json || !process.stdin.isTTY || !process.stdout.isTTY) {
115
+ throw new Error("This command needs input. Supply explicit arguments for non-interactive use.");
116
+ }
117
+ const input = createInterface({
118
+ input: process.stdin,
119
+ output: process.stdout,
120
+ });
121
+ try {
122
+ const answer = (await input.question(`${clean(question)}${fallback ? ` (${clean(fallback)})` : ""}: `)).trim();
123
+ if (!answer && fallback)
124
+ return fallback;
125
+ if (!answer)
126
+ throw new Error("A value is required.");
127
+ return answer;
128
+ }
129
+ finally {
130
+ input.close();
131
+ }
132
+ }
133
+ async confirm(message, yes = false) {
134
+ if (yes)
135
+ return;
136
+ const answer = await this.ask(`${message} [y/N]`, "n");
137
+ if (!/^y(es)?$/i.test(answer))
138
+ throw new Error("Cancelled.");
139
+ }
140
+ }
141
+ export function asRecord(value) {
142
+ return typeof value === "object" && value !== null && !Array.isArray(value)
143
+ ? value
144
+ : undefined;
145
+ }
146
+ export function integer(value, minimum = 1, maximum = Number.MAX_SAFE_INTEGER) {
147
+ const result = Number(value);
148
+ if (!/^\d+$/.test(value) ||
149
+ !Number.isSafeInteger(result) ||
150
+ result < minimum ||
151
+ result > maximum) {
152
+ throw new Error(`Expected an integer between ${minimum} and ${maximum}.`);
153
+ }
154
+ return result;
155
+ }
package/package.json CHANGED
@@ -1,6 +1,56 @@
1
1
  {
2
2
  "name": "gitdog",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Manage Gitdog projects, GitHub automation and API keys from your terminal",
5
+ "type": "module",
6
+ "bin": {
7
+ "gitdog": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "engines": {
14
+ "node": ">=22.14.0"
15
+ },
16
+ "packageManager": "bun@1.3.13",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/hyodotdev/gitdog-cli.git"
20
+ },
21
+ "homepage": "https://gitdog.dev",
22
+ "bugs": {
23
+ "url": "https://github.com/hyodotdev/gitdog-cli/issues"
24
+ },
25
+ "license": "UNLICENSED",
26
+ "keywords": [
27
+ "gitdog",
28
+ "github",
29
+ "cli",
30
+ "automation",
31
+ "ai"
32
+ ],
33
+ "publishConfig": {
34
+ "access": "public",
35
+ "registry": "https://registry.npmjs.org"
36
+ },
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.json",
39
+ "typecheck": "tsc --noEmit",
40
+ "lint": "prettier --check .",
41
+ "format": "prettier --write .",
42
+ "test": "bun run build && node --test test/*.test.mjs",
43
+ "test:dev": "bun run build && node scripts/test-dev.mjs",
44
+ "dev": "bun src/cli.ts",
45
+ "prepack": "bun run build"
46
+ },
47
+ "dependencies": {
48
+ "commander": "^15.0.0",
49
+ "convex": "^1.43.0"
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "^22.14.0",
53
+ "prettier": "^3.9.0",
54
+ "typescript": "~6.0.3"
55
+ }
56
+ }