@ritualai/cli 0.3.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.
Files changed (38) hide show
  1. package/README.md +120 -0
  2. package/dist/commands/doctor.js +87 -0
  3. package/dist/commands/doctor.js.map +1 -0
  4. package/dist/commands/init.js +158 -0
  5. package/dist/commands/init.js.map +1 -0
  6. package/dist/commands/login.js +63 -0
  7. package/dist/commands/login.js.map +1 -0
  8. package/dist/commands/logout.js +25 -0
  9. package/dist/commands/logout.js.map +1 -0
  10. package/dist/commands/refresh.js +42 -0
  11. package/dist/commands/refresh.js.map +1 -0
  12. package/dist/commands/whoami.js +52 -0
  13. package/dist/commands/whoami.js.map +1 -0
  14. package/dist/index.js +51 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/lib/agents/configure-mcp.js +127 -0
  17. package/dist/lib/agents/configure-mcp.js.map +1 -0
  18. package/dist/lib/agents/detector.js +32 -0
  19. package/dist/lib/agents/detector.js.map +1 -0
  20. package/dist/lib/agents/providers.js +118 -0
  21. package/dist/lib/agents/providers.js.map +1 -0
  22. package/dist/lib/api-client.js +120 -0
  23. package/dist/lib/api-client.js.map +1 -0
  24. package/dist/lib/config.js +154 -0
  25. package/dist/lib/config.js.map +1 -0
  26. package/dist/lib/oidc.js +213 -0
  27. package/dist/lib/oidc.js.map +1 -0
  28. package/dist/lib/pat-store.js +44 -0
  29. package/dist/lib/pat-store.js.map +1 -0
  30. package/dist/lib/skill-copy.js +57 -0
  31. package/dist/lib/skill-copy.js.map +1 -0
  32. package/package.json +59 -0
  33. package/skills/claude-code/ritual/SKILL.md +271 -0
  34. package/skills/codex/ritual/SKILL.md +271 -0
  35. package/skills/cursor/ritual/SKILL.md +271 -0
  36. package/skills/gemini/ritual/SKILL.md +271 -0
  37. package/skills/kiro/ritual/SKILL.md +271 -0
  38. package/skills/vscode/ritual/SKILL.md +271 -0
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # `@ritualai/cli`
2
+
3
+ The Ritual CLI. Connects your AI coding agent (Claude Code, Cursor,
4
+ Codex, Kiro, Gemini CLI, Windsurf, etc.) to Ritual Cloud via the
5
+ [Model Context Protocol][mcp].
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g @ritualai/cli
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```bash
16
+ ritual login # browser-based sign-in (OAuth 2.1 + PKCE)
17
+ ritual init # scaffold skills + register MCP with every detected agent
18
+ ritual doctor # sanity-check the environment
19
+ ```
20
+
21
+ That's it. `init` does three things:
22
+
23
+ 1. Detects which AI coding agents you have installed (Claude Code,
24
+ Cursor, Windsurf, Kiro, Gemini CLI, VS Code / Copilot, Codex).
25
+ 2. Mints a long-lived **Personal Access Token** via the Ritual API
26
+ named `Ritual CLI — <hostname>`, so the agent has a credential
27
+ that doesn't expire every 5 minutes the way OAuth tokens do.
28
+ 3. For each detected agent: copies the Ritual skill files into your
29
+ project (`.claude/skills/`, `.cursor/rules/`, etc.) AND writes
30
+ the MCP server config (`claude mcp add-json` for Claude Code, or
31
+ `mcp.json` merge for the others) with the PAT as the Bearer.
32
+
33
+ Restart your agent after `init` so it picks up the new MCP server,
34
+ then try `/ritual build <feature>` from inside the agent.
35
+
36
+ ## Commands
37
+
38
+ ```bash
39
+ ritual init [--agent <id>] [--list] # scaffold + register
40
+ ritual login # browser sign-in
41
+ ritual logout # clear creds
42
+ ritual whoami # session info, auto-refresh
43
+ ritual refresh # force token refresh
44
+ ritual doctor # env + detection report
45
+ ```
46
+
47
+ The agent ids are: `claude-code`, `cursor`, `windsurf`, `kiro`,
48
+ `gemini`, `vscode`, `codex`. Use `--agent <id>` if you want to scaffold
49
+ for a specific one without auto-detecting (handy when you've got the
50
+ agent installed somewhere non-standard).
51
+
52
+ ## How refresh works (v0.2)
53
+
54
+ Keycloak issues a short-lived access token (~5 min by default) and a
55
+ longer-lived refresh token (~30 min). The CLI handles this transparently:
56
+
57
+ - `ritual whoami` and any future Bearer-using command call
58
+ `getValidAccessToken()` internally. If the cached access token is
59
+ still fresh (>30s of life left), they use it as-is. If it's expired
60
+ but the refresh token is still good, they exchange it for a new
61
+ access token and persist the result before continuing.
62
+ - If the refresh token is also expired/revoked (Keycloak returns
63
+ `400 invalid_grant`), you'll see a clear "session expired, run
64
+ `ritual login`" message — not a confusing 401 from the API.
65
+ - `ritual refresh` exposes the refresh path explicitly for scripts
66
+ and debugging. Most users never need to run it directly.
67
+
68
+ Refresh-token rotation (Keycloak's default) means the stored refresh
69
+ token changes on every successful refresh — the CLI persists the new
70
+ one automatically.
71
+
72
+ ## How sign-in works
73
+
74
+ 1. CLI binds a one-shot HTTP server on a random localhost port.
75
+ 2. CLI opens your browser to the Ritual identity provider (Keycloak).
76
+ 3. You authenticate (or use SSO if your org has it configured).
77
+ 4. Keycloak redirects back to `http://127.0.0.1:<port>/callback` with
78
+ an authorization code.
79
+ 5. CLI exchanges the code at the token endpoint using PKCE proof.
80
+ 6. Tokens are stored at `~/.config/ritual/credentials.json` (chmod 0600).
81
+
82
+ This is the standard [RFC 8252][rfc8252] loopback pattern — no shared
83
+ secret, no manual API key copy-paste.
84
+
85
+ ## Override defaults
86
+
87
+ Defaults point at production. For dev, either:
88
+
89
+ ```bash
90
+ ritual login --issuer https://auth.dev.ritualapp.cloud/realms/ritual
91
+ ```
92
+
93
+ or set env vars:
94
+
95
+ ```bash
96
+ export RITUAL_KEYCLOAK_URL=https://auth.dev.ritualapp.cloud/realms/ritual
97
+ ritual login
98
+ ```
99
+
100
+ ## What's next
101
+
102
+ - Per-agent skill *format transforms* (currently every agent gets the
103
+ same SKILL.md; future versions emit Cursor `.mdc`, Kiro YAML,
104
+ Gemini's specific frontmatter).
105
+ - Workflow commands (`ritual explore`, `ritual run`, `ritual brief`)
106
+ that wrap the MCP tools so you can drive Ritual from the terminal
107
+ outside an agent session.
108
+
109
+ ## Security notes
110
+
111
+ - The credentials file is chmod 0600 (owner-only). On shared machines
112
+ this is your only protection — don't `chmod 644 ~/.config/ritual`.
113
+ - The CLI never sends your credentials anywhere except to Keycloak's
114
+ token endpoint over TLS.
115
+ - The `ritual-cli` Keycloak client is a public OAuth client (no
116
+ client secret). PKCE is what makes this safe — only the CLI process
117
+ that generated the verifier can exchange the code.
118
+
119
+ [mcp]: https://modelcontextprotocol.io
120
+ [rfc8252]: https://datatracker.ietf.org/doc/html/rfc8252
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.doctorCommand = doctorCommand;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_os_1 = require("node:os");
6
+ const config_1 = require("../lib/config");
7
+ const detector_1 = require("../lib/agents/detector");
8
+ const api_client_1 = require("../lib/api-client");
9
+ /**
10
+ * `ritual doctor` — sanity check the CLI's environment.
11
+ *
12
+ * Print, in order:
13
+ * 1. Runtime: node version, OS, hostname
14
+ * 2. Credentials: path + signed-in status + scopes + expiry
15
+ * 3. Resolved endpoints: API base, MCP URL (derived from issuer)
16
+ * 4. Agent detection: which agents we see installed
17
+ *
18
+ * Never modifies state. Always exits 0 unless the user asked for
19
+ * action items we can't complete (e.g. signed out — exit 1 so CI can
20
+ * gate on it).
21
+ */
22
+ async function doctorCommand() {
23
+ let problems = 0;
24
+ console.log('');
25
+ console.log(' Ritual CLI — environment report');
26
+ console.log('');
27
+ // --- Runtime --------------------------------------------------
28
+ console.log(' Runtime:');
29
+ console.log(` node ${process.version}`);
30
+ console.log(` platform ${(0, node_os_1.platform)()}`);
31
+ console.log(` hostname ${(0, node_os_1.hostname)()}`);
32
+ console.log('');
33
+ // --- Credentials ----------------------------------------------
34
+ console.log(' Credentials:');
35
+ console.log(` path ${(0, config_1.getCredentialsPath)()}`);
36
+ const tokenStatus = await (0, config_1.getValidAccessToken)();
37
+ if (tokenStatus.kind === 'not-signed-in') {
38
+ console.log(' state not signed in');
39
+ console.log(' fix run `ritual login`');
40
+ problems++;
41
+ console.log('');
42
+ }
43
+ else if (tokenStatus.kind === 're-auth-required') {
44
+ console.log(` state re-auth required (${tokenStatus.reason})`);
45
+ console.log(' fix run `ritual login`');
46
+ problems++;
47
+ console.log('');
48
+ }
49
+ else {
50
+ const { creds } = tokenStatus;
51
+ const nowSec = Math.floor(Date.now() / 1000);
52
+ const expiresInSec = creds.tokenSet.expires_at - nowSec;
53
+ console.log(` state signed in as ${creds.user?.email ?? creds.user?.sub ?? 'unknown'}`);
54
+ console.log(` issuer ${creds.issuer}`);
55
+ console.log(` expires in ${expiresInSec}s${tokenStatus.refreshed ? ' (auto-refreshed)' : ''}`);
56
+ console.log('');
57
+ // --- Resolved endpoints (only meaningful if signed in) ----
58
+ console.log(' Resolved endpoints:');
59
+ console.log(` api ${(0, api_client_1.apiBaseFromIssuer)(creds.issuer)}`);
60
+ console.log(` mcp ${(0, api_client_1.mcpUrlFromIssuer)(creds.issuer)}`);
61
+ console.log('');
62
+ }
63
+ // --- Agent detection ------------------------------------------
64
+ console.log(' AI coding agents:');
65
+ for (const a of (0, detector_1.detectAgents)()) {
66
+ const status = a.detected ? '✓' : '·';
67
+ // Pad id column so output aligns regardless of which agents
68
+ // the user has installed.
69
+ console.log(` ${status} ${a.id.padEnd(14)} ${a.name}`);
70
+ }
71
+ console.log('');
72
+ // --- Project state --------------------------------------------
73
+ // If we're inside a project that has any of the agent skill
74
+ // directories, mention it — useful for the "did init work" check.
75
+ const projectAgents = (0, detector_1.detectAgents)().filter((a) => (0, node_fs_1.existsSync)(`${process.cwd()}/${a.provider.projectSkillDir}`));
76
+ if (projectAgents.length > 0) {
77
+ console.log(' This project has skills for:');
78
+ for (const a of projectAgents) {
79
+ console.log(` ${a.provider.projectSkillDir}/`);
80
+ }
81
+ console.log('');
82
+ }
83
+ if (problems > 0) {
84
+ process.exitCode = 1;
85
+ }
86
+ }
87
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initCommand = initCommand;
4
+ const config_1 = require("../lib/config");
5
+ const api_client_1 = require("../lib/api-client");
6
+ const pat_store_1 = require("../lib/pat-store");
7
+ const detector_1 = require("../lib/agents/detector");
8
+ const providers_1 = require("../lib/agents/providers");
9
+ const configure_mcp_1 = require("../lib/agents/configure-mcp");
10
+ const skill_copy_1 = require("../lib/skill-copy");
11
+ async function initCommand(opts = {}) {
12
+ console.log('');
13
+ console.log(' Ritual — scaffolding skills + connecting AI coding agents');
14
+ console.log('');
15
+ // --- 0. Handle --list (early exit, no auth required) ------------
16
+ if (opts.list) {
17
+ console.log(' Known agents:');
18
+ for (const p of providers_1.PROVIDERS) {
19
+ console.log(` ${p.id.padEnd(14)} ${p.name}`);
20
+ }
21
+ console.log('');
22
+ return;
23
+ }
24
+ // --- 1. Auth check -----------------------------------------------
25
+ const tokenStatus = await (0, config_1.getValidAccessToken)();
26
+ if (tokenStatus.kind === 'not-signed-in') {
27
+ console.error(' ✗ Not signed in. Run `ritual login` first, then `ritual init`.');
28
+ console.error('');
29
+ process.exitCode = 1;
30
+ return;
31
+ }
32
+ if (tokenStatus.kind === 're-auth-required') {
33
+ console.error(' ✗ Your session expired. Run `ritual login` to re-authenticate.');
34
+ console.error(` Reason: ${tokenStatus.reason}`);
35
+ console.error('');
36
+ process.exitCode = 1;
37
+ return;
38
+ }
39
+ // --- 2. Detect agents (or pick a specific one) -------------------
40
+ let targets;
41
+ if (opts.agent) {
42
+ const provider = (0, providers_1.findProviderById)(opts.agent);
43
+ if (!provider) {
44
+ console.error(` ✗ Unknown agent: ${opts.agent}`);
45
+ console.error(' Run `ritual init --list` to see known agents.');
46
+ console.error('');
47
+ process.exitCode = 1;
48
+ return;
49
+ }
50
+ // Explicit `--agent` overrides detection — the user knows what
51
+ // they want, don't make them install/symlink the binary just
52
+ // to pass detection.
53
+ targets = [
54
+ {
55
+ id: provider.id,
56
+ name: provider.name,
57
+ detected: true,
58
+ provider,
59
+ },
60
+ ];
61
+ }
62
+ else {
63
+ const all = (0, detector_1.detectAgents)();
64
+ const detected = all.filter((a) => a.detected);
65
+ if (detected.length === 0) {
66
+ // Nothing detected → default to Claude Code. This matches
67
+ // legacy behavior and matches the documented MCP launch
68
+ // partner. User can override with `--agent <id>`.
69
+ const claudeProvider = (0, providers_1.findProviderById)('claude-code');
70
+ targets = [
71
+ {
72
+ id: claudeProvider.id,
73
+ name: claudeProvider.name,
74
+ detected: false,
75
+ provider: claudeProvider,
76
+ },
77
+ ];
78
+ console.log(' No coding agents detected on this machine.');
79
+ console.log(` Defaulting to ${claudeProvider.name}. Use --agent <id> to choose explicitly.`);
80
+ console.log('');
81
+ }
82
+ else {
83
+ targets = detected;
84
+ console.log(` Detected ${detected.length} agent(s): ${detected.map((d) => d.name).join(', ')}`);
85
+ console.log('');
86
+ }
87
+ }
88
+ // --- 3. Mint a long-lived PAT for agents to use ------------------
89
+ const issuer = tokenStatus.creds.issuer;
90
+ const api = new api_client_1.ApiClient({
91
+ issuer,
92
+ accessToken: tokenStatus.accessToken,
93
+ });
94
+ const mcpUrl = (0, api_client_1.mcpUrlFromIssuer)(issuer);
95
+ let pat;
96
+ try {
97
+ pat = await (0, pat_store_1.mintAgentPat)({ api });
98
+ }
99
+ catch (err) {
100
+ console.error(` ✗ Could not mint access token: ${err.message}`);
101
+ console.error('');
102
+ process.exitCode = 1;
103
+ return;
104
+ }
105
+ console.log(` ✓ Minted ${pat.name}`);
106
+ console.log(` Manage tokens at: ${webAppUrlFromIssuer(issuer)}/settings/tokens`);
107
+ console.log('');
108
+ // --- 4. Copy skills + register MCP for each target agent ---------
109
+ const projectDir = opts.cwd ?? process.cwd();
110
+ const copyResults = [];
111
+ const registrationResults = [];
112
+ for (const t of targets) {
113
+ copyResults.push((0, skill_copy_1.copySkillsForProvider)(t.provider, projectDir));
114
+ registrationResults.push((0, configure_mcp_1.configureMcpForAgent)(t.provider, { url: mcpUrl, token: pat.plaintextToken }));
115
+ }
116
+ // --- 5. Summary --------------------------------------------------
117
+ console.log(' Per-agent results:');
118
+ for (const t of targets) {
119
+ const copy = copyResults.find((r) => r.provider.id === t.provider.id);
120
+ const reg = registrationResults.find((r) => r.provider.id === t.provider.id);
121
+ const skillSummary = copy.copied > 0
122
+ ? `${copy.copied} skill(s) → ${t.provider.projectSkillDir}/`
123
+ : `skills skipped (${copy.reason ?? 'unknown'})`;
124
+ const mcpSummary = reg.success
125
+ ? `MCP server registered`
126
+ : `MCP register failed: ${reg.reason ?? 'unknown'}`;
127
+ console.log(` ${t.provider.name}`);
128
+ console.log(` ${skillSummary}`);
129
+ console.log(` ${mcpSummary}`);
130
+ }
131
+ console.log('');
132
+ console.log(' Next: restart your AI coding agent so it picks up the new MCP server.');
133
+ console.log(' In your agent, try: /ritual build <feature>');
134
+ console.log('');
135
+ }
136
+ /**
137
+ * Mirror of `apiBaseFromIssuer` but for the web app. Used in the
138
+ * "manage tokens at <url>" hint above.
139
+ *
140
+ * dev: https://dev.ritualapp.cloud
141
+ * prod: https://app.ritualapp.cloud (matches the legacy domain)
142
+ */
143
+ function webAppUrlFromIssuer(issuer) {
144
+ try {
145
+ const u = new URL(issuer);
146
+ const host = u.host;
147
+ if (host.startsWith('auth.dev.'))
148
+ return 'https://dev.ritualapp.cloud';
149
+ if (host === 'auth.ritualapp.cloud')
150
+ return 'https://app.ritualapp.cloud';
151
+ // Self-hosted / enterprise — replace auth. with app.
152
+ return `https://${host.replace(/^auth\./, 'app.')}`;
153
+ }
154
+ catch {
155
+ return 'https://app.ritualapp.cloud';
156
+ }
157
+ }
158
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loginCommand = loginCommand;
7
+ const open_1 = __importDefault(require("open"));
8
+ const oidc_1 = require("../lib/oidc");
9
+ const config_1 = require("../lib/config");
10
+ const DEFAULT_ISSUER = process.env.RITUAL_KEYCLOAK_URL ?? 'https://auth.ritualapp.cloud/realms/ritual';
11
+ const DEFAULT_CLIENT_ID = process.env.RITUAL_KEYCLOAK_CLIENT_ID ?? 'ritual-cli';
12
+ const DEFAULT_SCOPE = 'openid profile email ritual:read ritual:write';
13
+ /**
14
+ * `ritual login` — browser-based Auth Code + PKCE flow against
15
+ * Keycloak. On success, stores the access + refresh tokens in
16
+ * `~/.config/ritual/credentials.json` for subsequent commands.
17
+ *
18
+ * Defaults point at production (`auth.ritualapp.cloud`); override via
19
+ * `--issuer https://auth.dev.ritualapp.cloud/realms/ritual` for dev,
20
+ * or set `RITUAL_KEYCLOAK_URL` env var.
21
+ */
22
+ async function loginCommand(options) {
23
+ const issuer = options.issuer ?? DEFAULT_ISSUER;
24
+ const clientId = options.clientId ?? DEFAULT_CLIENT_ID;
25
+ console.log('');
26
+ console.log(' Ritual — sign in');
27
+ console.log('');
28
+ let tokenSet;
29
+ try {
30
+ tokenSet = await (0, oidc_1.performLoginFlow)({ issuer, clientId, scope: DEFAULT_SCOPE }, (url) => (0, open_1.default)(url), (msg) => console.log(` ${msg}`));
31
+ }
32
+ catch (err) {
33
+ console.error('');
34
+ console.error(` ✗ Login failed: ${err.message}`);
35
+ process.exitCode = 1;
36
+ return;
37
+ }
38
+ const claims = (0, oidc_1.decodeJwtPayloadUnsafe)(tokenSet.access_token);
39
+ const creds = {
40
+ version: 1,
41
+ issuer,
42
+ clientId,
43
+ tokenSet: {
44
+ access_token: tokenSet.access_token,
45
+ refresh_token: tokenSet.refresh_token,
46
+ expires_at: Math.floor(Date.now() / 1000) + tokenSet.expires_in,
47
+ id_token: tokenSet.id_token,
48
+ scope: tokenSet.scope,
49
+ },
50
+ user: claims
51
+ ? { sub: claims.sub, email: claims.email ?? claims.preferred_username }
52
+ : undefined,
53
+ };
54
+ (0, config_1.saveCredentials)(creds);
55
+ const display = creds.user?.email ?? creds.user?.sub ?? 'unknown';
56
+ console.log('');
57
+ console.log(` ✓ Signed in as ${display}`);
58
+ console.log('');
59
+ console.log(' Your AI coding agent can now connect to Ritual via MCP.');
60
+ console.log(' Run `ritual whoami` to confirm session details.');
61
+ console.log('');
62
+ }
63
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":";;;;;AAuBA,oCAkDC;AAzED,gDAAwB;AACxB,sCAAuE;AACvE,0CAAqE;AAOrE,MAAM,cAAc,GACnB,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,4CAA4C,CAAC;AACjF,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,YAAY,CAAC;AAChF,MAAM,aAAa,GAAG,+CAA+C,CAAC;AAEtE;;;;;;;;GAQG;AACI,KAAK,UAAU,YAAY,CAAC,OAAqB;IACvD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,cAAc,CAAC;IAChD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,iBAAiB,CAAC;IAEvD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAClC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,QAAQ,CAAC;IACb,IAAI,CAAC;QACJ,QAAQ,GAAG,MAAM,IAAA,uBAAgB,EAChC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,EAC1C,CAAC,GAAG,EAAE,EAAE,CAAC,IAAA,cAAI,EAAC,GAAG,CAAC,EAClB,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE,CAAC,CAChC,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,qBAAsB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACR,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,6BAAsB,EACpC,QAAQ,CAAC,YAAY,CACrB,CAAC;IAEF,MAAM,KAAK,GAAmB;QAC7B,OAAO,EAAE,CAAC;QACV,MAAM;QACN,QAAQ;QACR,QAAQ,EAAE;YACT,YAAY,EAAE,QAAQ,CAAC,YAAY;YACnC,aAAa,EAAE,QAAQ,CAAC,aAAa;YACrC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,QAAQ,CAAC,UAAU;YAC/D,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,KAAK,EAAE,QAAQ,CAAC,KAAK;SACrB;QACD,IAAI,EAAE,MAAM;YACX,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,kBAAkB,EAAE;YACvE,CAAC,CAAC,SAAS;KACZ,CAAC;IACF,IAAA,wBAAe,EAAC,KAAK,CAAC,CAAC;IAEvB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,GAAG,IAAI,SAAS,CAAC;IAClE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC;IACjE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACjB,CAAC"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logoutCommand = logoutCommand;
4
+ const config_1 = require("../lib/config");
5
+ /**
6
+ * `ritual logout` — clears local credentials. Does NOT call Keycloak's
7
+ * end-session endpoint — that's a browser-side concept; clearing local
8
+ * tokens is what matters from a CLI perspective. The user can sign out
9
+ * of the realm session itself via the web app.
10
+ */
11
+ async function logoutCommand() {
12
+ const had = (0, config_1.loadCredentials)();
13
+ (0, config_1.clearCredentials)();
14
+ console.log('');
15
+ if (had) {
16
+ const display = had.user?.email ?? had.user?.sub ?? 'unknown';
17
+ console.log(` ✓ Signed out (${display}).`);
18
+ }
19
+ else {
20
+ console.log(' No active session — nothing to clear.');
21
+ }
22
+ console.log(` Credentials path: ${(0, config_1.getCredentialsPath)()}`);
23
+ console.log('');
24
+ }
25
+ //# sourceMappingURL=logout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":";;AAQA,sCAYC;AApBD,0CAAsF;AAEtF;;;;;GAKG;AACI,KAAK,UAAU,aAAa;IAClC,MAAM,GAAG,GAAG,IAAA,wBAAe,GAAE,CAAC;IAC9B,IAAA,yBAAgB,GAAE,CAAC;IACnB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,IAAI,GAAG,EAAE,CAAC;QACT,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,IAAI,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,SAAS,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,mBAAmB,OAAO,IAAI,CAAC,CAAC;IAC7C,CAAC;SAAM,CAAC;QACP,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAA,2BAAkB,GAAE,EAAE,CAAC,CAAC;IAC3D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACjB,CAAC"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.refreshCommand = refreshCommand;
4
+ const config_1 = require("../lib/config");
5
+ /**
6
+ * `ritual refresh` — force-refresh the cached access token using the
7
+ * stored refresh token. Mostly useful for scripting + debugging
8
+ * (e.g. "I want to confirm my refresh token still works without
9
+ * waiting for the access token to expire").
10
+ *
11
+ * Most subcommands don't need this — they call `getValidAccessToken()`
12
+ * which refreshes lazily on demand.
13
+ */
14
+ async function refreshCommand() {
15
+ console.log('');
16
+ const status = await (0, config_1.getValidAccessToken)();
17
+ if (status.kind === 'not-signed-in') {
18
+ console.log(' Not signed in. Run `ritual login` to authenticate.');
19
+ console.log('');
20
+ process.exitCode = 1;
21
+ return;
22
+ }
23
+ if (status.kind === 're-auth-required') {
24
+ console.log(' ✗ Refresh failed.');
25
+ console.log(` Reason: ${status.reason}`);
26
+ console.log(' Run `ritual login` to sign in again.');
27
+ console.log(` Credentials path: ${(0, config_1.getCredentialsPath)()}`);
28
+ console.log('');
29
+ process.exitCode = 1;
30
+ return;
31
+ }
32
+ const display = status.creds.user?.email ?? status.creds.user?.sub ?? 'unknown';
33
+ const expiresInSec = status.creds.tokenSet.expires_at - Math.floor(Date.now() / 1000);
34
+ if (status.refreshed) {
35
+ console.log(` ✓ Refreshed token for ${display} (valid for ${expiresInSec}s)`);
36
+ }
37
+ else {
38
+ console.log(` Token still fresh for ${display} (${expiresInSec}s remaining) — no refresh needed.`);
39
+ }
40
+ console.log('');
41
+ }
42
+ //# sourceMappingURL=refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../src/commands/refresh.ts"],"names":[],"mappings":";;AAWA,wCAgCC;AA3CD,0CAAwE;AAExE;;;;;;;;GAQG;AACI,KAAK,UAAU,cAAc;IACnC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,MAAM,MAAM,GAAG,MAAM,IAAA,4BAAmB,GAAE,CAAC;IAE3C,IAAI,MAAM,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;QACpE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACR,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAA,2BAAkB,GAAE,EAAE,CAAC,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACR,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,IAAI,SAAS,CAAC;IAChF,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAEtF,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,2BAA2B,OAAO,eAAe,YAAY,IAAI,CAAC,CAAC;IAChF,CAAC;SAAM,CAAC;QACP,OAAO,CAAC,GAAG,CACV,2BAA2B,OAAO,KAAK,YAAY,mCAAmC,CACtF,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACjB,CAAC"}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.whoamiCommand = whoamiCommand;
4
+ const config_1 = require("../lib/config");
5
+ /**
6
+ * `ritual whoami` — print the active session's identity + token
7
+ * status. Calls `getValidAccessToken()` so an expired access token
8
+ * is transparently refreshed first; the user sees current state, not
9
+ * stale state.
10
+ *
11
+ * Three terminal states:
12
+ * - signed-in: print identity + token expiry
13
+ * - not-signed-in: suggest `ritual login`
14
+ * - re-auth-required: refresh failed; clear hint to re-login
15
+ */
16
+ async function whoamiCommand() {
17
+ console.log('');
18
+ const status = await (0, config_1.getValidAccessToken)();
19
+ if (status.kind === 'not-signed-in') {
20
+ console.log(' Not signed in. Run `ritual login` to authenticate.');
21
+ console.log('');
22
+ process.exitCode = 1;
23
+ return;
24
+ }
25
+ if (status.kind === 're-auth-required') {
26
+ console.log(' ✗ Session expired and refresh failed.');
27
+ console.log(` Reason: ${status.reason}`);
28
+ console.log(' Run `ritual login` to sign in again.');
29
+ console.log(` Credentials path: ${(0, config_1.getCredentialsPath)()}`);
30
+ console.log('');
31
+ process.exitCode = 1;
32
+ return;
33
+ }
34
+ const { creds, refreshed } = status;
35
+ const display = creds.user?.email ?? creds.user?.sub ?? 'unknown';
36
+ const issuerHost = (() => {
37
+ try {
38
+ return new URL(creds.issuer).host;
39
+ }
40
+ catch {
41
+ return creds.issuer;
42
+ }
43
+ })();
44
+ const expiresInSec = creds.tokenSet.expires_at - Math.floor(Date.now() / 1000);
45
+ console.log(` Signed in as: ${display}`);
46
+ console.log(` Issuer: ${issuerHost}`);
47
+ console.log(` Client: ${creds.clientId}`);
48
+ console.log(` Scopes: ${creds.tokenSet.scope ?? '(none)'}`);
49
+ console.log(` Access token: ✓ valid (expires in ${expiresInSec > 0 ? `${expiresInSec}s` : 'expired'})${refreshed ? ' — auto-refreshed' : ''}`);
50
+ console.log('');
51
+ }
52
+ //# sourceMappingURL=whoami.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":";;AAaA,sCAwCC;AArDD,0CAAwE;AAExE;;;;;;;;;;GAUG;AACI,KAAK,UAAU,aAAa;IAClC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,MAAM,MAAM,GAAG,MAAM,IAAA,4BAAmB,GAAE,CAAC;IAE3C,IAAI,MAAM,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;QACpE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACR,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;QACvD,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAA,2BAAkB,GAAE,EAAE,CAAC,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACR,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IACpC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,GAAG,IAAI,SAAS,CAAC;IAClE,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE;QACxB,IAAI,CAAC;YACJ,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,KAAK,CAAC,MAAM,CAAC;QACrB,CAAC;IACF,CAAC,CAAC,EAAE,CAAC;IACL,MAAM,YAAY,GAAG,KAAK,CAAC,QAAQ,CAAC,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAE/E,OAAO,CAAC,GAAG,CAAC,mBAAmB,OAAO,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,mBAAmB,UAAU,EAAE,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,CAAC,mBAAmB,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,mBAAmB,KAAK,CAAC,QAAQ,CAAC,KAAK,IAAI,QAAQ,EAAE,CAAC,CAAC;IACnE,OAAO,CAAC,GAAG,CACV,uCAAuC,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY,GAAG,CAAC,CAAC,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,EAAE,CAClI,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACjB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const login_1 = require("./commands/login");
6
+ const logout_1 = require("./commands/logout");
7
+ const whoami_1 = require("./commands/whoami");
8
+ const refresh_1 = require("./commands/refresh");
9
+ const init_1 = require("./commands/init");
10
+ const doctor_1 = require("./commands/doctor");
11
+ const program = new commander_1.Command();
12
+ program
13
+ .name('ritual')
14
+ .description('Ritual CLI — connect AI coding agents to Ritual Cloud. ' +
15
+ 'Scaffold skills, register MCP servers, manage sessions.')
16
+ .version('0.3.0');
17
+ // `init` is the headline command: scaffold + register against every
18
+ // detected agent. Listed first so `ritual --help` surfaces it.
19
+ program
20
+ .command('init')
21
+ .description('Scaffold Ritual skills + register MCP server with every detected AI coding agent')
22
+ .option('--agent <id>', 'Restrict to one agent (e.g. claude-code, cursor, kiro)')
23
+ .option('--list', 'List known agents and exit')
24
+ .action(init_1.initCommand);
25
+ program
26
+ .command('login')
27
+ .description('Authenticate with Ritual via your browser')
28
+ .option('--issuer <url>', 'OIDC issuer (defaults to https://auth.ritualapp.cloud/realms/ritual or RITUAL_KEYCLOAK_URL env)')
29
+ .option('--client-id <id>', 'OIDC client id (defaults to "ritual-cli" or RITUAL_KEYCLOAK_CLIENT_ID env)')
30
+ .action(login_1.loginCommand);
31
+ program
32
+ .command('logout')
33
+ .description('Clear local credentials')
34
+ .action(logout_1.logoutCommand);
35
+ program
36
+ .command('whoami')
37
+ .description('Show current session info (auto-refreshes token if expired)')
38
+ .action(whoami_1.whoamiCommand);
39
+ program
40
+ .command('refresh')
41
+ .description('Force-refresh the cached access token using the stored refresh token')
42
+ .action(refresh_1.refreshCommand);
43
+ program
44
+ .command('doctor')
45
+ .description('Sanity check the CLI environment: credentials, endpoints, detected agents')
46
+ .action(doctor_1.doctorCommand);
47
+ program.parseAsync(process.argv).catch((err) => {
48
+ console.error(`\n ✗ ${err.message}\n`);
49
+ process.exit(1);
50
+ });
51
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAEA,yCAAoC;AACpC,4CAAgD;AAChD,8CAAkD;AAClD,8CAAkD;AAClD,gDAAoD;AACpD,0CAA8C;AAC9C,8CAAkD;AAElD,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACL,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CACX,yDAAyD;IACxD,yDAAyD,CAC1D;KACA,OAAO,CAAC,OAAO,CAAC,CAAC;AAEnB,oEAAoE;AACpE,+DAA+D;AAC/D,OAAO;KACL,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,kFAAkF,CAAC;KAC/F,MAAM,CAAC,cAAc,EAAE,wDAAwD,CAAC;KAChF,MAAM,CAAC,QAAQ,EAAE,4BAA4B,CAAC;KAC9C,MAAM,CAAC,kBAAW,CAAC,CAAC;AAEtB,OAAO;KACL,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,2CAA2C,CAAC;KACxD,MAAM,CACN,gBAAgB,EAChB,iGAAiG,CACjG;KACA,MAAM,CACN,kBAAkB,EAClB,4EAA4E,CAC5E;KACA,MAAM,CAAC,oBAAY,CAAC,CAAC;AAEvB,OAAO;KACL,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,yBAAyB,CAAC;KACtC,MAAM,CAAC,sBAAa,CAAC,CAAC;AAExB,OAAO;KACL,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,6DAA6D,CAAC;KAC1E,MAAM,CAAC,sBAAa,CAAC,CAAC;AAExB,OAAO;KACL,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,sEAAsE,CAAC;KACnF,MAAM,CAAC,wBAAc,CAAC,CAAC;AAEzB,OAAO;KACL,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,2EAA2E,CAAC;KACxF,MAAM,CAAC,sBAAa,CAAC,CAAC;AAExB,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAU,EAAE,EAAE;IACrD,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;IACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC,CAAC,CAAC"}