@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
|
@@ -55,6 +55,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
55
55
|
exports.cleanSpecPointer = cleanSpecPointer;
|
|
56
56
|
exports.resolveToDocumentId = resolveToDocumentId;
|
|
57
57
|
exports.resolveBriefLink = resolveBriefLink;
|
|
58
|
+
exports.ideaCaptureFields = ideaCaptureFields;
|
|
58
59
|
exports.computeSpecFunnel = computeSpecFunnel;
|
|
59
60
|
exports.findBriefsForSpec = findBriefsForSpec;
|
|
60
61
|
exports.summarizeBriefFunnel = summarizeBriefFunnel;
|
|
@@ -131,6 +132,65 @@ function resolveBriefLink(sq, brief) {
|
|
|
131
132
|
specSide,
|
|
132
133
|
};
|
|
133
134
|
}
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// Idea capture fields (REQ-IDEAS-002.A2) — the single source of truth for a
|
|
137
|
+
// brief's capture date + labels, shared by the `ideas` review view and the
|
|
138
|
+
// list-mode inventory so both surfaces render the same values.
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
/**
|
|
141
|
+
* Read a brief's idea-capture fields from the frontmatter REQ-IDEAS-001 writes.
|
|
142
|
+
*
|
|
143
|
+
* - `capturedAt` — `metadata.created` (the capture moment) parsed to epoch ms.
|
|
144
|
+
* `spec.createdAt` is index-time, not capture-time, so age MUST come from
|
|
145
|
+
* here. Accepts an epoch-ms number or an ISO/date string; `null` when the
|
|
146
|
+
* key is absent or unparseable.
|
|
147
|
+
* - `labels` — the plural `metadata.labels` (a comma-delimited string OR a
|
|
148
|
+
* `string[]`) PLUS the singular `metadata.label` the capture verb writes
|
|
149
|
+
* (treated as a one-element list). `[]` when neither is present.
|
|
150
|
+
*
|
|
151
|
+
* Defensive throughout: older briefs (captured before REQ-IDEAS-001) carry
|
|
152
|
+
* neither field, and both surfaces must still render cleanly.
|
|
153
|
+
*/
|
|
154
|
+
function ideaCaptureFields(brief) {
|
|
155
|
+
const meta = brief.metadata && typeof brief.metadata === 'object'
|
|
156
|
+
? brief.metadata
|
|
157
|
+
: {};
|
|
158
|
+
let capturedAt = null;
|
|
159
|
+
const created = meta.created;
|
|
160
|
+
if (typeof created === 'number' && Number.isFinite(created)) {
|
|
161
|
+
capturedAt = created;
|
|
162
|
+
}
|
|
163
|
+
else if (typeof created === 'string' && created.trim().length > 0) {
|
|
164
|
+
const parsed = Date.parse(created.trim());
|
|
165
|
+
if (!Number.isNaN(parsed))
|
|
166
|
+
capturedAt = parsed;
|
|
167
|
+
}
|
|
168
|
+
const labels = [];
|
|
169
|
+
// Plural `labels`: comma-delimited string or an array.
|
|
170
|
+
const many = meta.labels;
|
|
171
|
+
if (typeof many === 'string') {
|
|
172
|
+
for (const part of many.split(',')) {
|
|
173
|
+
const v = part.trim();
|
|
174
|
+
if (v)
|
|
175
|
+
labels.push(v);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else if (Array.isArray(many)) {
|
|
179
|
+
for (const part of many) {
|
|
180
|
+
const v = String(part).trim();
|
|
181
|
+
if (v)
|
|
182
|
+
labels.push(v);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Singular `label`: the key the capture verb writes — a one-element list.
|
|
186
|
+
const one = meta.label;
|
|
187
|
+
if (typeof one === 'string') {
|
|
188
|
+
const v = one.trim();
|
|
189
|
+
if (v)
|
|
190
|
+
labels.push(v);
|
|
191
|
+
}
|
|
192
|
+
return { capturedAt, labels };
|
|
193
|
+
}
|
|
134
194
|
/** Implementation rollup for a single document's requirements. */
|
|
135
195
|
function documentRollup(sq, docId) {
|
|
136
196
|
const reqs = sq.getSpecsByParent(docId).filter((s) => s.kind === 'requirement');
|
|
@@ -181,7 +241,11 @@ function computeSpecFunnel(sq) {
|
|
|
181
241
|
}
|
|
182
242
|
const ideas = briefLinks
|
|
183
243
|
.filter((l) => l.state === 'idea')
|
|
184
|
-
.map((l) =>
|
|
244
|
+
.map((l) => {
|
|
245
|
+
const brief = sq.getSpecById(l.briefId);
|
|
246
|
+
const cap = brief ? ideaCaptureFields(brief) : { capturedAt: null, labels: [] };
|
|
247
|
+
return { briefId: l.briefId, title: brief?.title ?? '', capturedAt: cap.capturedAt, labels: cap.labels };
|
|
248
|
+
});
|
|
185
249
|
const conflicts = briefLinks
|
|
186
250
|
.filter((l) => l.state === 'conflict')
|
|
187
251
|
.map((l) => ({ briefId: l.briefId, briefSide: l.briefSide, specSide: l.specSide }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brief-link-resolver.js","sourceRoot":"","sources":["../../src/resolution/brief-link-resolver.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyBH,4CAgBC;AAKD,kDAWC;AAmBD,4CAqBC;
|
|
1
|
+
{"version":3,"file":"brief-link-resolver.js","sourceRoot":"","sources":["../../src/resolution/brief-link-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Domain gap-seed (REQ-DOMAIN-003).
|
|
3
3
|
*
|
|
4
4
|
* A read-only pass that surfaces what the domain knowledge layer does NOT yet
|
|
5
|
-
* cover, so an author (or the `/
|
|
5
|
+
* cover, so an author (or the `/specship:spec domain` command, REQ-DOMAIN-004) can decide
|
|
6
6
|
* what to document next. It answers two questions against current DB state:
|
|
7
7
|
*
|
|
8
8
|
* - which structural code entities (classes, structs, interfaces, routes,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Domain gap-seed (REQ-DOMAIN-003).
|
|
4
4
|
*
|
|
5
5
|
* A read-only pass that surfaces what the domain knowledge layer does NOT yet
|
|
6
|
-
* cover, so an author (or the `/
|
|
6
|
+
* cover, so an author (or the `/specship:spec domain` command, REQ-DOMAIN-004) can decide
|
|
7
7
|
* what to document next. It answers two questions against current DB state:
|
|
8
8
|
*
|
|
9
9
|
* - which structural code entities (classes, structs, interfaces, routes,
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* Highest-confidence signal wins per logical key — `upsertSpecLink` enforces
|
|
28
28
|
* this.
|
|
29
29
|
*/
|
|
30
|
-
import { Spec, SpecLink } from '../types';
|
|
30
|
+
import { Spec, SpecLink, SpecLinkState } from '../types';
|
|
31
31
|
import { QueryBuilder } from '../db/queries';
|
|
32
32
|
import { SpecQueries } from '../db/spec-queries';
|
|
33
33
|
import { SpecLinkCandidate } from '../extraction/specs/types';
|
|
@@ -44,6 +44,20 @@ export interface SpecLinkResolverStats {
|
|
|
44
44
|
driftedCode: number;
|
|
45
45
|
candidatesApplied: number;
|
|
46
46
|
commentLinksApplied: number;
|
|
47
|
+
/**
|
|
48
|
+
* Links that TRANSITIONED into `drifted` during this pass (REQ-DRIFT-PUSH-001).
|
|
49
|
+
* A link already in `drifted` that re-drifts is not recorded — consumers
|
|
50
|
+
* (the sync CLI's push notice) must see each drift once, not on every sync.
|
|
51
|
+
*/
|
|
52
|
+
transitions: DriftTransition[];
|
|
53
|
+
}
|
|
54
|
+
/** One link's transition into `drifted`, for push notification (DRIFT-PUSH-DOC). */
|
|
55
|
+
export interface DriftTransition {
|
|
56
|
+
specId: string;
|
|
57
|
+
fromState: SpecLinkState;
|
|
58
|
+
axis: 'code' | 'spec';
|
|
59
|
+
/** The linked symbol (`targetQualifiedName`). */
|
|
60
|
+
symbol: string;
|
|
47
61
|
}
|
|
48
62
|
/** One code link inherited by a spec through a spec→spec dependency edge. */
|
|
49
63
|
export interface InheritedLink {
|
|
@@ -126,7 +140,7 @@ export declare class SpecLinkResolver {
|
|
|
126
140
|
* The agent's job after seeing drift: re-read the spec, update code if
|
|
127
141
|
* needed, call specship_link_verify to take state back to `verified`.
|
|
128
142
|
*/
|
|
129
|
-
markSpecDrifted(specId: string, newSpecHash: string): number;
|
|
143
|
+
markSpecDrifted(specId: string, newSpecHash: string, stats?: SpecLinkResolverStats): number;
|
|
130
144
|
/**
|
|
131
145
|
* Compute the code links a spec inherits transitively through its
|
|
132
146
|
* spec→spec edges (`parentId` + `metadata.depends_on`). A `domain` fact
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-link-resolver.d.ts","sourceRoot":"","sources":["../../src/resolution/spec-link-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAGL,IAAI,EACJ,QAAQ,
|
|
1
|
+
{"version":3,"file":"spec-link-resolver.d.ts","sourceRoot":"","sources":["../../src/resolution/spec-link-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAGL,IAAI,EACJ,QAAQ,EACR,aAAa,EAEd,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAY9D,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,WAAW,EAAE,eAAe,EAAE,CAAC;CAChC;AAED,oFAAoF;AACpF,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,aAAa,CAAC;IACzB,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,IAAI,EAAE,QAAQ,CAAC;CAChB;AAED,4DAA4D;AAC5D,MAAM,WAAW,oBAAoB;IACnC,oFAAoF;IACpF,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,iFAAiF;IACjF,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;;;;;;OAMG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAKD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAalD;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,IAAI,CAA0B;gBAGpC,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,WAAW,EACxB,IAAI,GAAE,uBAA4B;IAOpC;;;OAGG;IACH,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,qBAAqB;IAQnE;;;OAGG;IACH,UAAU,IAAI,qBAAqB;IAQnC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,cAAc;IAwDtB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAwBzB;;;OAGG;IACH,0BAA0B,CACxB,UAAU,EAAE,iBAAiB,EAAE,EAC/B,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,EACnC,KAAK,CAAC,EAAE,qBAAqB,GAC5B,IAAI;IAgCP;;;;;;;;;;OAUG;IACH,qBAAqB,CACnB,YAAY,EAAE,MAAM,EAAE,EACtB,KAAK,CAAC,EAAE,qBAAqB,GAC5B,IAAI;IA4CP;;;;;;;OAOG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,qBAAqB,GAAG,MAAM;IAwB3F;;;;;;;;;;;;;;OAcG;IACH,iBAAiB,CACf,IAAI,EAAE,IAAI,EACV,QAAQ,GAAE,MAAiC,GAC1C,oBAAoB;IA4CvB,OAAO,CAAC,SAAS;CAWlB"}
|
|
@@ -131,6 +131,15 @@ class SpecLinkResolver {
|
|
|
131
131
|
!STICKY_STATES.has(link.state)) {
|
|
132
132
|
this.specQueries.updateSpecLinkState(link.id, 'drifted', 'code', now);
|
|
133
133
|
stats.driftedCode++;
|
|
134
|
+
if (link.state !== 'drifted') {
|
|
135
|
+
// A genuine transition, not a re-drift — record for push notification.
|
|
136
|
+
stats.transitions.push({
|
|
137
|
+
specId: link.specId,
|
|
138
|
+
fromState: link.state,
|
|
139
|
+
axis: 'code',
|
|
140
|
+
symbol: link.targetQualifiedName,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
134
143
|
if (this.opts.verbose) {
|
|
135
144
|
// eslint-disable-next-line no-console
|
|
136
145
|
console.error(`[SpecLinkResolver] drift(code) ${link.targetQualifiedName} for spec ${link.specId}`);
|
|
@@ -254,7 +263,7 @@ class SpecLinkResolver {
|
|
|
254
263
|
* The agent's job after seeing drift: re-read the spec, update code if
|
|
255
264
|
* needed, call specship_link_verify to take state back to `verified`.
|
|
256
265
|
*/
|
|
257
|
-
markSpecDrifted(specId, newSpecHash) {
|
|
266
|
+
markSpecDrifted(specId, newSpecHash, stats) {
|
|
258
267
|
const now = Date.now();
|
|
259
268
|
let count = 0;
|
|
260
269
|
for (const link of this.specQueries.getLinksBySpec(specId)) {
|
|
@@ -264,6 +273,14 @@ class SpecLinkResolver {
|
|
|
264
273
|
continue;
|
|
265
274
|
this.specQueries.updateSpecLinkState(link.id, 'drifted', 'spec', now);
|
|
266
275
|
count++;
|
|
276
|
+
if (stats && link.state !== 'drifted') {
|
|
277
|
+
stats.transitions.push({
|
|
278
|
+
specId: link.specId,
|
|
279
|
+
fromState: link.state,
|
|
280
|
+
axis: 'spec',
|
|
281
|
+
symbol: link.targetQualifiedName,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
267
284
|
}
|
|
268
285
|
return count;
|
|
269
286
|
}
|
|
@@ -330,6 +347,7 @@ class SpecLinkResolver {
|
|
|
330
347
|
driftedCode: 0,
|
|
331
348
|
candidatesApplied: 0,
|
|
332
349
|
commentLinksApplied: 0,
|
|
350
|
+
transitions: [],
|
|
333
351
|
};
|
|
334
352
|
}
|
|
335
353
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-link-resolver.js","sourceRoot":"","sources":["../../src/resolution/spec-link-resolver.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;;;
|
|
1
|
+
{"version":3,"file":"spec-link-resolver.js","sourceRoot":"","sources":["../../src/resolution/spec-link-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,6 +22,24 @@
|
|
|
22
22
|
* identical to what the search path expects.
|
|
23
23
|
*/
|
|
24
24
|
import { extractSubject } from '../routes/chat-answer.js';
|
|
25
|
+
/**
|
|
26
|
+
* Slash-command → intent routing table (CMD-NS-DOC, REQ-CMD-NS-005). Each door
|
|
27
|
+
* is reachable by three tokens: the canonical `/specship:*` namespace form, the
|
|
28
|
+
* legacy flat `/ss-*` form (kept as a deprecation alias), and the bare verb.
|
|
29
|
+
* The check family routes to `drift` — the launch set has no separate
|
|
30
|
+
* gate/health intent. Keys are lowercased to match `cmd`.
|
|
31
|
+
*/
|
|
32
|
+
const SLASH_ROUTES = {
|
|
33
|
+
'specship:spec': 'spec', 'ss-spec': 'spec', spec: 'spec',
|
|
34
|
+
'specship:explore': 'explore', 'ss-explore': 'explore', explore: 'explore',
|
|
35
|
+
'specship:check': 'drift', 'ss-check': 'drift', check: 'drift',
|
|
36
|
+
};
|
|
37
|
+
/** Legacy `/ss-*` slash forms → the canonical `/specship:*` command they were renamed to. */
|
|
38
|
+
const RENAMED_SLASH = {
|
|
39
|
+
'ss-spec': '/specship:spec',
|
|
40
|
+
'ss-explore': '/specship:explore',
|
|
41
|
+
'ss-check': '/specship:check',
|
|
42
|
+
};
|
|
25
43
|
/** A spec-id-shaped token — `REQ-DASH-CHAT-002`, `DOMAIN-LEDGER`, `DASH-CHAT-DOC`. */
|
|
26
44
|
const SPEC_ID = /\b[A-Za-z][A-Za-z0-9]*(?:-[A-Za-z0-9]+)+\b/;
|
|
27
45
|
/**
|
|
@@ -56,14 +74,16 @@ export function classifyIntent(message) {
|
|
|
56
74
|
if (slash) {
|
|
57
75
|
const cmd = (slash[1] ?? '').toLowerCase();
|
|
58
76
|
const rest = (slash[2] ?? '').trim();
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
77
|
+
const routed = SLASH_ROUTES[cmd];
|
|
78
|
+
if (routed) {
|
|
79
|
+
const result = {
|
|
80
|
+
intent: routed, query: rest, subject: rest || undefined, confident: true,
|
|
81
|
+
};
|
|
82
|
+
// Legacy `/ss-*` form → flag it so the answer nudges toward `/specship:*`.
|
|
83
|
+
const renamedTo = RENAMED_SLASH[cmd];
|
|
84
|
+
if (renamedTo)
|
|
85
|
+
result.deprecatedAlias = { from: `/${cmd}`, to: renamedTo };
|
|
86
|
+
return result;
|
|
67
87
|
}
|
|
68
88
|
// Unknown slash command → fall through to keyword matching on the subject
|
|
69
89
|
// (extractSubject has already stripped the leading slash token).
|
|
@@ -97,6 +97,7 @@ export function listTranscriptFiles(claudeRoot) {
|
|
|
97
97
|
* the ingestor seeds on first pass instead of failing silently.
|
|
98
98
|
*/
|
|
99
99
|
const DEFAULT_PRICING = [
|
|
100
|
+
['claude-fable-5', 10.0, 50.0, 12.5, 1.0],
|
|
100
101
|
['claude-opus-4-7', 15.0, 75.0, 18.75, 1.5],
|
|
101
102
|
['claude-opus-4', 15.0, 75.0, 18.75, 1.5],
|
|
102
103
|
['claude-sonnet-4-6', 3.0, 15.0, 3.75, 0.3],
|
|
@@ -105,25 +106,24 @@ const DEFAULT_PRICING = [
|
|
|
105
106
|
['claude-haiku-4-5', 0.80, 4.0, 1.0, 0.08],
|
|
106
107
|
['claude-haiku-4', 0.80, 4.0, 1.0, 0.08],
|
|
107
108
|
];
|
|
108
|
-
/**
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
/**
|
|
110
|
+
* Load the pricing table once per ingest pass. Always INSERT OR IGNOREs the
|
|
111
|
+
* default rows first — self-healing for DBs seeded by an older binary that
|
|
112
|
+
* lacked newer model families (e.g. fable), while user-edited rates survive
|
|
113
|
+
* untouched (REQ-DASHINT-001).
|
|
114
|
+
*/
|
|
115
|
+
export function loadPricing(db) {
|
|
116
|
+
const now = Date.now();
|
|
117
|
+
const ins = db.prepare(`
|
|
118
|
+
INSERT OR IGNORE INTO claude_pricing
|
|
119
|
+
(model, input_per_mtok, output_per_mtok, cache_creation_per_mtok, cache_read_per_mtok, updated_at)
|
|
120
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
121
|
+
`);
|
|
122
|
+
for (const r of DEFAULT_PRICING)
|
|
123
|
+
ins.run(r[0], r[1], r[2], r[3], r[4], now);
|
|
124
|
+
return db
|
|
111
125
|
.prepare('SELECT model, input_per_mtok, output_per_mtok, cache_creation_per_mtok, cache_read_per_mtok FROM claude_pricing')
|
|
112
126
|
.all();
|
|
113
|
-
if (rows.length === 0) {
|
|
114
|
-
const now = Date.now();
|
|
115
|
-
const ins = db.prepare(`
|
|
116
|
-
INSERT OR IGNORE INTO claude_pricing
|
|
117
|
-
(model, input_per_mtok, output_per_mtok, cache_creation_per_mtok, cache_read_per_mtok, updated_at)
|
|
118
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
119
|
-
`);
|
|
120
|
-
for (const r of DEFAULT_PRICING)
|
|
121
|
-
ins.run(r[0], r[1], r[2], r[3], r[4], now);
|
|
122
|
-
rows = db
|
|
123
|
-
.prepare('SELECT model, input_per_mtok, output_per_mtok, cache_creation_per_mtok, cache_read_per_mtok FROM claude_pricing')
|
|
124
|
-
.all();
|
|
125
|
-
}
|
|
126
|
-
return rows;
|
|
127
127
|
}
|
|
128
128
|
/**
|
|
129
129
|
* Read a file's tail from `fromOffset` to end. Returns the full text plus
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-cost prompts that were ingested while their model had no pricing row
|
|
3
|
+
* (REQ-DASHINT-001.A2). Before the fable family was priced, every
|
|
4
|
+
* `claude-fable-5` prompt landed with `cost_usd = 0` despite real token
|
|
5
|
+
* usage — poisoning session totals, the dashboard's last-session tile, and
|
|
6
|
+
* the Costs/Compare rankings.
|
|
7
|
+
*
|
|
8
|
+
* Idempotent: only rows with `cost_usd = 0` AND non-zero tokens are
|
|
9
|
+
* touched, and only when their model now resolves to a pricing row with a
|
|
10
|
+
* non-zero computed cost. Session totals are bumped by the same delta the
|
|
11
|
+
* prompt rows gained. Run once per server boot (cheap: indexed scan).
|
|
12
|
+
*/
|
|
13
|
+
import { loadPricing } from './ingestor.js';
|
|
14
|
+
import { computeCost, resolvePricing } from './pricing.js';
|
|
15
|
+
/** Returns the number of prompt rows re-costed. */
|
|
16
|
+
export function recostUnpricedPrompts(db) {
|
|
17
|
+
const pricingRows = loadPricing(db);
|
|
18
|
+
const candidates = db
|
|
19
|
+
.prepare(`
|
|
20
|
+
SELECT id, session_id, model, input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens
|
|
21
|
+
FROM claude_prompts
|
|
22
|
+
WHERE cost_usd = 0 AND model IS NOT NULL
|
|
23
|
+
AND (input_tokens > 0 OR output_tokens > 0 OR cache_creation_tokens > 0 OR cache_read_tokens > 0)
|
|
24
|
+
`)
|
|
25
|
+
.all();
|
|
26
|
+
if (candidates.length === 0)
|
|
27
|
+
return 0;
|
|
28
|
+
const updatePrompt = db.prepare('UPDATE claude_prompts SET cost_usd = ? WHERE id = ?');
|
|
29
|
+
const bumpSession = db.prepare('UPDATE claude_sessions SET total_cost_usd = total_cost_usd + ? WHERE id = ?');
|
|
30
|
+
let updated = 0;
|
|
31
|
+
const run = db.transaction(() => {
|
|
32
|
+
const sessionDeltas = new Map();
|
|
33
|
+
for (const row of candidates) {
|
|
34
|
+
const pricing = resolvePricing(row.model, pricingRows);
|
|
35
|
+
if (!pricing)
|
|
36
|
+
continue;
|
|
37
|
+
const cost = computeCost({
|
|
38
|
+
input_tokens: row.input_tokens,
|
|
39
|
+
output_tokens: row.output_tokens,
|
|
40
|
+
cache_creation_input_tokens: row.cache_creation_tokens,
|
|
41
|
+
cache_read_input_tokens: row.cache_read_tokens,
|
|
42
|
+
}, pricing);
|
|
43
|
+
if (cost <= 0)
|
|
44
|
+
continue;
|
|
45
|
+
updatePrompt.run(cost, row.id);
|
|
46
|
+
sessionDeltas.set(row.session_id, (sessionDeltas.get(row.session_id) ?? 0) + cost);
|
|
47
|
+
updated++;
|
|
48
|
+
}
|
|
49
|
+
for (const [sessionId, delta] of sessionDeltas) {
|
|
50
|
+
bumpSession.run(delta, sessionId);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
run();
|
|
54
|
+
return updated;
|
|
55
|
+
}
|
|
@@ -15,6 +15,9 @@ const FAMILY_FALLBACKS = {
|
|
|
15
15
|
opus: 'claude-opus-4',
|
|
16
16
|
sonnet: 'claude-sonnet-4',
|
|
17
17
|
haiku: 'claude-haiku-4',
|
|
18
|
+
fable: 'claude-fable-5',
|
|
19
|
+
// Claude Mythos shares Fable's capabilities and pricing.
|
|
20
|
+
mythos: 'claude-fable-5',
|
|
18
21
|
};
|
|
19
22
|
/**
|
|
20
23
|
* Normalize a Claude model ID:
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slug→path resolution for Claude Code project dirs (REQ-SLUGRES-001).
|
|
3
|
+
*
|
|
4
|
+
* Claude Code names `~/.claude/projects/<slug>` by replacing every character
|
|
5
|
+
* outside [A-Za-z0-9] in the project's absolute path with '-'. The encoding
|
|
6
|
+
* is lossy — '-', '/', '.', '_' all collapse into '-' — so decoding by
|
|
7
|
+
* mapping '-' back to '/' mangles any real path containing those characters
|
|
8
|
+
* (`/Users/a/dev/claude-projects/x` → `/Users/a/dev/claude/projects/x`).
|
|
9
|
+
*
|
|
10
|
+
* Real paths are recovered from authoritative local sources instead:
|
|
11
|
+
* 1. `~/.claude.json` — its `projects` object is keyed by real absolute
|
|
12
|
+
* paths. We slug-encode each key into a reverse index.
|
|
13
|
+
* 2. Transcript sniffing — JSONL lines carry a `"cwd"` field; a cwd (or
|
|
14
|
+
* one of its ancestors) whose encoding equals the slug is the real path.
|
|
15
|
+
* 3. The legacy lossy decode, preserving behavior for hyphen-free paths.
|
|
16
|
+
*/
|
|
17
|
+
import * as fs from 'fs';
|
|
18
|
+
import * as os from 'os';
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
import { decodeProjectSlug } from './ingestor.js';
|
|
21
|
+
/** Claude Code's encoding: every char outside [A-Za-z0-9] becomes '-'. */
|
|
22
|
+
export function encodeProjectSlug(realPath) {
|
|
23
|
+
return realPath.replace(/[^A-Za-z0-9]/g, '-');
|
|
24
|
+
}
|
|
25
|
+
/** Max bytes read per transcript while sniffing for a cwd line. */
|
|
26
|
+
const SNIFF_BYTES = 64 * 1024;
|
|
27
|
+
/** Newest transcripts to try per slug before giving up. */
|
|
28
|
+
const SNIFF_FILES = 3;
|
|
29
|
+
// Keyed by claudeJsonPath so tests with distinct temp files don't collide.
|
|
30
|
+
const indexCache = new Map();
|
|
31
|
+
/**
|
|
32
|
+
* Reverse index of slug → real path from `~/.claude.json`'s projects keys.
|
|
33
|
+
* Cached per file and refreshed when the file's mtime changes. Any read or
|
|
34
|
+
* parse failure yields an empty index (the caller falls through to sniffing).
|
|
35
|
+
*/
|
|
36
|
+
function claudeJsonIndex(claudeJsonPath) {
|
|
37
|
+
let mtimeMs;
|
|
38
|
+
try {
|
|
39
|
+
mtimeMs = fs.statSync(claudeJsonPath).mtimeMs;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return new Map();
|
|
43
|
+
}
|
|
44
|
+
const cached = indexCache.get(claudeJsonPath);
|
|
45
|
+
if (cached && cached.mtimeMs === mtimeMs)
|
|
46
|
+
return cached.bySlug;
|
|
47
|
+
const bySlug = new Map();
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(fs.readFileSync(claudeJsonPath, 'utf8'));
|
|
50
|
+
for (const realPath of Object.keys(parsed.projects ?? {})) {
|
|
51
|
+
bySlug.set(encodeProjectSlug(realPath), realPath);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
/* malformed — serve the empty index, retry on next mtime change */
|
|
56
|
+
}
|
|
57
|
+
indexCache.set(claudeJsonPath, { mtimeMs, bySlug });
|
|
58
|
+
return bySlug;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Scan the newest transcripts in `<claudeRoot>/<slug>/` for a `"cwd"` whose
|
|
62
|
+
* encoding matches the slug. Bounded read (first SNIFF_BYTES of up to
|
|
63
|
+
* SNIFF_FILES files). Also accepts an ancestor of the cwd — sessions can
|
|
64
|
+
* record a subdirectory cwd while the slug is for the project root.
|
|
65
|
+
*/
|
|
66
|
+
function sniffCwd(claudeRoot, slug) {
|
|
67
|
+
const dir = path.join(claudeRoot, slug);
|
|
68
|
+
let files;
|
|
69
|
+
try {
|
|
70
|
+
files = fs
|
|
71
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
72
|
+
.filter((f) => f.isFile() && f.name.toLowerCase().endsWith('.jsonl'))
|
|
73
|
+
.map((f) => {
|
|
74
|
+
try {
|
|
75
|
+
return { name: f.name, mtimeMs: fs.statSync(path.join(dir, f.name)).mtimeMs };
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return { name: f.name, mtimeMs: 0 };
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
files.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
86
|
+
const cwdRe = /"cwd"\s*:\s*"((?:[^"\\]|\\.)*)"/g;
|
|
87
|
+
for (const f of files.slice(0, SNIFF_FILES)) {
|
|
88
|
+
let chunk;
|
|
89
|
+
try {
|
|
90
|
+
const fd = fs.openSync(path.join(dir, f.name), 'r');
|
|
91
|
+
try {
|
|
92
|
+
const buf = Buffer.alloc(SNIFF_BYTES);
|
|
93
|
+
const read = fs.readSync(fd, buf, 0, SNIFF_BYTES, 0);
|
|
94
|
+
chunk = buf.toString('utf8', 0, read);
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
fs.closeSync(fd);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
cwdRe.lastIndex = 0;
|
|
104
|
+
for (let m = cwdRe.exec(chunk); m; m = cwdRe.exec(chunk)) {
|
|
105
|
+
let cwd;
|
|
106
|
+
try {
|
|
107
|
+
cwd = JSON.parse(`"${m[1]}"`);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
// Exact match, then walk up: a subdir cwd still identifies the root.
|
|
113
|
+
for (let p = cwd;;) {
|
|
114
|
+
if (encodeProjectSlug(p) === slug)
|
|
115
|
+
return p;
|
|
116
|
+
const parent = path.dirname(p);
|
|
117
|
+
if (parent === p)
|
|
118
|
+
break;
|
|
119
|
+
p = parent;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Build a resolver closure. Positive sniff results are memoized per slug so
|
|
127
|
+
* per-request resolution stays cheap; the claude.json index refreshes on
|
|
128
|
+
* mtime change (REQ-SLUGRES-001).
|
|
129
|
+
*/
|
|
130
|
+
export function createSlugResolver(opts = {}) {
|
|
131
|
+
const claudeJsonPath = opts.claudeJsonPath ?? path.join(os.homedir(), '.claude.json');
|
|
132
|
+
const claudeRoot = opts.claudeRoot ?? path.join(os.homedir(), '.claude', 'projects');
|
|
133
|
+
const sniffed = new Map();
|
|
134
|
+
return (slug) => {
|
|
135
|
+
const fromIndex = claudeJsonIndex(claudeJsonPath).get(slug);
|
|
136
|
+
if (fromIndex)
|
|
137
|
+
return fromIndex;
|
|
138
|
+
const cached = sniffed.get(slug);
|
|
139
|
+
if (cached)
|
|
140
|
+
return cached;
|
|
141
|
+
const fromCwd = sniffCwd(claudeRoot, slug);
|
|
142
|
+
if (fromCwd) {
|
|
143
|
+
sniffed.set(slug, fromCwd);
|
|
144
|
+
return fromCwd;
|
|
145
|
+
}
|
|
146
|
+
return decodeProjectSlug(slug);
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/** One-shot convenience for callers without a resolver to hold on to. */
|
|
150
|
+
export function resolveProjectSlug(slug, opts = {}) {
|
|
151
|
+
return createSlugResolver(opts)(slug);
|
|
152
|
+
}
|