@getpawl/setup 1.3.7 → 1.4.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.
Files changed (2) hide show
  1. package/dist/index.js +119 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -15,7 +15,8 @@ async function main() {
15
15
  if (arg === "sync") {
16
16
  pawlSync(process.argv[3]);
17
17
  } else if (arg === "connect") {
18
- await pawlConnect();
18
+ const connected = await pawlConnect();
19
+ if (!connected) process.exit(1);
19
20
  } else if (arg === "init") {
20
21
  const rest = process.argv.slice(3);
21
22
  const noConnect = rest.includes("--no-connect");
@@ -149,7 +150,7 @@ async function pawlConnect() {
149
150
  const cwd = process.cwd();
150
151
  if (!(0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, ".pawl"))) {
151
152
  console.error("Error: .pawl/ not found \u2014 run `pawl init` first.");
152
- process.exit(1);
153
+ return null;
153
154
  }
154
155
  const apiUrl = DEFAULT_API_URL;
155
156
  const code = generateCode();
@@ -161,7 +162,7 @@ async function pawlConnect() {
161
162
  });
162
163
  if (!startRes.ok) {
163
164
  console.error("Error: Could not start connect session. Is the API reachable?");
164
- process.exit(1);
165
+ return null;
165
166
  }
166
167
  const connectUrl = `https://app.getpawl.dev/connect?code=${code}&repo=${encodeURIComponent(repoName)}`;
167
168
  console.log(`
@@ -173,7 +174,7 @@ async function pawlConnect() {
173
174
  const result = await pollForKey(apiUrl, code);
174
175
  if (!result) {
175
176
  console.error(" Session expired or cancelled. Run `pawl connect` to try again.");
176
- process.exit(1);
177
+ return null;
177
178
  }
178
179
  (0, import_node_fs.mkdirSync)((0, import_node_path.join)(cwd, ".pawl"), { recursive: true });
179
180
  writePawlEnvFile(cwd, result);
@@ -183,6 +184,103 @@ async function pawlConnect() {
183
184
  console.log(" Useful commands:");
184
185
  console.log(" pawl sync --pull Pull latest context before starting work");
185
186
  console.log(" pawl sync Push session data manually\n");
187
+ return result;
188
+ }
189
+ function hyperlink(url, text) {
190
+ return `\x1B]8;;${url}\x1B\\${text}\x1B]8;;\x1B\\`;
191
+ }
192
+ function getRepoUrl(cwd) {
193
+ try {
194
+ return (0, import_node_child_process.execSync)("git remote get-url origin", { cwd, stdio: "pipe" }).toString().trim();
195
+ } catch {
196
+ return null;
197
+ }
198
+ }
199
+ var PHASE_MESSAGES = {
200
+ scanned: "scanned",
201
+ generating: " Generating spec tree with AI...",
202
+ fetching: " Fetching file samples...",
203
+ refining: " Refining coverage status...",
204
+ extracting: " Extracting decisions..."
205
+ };
206
+ async function autoBootstrap(projectId, apiKey, repoUrl, apiUrl) {
207
+ let sseController = null;
208
+ try {
209
+ sseController = new AbortController();
210
+ const ssePromise = listenForProgress(
211
+ `${apiUrl}/api/projects/${projectId}/events`,
212
+ apiKey,
213
+ sseController.signal
214
+ );
215
+ const res = await fetch(`${apiUrl}/api/projects/${projectId}/bootstrap/cli-scan`, {
216
+ method: "POST",
217
+ headers: {
218
+ "Content-Type": "application/json",
219
+ Authorization: `Bearer ${apiKey}`
220
+ },
221
+ body: JSON.stringify({ repoUrl })
222
+ });
223
+ sseController.abort();
224
+ await ssePromise.catch(() => {
225
+ });
226
+ if (!res.ok) return null;
227
+ const data = await res.json();
228
+ return { specsGenerated: data.specsGenerated, previewUrl: data.previewUrl };
229
+ } catch {
230
+ sseController?.abort();
231
+ return null;
232
+ }
233
+ }
234
+ async function listenForProgress(url, apiKey, signal) {
235
+ let res;
236
+ try {
237
+ res = await fetch(url, {
238
+ headers: { Authorization: `Bearer ${apiKey}` },
239
+ signal
240
+ });
241
+ } catch {
242
+ return;
243
+ }
244
+ if (!res.ok || !res.body) return;
245
+ const reader = res.body.getReader();
246
+ const decoder = new TextDecoder();
247
+ let buffer = "";
248
+ try {
249
+ while (true) {
250
+ const { done, value } = await reader.read();
251
+ if (done) break;
252
+ buffer += decoder.decode(value, { stream: true });
253
+ const lines = buffer.split("\n");
254
+ buffer = lines.pop() ?? "";
255
+ let eventType = "";
256
+ for (const line of lines) {
257
+ if (line.startsWith("event: ")) {
258
+ eventType = line.slice(7).trim();
259
+ } else if (line.startsWith("data: ") && eventType) {
260
+ try {
261
+ const data = JSON.parse(line.slice(6));
262
+ if (eventType === "bootstrap:progress") {
263
+ const phase = data.phase;
264
+ if (phase === "scanned") {
265
+ console.log(` \u2713 ${data.detail || "Repository scanned"}`);
266
+ } else if (PHASE_MESSAGES[phase]) {
267
+ console.log(PHASE_MESSAGES[phase]);
268
+ }
269
+ } else if (eventType === "bootstrap:completed") {
270
+ return;
271
+ }
272
+ } catch {
273
+ }
274
+ eventType = "";
275
+ } else if (line === "") {
276
+ eventType = "";
277
+ }
278
+ }
279
+ }
280
+ } catch {
281
+ } finally {
282
+ reader.releaseLock();
283
+ }
186
284
  }
187
285
  function legacySetup(encoded) {
188
286
  let config;
@@ -217,15 +315,15 @@ async function pawlInit(key, opts) {
217
315
  migrateIfNeeded(cwd, detected.hasAgentMapDir);
218
316
  (0, import_node_fs.mkdirSync)((0, import_node_path.join)(cwd, ".pawl"), { recursive: true });
219
317
  if (key) {
220
- let config;
318
+ let config2;
221
319
  try {
222
320
  const decoded = Buffer.from(key, "base64").toString("utf-8");
223
- config = JSON.parse(decoded);
321
+ config2 = JSON.parse(decoded);
224
322
  } catch {
225
323
  console.error("Error: Invalid project key \u2014 could not decode.");
226
324
  process.exit(1);
227
325
  }
228
- writePawlEnvFile(cwd, config);
326
+ writePawlEnvFile(cwd, config2);
229
327
  }
230
328
  writePawlSyncScript(cwd);
231
329
  writePawlSyncMjs(cwd);
@@ -242,9 +340,22 @@ async function pawlInit(key, opts) {
242
340
  writeAgentsMd(cwd);
243
341
  updateGitignore(cwd);
244
342
  printSummary(detected, !!key, !!opts?.noConnect);
343
+ let config = null;
245
344
  if (!key && !opts?.noConnect) {
246
345
  console.log("\nConnecting to Pawl dashboard...\n");
247
- await pawlConnect();
346
+ config = await pawlConnect();
347
+ }
348
+ if (config) {
349
+ const repoUrl = getRepoUrl(cwd);
350
+ if (repoUrl) {
351
+ console.log("Scanning your repo to generate specs...");
352
+ const result = await autoBootstrap(config.projectId, config.apiKey, repoUrl, config.apiUrl);
353
+ if (result && result.specsGenerated > 0) {
354
+ console.log(` Generated ${result.specsGenerated} draft specs`);
355
+ console.log(` Review and confirm at: ${hyperlink(result.previewUrl, result.previewUrl)}
356
+ `);
357
+ }
358
+ }
248
359
  }
249
360
  }
250
361
  function detectAgents(cwd) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getpawl/setup",
3
- "version": "1.3.7",
3
+ "version": "1.4.1",
4
4
  "type": "commonjs",
5
5
  "description": "One-shot setup for Pawl + Claude Code hooks",
6
6
  "bin": {