Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/assets/terminal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 9 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ jobs:
"$RUNNER_TEMP/gitdog-smoke/node_modules/.bin/gitdog" --json api list > "$RUNNER_TEMP/catalog.json"
node --input-type=module -e "import fs from 'node:fs'; import assert from 'node:assert/strict'; assert.ok(JSON.parse(fs.readFileSync(process.env.RUNNER_TEMP+'/catalog.json')).length >= 86)"
sha512sum "$archive" > release.sha512
- name: Run the exact archive through npx without a global install
working-directory: ${{ runner.temp }}
shell: bash
run: |
set -euo pipefail
version="$(node -p "require(process.env.GITHUB_WORKSPACE + '/package.json').version")"
actual="$(npx --yes --package "$GITHUB_WORKSPACE/gitdog-$version.tgz" gitdog --version)"
test "$actual" = "$version"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: npm-package
Expand All @@ -58,7 +66,7 @@ jobs:
publish:
needs: package
runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 15
environment: npm
permissions:
contents: read
Expand Down
44 changes: 22 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,53 +17,53 @@
</p>

<p align="center">
<img src="https://raw.githubusercontent.com/hyodotdev/gitdog-cli/main/.github/assets/terminal.svg?v=0.1.1" alt="Gitdog terminal: connect a project, configure automation and review pending actions" width="800">
<img src="https://raw.githubusercontent.com/hyodotdev/gitdog-cli/main/.github/assets/terminal.svg?v=0.1.2" alt="Gitdog terminal: connect a project, configure automation and review pending actions" width="800">
</p>

Connect repositories, configure Gitdog's issue and discussion assistants, review
AI actions, and automate project tasks from your terminal or CI.

## Install
## Run with npx

Requires **Node.js 22.14+**.
Requires **Node.js 22.14+**. No global installation needed.

```sh
npm install --global gitdog
npx gitdog@latest --help
```

## Quick start

```sh
gitdog login
gitdog github install
gitdog github recover
gitdog init owner/repo
gitdog config set requireApproval true
gitdog config push
npx gitdog@latest login
npx gitdog@latest github install
npx gitdog@latest github recover
npx gitdog@latest init owner/repo
npx gitdog@latest config set requireApproval true
npx gitdog@latest config push
```

GitHub App setup is only needed once. A project connects an existing GitHub
repository to Gitdog.

## Everyday commands

| Task | Command |
| ------------------------ | ---------------------------- |
| List projects | `gitdog projects list` |
| Read automation settings | `gitdog config get --remote` |
| Review pending actions | `gitdog tickets pending` |
| Check code indexing | `gitdog index status` |
| List team members | `gitdog team members` |
| Check usage | `gitdog usage summary` |
| Task | Command |
| ------------------------ | --------------------------------------- |
| List projects | `npx gitdog@latest projects list` |
| Read automation settings | `npx gitdog@latest config get --remote` |
| Review pending actions | `npx gitdog@latest tickets pending` |
| Check code indexing | `npx gitdog@latest index status` |
| List team members | `npx gitdog@latest team members` |
| Check usage | `npx gitdog@latest usage summary` |

Use `gitdog --help` to explore commands. Add `--json` for scripts.
Use `npx gitdog@latest --help` to explore commands. Add `--json` for scripts.

## Use in CI

```sh
gitdog keys create --name ci
gitdog --key-file /path/to/key.json projects list --json
gitdog keys revoke KEY_ID --yes
npx gitdog@latest keys create --name ci
npx --yes gitdog@0.1.2 --key-file /path/to/key.json projects list --json
npx gitdog@latest keys revoke KEY_ID --yes
```

`keys create` prints the private key file's path. Keys are project-scoped and
Expand Down
5 changes: 5 additions & 0 deletions docs/REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

Detailed commands, credentials and development setup for [Gitdog CLI](../README.md).

Every command below works without global installation: replace `gitdog` with
`npx gitdog@latest` (add `--yes` before the package name in non-interactive scripts).
For example: `npx gitdog@latest login`. An optional global installation with
`npm install --global gitdog` provides the shorter `gitdog` command.

## Sign in

