@coopcli/specsketch 0.1.2 → 0.1.3
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.
- package/README.md +5 -2
- package/dist/cli/index.js +51 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,7 +51,10 @@ specsketch <path-to-openspec-change-dir> [--port <n>]
|
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
- The directory is created if it doesn't exist; its basename becomes the change name.
|
|
54
|
-
- `--port`
|
|
54
|
+
- `--port` sets the preferred port (default 8787). If it's already in use — say another
|
|
55
|
+
specsketch session is running — the server moves to the next available port and the
|
|
56
|
+
printed URL reflects the one actually bound. Binds to 127.0.0.1 exclusively — there is
|
|
57
|
+
no `--host`.
|
|
55
58
|
- **Model picker** (top bar): `claude-opus-4-8` (default), `claude-sonnet-5`, or
|
|
56
59
|
`claude-fable-5`. The choice is saved with the change directory. Fable runs with
|
|
57
60
|
server-side refusal fallbacks to Opus and requires your org to allow 30-day data
|
|
@@ -65,7 +68,7 @@ specsketch <path-to-openspec-change-dir> [--port <n>]
|
|
|
65
68
|
| Startup warning "No Anthropic credentials detected" | Set `ANTHROPIC_API_KEY` or run `ant auth login`, then restart |
|
|
66
69
|
| Generate fails with a 401 / "anthropic-auth" | Same as above — the server found no credentials, or the login profile expired (`ant auth status`) |
|
|
67
70
|
| "Node NN detected — specsketch needs Node >= 22" | Upgrade Node (https://nodejs.org) |
|
|
68
|
-
| Port already in use |
|
|
71
|
+
| Port already in use | Handled automatically — the server moves to the next free port and prints it; `--port <n>` sets the preferred starting port |
|
|
69
72
|
| Regeneration changed a file you hand-edited | Expected when the drawing implicates it — the diagram is the source of truth; use `git diff`/`git checkout` to recover, and keep change dirs in version control |
|
|
70
73
|
|
|
71
74
|
More: [getting started page](https://coopcli.com/products/specsketch) ·
|
package/dist/cli/index.js
CHANGED
|
@@ -374,7 +374,10 @@ var USAGE = `Usage: specsketch <path-to-openspec-change-dir> [--port <n>]
|
|
|
374
374
|
|
|
375
375
|
The directory is created if it doesn't exist. Diagrams autosave to
|
|
376
376
|
diagram.excalidraw inside it; Generate Spec writes proposal.md, design.md,
|
|
377
|
-
tasks.md and specs/<capability>/spec.md alongside
|
|
377
|
+
tasks.md and specs/<capability>/spec.md alongside.
|
|
378
|
+
|
|
379
|
+
--port sets the preferred port (default 8787); if it's already in use the
|
|
380
|
+
server moves to the next available port and prints the actual URL.`;
|
|
378
381
|
var UsageError = class extends Error {
|
|
379
382
|
};
|
|
380
383
|
function parseCliArgs(argv) {
|
|
@@ -396,6 +399,18 @@ function parseCliArgs(argv) {
|
|
|
396
399
|
}
|
|
397
400
|
return { dir, port };
|
|
398
401
|
}
|
|
402
|
+
var PORT_SCAN_LIMIT = 100;
|
|
403
|
+
async function bindAvailablePort(start, tryBind) {
|
|
404
|
+
const end = Math.min(start + PORT_SCAN_LIMIT - 1, 65535);
|
|
405
|
+
for (let port = start; port <= end; port++) {
|
|
406
|
+
try {
|
|
407
|
+
return { port: await tryBind(port), moved: port !== start };
|
|
408
|
+
} catch (err) {
|
|
409
|
+
if (err.code !== "EADDRINUSE") throw err;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
throw new Error(`no free port between ${start} and ${end} \u2014 free one up or pass --port`);
|
|
413
|
+
}
|
|
399
414
|
var MIME = {
|
|
400
415
|
".html": "text/html; charset=utf-8",
|
|
401
416
|
".js": "text/javascript; charset=utf-8",
|
|
@@ -457,7 +472,7 @@ function preflight(log = console.log) {
|
|
|
457
472
|
process.exit(1);
|
|
458
473
|
}
|
|
459
474
|
}
|
|
460
|
-
function main(argv) {
|
|
475
|
+
async function main(argv) {
|
|
461
476
|
preflight();
|
|
462
477
|
let args;
|
|
463
478
|
try {
|
|
@@ -479,20 +494,42 @@ ${USAGE}` : err);
|
|
|
479
494
|
const res = staticResponse(clientDir, new URL(c.req.url).pathname);
|
|
480
495
|
return res ?? c.notFound();
|
|
481
496
|
});
|
|
482
|
-
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
497
|
+
const tryBind = (port2) => new Promise((bound, failed) => {
|
|
498
|
+
const server = serve(
|
|
499
|
+
{ fetch: app.fetch, port: port2, hostname: "127.0.0.1" },
|
|
500
|
+
(info) => bound(info.port)
|
|
501
|
+
);
|
|
502
|
+
server.once("error", (err) => {
|
|
503
|
+
try {
|
|
504
|
+
server.close();
|
|
505
|
+
} catch {
|
|
506
|
+
}
|
|
507
|
+
failed(err);
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
let port;
|
|
511
|
+
let moved;
|
|
512
|
+
try {
|
|
513
|
+
({ port, moved } = await bindAvailablePort(args.port, tryBind));
|
|
514
|
+
} catch (err) {
|
|
515
|
+
console.error(`specsketch: ${err instanceof Error ? err.message : String(err)}`);
|
|
516
|
+
process.exit(1);
|
|
517
|
+
}
|
|
518
|
+
const source = detectCredentialSource();
|
|
519
|
+
console.log(`coopcli specsketch`);
|
|
520
|
+
console.log(` workspace: ${workspace.dir}`);
|
|
521
|
+
console.log(` open: http://127.0.0.1:${port}`);
|
|
522
|
+
if (moved) {
|
|
523
|
+
console.log(` port: ${args.port} was in use \u2014 moved to ${port}`);
|
|
524
|
+
}
|
|
525
|
+
if (source) {
|
|
526
|
+
console.log(` auth: ${source}`);
|
|
527
|
+
} else {
|
|
528
|
+
console.log(`
|
|
491
529
|
\u26A0 ${CREDENTIALS_HINT}
|
|
492
530
|
`);
|
|
493
|
-
|
|
494
|
-
});
|
|
531
|
+
}
|
|
495
532
|
}
|
|
496
533
|
|
|
497
534
|
// server/cli-entry.ts
|
|
498
|
-
main(process.argv.slice(2));
|
|
535
|
+
void main(process.argv.slice(2));
|
package/package.json
CHANGED