@openparachute/vault 0.6.0 → 0.6.2-rc.1

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.
@@ -1,8 +1,14 @@
1
1
  /**
2
2
  * Tests for `buildInitSummaryLines` — the post-install summary printed at the
3
- * end of `vault init`. The summary branches on the (addMcp, addToken) decision
4
- * matrix; these tests cover all four cells plus the token surfacing /
5
- * Bearer-example rules.
3
+ * end of `vault init`.
4
+ *
5
+ * 2026-06-23 messaging realignment: the site no longer claims "Claude Code is
6
+ * auto-configured," and init no longer writes ~/.claude.json by default. The
7
+ * summary now (1) leads with the web setup wizard hand-off and (2) always
8
+ * surfaces the self-serve connect info — the connector URL plus a
9
+ * ready-to-paste `claude mcp add` command — so a Claude Code user opts in by
10
+ * copy-paste rather than via a silent side effect. These tests pin that copy
11
+ * across the (addMcp, addToken) decision matrix.
6
12
  */
7
13
 
8
14
  import { describe, test, expect } from "bun:test";
@@ -13,6 +19,7 @@ const baseInput = {
13
19
  bindHost: "127.0.0.1",
14
20
  port: 1940,
15
21
  mcpUrl: "http://127.0.0.1:1940/vault/default/mcp",
22
+ wizardUrl: "http://127.0.0.1:1939/admin/setup",
16
23
  vaultName: "default",
17
24
  };
18
25
 
@@ -21,161 +28,154 @@ function lines(addMcp: boolean, addToken: boolean, apiKey: string | undefined) {
21
28
  }
22
29
 
