with one click
github
// Set up GitHub access for NatStack with fine-grained personal access tokens, URL-bound credentials, deep links, and verification helpers.
// Set up GitHub access for NatStack with fine-grained personal access tokens, URL-bound credentials, deep links, and verification helpers.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | github |
| description | Set up GitHub access for NatStack with fine-grained personal access tokens, URL-bound credentials, deep links, and verification helpers. |
Use this skill when a user wants NatStack to connect to GitHub for repository metadata, issues, pull requests, contents API calls, Actions-related reads, or to create the PAT needed for direct GitHub clone/pull/push support.
Use a GitHub fine-grained personal access token. This is the simplest approach that requires no centralized NatStack server, no app registration controlled by NatStack, and no OAuth callback infrastructure.
If the user wants blanket or higher-trust access instead of least-privilege setup, say so plainly and offer the broad route:
repo when the user explicitly wants
broad access and accepts the larger blast radius.requestGitHubTokenCredential() can store either token style. Use
tokenKind: "classic" when the user chose a classic PAT so the credential
metadata matches what was saved.
GitHub Apps are more granular for multi-user products, but they require app registration and installation flows. OAuth apps can work, but for this personal sandbox they add more moving pieces than a user-owned fine-grained PAT and still do not avoid broad user authorization concerns.
getGitHubOnboardingStatus() from @workspace-skills/github.needs-token, ask the user to choose token style:
repo; use only when the user explicitly accepts the larger blast radius.repo.openGitHubTokenSettings({ tokenKind, accessLevel, browser: "internal" }) or
createBrowserPanel(url, { focus: true }).openGitHubTokenSettings({ tokenKind, accessLevel, browser: "external" }) or
openExternal(url).requestGitHubTokenCredential() so the shell-owned approval UI collects
the PAT. Do not ask the user to paste the PAT into chat or a panel-owned form.
Access levels choose the right mode automatically. Use explicit
mode: "api" only for API-only access, mode: "git" only for
clone/pull/push permissions, or mode: "api-and-git" when the user wants
both.
If the user chose a broad classic token, pass tokenKind: "classic".verifyGitHubCredential(credentialId) or
getGitHubOnboardingStatus({ verify: true }).verifyGitHubGitRemoteAccess(remoteUrl, credentialId) before declaring git
remote access complete.Only render the full SETUP.md checklist when the user asks for guided setup or needs help choosing repository access and permissions. Do not lead with the checklist for routine GitHub access.
import {
getGitHubOnboardingStatus,
openGitHubTokenSettings,
requestGitHubTokenCredential,
verifyGitHubCredential,
verifyGitHubGitRemoteAccess,
} from "@workspace-skills/github";
const status = await getGitHubOnboardingStatus();
if (status.stage === "needs-token") {
const tokenKind = "fine-grained"; // or "classic" if the user chose broad access
const accessLevel = "collaborate";
await openGitHubTokenSettings({ tokenKind, accessLevel, browser: "internal" });
const stored = await requestGitHubTokenCredential({
tokenKind,
accessLevel,
});
await verifyGitHubCredential(stored.id);
await verifyGitHubGitRemoteAccess("https://github.com/owner/repo.git", stored.id);
}
For broad access, the storage call is still simple:
const stored = await requestGitHubTokenCredential({
accessLevel: "broad",
tokenKind: "classic",
});
For setup links, use Internal when the user wants to keep setup inside NatStack;
use External when their normal browser already has GitHub auth, passkeys, or
password-manager state. The full workflow UI in SETUP.md is optional guidance,
not the default happy path.
The stored credential is URL-bound to https://api.github.com/ and, for every
friendly access level, to https://github.com/ git HTTP as well. API requests
should use credentials.fetch(). Direct GitHub clone/pull/push should use
git.client() or credentials.gitHttp(). The internal git server does not
consume GitHub credentials; NatStack's host-mediated isomorphic-git HTTP
adapter handles https://github.com/... git remotes without exposing the PAT to
panels or workers.
import { credentials, fs } from "@workspace/runtime";
import { GitClient } from "@natstack/git";
const git = new GitClient(fs, { http: credentials.gitHttp() });
await git.clone({
url: "https://github.com/owner/repo.git",
dir: "/repo",
});
For normal runtime code, prefer the runtime helper. It routes relative NatStack repositories to the internal git server and absolute GitHub remotes through URL-bound credentials:
import { git } from "@workspace/runtime";
const client = git.client();
await client.clone({ url: "https://github.com/owner/repo.git", dir: "/repo" });
await client.push({ dir: "/repo" });
To make a GitHub remote available to future workspace contexts, configure it as
a shared remote instead of only editing the current context's .git/config.
This uses a targeted approval prompt and commits the declaration to
meta/natstack.yml:
import { git } from "@workspace/runtime";
await git.setSharedRemote("panels/my-panel", {
name: "origin",
url: "https://github.com/owner/my-panel.git",
});
The durable config shape is:
git:
remotes:
panels:
my-panel:
origin: https://github.com/owner/my-panel.git
ci: https://github.com/owner/my-panel-ci.git
To import a remote repository into workspace source, use git.importProject()
with the destination path where it should live:
import { git } from "@workspace/runtime";
await git.importProject({
path: "panels/my-panel",
remote: {
name: "origin",
url: "https://github.com/owner/my-panel.git",
},
credentialId: "cred_github_...",
});
Supported parent directories are panels, packages, agents, workers,
skills, about, templates, and projects. git.importProject() clones
into canonical workspace source, records the shared remote in
meta/natstack.yml, and makes the repo available to future contexts. It may
also prompt to use the selected GitHub credential for the clone.
When meta/natstack.yml already declares shared remotes, use
git.completeWorkspaceDependencies() to import every configured remote whose
workspace repo is currently missing:
const result = await git.completeWorkspaceDependencies({ credentialId: "cred_github_..." });
console.log(result.imported, result.skipped, result.failed);
Use the narrowest set that supports the requested workflow. Avoid workflow write unless the user explicitly wants to edit GitHub Actions workflow files.
401 Bad credentials: the PAT was revoked, expired, or copied incorrectly.403 Resource not accessible by personal access token: the token does not
have access to that repository or the needed permission.mode: "git" / mode: "api-and-git" when creating the PAT. Verify a target
remote with verifyGitHubGitRemoteAccess(remoteUrl, credentialId). Git
transport should use git.client() or credentials.gitHttp(), not the
internal git server.