@specship/specship-win32-arm64 0.11.7 → 0.11.9
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/lib/dist/bin/specship.js +139 -13
- package/lib/dist/bin/specship.js.map +1 -1
- package/lib/dist/commands/{ss-check.md → specship/check.md} +5 -0
- package/lib/dist/commands/specship/spec.md +338 -0
- package/lib/dist/db/migrations.d.ts.map +1 -1
- package/lib/dist/db/migrations.js +1 -0
- package/lib/dist/db/migrations.js.map +1 -1
- package/lib/dist/enforce/enforce.d.ts +15 -0
- package/lib/dist/enforce/enforce.d.ts.map +1 -1
- package/lib/dist/enforce/enforce.js +43 -1
- package/lib/dist/enforce/enforce.js.map +1 -1
- package/lib/dist/extraction/index.d.ts +6 -0
- package/lib/dist/extraction/index.d.ts.map +1 -1
- package/lib/dist/extraction/index.js.map +1 -1
- package/lib/dist/index.d.ts +10 -2
- package/lib/dist/index.d.ts.map +1 -1
- package/lib/dist/index.js +54 -10
- package/lib/dist/index.js.map +1 -1
- package/lib/dist/installer/index.d.ts.map +1 -1
- package/lib/dist/installer/index.js +35 -0
- package/lib/dist/installer/index.js.map +1 -1
- package/lib/dist/installer/instructions-template.d.ts +2 -2
- package/lib/dist/installer/instructions-template.js +5 -5
- package/lib/dist/installer/instructions-template.js.map +1 -1
- package/lib/dist/installer/targets/claude.d.ts +16 -4
- package/lib/dist/installer/targets/claude.d.ts.map +1 -1
- package/lib/dist/installer/targets/claude.js +99 -21
- package/lib/dist/installer/targets/claude.js.map +1 -1
- package/lib/dist/mcp/designer-tools.js +1 -1
- package/lib/dist/mcp/designer-tools.js.map +1 -1
- package/lib/dist/mcp/server-instructions.d.ts +1 -1
- package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
- package/lib/dist/mcp/server-instructions.js +1 -1
- package/lib/dist/mcp/spec-tools.d.ts +10 -0
- package/lib/dist/mcp/spec-tools.d.ts.map +1 -1
- package/lib/dist/mcp/spec-tools.js +231 -5
- package/lib/dist/mcp/spec-tools.js.map +1 -1
- package/lib/dist/mcp/tools.d.ts +12 -0
- package/lib/dist/mcp/tools.d.ts.map +1 -1
- package/lib/dist/mcp/tools.js +34 -1
- package/lib/dist/mcp/tools.js.map +1 -1
- package/lib/dist/resolution/brief-link-resolver.d.ts +20 -0
- package/lib/dist/resolution/brief-link-resolver.d.ts.map +1 -1
- package/lib/dist/resolution/brief-link-resolver.js +65 -1
- package/lib/dist/resolution/brief-link-resolver.js.map +1 -1
- package/lib/dist/resolution/domain-gap-seed.d.ts +1 -1
- package/lib/dist/resolution/domain-gap-seed.js +1 -1
- package/lib/dist/resolution/spec-link-resolver.d.ts +16 -2
- package/lib/dist/resolution/spec-link-resolver.d.ts.map +1 -1
- package/lib/dist/resolution/spec-link-resolver.js +19 -1
- package/lib/dist/resolution/spec-link-resolver.js.map +1 -1
- package/lib/dist/server/chat/classify.js +28 -8
- package/lib/dist/server/ingest/ingestor.js +17 -17
- package/lib/dist/server/ingest/pricing-backfill.js +55 -0
- package/lib/dist/server/ingest/pricing.js +3 -0
- package/lib/dist/server/ingest/project-paths.js +152 -0
- package/lib/dist/server/project-registry.js +15 -2
- package/lib/dist/server/routes/chat-answer.js +11 -0
- package/lib/dist/server/routes/claude.js +34 -13
- package/lib/dist/server/routes/events.js +20 -1
- package/lib/dist/server/routes/projects.js +6 -3
- package/lib/dist/server/routes/spec.js +22 -2
- package/lib/dist/server/server.js +21 -2
- package/lib/dist/web/chunk-AHI5XEVX.js +1 -0
- package/lib/dist/web/chunk-AS2GV63A.js +1 -0
- package/lib/dist/web/chunk-CGVC666W.js +1 -0
- package/lib/dist/web/chunk-KYSTCHAH.js +1 -0
- package/lib/dist/web/{chunk-E27QAKAB.js → chunk-M42QOYAE.js} +1 -1
- package/lib/dist/web/chunk-ML5ULGHY.js +1 -0
- package/lib/dist/web/{chunk-HPKNOV45.js → chunk-OTQR5VFM.js} +1 -1
- package/lib/dist/web/chunk-PS6HRYQQ.js +3 -0
- package/lib/dist/web/chunk-S5J5WQXB.js +6 -0
- package/lib/dist/web/chunk-W5TEIONI.js +2 -0
- package/lib/dist/web/chunk-XKG5X37F.js +1 -0
- package/lib/dist/web/index.html +2 -2
- package/lib/dist/web/main-JT2RR3JS.js +1 -0
- package/lib/dist/web/styles-ATJUD7TT.css +1 -0
- package/lib/dist/workflows/defaults/claude-design-implement.yaml +6 -6
- package/lib/dist/workflows/defaults/spec-author.yaml +1 -1
- package/lib/dist/workflows/defaults/spec-implement.yaml +41 -9
- package/lib/node_modules/.package-lock.json +1 -1
- package/lib/package.json +1 -1
- package/package.json +1 -1
- package/lib/dist/commands/ss-behaviour.md +0 -116
- package/lib/dist/commands/ss-design-implement.md +0 -84
- package/lib/dist/commands/ss-design-loop.md +0 -125
- package/lib/dist/commands/ss-spec.md +0 -118
- package/lib/dist/web/chunk-7MUNFW56.js +0 -1
- package/lib/dist/web/chunk-CYXQILZI.js +0 -1
- package/lib/dist/web/chunk-F5TBUYDH.js +0 -1
- package/lib/dist/web/chunk-JPSYO24K.js +0 -1
- package/lib/dist/web/chunk-LIFS4XH2.js +0 -6
- package/lib/dist/web/chunk-MADMRWIK.js +0 -3
- package/lib/dist/web/chunk-N5WSFE4J.js +0 -2
- package/lib/dist/web/chunk-UYOKO7C5.js +0 -1
- package/lib/dist/web/chunk-YNYVOPWF.js +0 -1
- package/lib/dist/web/chunk-YXYPTZ6E.js +0 -1
- package/lib/dist/web/main-5B4RR3BC.js +0 -1
- package/lib/dist/web/styles-KSOPUVDA.css +0 -1
- /package/lib/dist/commands/{ss-explore.md → specship/explore.md} +0 -0
package/lib/dist/bin/specship.js
CHANGED
|
@@ -115,11 +115,79 @@ async function loadServerPackage() {
|
|
|
115
115
|
* The JSONL ingest watcher now ships inside `@specship/specship-server`
|
|
116
116
|
* itself — `createServer({ ingest: true })` starts it. No separate package.
|
|
117
117
|
*/
|
|
118
|
+
/**
|
|
119
|
+
* Claude Code's slug encoding (every non-alphanumeric char → '-') is lossy,
|
|
120
|
+
* so decoding by mapping '-' back to '/' mangles any path containing
|
|
121
|
+
* hyphens/dots/underscores. Recover real paths from `~/.claude.json` (its
|
|
122
|
+
* `projects` object is keyed by real absolute paths), then from a `"cwd"`
|
|
123
|
+
* value in the slug's newest transcript, before falling back to the lossy
|
|
124
|
+
* decode. Mirrors `packages/server/src/ingest/project-paths.ts`
|
|
125
|
+
* (REQ-SLUGRES-004) — the server package depends on this one, so the CLI
|
|
126
|
+
* can't import it.
|
|
127
|
+
*/
|
|
128
|
+
function buildClaudeSlugIndex() {
|
|
129
|
+
const bySlug = new Map();
|
|
130
|
+
try {
|
|
131
|
+
const raw = fs.readFileSync(path.join(os.homedir(), '.claude.json'), 'utf8');
|
|
132
|
+
const parsed = JSON.parse(raw);
|
|
133
|
+
for (const realPath of Object.keys(parsed.projects ?? {})) {
|
|
134
|
+
bySlug.set(realPath.replace(/[^A-Za-z0-9]/g, '-'), realPath);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch { /* missing or malformed — sniff/fallback below still work */ }
|
|
138
|
+
return bySlug;
|
|
139
|
+
}
|
|
140
|
+
function sniffClaudeSlugCwd(claudeRoot, slug) {
|
|
141
|
+
const dir = path.join(claudeRoot, slug);
|
|
142
|
+
let newest = null;
|
|
143
|
+
try {
|
|
144
|
+
for (const f of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
145
|
+
if (!f.isFile() || !f.name.toLowerCase().endsWith('.jsonl'))
|
|
146
|
+
continue;
|
|
147
|
+
const st = fs.statSync(path.join(dir, f.name));
|
|
148
|
+
if (!newest || st.mtimeMs > newest.mtimeMs)
|
|
149
|
+
newest = { file: f.name, mtimeMs: st.mtimeMs };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
if (!newest)
|
|
156
|
+
return null;
|
|
157
|
+
try {
|
|
158
|
+
const fd = fs.openSync(path.join(dir, newest.file), 'r');
|
|
159
|
+
let chunk;
|
|
160
|
+
try {
|
|
161
|
+
const buf = Buffer.alloc(64 * 1024);
|
|
162
|
+
const read = fs.readSync(fd, buf, 0, buf.length, 0);
|
|
163
|
+
chunk = buf.toString('utf8', 0, read);
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
fs.closeSync(fd);
|
|
167
|
+
}
|
|
168
|
+
const m = /"cwd"\s*:\s*"((?:[^"\\]|\\.)*)"/.exec(chunk);
|
|
169
|
+
if (!m)
|
|
170
|
+
return null;
|
|
171
|
+
let cwd = JSON.parse(`"${m[1]}"`);
|
|
172
|
+
// Accept the cwd or an ancestor of it — sessions may record a subdir.
|
|
173
|
+
for (;;) {
|
|
174
|
+
if (cwd.replace(/[^A-Za-z0-9]/g, '-') === slug)
|
|
175
|
+
return cwd;
|
|
176
|
+
const parent = path.dirname(cwd);
|
|
177
|
+
if (parent === cwd)
|
|
178
|
+
return null;
|
|
179
|
+
cwd = parent;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
118
186
|
/**
|
|
119
187
|
* Auto-pick a default project for `specship serve --ui` when the user
|
|
120
188
|
* didn't pass `-p` and the cwd isn't initialized either. Walks
|
|
121
189
|
* `~/.claude/projects/*` (Claude Code's per-project transcript dirs),
|
|
122
|
-
*
|
|
190
|
+
* resolves each slug back to a real path, and returns the most-recently-
|
|
123
191
|
* touched one that's still on disk AND has been `specship init`'d.
|
|
124
192
|
*
|
|
125
193
|
* Returns null when nothing qualifies — the server boots projectless and
|
|
@@ -134,13 +202,15 @@ async function pickRecentInitializedProject() {
|
|
|
134
202
|
catch {
|
|
135
203
|
return null;
|
|
136
204
|
}
|
|
205
|
+
const slugIndex = buildClaudeSlugIndex();
|
|
137
206
|
const candidates = [];
|
|
138
207
|
for (const ent of entries) {
|
|
139
208
|
if (!ent.isDirectory())
|
|
140
209
|
continue;
|
|
141
210
|
const slug = ent.name;
|
|
142
|
-
|
|
143
|
-
|
|
211
|
+
const decoded = slugIndex.get(slug) ??
|
|
212
|
+
sniffClaudeSlugCwd(claudeRoot, slug) ??
|
|
213
|
+
(slug.startsWith('-') ? '/' + slug.slice(1).replace(/-/g, '/') : slug);
|
|
144
214
|
try {
|
|
145
215
|
const st = fs.statSync(decoded);
|
|
146
216
|
if (!st.isDirectory())
|
|
@@ -769,9 +839,28 @@ function main() {
|
|
|
769
839
|
program
|
|
770
840
|
.command('sync [path]')
|
|
771
841
|
.description('Sync changes since last index')
|
|
772
|
-
.option('-q, --quiet', 'Suppress output (for git hooks)')
|
|
842
|
+
.option('-q, --quiet', 'Suppress output (for git hooks); drift-transition notices still print')
|
|
843
|
+
.option('--drift-summary', 'After syncing, print a one-line drifted-link summary when the queue is non-empty (for the SessionStart hook)')
|
|
773
844
|
.action(async (pathArg, options) => {
|
|
774
845
|
const projectPath = resolveProjectPath(pathArg);
|
|
846
|
+
// One line per link that TRANSITIONED into drifted this sync
|
|
847
|
+
// (REQ-DRIFT-PUSH-001). Deliberately printed even under --quiet — the
|
|
848
|
+
// auto-sync hook runs with --quiet, and these notices are its payload.
|
|
849
|
+
const pushDriftNotices = (result) => {
|
|
850
|
+
for (const t of result.driftedTransitions ?? []) {
|
|
851
|
+
console.log(`⚠ spec drift: ${t.specId} ${t.fromState}→drifted (${t.symbol} ${t.axis === 'spec' ? 'spec changed' : 'changed'}) — ` +
|
|
852
|
+
`re-assert with specship_link_assert, or run /specship:check fix ${t.specId}`);
|
|
853
|
+
}
|
|
854
|
+
};
|
|
855
|
+
// One-line drift-queue summary, only when non-empty (REQ-DRIFT-PUSH-002).
|
|
856
|
+
const pushDriftSummary = (cg) => {
|
|
857
|
+
if (!options.driftSummary)
|
|
858
|
+
return;
|
|
859
|
+
const drifted = cg.getSpecQueries().getLinksByState(['drifted']).length;
|
|
860
|
+
if (drifted > 0) {
|
|
861
|
+
console.log(`⚠ ${drifted} spec link(s) drifted — review with /specship:check drifted`);
|
|
862
|
+
}
|
|
863
|
+
};
|
|
775
864
|
try {
|
|
776
865
|
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
777
866
|
if (!options.quiet) {
|
|
@@ -782,7 +871,9 @@ function main() {
|
|
|
782
871
|
const { default: SpecShip } = await loadSpecShip();
|
|
783
872
|
const cg = await SpecShip.open(projectPath);
|
|
784
873
|
if (options.quiet) {
|
|
785
|
-
await cg.sync();
|
|
874
|
+
const result = await cg.sync();
|
|
875
|
+
pushDriftNotices(result);
|
|
876
|
+
pushDriftSummary(cg);
|
|
786
877
|
cg.destroy();
|
|
787
878
|
return;
|
|
788
879
|
}
|
|
@@ -809,6 +900,8 @@ function main() {
|
|
|
809
900
|
details.push(`Removed: ${result.filesRemoved}`);
|
|
810
901
|
clack.log.info(`${details.join(', ')} ${(0, glyphs_1.getGlyphs)().dash} ${formatNumber(result.nodesUpdated)} nodes in ${formatDuration(result.durationMs)}`);
|
|
811
902
|
}
|
|
903
|
+
pushDriftNotices(result);
|
|
904
|
+
pushDriftSummary(cg);
|
|
812
905
|
clack.outro('Done');
|
|
813
906
|
cg.destroy();
|
|
814
907
|
}
|
|
@@ -1450,11 +1543,18 @@ function main() {
|
|
|
1450
1543
|
* GATING check fails. Which checks gate vs advise is configured in
|
|
1451
1544
|
* specship.config.json `enforce.gate`; with no config every check is advisory
|
|
1452
1545
|
* and the command exits 0 (opt-in — never breaks an existing repo).
|
|
1546
|
+
*
|
|
1547
|
+
* The graduation ramp (REQ-ENFORCE-004): `--strict` gates every check for
|
|
1548
|
+
* one run; `--enable-gate <checks...>` persists gating into the config and
|
|
1549
|
+
* then runs; an advisory run with would-be-gating findings ends by printing
|
|
1550
|
+
* the exact `--enable-gate` command that turns the gate on.
|
|
1453
1551
|
*/
|
|
1454
1552
|
program
|
|
1455
1553
|
.command('check [path]')
|
|
1456
1554
|
.description('Run the enforcement gate (drift + fitness + maintainability + behaviour); exits non-zero on a gating failure')
|
|
1457
1555
|
.option('-j, --json', 'Output as JSON')
|
|
1556
|
+
.option('--strict', 'Treat every check as gating for this run only (nothing read from or written to config)')
|
|
1557
|
+
.option('--enable-gate <checks...>', 'Persist gating for the named checks into specship.config.json, then run the gate')
|
|
1458
1558
|
.action(async (pathArg, options) => {
|
|
1459
1559
|
const projectPath = resolveProjectPath(pathArg);
|
|
1460
1560
|
try {
|
|
@@ -1462,9 +1562,25 @@ function main() {
|
|
|
1462
1562
|
error(`SpecShip not initialized in ${projectPath}`);
|
|
1463
1563
|
process.exit(1);
|
|
1464
1564
|
}
|
|
1565
|
+
const enforce = await Promise.resolve().then(() => __importStar(require('../enforce/enforce')));
|
|
1566
|
+
// --enable-gate writes the opt-in config, then falls through to a
|
|
1567
|
+
// normal run so the user immediately sees the gate with teeth
|
|
1568
|
+
// (REQ-ENFORCE-004.A1).
|
|
1569
|
+
if (options.enableGate?.length) {
|
|
1570
|
+
const invalid = options.enableGate.filter((c) => !enforce.ALL_CHECKS.includes(c));
|
|
1571
|
+
if (invalid.length) {
|
|
1572
|
+
error(`Unknown check(s): ${invalid.join(', ')}. Valid: ${enforce.ALL_CHECKS.join(', ')}`);
|
|
1573
|
+
process.exit(1);
|
|
1574
|
+
}
|
|
1575
|
+
const enabled = enforce.enableGateChecks(projectPath, options.enableGate);
|
|
1576
|
+
if (enabled.length) {
|
|
1577
|
+
success(`Gating enabled for ${enabled.join(', ')} — written to specship.config.json.`);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1465
1580
|
const { default: SpecShip } = await loadSpecShip();
|
|
1466
1581
|
const cg = await SpecShip.open(projectPath);
|
|
1467
|
-
|
|
1582
|
+
// --strict gates everything for this run only (REQ-ENFORCE-004.A2).
|
|
1583
|
+
const r = options.strict ? cg.getEnforce(enforce.strictEnforceConfig()) : cg.getEnforce();
|
|
1468
1584
|
if (options.json) {
|
|
1469
1585
|
console.log(JSON.stringify(r, null, 2));
|
|
1470
1586
|
cg.destroy();
|
|
@@ -1486,6 +1602,16 @@ function main() {
|
|
|
1486
1602
|
success(r.gatedFailures.length === 0 && r.checks.some((c) => c.gating)
|
|
1487
1603
|
? 'All gating checks pass.'
|
|
1488
1604
|
: 'Pass (no gating checks failed).');
|
|
1605
|
+
// The advisory sell (REQ-ENFORCE-004.A1): findings that would fail
|
|
1606
|
+
// a gated run end the report with the exact opt-in command.
|
|
1607
|
+
const wouldGate = r.checks.filter((c) => !c.gating && !c.passed).map((c) => c.check);
|
|
1608
|
+
if (wouldGate.length) {
|
|
1609
|
+
const total = r.checks
|
|
1610
|
+
.filter((c) => wouldGate.includes(c.check))
|
|
1611
|
+
.reduce((n, c) => n + c.findings.length, 0);
|
|
1612
|
+
console.log(chalk.dim(`\n ${total} advisory finding(s) would fail a gated run.` +
|
|
1613
|
+
`\n Enable: specship check --enable-gate ${wouldGate.join(' ')}\n`));
|
|
1614
|
+
}
|
|
1489
1615
|
cg.destroy();
|
|
1490
1616
|
return;
|
|
1491
1617
|
}
|
|
@@ -2184,14 +2310,14 @@ function main() {
|
|
|
2184
2310
|
});
|
|
2185
2311
|
/**
|
|
2186
2312
|
* specship starter-prompt — print the manufactured first-run flow/impact prompt
|
|
2187
|
-
* (ACTIVATION-DOC). The single delivery mechanism for the bare `/
|
|
2313
|
+
* (ACTIVATION-DOC). The single delivery mechanism for the bare `/specship:explore`
|
|
2188
2314
|
* door: it self-gates (prints nothing if the index can't be queried —
|
|
2189
2315
|
* REQ-ACTIVATION-004) and self-retires (prints nothing once a real specship
|
|
2190
2316
|
* lookup has been recorded this session — REQ-ACTIVATION-003).
|
|
2191
2317
|
*/
|
|
2192
2318
|
program
|
|
2193
2319
|
.command('starter-prompt [path]')
|
|
2194
|
-
.description('Print a suggested first flow/impact prompt for this project (used by the /
|
|
2320
|
+
.description('Print a suggested first flow/impact prompt for this project (used by the /specship:explore door).')
|
|
2195
2321
|
.option('-j, --json', 'Output as JSON')
|
|
2196
2322
|
.action(async (pathArg, options) => {
|
|
2197
2323
|
const projectRoot = path.resolve(pathArg ?? process.cwd());
|
|
@@ -2278,7 +2404,7 @@ function main() {
|
|
|
2278
2404
|
if (shouldNudge(prompt)) {
|
|
2279
2405
|
const additionalContext = 'This repo uses spec-driven development (SpecShip). Before any brainstorming or ' +
|
|
2280
2406
|
'planning skill, FIRST invoke spec-author to author the spec under specs/ for this ' +
|
|
2281
|
-
'work — the spec is the contract; implement from it with /
|
|
2407
|
+
'work — the spec is the contract; implement from it with /specship:spec implement.';
|
|
2282
2408
|
process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext } }) + '\n');
|
|
2283
2409
|
}
|
|
2284
2410
|
process.exit(0);
|
|
@@ -2370,12 +2496,12 @@ function main() {
|
|
|
2370
2496
|
});
|
|
2371
2497
|
// @implements REQ-DOMAIN-003
|
|
2372
2498
|
// Thin surface over the read-only gap-seed pass (SpecShip.getDomainGapSeed,
|
|
2373
|
-
// REQ-DOMAIN-003) so the `/
|
|
2499
|
+
// REQ-DOMAIN-003) so the `/specship:spec domain` capture command can cite the SAME real
|
|
2374
2500
|
// undocumented entities/specs the library computes (REQ-DOMAIN-004.A4) without a
|
|
2375
2501
|
// new MCP tool (REQ-DOMAIN-005) or a runtime package import. Writes nothing.
|
|
2376
2502
|
program
|
|
2377
2503
|
.command('domain-gaps [path]')
|
|
2378
|
-
.description('List code entities and specs not yet covered by a domain fact (the domain gap-seed). Feeds the /
|
|
2504
|
+
.description('List code entities and specs not yet covered by a domain fact (the domain gap-seed). Feeds the /specship:spec domain capture interview.')
|
|
2379
2505
|
.option('-l, --limit <n>', 'max entities and specs to print in text mode (default: 50)')
|
|
2380
2506
|
.option('--json', 'emit JSON')
|
|
2381
2507
|
.action(async (pathArg, options) => {
|
|
@@ -2418,7 +2544,7 @@ function main() {
|
|
|
2418
2544
|
if (seed.specs.length > limit)
|
|
2419
2545
|
console.log(` … and ${seed.specs.length - limit} more`);
|
|
2420
2546
|
}
|
|
2421
|
-
console.log(`\nCapture a fact for any of these with \`/
|
|
2547
|
+
console.log(`\nCapture a fact for any of these with \`/specship:spec domain\`.`);
|
|
2422
2548
|
}
|
|
2423
2549
|
/* eslint-enable no-console */
|
|
2424
2550
|
}
|
|
@@ -2544,7 +2670,7 @@ function main() {
|
|
|
2544
2670
|
}
|
|
2545
2671
|
else {
|
|
2546
2672
|
// eslint-disable-next-line no-console
|
|
2547
|
-
console.log('No specs or briefs found. Author one with /
|
|
2673
|
+
console.log('No specs or briefs found. Author one with /specship:spec new.');
|
|
2548
2674
|
}
|
|
2549
2675
|
return;
|
|
2550
2676
|
}
|