23
30
  describe("buildInitSummaryLines", () => {
24
- describe("MCP=Y + token=Y (most common)", () => {
25
- const out = lines(true, true, "pvt_abc123").join("\n");
31
+ // The wizard hand-off + self-serve connect info are the load-bearing pieces
32
+ // of the new messaging — they must appear in every branch.
33
+ describe("always surfaces the wizard + the copy-paste connect info", () => {
34
+ for (const [addMcp, addToken] of [
35
+ [true, true],
36
+ [true, false],
37
+ [false, true],
38
+ [false, false],
39
+ ] as const) {
40
+ const out = lines(addMcp, addToken, addMcp || addToken ? "pvt_k" : undefined).join("\n");
26
41
 
27
- test("prints token prominently", () => {
28
- expect(out).toContain("Your API token: pvt_abc123");
29
- });
42
+ test(`(addMcp=${addMcp}, addToken=${addToken}) prints the web wizard URL prominently`, () => {
43
+ expect(out).toContain("Finish setup in your browser:");
44
+ expect(out).toContain("http://127.0.0.1:1939/admin/setup");
45
+ });
30
46
 
31
- test("notes token is baked into ~/.claude.json", () => {
32
- expect(out).toContain("Baked into ~/.claude.json for Claude Code");
33
- });
47
+ test(`(addMcp=${addMcp}, addToken=${addToken}) surfaces the connector URL`, () => {
48
+ expect(out).toContain("http://127.0.0.1:1940/vault/default/mcp");
49
+ });
34
50
 
35
- test("includes save-it-now warning", () => {
36
- expect(out).toContain("Won't be shown again — save it now.");
51
+ test(`(addMcp=${addMcp}, addToken=${addToken}) always prints Config: and Server: lines`, () => {
52
+ expect(out).toContain("Config: /tmp/parachute");
53
+ expect(out).toContain("Server: http://127.0.0.1:1940");
54
+ });
55
+ }
56
+ });
57
+
58
+ // The new DEFAULT init path — no MCP write, no token minted (per-user OAuth).
59
+ // init pointed the operator at the wizard and surfaced the copy-paste connect
60
+ // info; it did NOT write ~/.claude.json.
61
+ describe("DEFAULT (addMcp=N, token=N) — wizard hand-off + copy-paste opt-in", () => {
62
+ const out = lines(false, false, undefined).join("\n");
63
+
64
+ test("does NOT claim Claude Code is already wired in", () => {
65
+ expect(out).not.toContain("already wired in");
66
+ expect(out).not.toContain("Baked into ~/.claude.json");
37
67
  });
38
68
 
39
- test("includes Bearer curl example", () => {
69
+ test("offers the ready-to-paste `claude mcp add` opt-in command", () => {
40
70
  expect(out).toContain(
41
- 'curl -H "Authorization: Bearer pvt_abc123" http://localhost:1940/api/notes',
71
+ "claude mcp add --transport http parachute-vault http://127.0.0.1:1940/vault/default/mcp",
42
72
  );
43
73
  });
44
74
 
45
- test("Next steps mentions starting a Claude Code session", () => {
46
- expect(out).toContain("Start a new Claude Code session");
47
- });
48
- });
49
-
50
- describe("MCP=Y + token=N (MCP wired, token not surfaced)", () => {
51
- const out = lines(true, false, "pvt_secret").join("\n");
52
-
53
- test("does not print the token prominently", () => {
54
- expect(out).not.toContain("pvt_secret");
75
+ test("points at the guided installer as an alternative", () => {
76
+ expect(out).toContain("parachute-vault mcp-install");
55
77
  });
56
78
 
57
- test("does not include the 'Baked into' bullet", () => {
58
- expect(out).not.toContain("Baked into ~/.claude.json");
79
+ test("frames OAuth-first connect no token needed", () => {
80
+ expect(out).toContain("no token needed, you'll sign in on first use");
59
81
  });
60
82
 
61
- test("includes the mcp-install-later hint", () => {
62
- expect(out).toContain("Token in ~/.claude.json");
63
- expect(out).toContain("parachute-vault mcp-install");
83
+ test("offers the scope-narrow opt-in mint for scripts (full vault:<name>:read, never admin)", () => {
84
+ // Must be the three-segment named-resource form the hub mint-token model
85
+ // requires — a bare `vault:read` would mint a malformed scope (vault#443).
86
+ expect(out).toContain("parachute auth mint-token --scope vault:default:read");
87
+ expect(out).not.toContain("vault:admin");
64
88
  });
65
89
 
66
- test("omits the Bearer curl example", () => {
90
+ test("does not print any token", () => {
91
+ expect(out).not.toContain("Your API token:");
92
+ expect(out).not.toMatch(/pvt_/);
67
93
  expect(out).not.toContain("Authorization: Bearer");
68
94
  });
69
95
 
70
- test("still shows the Claude-Code-session next step", () => {
71
- expect(out).toContain("Start a new Claude Code session");
96
+ test("threads a non-default vault name into the mint-token scope + connector URL", () => {
97
+ const out2 = buildInitSummaryLines({
98
+ ...baseInput,
99
+ vaultName: "journal",
100
+ mcpUrl: "http://127.0.0.1:1940/vault/journal/mcp",
101
+ addMcp: false,
102
+ addToken: false,
103
+ apiKey: undefined,
104
+ }).join("\n");
105
+ expect(out2).toContain("parachute auth mint-token --scope vault:journal:read");
106
+ expect(out2).toContain("http://127.0.0.1:1940/vault/journal/mcp");
72
107
  });
73
108
  });
74
109
 
75
- describe("MCP=N + token=Y (token only)", () => {
76
- const out = lines(false, true, "pvt_xyz").join("\n");
77
-
78
- test("prints token prominently", () => {
79
- expect(out).toContain("Your API token: pvt_xyz");
80
- });
81
-
82
- test("omits the 'Baked into' bullet (no claude.json entry written)", () => {
83
- expect(out).not.toContain("Baked into ~/.claude.json");
84
- });
110
+ // Opt-in: operator passed --configure-claude-code, so init DID write the entry.
111
+ describe("opted into MCP write (addMcp=Y, token=N, OAuth)", () => {
112
+ const out = lines(true, false, undefined).join("\n");
85
113
 
86
- test("includes Bearer curl example", () => {
87
- expect(out).toContain('Authorization: Bearer pvt_xyz');
114
+ test("tells the user Claude Code is already wired in", () => {
115
+ expect(out).toContain("Claude Code is already wired in");
88
116
  });
89
117
 
90
- test("Next steps points at any local MCP client", () => {
91
- expect(out).toContain("Point any local MCP client");
118
+ test("still surfaces the connector URL for other clients", () => {
92
119
  expect(out).toContain("http://127.0.0.1:1940/vault/default/mcp");
93
120
  });
94
121
 
95
- test("Next steps offers mcp-install as a way back", () => {
96
- expect(out).toContain("parachute-vault mcp-install");
122
+ test("does NOT print or imply any minted token", () => {
123
+ expect(out).not.toContain("Your API token:");
124
+ expect(out).not.toContain("Authorization: Bearer");
97
125
  });
98
126
  });
99
127
 
100
- // vault#442: the DEFAULT init path MCP wired, NO token minted (per-user
101
- // OAuth). The summary must LEAD with the OAuth connect path, never mint, and
102
- // never surface the old "no token issued" failure copy.
103
- describe("MCP=Y + no token (vault#442 OAuth default)", () => {
104
- const out = lines(true, false, undefined).join("\n");
105
-
106
- test("leads with the OAuth connect message — no token needed", () => {
107
- expect(out).toContain("no token needed, you'll sign in on first use");
108
- });
109
-
110
- test("tells the user Claude Code is already wired in", () => {
111
- expect(out).toContain("Claude Code is already wired in");
112
- });
128
+ describe("opted into MCP write + token minted (addMcp=Y, token=Y)", () => {
129
+ const out = lines(true, true, "pvt_abc123").join("\n");
113
130
 
114
- test("shows the OAuth `claude mcp add` command for other clients", () => {
115
- expect(out).toContain(
116
- "claude mcp add --transport http parachute-vault http://127.0.0.1:1940/vault/default/mcp",
117
- );
131
+ test("prints token prominently", () => {
132
+ expect(out).toContain("Your API token: pvt_abc123");
118
133
  });
119
134
 
120
- test("offers the scope-narrow opt-in mint for scripts (full vault:<name>:read, never admin)", () => {
121
- // Must be the three-segment named-resource form the hub mint-token model
122
- // requires — a bare `vault:read` would mint a malformed scope (vault#443).
123
- expect(out).toContain("parachute auth mint-token --scope vault:default:read");
124
- expect(out).not.toContain("--scope vault:read ");
125
- expect(out).not.toMatch(/--scope vault:read$/m);
126
- expect(out).not.toContain("vault:admin");
135
+ test("notes token is baked into ~/.claude.json", () => {
136
+ expect(out).toContain("Baked into ~/.claude.json for Claude Code");
127
137
  });
128
138
 
129
- test("does NOT print or imply any minted token", () => {
130
- expect(out).not.toContain("Your API token:");
131
- expect(out).not.toContain("Baked into ~/.claude.json");
132
- expect(out).not.toContain("Authorization: Bearer");
139
+ test("includes save-it-now warning", () => {
140
+ expect(out).toContain("Won't be shown again — save it now.");
133
141
  });
134
142
 
135
- test("does NOT surface the old no-token-issued failure copy", () => {
136
- expect(out).not.toContain("No token issued");
143
+ test("includes Bearer curl example", () => {
144
+ expect(out).toContain(
145
+ 'curl -H "Authorization: Bearer pvt_abc123" http://localhost:1940/api/notes',
146
+ );
137
147
  });
138
148
 
139
- test("threads a non-default vault name into the mint-token scope", () => {
140
- const out2 = buildInitSummaryLines({
141
- ...baseInput,
142
- vaultName: "journal",
143
- mcpUrl: "http://127.0.0.1:1940/vault/journal/mcp",
144
- addMcp: true,
145
- addToken: false,
146
- apiKey: undefined,
147
- }).join("\n");
148
- expect(out2).toContain("parachute auth mint-token --scope vault:journal:read");
149
- expect(out2).not.toContain("vault:default:read");
149
+ test("Next steps mentions starting a Claude Code session", () => {
150
+ expect(out).toContain("Start a new Claude Code session");
150
151
  });
151
152
  });
152
153
 
153
- describe("MCP=N + token=N (OAuth default, Claude Code not wired)", () => {
154
- const out = lines(false, false, undefined).join("\n");
154
+ describe("token only, no MCP write (addMcp=N, token=Y, minted)", () => {
155
+ const out = lines(false, true, "pvt_xyz").join("\n");
155
156
 
156
- test("frames skipping the MCP entry as OAuth-first, not 'unreachable'", () => {
157
- expect(out).toContain("uses per-user OAuth, no token needed");
158
- expect(out).not.toContain("your vault isn't reachable by any client");
157
+ test("prints token prominently", () => {
158
+ expect(out).toContain("Your API token: pvt_xyz");
159
159
  });
160
160
 
161
- test("points to mcp-install (no token-minting framing)", () => {
162
- expect(out).toContain("parachute-vault mcp-install");
163
- expect(out).not.toContain("mints a hub JWT");
161
+ test("omits the 'Baked into' bullet (no claude.json entry written)", () => {
162
+ expect(out).not.toContain("Baked into ~/.claude.json");
164
163
  });
165
164
 
166
- test("does not print any token", () => {
167
- expect(out).not.toContain("Your API token:");
168
- expect(out).not.toMatch(/pvt_/);
165
+ test("includes Bearer curl example", () => {
166
+ expect(out).toContain("Authorization: Bearer pvt_xyz");
169
167
  });
170
168
 
171
- test("omits the Bearer curl example", () => {
172
- expect(out).not.toContain("Authorization: Bearer");
169
+ test("surfaces the connector URL + a copy-paste Claude Code opt-in", () => {
170
+ expect(out).toContain("http://127.0.0.1:1940/vault/default/mcp");
171
+ expect(out).toContain(
172
+ "claude mcp add --transport http parachute-vault http://127.0.0.1:1940/vault/default/mcp",
173
+ );
173
174
  });
174
175
  });
175
176
 
176
- // Explicit opt-in but no hub reachable to mint (vault#282 Stage 2 path,
177
- // reached only when the operator passes --token without a hub).
178
- describe("MCP=N + token=Y but no hub (opt-in mint failed, standalone)", () => {
177
+ // Explicit opt-in to a token but no hub reachable to mint (vault#282 Stage 2).
178
+ describe("token opt-in but no hub (standalone, mint failed)", () => {
179
179
  const out = buildInitSummaryLines({
180
180
  ...baseInput,
181
181
  addMcp: false,
@@ -198,12 +198,14 @@ describe("buildInitSummaryLines", () => {
198
198
  test("does NOT claim the vault is reachable (no hub present)", () => {
199
199
  expect(out).not.toContain("Your vault is still reachable");
200
200
  });
201
+
202
+ test("still surfaces the connector URL for self-serve connect", () => {
203
+ expect(out).toContain("http://127.0.0.1:1940/vault/default/mcp");
204
+ });
201
205
  });
202
206
 
203
- // #445: opted into a token, none minted, but a HUB IS PRESENT. The vault is
204
- // reachable via the hub's browser OAuth flow even with no header-auth token,
205
- // so the standalone "isn't reachable" framing would be false here.
206
- describe("MCP=N + token=Y, no token minted, but hub present (#445)", () => {
207
+ // #445: opted into a token, none minted, but a HUB IS PRESENT.
208
+ describe("token opt-in, none minted, hub present (#445)", () => {
207
209
  const out = buildInitSummaryLines({
208
210
  ...baseInput,
209
211
  addMcp: false,
@@ -227,22 +229,20 @@ describe("buildInitSummaryLines", () => {
227
229
  expect(out).not.toContain("Once a hub is running");
228
230
  expect(out).not.toContain("VAULT_AUTH_TOKEN");
229
231
  });
230
-
231
- test("never claims the vault isn't reachable by any client", () => {
232
- expect(out).not.toContain("isn't reachable by any client");
233
- });
234
232
  });
235
233
 
236
- test("always prints Config: and Server: lines", () => {
237
- for (const [addMcp, addToken] of [
238
- [true, true],
239
- [true, false],
240
- [false, true],
241
- [false, false],
242
- ] as const) {
243
- const out = lines(addMcp, addToken, addMcp || addToken ? "pvt_k" : undefined).join("\n");
244
- expect(out).toContain("Config: /tmp/parachute");
245
- expect(out).toContain("Server: http://127.0.0.1:1940");
246
- }
234
+ // Defensive: the summary must still render coherently if no wizard URL is
235
+ // supplied (e.g. an older caller / a hub-origin resolution failure).
236
+ test("omits the wizard hand-off cleanly when wizardUrl is absent", () => {
237
+ const out = buildInitSummaryLines({
238
+ ...baseInput,
239
+ wizardUrl: undefined,
240
+ addMcp: false,
241
+ addToken: false,
242
+ apiKey: undefined,
243
+ }).join("\n");
244
+ expect(out).not.toContain("Finish setup in your browser:");
245
+ // The connect info is still surfaced.
246
+ expect(out).toContain("http://127.0.0.1:1940/vault/default/mcp");
247
247
  });
248
248
  });
@@ -5,13 +5,32 @@
5
5
  */
6
6
 
7
7
  export type InitSummaryInput = {
8
+ /**
9
+ * Whether init WROTE the Claude Code MCP config (~/.claude.json) this run.
10
+ * As of 2026-06-23 this is opt-in (default false) — init's primary job is to
11
+ * point the operator at the web setup wizard and surface the self-serve
12
+ * connect info, not to write a config file as a side effect.
13
+ */
8
14
  addMcp: boolean;
9
15
  addToken: boolean;
10
16
  apiKey: string | undefined;
11
17
  configDir: string;
12
18
  bindHost: string;
13
19
  port: number;
20
+ /**
21
+ * The vault's MCP connector URL — `<hub-origin>/vault/<name>/mcp` (hub-origin
22
+ * / expose-state aware). Surfaced in the summary for self-serve copy-paste:
23
+ * a ready-to-run `claude mcp add ...` command is built from it so a Claude
24
+ * Code user can opt in by pasting one line, AND it's printed plain so any
25
+ * other MCP client can be pointed at it.
26
+ */
14
27
  mcpUrl: string;
28
+ /**
29
+ * The web setup wizard URL — `<hub-origin>/admin/setup`. init's primary job
30
+ * is to get the operator into this wizard, so it's printed prominently at the
31
+ * top of the summary.
32
+ */
33
+ wizardUrl?: string | undefined;
15
34
  /**
16
35
  * The default vault's name — used to emit the three-segment
17
36
  * `vault:<vaultName>:read` scope in the OAuth-first mint-token suggestion
@@ -22,8 +41,8 @@ export type InitSummaryInput = {
22
41
  /**
23
42
  * Guidance from the bootstrap-credential step when no token could be issued
24
43
  * (standalone install, no hub reachable — vault#282 Stage 2). Surfaced when
25
- * the operator wanted a token (`addMcp || addToken`) but `apiKey` is
26
- * undefined, so they know why and how to make the vault reachable.
44
+ * the operator wanted a token (`addToken`) but `apiKey` is undefined, so they
45
+ * know why and how to make the vault reachable.
27
46
  */
28
47
  noTokenGuidance?: string | undefined;
29
48
  /**
@@ -38,54 +57,66 @@ export type InitSummaryInput = {
38
57
  };
39
58
 
40
59
  /**
41
- * Build the post-install summary lines for `vault init`, branched on the
42
- * (addMcp, addToken, apiKey) decision matrix.
60
+ * Build the post-install summary lines for `vault init`.
61
+ *
62
+ * 2026-06-23 messaging realignment: the site no longer claims "Claude Code is
63
+ * auto-configured," and init no longer writes `~/.claude.json` by default.
64
+ * init's job is to (1) point the operator at the web setup wizard, and
65
+ * (2) SURFACE the self-serve connect info — the connector URL + a ready-to-paste
66
+ * `claude mcp add ...` line — so a Claude Code user opts in by copy-paste rather
67
+ * than a silent side effect.
43
68
  *
44
- * vault#442: the DEFAULT is per-user OAuth no token is minted, and the
45
- * Claude Code MCP entry is written without a baked bearer (browser sign-in on
46
- * first connect). A token is minted only on explicit opt-in (`addToken`), and
47
- * then scope-narrow. Branches:
69
+ * The summary is built in three parts:
70
+ * 1. Wizard hand-off (always) "finish setup in your browser: <wizardUrl>".
71
+ * 2. Connect-your-AI block the connector URL + copy-paste `claude mcp add`,
72
+ * branched on whether init wrote the MCP entry / minted a token.
73
+ * 3. Config / server / next-steps footer.
48
74
  *
49
- * addMcp, !apiKey → OAuth-first: connect, sign in on first use
50
- * addMcp, addToken, apiKey → token baked into claude.json + printed
51
- * addMcp, !addToken, apiKey → token baked into claude.json, hint
52
- * !addMcp, addToken, apiKey → token printed prominently
53
- * !addMcp, addToken, !apiKey → opted into a token but no hub reachable
54
- * !addMcp, !addToken → OAuth-first: add Claude Code later
75
+ * vault#442: per-user OAuth is the default — no token is minted unless the
76
+ * operator opts in (`addToken`), and then it's scope-narrow.
55
77
  */
56
78
  export function buildInitSummaryLines(input: InitSummaryInput): string[] {
57
- const { addMcp, addToken, apiKey, configDir, bindHost, port, mcpUrl, vaultName, noTokenGuidance, hubPresent } = input;
79
+ const { addMcp, addToken, apiKey, configDir, bindHost, port, mcpUrl, wizardUrl, vaultName, noTokenGuidance, hubPresent } = input;
58
80
  const lines: string[] = [];
59
81
  lines.push("");
60
82
  lines.push("---");
61
83
 
62
- if (addMcp && apiKey && addToken) {
84
+ // 1. Wizard hand-off the primary purpose of init is to get the operator
85
+ // into the web setup wizard. Lead with it.
86
+ if (wizardUrl) {
63
87
  lines.push("");
64
- lines.push(`Your API token: ${apiKey}`);
65
- lines.push(` - Baked into ~/.claude.json for Claude Code ✓`);
66
- lines.push(` - Paste into your other MCP client's config, or use as Authorization: Bearer <token>`);
67
- lines.push(` - Won't be shown again — save it now.`);
68
- } else if (addMcp && apiKey && !addToken) {
69
- lines.push("");
70
- lines.push(
71
- "Token in ~/.claude.json; run `parachute-vault mcp-install` later if you need one for other clients.",
72
- );
73
- } else if (addMcp && !apiKey) {
74
- // vault#442 default: OAuth-first. The MCP entry is wired without a bearer —
75
- // Claude Code signs in via browser OAuth on first connect. No token needed.
76
- lines.push("");
77
- lines.push("Connect your AI — no token needed, you'll sign in on first use:");
78
- lines.push(` Claude Code is already wired in (~/.claude.json) — just start a session.`);
79
- lines.push(` Other clients: claude mcp add --transport http parachute-vault ${mcpUrl}`);
80
- lines.push(` Need a header-auth token for a script? parachute auth mint-token --scope vault:${vaultName}:read`);
81
- } else if (!addMcp && addToken && apiKey) {
88
+ lines.push("Finish setup in your browser:");
89
+ lines.push(` ${wizardUrl}`);
90
+ }
91
+
92
+ // The copy-paste opt-in line for Claude Code (and any client that speaks the
93
+ // `claude mcp add` form). Built from the connector URL so it's the real
94
+ // endpoint, hub-origin aware.
95
+ const claudeAddCmd = `claude mcp add --transport http parachute-vault ${mcpUrl}`;
96
+
97
+ // 2. Connect-your-AI surface the self-serve connect info every time.
98
+ if (addToken && apiKey) {
99
+ // Operator opted into a header-auth token AND it was minted. Surface it
100
+ // prominently (won't be shown again), plus the connector URL.
82
101
  lines.push("");
83
102
  lines.push(`Your API token: ${apiKey}`);
84
- lines.push(` - Paste into your other MCP client's config, or use as Authorization: Bearer <token>`);
103
+ if (addMcp) {
104
+ lines.push(` - Baked into ~/.claude.json for Claude Code ✓`);
105
+ }
106
+ lines.push(` - Paste into another MCP client's config, or use as Authorization: Bearer <token>`);
85
107
  lines.push(` - Won't be shown again — save it now.`);
86
- } else if (!addMcp && addToken && !apiKey) {
108
+ lines.push("");
109
+ lines.push("Connector URL (point any MCP client here):");
110
+ lines.push(` ${mcpUrl}`);
111
+ if (!addMcp) {
112
+ lines.push("");
113
+ lines.push("Add Claude Code by copy-paste:");
114
+ lines.push(` ${claudeAddCmd}`);
115
+ }
116
+ } else if (addToken && !apiKey) {
87
117
  // Explicitly opted into a token but none was minted (vault#282 Stage 2 —
88
- // vault no longer mints local pvt_* tokens). Surface why + recovery.
118
+ // vault no longer mints local pvt_* tokens). Surface why + recovery, then
119
+ // still print the self-serve connect info.
89
120
  lines.push("");
90
121
  lines.push(
91
122
  noTokenGuidance ??
@@ -113,15 +144,23 @@ export function buildInitSummaryLines(input: InitSummaryInput): string[] {
113
144
  " or set VAULT_AUTH_TOKEN for an operator-channel bearer.",
114
145
  );
115
146
  }
116
- } else if (!addMcp && !addToken) {
117
- // OAuth-first, but the operator skipped wiring Claude Code too.
118
147
  lines.push("");
119
- lines.push(
120
- "Skipped the Claude Code MCP entry. Add it anytime — it uses per-user OAuth, no token needed:",
121
- );
122
- lines.push(
123
- " parachute-vault mcp-install",
124
- );
148
+ lines.push("Connect your AI — no token needed, you'll sign in on first use:");
149
+ lines.push(` Connector URL: ${mcpUrl}`);
150
+ lines.push(` Claude Code: ${claudeAddCmd}`);
151
+ } else {
152
+ // Default path (no token). Per-user OAuth — sign in on first connect.
153
+ lines.push("");
154
+ lines.push("Connect your AI — no token needed, you'll sign in on first use:");
155
+ lines.push(` Connector URL: ${mcpUrl}`);
156
+ if (addMcp) {
157
+ lines.push(` Claude Code is already wired in (~/.claude.json) — just start a session.`);
158
+ lines.push(` Other clients: ${claudeAddCmd}`);
159
+ } else {
160
+ lines.push(` Claude Code: ${claudeAddCmd}`);
161
+ lines.push(` Other clients (Codex, Goose, OpenCode, Cursor, Zed, Cline): point them at the connector URL above.`);
162
+ }
163
+ lines.push(` Need a header-auth token for a script? parachute auth mint-token --scope vault:${vaultName}:read`);
125
164
  }
126
165
 
127
166
  lines.push("");
@@ -137,19 +176,15 @@ export function buildInitSummaryLines(input: InitSummaryInput): string[] {
137
176
 
138
177
  lines.push("");
139
178
  lines.push(`Next steps:`);
179
+ if (wizardUrl) {
180
+ lines.push(` - Finish setup in the web wizard: ${wizardUrl}`);
181
+ }
140
182
  if (addMcp) {
141
183
  lines.push(` - Start a new Claude Code session — your Vault is already wired in. Try:`);
142
184
  lines.push(` claude "Help me set up my parachute vault"`);
143
- lines.push(` - Or point any other local MCP client (Codex, Goose, OpenCode, Cursor,`);
144
- lines.push(` Zed, Cline, your own agent) at:`);
145
- lines.push(` ${mcpUrl}`);
146
- } else if (addToken) {
147
- lines.push(` - Point any local MCP client (Codex, Goose, OpenCode, Cursor, Zed,`);
148
- lines.push(` Cline, your own agent) at:`);
149
- lines.push(` ${mcpUrl}`);
150
- lines.push(` - Or add Claude Code back anytime: parachute-vault mcp-install`);
151
185
  } else {
152
- lines.push(` - Add Claude Code: parachute-vault mcp-install`);
186
+ lines.push(` - Wire Claude Code (copy-paste): ${claudeAddCmd}`);
187
+ lines.push(` or run the guided installer: parachute-vault mcp-install`);
153
188
  }
154
189
  lines.push(` - Check status: parachute-vault status`);
155
190
  lines.push(` - Edit config: parachute-vault config`);