`gitdog login` opens GitHub in your browser. Use a browser on the same computer,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gitdog",
"version": "0.1.1",
"version": "0.1.2",
"description": "Manage Gitdog projects, GitHub automation and API keys from your terminal",
"type": "module",
"bin": {
Expand Down
6 changes: 3 additions & 3 deletions scripts/release.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ async function main() {
{ stdio: "inherit" },
);
// Registry propagation is bounded; no repeated publishing.
for (let attempt = 0; attempt < 12; attempt++) {
for (let attempt = 0; attempt < 60; attempt++) {
try {
if (await verifyPublished(release, tarball, false)) return;
} catch (error) {
Expand All @@ -224,13 +224,13 @@ async function main() {
!transientNetworkFailure
)
throw error;
if (attempt === 11) throw error;
if (attempt === 59) throw error;
console.log("Waiting for npm provenance propagation");
}
await new Promise((resolve) => setTimeout(resolve, 5000));
}
throw new Error(
"Registry did not expose the release within one minute; rerun to verify",
"Registry did not expose the release within five minutes; rerun to verify",
);
}
default:
Expand Down
12 changes: 8 additions & 4 deletions src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ export async function createLoginListener(timeoutMs = 300_000) {
);
const timer = setTimeout(
() =>
finish(new Error("Sign-in timed out. Run gitdog login to try again.")),
finish(
new Error(
"Sign-in timed out. Run npx gitdog@latest login to try again.",
),
),
timeoutMs,
);
try {
Expand Down Expand Up @@ -232,13 +236,13 @@ export async function login(
} catch {
listener.signal.throwIfAborted();
throw new Error(
"Could not finish sign-in. Check that the CLI loopback callback is deployed, then run gitdog login again.",
"Could not finish sign-in. Check that the CLI loopback callback is deployed, then run npx gitdog@latest login again.",
);
}
const tokens = asRecord(exchange)?.tokens;
if (!validTokens(tokens))
throw new Error(
"Sign-in did not return a session. Run gitdog login again.",
"Sign-in did not return a session. Run npx gitdog@latest login again.",
);
client.setAuth(tokens.token);
const user = asRecord(
Expand All @@ -258,7 +262,7 @@ export async function login(
ui.info(
`Complete account consent at ${siteUrl}/legal/consent before running AI workflows.`,
);
ui.info("Next: gitdog init");
ui.info("Next: npx gitdog@latest init");
} finally {
await listener.close();
}
Expand Down
2 changes: 1 addition & 1 deletion src/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export function publicOperation(path: string) {
const operation = API_CATALOG.find((entry) => entry.path === path);
if (!operation)
throw new Error(
"Unknown public operation. Run gitdog api list. Internal functions cannot be called.",
"Unknown public operation. Run npx gitdog@latest api list. Internal functions cannot be called.",
);
return operation;
}
15 changes: 10 additions & 5 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,20 @@ export class GitdogApi implements Api {
return;
}
const initial = await this.store.read();
if (!initial) throw new Error("You are not logged in. Run gitdog login.");
if (!initial)
throw new Error("You are not logged in. Run npx gitdog@latest login.");
if (initial.apiUrl !== this.apiUrl)
throw new Error(
"This session belongs to another API URL. Run gitdog login --api-url <url> first.",
"This session belongs to another API URL. Run npx gitdog@latest login --api-url <url> first.",
);
this.session = initial;
if (tokenExpiresSoon(initial.token)) {
this.session = await this.store.locked(async () => {
const current = await this.store.read();
if (!current || current.apiUrl !== this.apiUrl)
throw new Error("Session changed. Run gitdog login again.");
throw new Error(
"Session changed. Run npx gitdog@latest login again.",
);
if (!tokenExpiresSoon(current.token)) return current;
let result: unknown;
try {
Expand All @@ -121,12 +124,14 @@ export class GitdogApi implements Api {
);
} catch {
throw new Error(
"Could not refresh your session. Check your connection or run gitdog login again.",
"Could not refresh your session. Check your connection or run npx gitdog@latest login again.",
);
}
const tokens = asRecord(result)?.tokens;
if (!validTokens(tokens))
throw new Error("Your session expired. Run gitdog login again.");
throw new Error(
"Your session expired. Run npx gitdog@latest login again.",
);
const updated = { ...current, ...tokens };
await this.store.save(updated);
return updated;
Expand Down
30 changes: 19 additions & 11 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export function createProgram(
.showSuggestionAfterError()
.addHelpText(
"after",
`\nGetting started:\n gitdog login\n gitdog init\n gitdog config set requireApproval true\n gitdog config push --dry-run\n gitdog config push\n\nFirebase-style aliases also work: projects:list, projects:create, config:push.\nRun gitdog <command> --help for details.`,
`\nGetting started:\n npx gitdog@latest login\n npx gitdog@latest init\n npx gitdog@latest config set requireApproval true\n npx gitdog@latest config push --dry-run\n npx gitdog@latest config push\n\nFirebase-style aliases also work: projects:list, projects:create, config:push.\nRun npx gitdog@latest <command> --help for details.`,
);

async function context(command: Command): Promise<Context> {
Expand Down Expand Up @@ -186,11 +186,15 @@ export function createProgram(
ui.output({
name: "gitdog",
version: VERSION,
next: ["gitdog login", "gitdog init", "gitdog --help"],
next: [
"npx gitdog@latest login",
"npx gitdog@latest init",
"npx gitdog@latest --help",
],
});
else
ui.output(
"Get started\n 1. gitdog login Sign in with GitHub\n 2. gitdog init Connect a project\n 3. gitdog status Check your assistant\n\nRun gitdog --help to explore all commands.",
"Get started\n 1. npx gitdog@latest login Sign in with GitHub\n 2. npx gitdog@latest init Connect a project\n 3. npx gitdog@latest status Check your assistant\n\nRun npx gitdog@latest --help to explore all commands.",
);
});

Expand Down Expand Up @@ -229,7 +233,9 @@ export function createProgram(
!(await new SessionStore().read())
) {
if (!process.stdin.isTTY || ui.json)
throw new Error("Run gitdog login before gitdog init.");
throw new Error(
"Run npx gitdog@latest login before npx gitdog@latest init.",
);
await login(apiUrl, siteUrl, ui);
}
await project.init(
Expand Down Expand Up @@ -359,7 +365,7 @@ export function createProgram(
keyFile,
});
ui.info(
`Key saved privately. Use: gitdog --key-file ${JSON.stringify(keyFile)} projects list`,
`Key saved privately. Use: npx gitdog@latest --key-file ${JSON.stringify(keyFile)} projects list`,
);
},
)
Expand Down Expand Up @@ -618,7 +624,7 @@ export function createProgram(
const remaining = deadline - Date.now();
if (flag(opts.wait) && remaining <= 0)
throw new Error(
"Timed out waiting for indexing. Indexing continues on the server; check gitdog index status.",
"Timed out waiting for indexing. Indexing continues on the server; check npx gitdog@latest index status.",
);
const state = await api.call(
"query",
Expand All @@ -641,7 +647,7 @@ export function createProgram(
}
if (record.status === "failed" || record.ragStatus === "failed")
throw new Error(
"Indexing failed. Run gitdog index logs for details.",
"Indexing failed. Run npx gitdog@latest index logs for details.",
);
if (
record.status === "completed" &&
Expand All @@ -652,7 +658,7 @@ export function createProgram(
}
if (Date.now() >= deadline)
throw new Error(
"Timed out waiting for indexing. Indexing continues on the server; check gitdog index status.",
"Timed out waiting for indexing. Indexing continues on the server; check npx gitdog@latest index status.",
);
await new Promise((resolve) =>
setTimeout(
Expand Down Expand Up @@ -1132,7 +1138,7 @@ export function createProgram(
if (!ui.json && !flag(opts.print)) await openBrowser(url);
ui.output({
url,
next: "After GitHub authorization, run gitdog github recover, then gitdog projects available.",
next: "After GitHub authorization, run npx gitdog@latest github recover, then npx gitdog@latest projects available.",
});
},
)
Expand All @@ -1155,7 +1161,7 @@ export function createProgram(
ui.output({ ...result, recoveredCount: count });
if (result?.status === "reauth_required")
throw new Error(
"Refresh GitHub authorization with gitdog login, then run github recover again.",
"Refresh GitHub authorization with npx gitdog@latest login, then run github recover again.",
);
return;
}
Expand Down Expand Up @@ -1256,7 +1262,9 @@ export function createProgram(
"Use keys create to issue API keys securely into a private file.",
);
if (!operation.path.startsWith("admin/"))
throw new Error("Use gitdog api call for non-admin operations.");
throw new Error(
"Use npx gitdog@latest api call for non-admin operations.",
);
const data = await jsonArguments(text(opts.data) ?? "{}");
if (operation.kind !== "query") {
if (!ui.json) ui.output({ operation: operation.path, arguments: data });
Expand Down
Loading
Loading