specweave 1.0.579 → 1.0.581
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/dist/dashboard/assets/{index-C434W7yF.js → index-Dpn4T2Mx.js} +7 -7
- package/dist/dashboard/index.html +1 -1
- package/dist/src/core/hooks/LifecycleHookDispatcher.d.ts +6 -1
- package/dist/src/core/hooks/LifecycleHookDispatcher.d.ts.map +1 -1
- package/dist/src/core/hooks/LifecycleHookDispatcher.js +66 -8
- package/dist/src/core/hooks/LifecycleHookDispatcher.js.map +1 -1
- package/dist/src/core/increment/completion-validator.d.ts +18 -0
- package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
- package/dist/src/core/increment/completion-validator.js +75 -0
- package/dist/src/core/increment/completion-validator.js.map +1 -1
- package/dist/src/core/increment/status-commands.d.ts.map +1 -1
- package/dist/src/core/increment/status-commands.js +16 -1
- package/dist/src/core/increment/status-commands.js.map +1 -1
- package/dist/src/core/reflect-nudge/aggregator.d.ts +33 -0
- package/dist/src/core/reflect-nudge/aggregator.d.ts.map +1 -0
- package/dist/src/core/reflect-nudge/aggregator.js +80 -0
- package/dist/src/core/reflect-nudge/aggregator.js.map +1 -0
- package/dist/src/core/reflect-nudge/eligibility.d.ts +27 -0
- package/dist/src/core/reflect-nudge/eligibility.d.ts.map +1 -0
- package/dist/src/core/reflect-nudge/eligibility.js +114 -0
- package/dist/src/core/reflect-nudge/eligibility.js.map +1 -0
- package/dist/src/core/reflect-nudge/index.d.ts +14 -0
- package/dist/src/core/reflect-nudge/index.d.ts.map +1 -0
- package/dist/src/core/reflect-nudge/index.js +14 -0
- package/dist/src/core/reflect-nudge/index.js.map +1 -0
- package/dist/src/core/reflect-nudge/interactive-prompt.d.ts +28 -0
- package/dist/src/core/reflect-nudge/interactive-prompt.d.ts.map +1 -0
- package/dist/src/core/reflect-nudge/interactive-prompt.js +52 -0
- package/dist/src/core/reflect-nudge/interactive-prompt.js.map +1 -0
- package/dist/src/core/reflect-nudge/prompt-renderer.d.ts +23 -0
- package/dist/src/core/reflect-nudge/prompt-renderer.d.ts.map +1 -0
- package/dist/src/core/reflect-nudge/prompt-renderer.js +26 -0
- package/dist/src/core/reflect-nudge/prompt-renderer.js.map +1 -0
- package/dist/src/core/reflect-nudge/session-end-nudge.d.ts +31 -0
- package/dist/src/core/reflect-nudge/session-end-nudge.d.ts.map +1 -0
- package/dist/src/core/reflect-nudge/session-end-nudge.js +44 -0
- package/dist/src/core/reflect-nudge/session-end-nudge.js.map +1 -0
- package/dist/src/core/reflection/reflect-handler.d.ts +2 -0
- package/dist/src/core/reflection/reflect-handler.d.ts.map +1 -1
- package/dist/src/core/reflection/reflect-handler.js +4 -0
- package/dist/src/core/reflection/reflect-handler.js.map +1 -1
- package/dist/src/core/rubric/rubric-evaluator.d.ts +15 -0
- package/dist/src/core/rubric/rubric-evaluator.d.ts.map +1 -1
- package/dist/src/core/rubric/rubric-evaluator.js +59 -0
- package/dist/src/core/rubric/rubric-evaluator.js.map +1 -1
- package/dist/src/core/skill-attribution.d.ts +78 -0
- package/dist/src/core/skill-attribution.d.ts.map +1 -0
- package/dist/src/core/skill-attribution.js +168 -0
- package/dist/src/core/skill-attribution.js.map +1 -0
- package/dist/src/core/skill-refine/aggregator.d.ts +36 -0
- package/dist/src/core/skill-refine/aggregator.d.ts.map +1 -0
- package/dist/src/core/skill-refine/aggregator.js +58 -0
- package/dist/src/core/skill-refine/aggregator.js.map +1 -0
- package/dist/src/core/skill-refine/apply.d.ts +48 -0
- package/dist/src/core/skill-refine/apply.d.ts.map +1 -0
- package/dist/src/core/skill-refine/apply.js +97 -0
- package/dist/src/core/skill-refine/apply.js.map +1 -0
- package/dist/src/core/skill-refine/approval.d.ts +71 -0
- package/dist/src/core/skill-refine/approval.d.ts.map +1 -0
- package/dist/src/core/skill-refine/approval.js +113 -0
- package/dist/src/core/skill-refine/approval.js.map +1 -0
- package/dist/src/core/skill-refine/haiku-diff.d.ts +66 -0
- package/dist/src/core/skill-refine/haiku-diff.d.ts.map +1 -0
- package/dist/src/core/skill-refine/haiku-diff.js +111 -0
- package/dist/src/core/skill-refine/haiku-diff.js.map +1 -0
- package/dist/src/core/skill-refine/ledger.d.ts +34 -0
- package/dist/src/core/skill-refine/ledger.d.ts.map +1 -0
- package/dist/src/core/skill-refine/ledger.js +81 -0
- package/dist/src/core/skill-refine/ledger.js.map +1 -0
- package/dist/src/core/skill-signal-emit.d.ts +46 -0
- package/dist/src/core/skill-signal-emit.d.ts.map +1 -0
- package/dist/src/core/skill-signal-emit.js +59 -0
- package/dist/src/core/skill-signal-emit.js.map +1 -0
- package/dist/src/core/skill-signals/index.d.ts +9 -0
- package/dist/src/core/skill-signals/index.d.ts.map +1 -0
- package/dist/src/core/skill-signals/index.js +9 -0
- package/dist/src/core/skill-signals/index.js.map +1 -0
- package/dist/src/core/skill-signals/reader.d.ts +30 -0
- package/dist/src/core/skill-signals/reader.d.ts.map +1 -0
- package/dist/src/core/skill-signals/reader.js +102 -0
- package/dist/src/core/skill-signals/reader.js.map +1 -0
- package/dist/src/core/skill-signals/writer.d.ts +34 -0
- package/dist/src/core/skill-signals/writer.d.ts.map +1 -0
- package/dist/src/core/skill-signals/writer.js +62 -0
- package/dist/src/core/skill-signals/writer.js.map +1 -0
- package/dist/src/core/skills/skill-judge.d.ts +8 -0
- package/dist/src/core/skills/skill-judge.d.ts.map +1 -1
- package/dist/src/core/skills/skill-judge.js +55 -0
- package/dist/src/core/skills/skill-judge.js.map +1 -1
- package/dist/src/skills/reflect-status.d.ts +23 -0
- package/dist/src/skills/reflect-status.d.ts.map +1 -0
- package/dist/src/skills/reflect-status.js +55 -0
- package/dist/src/skills/reflect-status.js.map +1 -0
- package/dist/src/skills/skill-refine.d.ts +81 -0
- package/dist/src/skills/skill-refine.d.ts.map +1 -0
- package/dist/src/skills/skill-refine.js +226 -0
- package/dist/src/skills/skill-refine.js.map +1 -0
- package/dist/src/sync/sync-coordinator.d.ts +14 -4
- package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
- package/dist/src/sync/sync-coordinator.js +100 -38
- package/dist/src/sync/sync-coordinator.js.map +1 -1
- package/dist/src/types/skill-refinements.d.ts +75 -0
- package/dist/src/types/skill-refinements.d.ts.map +1 -0
- package/dist/src/types/skill-refinements.js +43 -0
- package/dist/src/types/skill-refinements.js.map +1 -0
- package/dist/src/types/skill-signals.d.ts +200 -0
- package/dist/src/types/skill-signals.d.ts.map +1 -0
- package/dist/src/types/skill-signals.js +115 -0
- package/dist/src/types/skill-signals.js.map +1 -0
- package/package.json +2 -2
- package/plugins/specweave/commands/reflect.md +48 -0
- package/plugins/specweave/skills/code-reviewer/SKILL.md +4 -0
- package/plugins/specweave/skills/judge-llm/SKILL.md +4 -0
- package/plugins/specweave/skills/skill-gen/SKILL.md +8 -0
- package/plugins/specweave/skills/skill-refine/SKILL.md +96 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reflect-nudge aggregator — groups refinement signals by target skill,
|
|
3
|
+
* applies the ≥3 signal threshold, and ranks by severity × recency.
|
|
4
|
+
*
|
|
5
|
+
* Shared by the session-end nudge (picks the top suggestion) and the
|
|
6
|
+
* `sw:reflect --status` dashboard section (lists everything above threshold).
|
|
7
|
+
*
|
|
8
|
+
* @module core/reflect-nudge/aggregator
|
|
9
|
+
*/
|
|
10
|
+
import { readSignals } from '../skill-signals/reader.js';
|
|
11
|
+
/** Minimum number of refinement signals a skill needs to surface. */
|
|
12
|
+
export const REFINEMENT_SUGGESTION_THRESHOLD = 3;
|
|
13
|
+
const SEVERITY_RANK = {
|
|
14
|
+
high: 3,
|
|
15
|
+
medium: 2,
|
|
16
|
+
low: 1,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Aggregate signals from disk and return skills meeting the threshold,
|
|
20
|
+
* sorted by severity × recency (descending).
|
|
21
|
+
*
|
|
22
|
+
* `threshold` defaults to {@link REFINEMENT_SUGGESTION_THRESHOLD}. Pass `1`
|
|
23
|
+
* for the nudge path (which wants the top suggestion regardless of total).
|
|
24
|
+
*/
|
|
25
|
+
export async function aggregateSuggestions(signalsFilePath, opts = {}) {
|
|
26
|
+
const threshold = opts.threshold ?? REFINEMENT_SUGGESTION_THRESHOLD;
|
|
27
|
+
const file = await readSignals(signalsFilePath);
|
|
28
|
+
const refinements = file.signals.filter((s) => s.type === 'refinement');
|
|
29
|
+
const bySkill = new Map();
|
|
30
|
+
for (const sig of refinements) {
|
|
31
|
+
const list = bySkill.get(sig.targetSkill) ?? [];
|
|
32
|
+
list.push(sig);
|
|
33
|
+
bySkill.set(sig.targetSkill, list);
|
|
34
|
+
}
|
|
35
|
+
const suggestions = [];
|
|
36
|
+
for (const [slug, signals] of bySkill) {
|
|
37
|
+
if (signals.length < threshold)
|
|
38
|
+
continue;
|
|
39
|
+
suggestions.push(toSuggestion(slug, signals));
|
|
40
|
+
}
|
|
41
|
+
return suggestions.sort(compareBySeverityThenRecency);
|
|
42
|
+
}
|
|
43
|
+
function toSuggestion(slug, signals) {
|
|
44
|
+
const countsBySource = {
|
|
45
|
+
'judge-llm': 0,
|
|
46
|
+
rubric: 0,
|
|
47
|
+
'code-reviewer': 0,
|
|
48
|
+
};
|
|
49
|
+
let highestSeverity = 'low';
|
|
50
|
+
let lastDetectedAt = signals[0].detectedAt;
|
|
51
|
+
let mostRecent = signals[0];
|
|
52
|
+
for (const s of signals) {
|
|
53
|
+
countsBySource[s.source] += 1;
|
|
54
|
+
if (SEVERITY_RANK[s.severity] > SEVERITY_RANK[highestSeverity]) {
|
|
55
|
+
highestSeverity = s.severity;
|
|
56
|
+
}
|
|
57
|
+
if (s.detectedAt > lastDetectedAt) {
|
|
58
|
+
lastDetectedAt = s.detectedAt;
|
|
59
|
+
mostRecent = s;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
slug,
|
|
64
|
+
totalCount: signals.length,
|
|
65
|
+
countsBySource,
|
|
66
|
+
highestSeverity,
|
|
67
|
+
severityScore: SEVERITY_RANK[highestSeverity],
|
|
68
|
+
lastDetectedAt,
|
|
69
|
+
sampleEvidence: mostRecent.evidence,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function compareBySeverityThenRecency(a, b) {
|
|
73
|
+
const sev = b.severityScore - a.severityScore;
|
|
74
|
+
if (sev !== 0)
|
|
75
|
+
return sev;
|
|
76
|
+
if (a.lastDetectedAt === b.lastDetectedAt)
|
|
77
|
+
return 0;
|
|
78
|
+
return a.lastDetectedAt < b.lastDetectedAt ? 1 : -1;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=aggregator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregator.js","sourceRoot":"","sources":["../../../../src/core/reflect-nudge/aggregator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAMzD,qEAAqE;AACrE,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAC;AAajD,MAAM,aAAa,GAA8C;IAC/D,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,GAAG,EAAE,CAAC;CACP,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,eAAuB,EACvB,OAA+B,EAAE;IAEjC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,+BAA+B,CAAC;IACpE,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,eAAe,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CACrC,CAAC,CAAC,EAAyB,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,CACtD,CAAC;IAEF,MAAM,OAAO,GAAG,IAAI,GAAG,EAA8B,CAAC;IACtD,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,WAAW,GAAsB,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,OAAO,EAAE,CAAC;QACtC,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS;YAAE,SAAS;QACzC,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAChD,CAAC;IAED,OAAO,WAAW,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,YAAY,CAAC,IAAY,EAAE,OAA2B;IAC7D,MAAM,cAAc,GAAiC;QACnD,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,CAAC;QACT,eAAe,EAAE,CAAC;KACnB,CAAC;IACF,IAAI,eAAe,GAA8B,KAAK,CAAC;IACvD,IAAI,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IAC3C,IAAI,UAAU,GAAqB,OAAO,CAAC,CAAC,CAAC,CAAC;IAE9C,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,aAAa,CAAC,eAAe,CAAC,EAAE,CAAC;YAC/D,eAAe,GAAG,CAAC,CAAC,QAAQ,CAAC;QAC/B,CAAC;QACD,IAAI,CAAC,CAAC,UAAU,GAAG,cAAc,EAAE,CAAC;YAClC,cAAc,GAAG,CAAC,CAAC,UAAU,CAAC;YAC9B,UAAU,GAAG,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI;QACJ,UAAU,EAAE,OAAO,CAAC,MAAM;QAC1B,cAAc;QACd,eAAe;QACf,aAAa,EAAE,aAAa,CAAC,eAAe,CAAC;QAC7C,cAAc;QACd,cAAc,EAAE,UAAU,CAAC,QAAQ;KACpC,CAAC;AACJ,CAAC;AAED,SAAS,4BAA4B,CAAC,CAAkB,EAAE,CAAkB;IAC1E,MAAM,GAAG,GAAG,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,CAAC;IAC9C,IAAI,GAAG,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAC1B,IAAI,CAAC,CAAC,cAAc,KAAK,CAAC,CAAC,cAAc;QAAE,OAAO,CAAC,CAAC;IACpD,OAAO,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nudge eligibility — checks whether the session-end prompt should be
|
|
3
|
+
* shown at /sw:done close time (AC-US3-01, AC-US3-02).
|
|
4
|
+
*
|
|
5
|
+
* @module core/reflect-nudge/eligibility
|
|
6
|
+
*/
|
|
7
|
+
import { type SkillSuggestion } from './aggregator.js';
|
|
8
|
+
export interface NudgeEligibility {
|
|
9
|
+
shouldNudge: boolean;
|
|
10
|
+
reason: 'disabled' | 'no-signal' | 'has-refinement' | 'has-learning';
|
|
11
|
+
topRefinement: SkillSuggestion | null;
|
|
12
|
+
hasHighConfidenceLearning: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface EligibilityOptions {
|
|
15
|
+
/** Project root — defaults to cwd. */
|
|
16
|
+
projectRoot?: string;
|
|
17
|
+
/** Increment ID, used to scope the learning-window check. */
|
|
18
|
+
incrementId: string;
|
|
19
|
+
/** Override the autoNudge config flag (for tests). */
|
|
20
|
+
autoNudge?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Evaluate whether to show the session-end nudge. Never throws; failure
|
|
24
|
+
* to read state is treated as "no signal".
|
|
25
|
+
*/
|
|
26
|
+
export declare function checkNudgeEligibility(opts: EligibilityOptions): Promise<NudgeEligibility>;
|
|
27
|
+
//# sourceMappingURL=eligibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eligibility.d.ts","sourceRoot":"","sources":["../../../../src/core/reflect-nudge/eligibility.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAE7E,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,gBAAgB,GAAG,cAAc,CAAC;IACrE,aAAa,EAAE,eAAe,GAAG,IAAI,CAAC;IACtC,yBAAyB,EAAE,OAAO,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,gBAAgB,CAAC,CA2C3B"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nudge eligibility — checks whether the session-end prompt should be
|
|
3
|
+
* shown at /sw:done close time (AC-US3-01, AC-US3-02).
|
|
4
|
+
*
|
|
5
|
+
* @module core/reflect-nudge/eligibility
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { aggregateSuggestions } from './aggregator.js';
|
|
10
|
+
/**
|
|
11
|
+
* Evaluate whether to show the session-end nudge. Never throws; failure
|
|
12
|
+
* to read state is treated as "no signal".
|
|
13
|
+
*/
|
|
14
|
+
export async function checkNudgeEligibility(opts) {
|
|
15
|
+
const root = opts.projectRoot ?? process.cwd();
|
|
16
|
+
const autoNudge = opts.autoNudge ?? readAutoNudge(root);
|
|
17
|
+
if (!autoNudge) {
|
|
18
|
+
return {
|
|
19
|
+
shouldNudge: false,
|
|
20
|
+
reason: 'disabled',
|
|
21
|
+
topRefinement: null,
|
|
22
|
+
hasHighConfidenceLearning: false,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const signalsPath = path.join(root, '.specweave', 'state', 'skill-signals.json');
|
|
26
|
+
const suggestions = await aggregateSuggestions(signalsPath, { threshold: 1 }).catch(() => []);
|
|
27
|
+
const topRefinement = suggestions[0] ?? null;
|
|
28
|
+
const hasHighConfidenceLearning = detectFreshLearning(root, opts.incrementId);
|
|
29
|
+
if (topRefinement) {
|
|
30
|
+
return {
|
|
31
|
+
shouldNudge: true,
|
|
32
|
+
reason: 'has-refinement',
|
|
33
|
+
topRefinement,
|
|
34
|
+
hasHighConfidenceLearning,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (hasHighConfidenceLearning) {
|
|
38
|
+
return {
|
|
39
|
+
shouldNudge: true,
|
|
40
|
+
reason: 'has-learning',
|
|
41
|
+
topRefinement: null,
|
|
42
|
+
hasHighConfidenceLearning: true,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
shouldNudge: false,
|
|
47
|
+
reason: 'no-signal',
|
|
48
|
+
topRefinement: null,
|
|
49
|
+
hasHighConfidenceLearning: false,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// ── Internals ────────────────────────────────────────────────────────
|
|
53
|
+
function readAutoNudge(root) {
|
|
54
|
+
try {
|
|
55
|
+
const configPath = path.join(root, '.specweave', 'config.json');
|
|
56
|
+
if (!fs.existsSync(configPath))
|
|
57
|
+
return true;
|
|
58
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
59
|
+
if (config?.reflect?.autoNudge === false)
|
|
60
|
+
return false;
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A "high-confidence learning" at close time is a skill-memory file under
|
|
69
|
+
* `.specweave/skill-memories/` whose mtime is newer than the increment's
|
|
70
|
+
* metadata.json mtime — i.e. something written during the session.
|
|
71
|
+
*
|
|
72
|
+
* This is a heuristic, not a guarantee. On read errors we return false,
|
|
73
|
+
* which just means the nudge falls back to the refinement-only path.
|
|
74
|
+
*/
|
|
75
|
+
function detectFreshLearning(root, incrementId) {
|
|
76
|
+
try {
|
|
77
|
+
const memoriesDir = path.join(root, '.specweave', 'skill-memories');
|
|
78
|
+
if (!fs.existsSync(memoriesDir))
|
|
79
|
+
return false;
|
|
80
|
+
const metadataPath = resolveMetadataPath(root, incrementId);
|
|
81
|
+
if (!metadataPath)
|
|
82
|
+
return false;
|
|
83
|
+
const since = fs.statSync(metadataPath).mtimeMs;
|
|
84
|
+
const files = fs.readdirSync(memoriesDir).filter((f) => f.endsWith('.md'));
|
|
85
|
+
for (const f of files) {
|
|
86
|
+
const stat = fs.statSync(path.join(memoriesDir, f));
|
|
87
|
+
if (stat.mtimeMs > since)
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function resolveMetadataPath(root, incrementId) {
|
|
97
|
+
try {
|
|
98
|
+
const incrementsDir = path.join(root, '.specweave', 'increments');
|
|
99
|
+
if (!fs.existsSync(incrementsDir))
|
|
100
|
+
return null;
|
|
101
|
+
for (const entry of fs.readdirSync(incrementsDir)) {
|
|
102
|
+
if (entry === incrementId || entry.startsWith(`${incrementId}-`)) {
|
|
103
|
+
const candidate = path.join(incrementsDir, entry, 'metadata.json');
|
|
104
|
+
if (fs.existsSync(candidate))
|
|
105
|
+
return candidate;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=eligibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eligibility.js","sourceRoot":"","sources":["../../../../src/core/reflect-nudge/eligibility.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Barrel export for the reflect-nudge module.
|
|
3
|
+
*
|
|
4
|
+
* Consumers: `completeIncrement` (session-end nudge at /sw:done close),
|
|
5
|
+
* `sw:reflect --status` (dashboard section).
|
|
6
|
+
*
|
|
7
|
+
* @module core/reflect-nudge
|
|
8
|
+
*/
|
|
9
|
+
export { aggregateSuggestions, REFINEMENT_SUGGESTION_THRESHOLD, type SkillSuggestion, } from './aggregator.js';
|
|
10
|
+
export { checkNudgeEligibility, type NudgeEligibility } from './eligibility.js';
|
|
11
|
+
export { renderNudgePrompt, type NudgeInputs } from './prompt-renderer.js';
|
|
12
|
+
export { promptNudge, type PromptResponse, type PromptOptions } from './interactive-prompt.js';
|
|
13
|
+
export { runSessionEndNudge, type NudgeRunResult } from './session-end-nudge.js';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/reflect-nudge/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,oBAAoB,EACpB,+BAA+B,EAC/B,KAAK,eAAe,GACrB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,qBAAqB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE3E,OAAO,EAAE,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE/F,OAAO,EAAE,kBAAkB,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Barrel export for the reflect-nudge module.
|
|
3
|
+
*
|
|
4
|
+
* Consumers: `completeIncrement` (session-end nudge at /sw:done close),
|
|
5
|
+
* `sw:reflect --status` (dashboard section).
|
|
6
|
+
*
|
|
7
|
+
* @module core/reflect-nudge
|
|
8
|
+
*/
|
|
9
|
+
export { aggregateSuggestions, REFINEMENT_SUGGESTION_THRESHOLD, } from './aggregator.js';
|
|
10
|
+
export { checkNudgeEligibility } from './eligibility.js';
|
|
11
|
+
export { renderNudgePrompt } from './prompt-renderer.js';
|
|
12
|
+
export { promptNudge } from './interactive-prompt.js';
|
|
13
|
+
export { runSessionEndNudge } from './session-end-nudge.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/core/reflect-nudge/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,oBAAoB,EACpB,+BAA+B,GAEhC,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,qBAAqB,EAAyB,MAAM,kBAAkB,CAAC;AAEhF,OAAO,EAAE,iBAAiB,EAAoB,MAAM,sBAAsB,CAAC;AAE3E,OAAO,EAAE,WAAW,EAA2C,MAAM,yBAAyB,CAAC;AAE/F,OAAO,EAAE,kBAAkB,EAAuB,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-end nudge printer with 5-second input timeout.
|
|
3
|
+
*
|
|
4
|
+
* Prints the prompt line to stdout; waits up to `timeoutMs` for a single
|
|
5
|
+
* character on stdin. Non-TTY, empty input, "N", "n", or timeout → treated
|
|
6
|
+
* as declined (AC-US3-03). Only explicit "y"/"Y" is accepted.
|
|
7
|
+
*
|
|
8
|
+
* Per AC-US3-04 this module NEVER executes the suggested command — it
|
|
9
|
+
* only returns the user's response to the caller.
|
|
10
|
+
*
|
|
11
|
+
* @module core/reflect-nudge/interactive-prompt
|
|
12
|
+
*/
|
|
13
|
+
export interface PromptOptions {
|
|
14
|
+
/** Timeout in milliseconds before defaulting to No. Default: 5000. */
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
/** Override the stdin/stdout streams (mainly for tests). */
|
|
17
|
+
stdin?: NodeJS.ReadableStream;
|
|
18
|
+
stdout?: NodeJS.WritableStream;
|
|
19
|
+
}
|
|
20
|
+
export type PromptResponse = 'yes' | 'no' | 'timeout' | 'non-tty';
|
|
21
|
+
/**
|
|
22
|
+
* Print `line` and wait for a y/N answer. Returns which branch fired.
|
|
23
|
+
*
|
|
24
|
+
* Non-interactive contexts (piped stdin, CI, auto-mode) short-circuit
|
|
25
|
+
* immediately to `'non-tty'` — treated as a decline.
|
|
26
|
+
*/
|
|
27
|
+
export declare function promptNudge(line: string, opts?: PromptOptions): Promise<PromptResponse>;
|
|
28
|
+
//# sourceMappingURL=interactive-prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive-prompt.d.ts","sourceRoot":"","sources":["../../../../src/core/reflect-nudge/interactive-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;CAChC;AAED,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,CAAC;AAElE;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,cAAc,CAAC,CAuCzB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-end nudge printer with 5-second input timeout.
|
|
3
|
+
*
|
|
4
|
+
* Prints the prompt line to stdout; waits up to `timeoutMs` for a single
|
|
5
|
+
* character on stdin. Non-TTY, empty input, "N", "n", or timeout → treated
|
|
6
|
+
* as declined (AC-US3-03). Only explicit "y"/"Y" is accepted.
|
|
7
|
+
*
|
|
8
|
+
* Per AC-US3-04 this module NEVER executes the suggested command — it
|
|
9
|
+
* only returns the user's response to the caller.
|
|
10
|
+
*
|
|
11
|
+
* @module core/reflect-nudge/interactive-prompt
|
|
12
|
+
*/
|
|
13
|
+
import * as readline from 'readline';
|
|
14
|
+
/**
|
|
15
|
+
* Print `line` and wait for a y/N answer. Returns which branch fired.
|
|
16
|
+
*
|
|
17
|
+
* Non-interactive contexts (piped stdin, CI, auto-mode) short-circuit
|
|
18
|
+
* immediately to `'non-tty'` — treated as a decline.
|
|
19
|
+
*/
|
|
20
|
+
export async function promptNudge(line, opts = {}) {
|
|
21
|
+
const stdin = opts.stdin ?? process.stdin;
|
|
22
|
+
const stdout = opts.stdout ?? process.stdout;
|
|
23
|
+
const timeoutMs = opts.timeoutMs ?? 5000;
|
|
24
|
+
stdout.write(`${line}\n`);
|
|
25
|
+
const isInteractive = typeof stdin.isTTY !== 'undefined'
|
|
26
|
+
? Boolean(stdin.isTTY)
|
|
27
|
+
: true;
|
|
28
|
+
if (!isInteractive) {
|
|
29
|
+
return 'non-tty';
|
|
30
|
+
}
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
const rl = readline.createInterface({ input: stdin, output: stdout, terminal: false });
|
|
33
|
+
let settled = false;
|
|
34
|
+
const done = (response) => {
|
|
35
|
+
if (settled)
|
|
36
|
+
return;
|
|
37
|
+
settled = true;
|
|
38
|
+
clearTimeout(timer);
|
|
39
|
+
rl.close();
|
|
40
|
+
resolve(response);
|
|
41
|
+
};
|
|
42
|
+
const timer = setTimeout(() => done('timeout'), timeoutMs);
|
|
43
|
+
rl.once('line', (input) => {
|
|
44
|
+
const trimmed = input.trim().toLowerCase();
|
|
45
|
+
done(trimmed === 'y' || trimmed === 'yes' ? 'yes' : 'no');
|
|
46
|
+
});
|
|
47
|
+
rl.once('close', () => {
|
|
48
|
+
done('no');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=interactive-prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive-prompt.js","sourceRoot":"","sources":["../../../../src/core/reflect-nudge/interactive-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAYrC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAY,EACZ,OAAsB,EAAE;IAExB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC;IAEzC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;IAE1B,MAAM,aAAa,GACjB,OAAQ,KAA2B,CAAC,KAAK,KAAK,WAAW;QACvD,CAAC,CAAC,OAAO,CAAE,KAA2B,CAAC,KAAK,CAAC;QAC7C,CAAC,CAAC,IAAI,CAAC;IAEX,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,IAAI,OAAO,CAAiB,CAAC,OAAO,EAAE,EAAE;QAC7C,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QACvF,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,IAAI,GAAG,CAAC,QAAwB,EAAQ,EAAE;YAC9C,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpB,CAAC,CAAC;QAEF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC;QAE3D,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAChC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC3C,IAAI,CAAC,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;YACpB,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders the one-line session-end nudge prompt shown at /sw:done close.
|
|
3
|
+
*
|
|
4
|
+
* Format (AC-US3-01): `Detected: <short summary> — run <command>? (y/N)`
|
|
5
|
+
*
|
|
6
|
+
* Picks between refinement and learning suggestions deterministically —
|
|
7
|
+
* prefers highest-severity refinement over highest-confidence learning so
|
|
8
|
+
* repeat invocations with the same state render the same prompt.
|
|
9
|
+
*
|
|
10
|
+
* @module core/reflect-nudge/prompt-renderer
|
|
11
|
+
*/
|
|
12
|
+
import type { SkillSuggestion } from './aggregator.js';
|
|
13
|
+
export interface NudgeInputs {
|
|
14
|
+
/** Top refinement suggestion (threshold=1 aggregator output), or null. */
|
|
15
|
+
topRefinement: SkillSuggestion | null;
|
|
16
|
+
/** True when a high-confidence learning was captured this session. */
|
|
17
|
+
hasHighConfidenceLearning: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Returns the nudge line, or null if nothing should be printed.
|
|
21
|
+
*/
|
|
22
|
+
export declare function renderNudgePrompt(inputs: NudgeInputs): string | null;
|
|
23
|
+
//# sourceMappingURL=prompt-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-renderer.d.ts","sourceRoot":"","sources":["../../../../src/core/reflect-nudge/prompt-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,MAAM,WAAW,WAAW;IAC1B,0EAA0E;IAC1E,aAAa,EAAE,eAAe,GAAG,IAAI,CAAC;IACtC,sEAAsE;IACtE,yBAAyB,EAAE,OAAO,CAAC;CACpC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAapE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders the one-line session-end nudge prompt shown at /sw:done close.
|
|
3
|
+
*
|
|
4
|
+
* Format (AC-US3-01): `Detected: <short summary> — run <command>? (y/N)`
|
|
5
|
+
*
|
|
6
|
+
* Picks between refinement and learning suggestions deterministically —
|
|
7
|
+
* prefers highest-severity refinement over highest-confidence learning so
|
|
8
|
+
* repeat invocations with the same state render the same prompt.
|
|
9
|
+
*
|
|
10
|
+
* @module core/reflect-nudge/prompt-renderer
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Returns the nudge line, or null if nothing should be printed.
|
|
14
|
+
*/
|
|
15
|
+
export function renderNudgePrompt(inputs) {
|
|
16
|
+
const { topRefinement, hasHighConfidenceLearning } = inputs;
|
|
17
|
+
if (topRefinement) {
|
|
18
|
+
const count = topRefinement.totalCount;
|
|
19
|
+
return `Detected: ${topRefinement.slug} (${count} ${count === 1 ? 'signal' : 'signals'}) — run \`sw:skill-refine ${topRefinement.slug}\`? (y/N)`;
|
|
20
|
+
}
|
|
21
|
+
if (hasHighConfidenceLearning) {
|
|
22
|
+
return 'Detected: new learning worth persisting — run `sw:reflect`? (y/N)';
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=prompt-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-renderer.js","sourceRoot":"","sources":["../../../../src/core/reflect-nudge/prompt-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAmB;IACnD,MAAM,EAAE,aAAa,EAAE,yBAAyB,EAAE,GAAG,MAAM,CAAC;IAE5D,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,KAAK,GAAG,aAAa,CAAC,UAAU,CAAC;QACvC,OAAO,aAAa,aAAa,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,6BAA6B,aAAa,CAAC,IAAI,WAAW,CAAC;IACnJ,CAAC;IAED,IAAI,yBAAyB,EAAE,CAAC;QAC9B,OAAO,mEAAmE,CAAC;IAC7E,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrator for the /sw:done close-path nudge.
|
|
3
|
+
*
|
|
4
|
+
* Composes eligibility + renderer + prompt. Single entry point consumed
|
|
5
|
+
* by `completeIncrement` so the close-path adds <100ms even when no
|
|
6
|
+
* signals are present (spec: Non-Functional Performance).
|
|
7
|
+
*
|
|
8
|
+
* @module core/reflect-nudge/session-end-nudge
|
|
9
|
+
*/
|
|
10
|
+
import { type PromptResponse, type PromptOptions } from './interactive-prompt.js';
|
|
11
|
+
export interface NudgeRunResult {
|
|
12
|
+
printed: boolean;
|
|
13
|
+
response: PromptResponse | 'skipped';
|
|
14
|
+
reason: 'disabled' | 'no-signal' | 'has-refinement' | 'has-learning';
|
|
15
|
+
}
|
|
16
|
+
export interface NudgeRunOptions {
|
|
17
|
+
projectRoot?: string;
|
|
18
|
+
incrementId: string;
|
|
19
|
+
/** Suppress side effects (fully silent). Used when the caller is silent/json. */
|
|
20
|
+
silent?: boolean;
|
|
21
|
+
/** Overrides forwarded to the interactive prompt — stdin/stdout/timeout. */
|
|
22
|
+
prompt?: PromptOptions;
|
|
23
|
+
/** Override autoNudge for tests. */
|
|
24
|
+
autoNudge?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Run the nudge. Never throws; any failure returns `{printed: false, response: 'skipped'}`.
|
|
28
|
+
* Caller should not branch on the response — AC-US3-04 forbids auto-execution.
|
|
29
|
+
*/
|
|
30
|
+
export declare function runSessionEndNudge(opts: NudgeRunOptions): Promise<NudgeRunResult>;
|
|
31
|
+
//# sourceMappingURL=session-end-nudge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-end-nudge.d.ts","sourceRoot":"","sources":["../../../../src/core/reflect-nudge/session-end-nudge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAe,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE/F,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;IACrC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,gBAAgB,GAAG,cAAc,CAAC;CACtE;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,oCAAoC;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,cAAc,CAAC,CA8BzB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrator for the /sw:done close-path nudge.
|
|
3
|
+
*
|
|
4
|
+
* Composes eligibility + renderer + prompt. Single entry point consumed
|
|
5
|
+
* by `completeIncrement` so the close-path adds <100ms even when no
|
|
6
|
+
* signals are present (spec: Non-Functional Performance).
|
|
7
|
+
*
|
|
8
|
+
* @module core/reflect-nudge/session-end-nudge
|
|
9
|
+
*/
|
|
10
|
+
import { checkNudgeEligibility } from './eligibility.js';
|
|
11
|
+
import { renderNudgePrompt } from './prompt-renderer.js';
|
|
12
|
+
import { promptNudge } from './interactive-prompt.js';
|
|
13
|
+
/**
|
|
14
|
+
* Run the nudge. Never throws; any failure returns `{printed: false, response: 'skipped'}`.
|
|
15
|
+
* Caller should not branch on the response — AC-US3-04 forbids auto-execution.
|
|
16
|
+
*/
|
|
17
|
+
export async function runSessionEndNudge(opts) {
|
|
18
|
+
if (opts.silent) {
|
|
19
|
+
return { printed: false, response: 'skipped', reason: 'disabled' };
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const eligibility = await checkNudgeEligibility({
|
|
23
|
+
projectRoot: opts.projectRoot,
|
|
24
|
+
incrementId: opts.incrementId,
|
|
25
|
+
autoNudge: opts.autoNudge,
|
|
26
|
+
});
|
|
27
|
+
if (!eligibility.shouldNudge) {
|
|
28
|
+
return { printed: false, response: 'skipped', reason: eligibility.reason };
|
|
29
|
+
}
|
|
30
|
+
const line = renderNudgePrompt({
|
|
31
|
+
topRefinement: eligibility.topRefinement,
|
|
32
|
+
hasHighConfidenceLearning: eligibility.hasHighConfidenceLearning,
|
|
33
|
+
});
|
|
34
|
+
if (!line) {
|
|
35
|
+
return { printed: false, response: 'skipped', reason: eligibility.reason };
|
|
36
|
+
}
|
|
37
|
+
const response = await promptNudge(line, opts.prompt);
|
|
38
|
+
return { printed: true, response, reason: eligibility.reason };
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return { printed: false, response: 'skipped', reason: 'no-signal' };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=session-end-nudge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-end-nudge.js","sourceRoot":"","sources":["../../../../src/core/reflect-nudge/session-end-nudge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,WAAW,EAA2C,MAAM,yBAAyB,CAAC;AAmB/F;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAAqB;IAErB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACrE,CAAC;IAED,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,MAAM,qBAAqB,CAAC;YAC9C,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC,CAAC;QAEH,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC;YAC7B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC;QAC7E,CAAC;QAED,MAAM,IAAI,GAAG,iBAAiB,CAAC;YAC7B,aAAa,EAAE,WAAW,CAAC,aAAa;YACxC,yBAAyB,EAAE,WAAW,CAAC,yBAAyB;SACjE,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC;QAC7E,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACtD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IACtE,CAAC;AACH,CAAC"}
|
|
@@ -29,6 +29,8 @@ export interface ReflectConfig {
|
|
|
29
29
|
model: ClaudeModel;
|
|
30
30
|
/** Maximum learnings per session */
|
|
31
31
|
maxLearningsPerSession: number;
|
|
32
|
+
/** Session-end nudge on /sw:done close (default: true). Set false to disable. */
|
|
33
|
+
autoNudge: boolean;
|
|
32
34
|
}
|
|
33
35
|
/**
|
|
34
36
|
* Default configuration
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reflect-handler.d.ts","sourceRoot":"","sources":["../../../../src/core/reflection/reflect-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAqC,KAAK,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAWvG;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,mCAAmC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,mDAAmD;IACnD,KAAK,EAAE,WAAW,CAAC;IACnB,oCAAoC;IACpC,sBAAsB,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"reflect-handler.d.ts","sourceRoot":"","sources":["../../../../src/core/reflection/reflect-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAqC,KAAK,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAWvG;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,mCAAmC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,mDAAmD;IACnD,KAAK,EAAE,WAAW,CAAC;IACnB,oCAAoC;IACpC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,iFAAiF;IACjF,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,eAAO,MAAM,sBAAsB,EAAE,aAKpC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,mCAAmC;IACnC,cAAc,EAAE,aAAa,EAAE,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,0BAA0B;IAC1B,GAAG,EAAE,OAAO,CAAC;IACb,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,oBAAoB;IACpB,YAAY,EAAE;QACZ,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,yBAAyB;IACzB,SAAS,EAAE;QACT,cAAc,EAAE,aAAa,EAAE,CAAC;KACjC,CAAC;IACF,uBAAuB;IACvB,OAAO,EAAE;QACP,cAAc,EAAE,MAAM,CAAC;QACvB,yBAAyB,EAAE,MAAM,CAAC;QAClC,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,qBAAqB;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAyZD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,CA+BpE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,aAAa,CAAC,CA2GxB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAqDjE;AAoDD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAyFlG;AAED;;;;;;;;GAQG;AACH,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAsC5E"}
|
|
@@ -30,6 +30,7 @@ export const DEFAULT_REFLECT_CONFIG = {
|
|
|
30
30
|
enabled: true,
|
|
31
31
|
model: 'haiku',
|
|
32
32
|
maxLearningsPerSession: 3,
|
|
33
|
+
autoNudge: true,
|
|
33
34
|
};
|
|
34
35
|
/**
|
|
35
36
|
* Known SpecWeave skills/plugins for validation
|
|
@@ -403,6 +404,9 @@ export function readReflectConfig(projectRoot) {
|
|
|
403
404
|
if (typeof configContent.reflect.maxLearningsPerSession === 'number') {
|
|
404
405
|
config.maxLearningsPerSession = configContent.reflect.maxLearningsPerSession;
|
|
405
406
|
}
|
|
407
|
+
if (typeof configContent.reflect.autoNudge === 'boolean') {
|
|
408
|
+
config.autoNudge = configContent.reflect.autoNudge;
|
|
409
|
+
}
|
|
406
410
|
}
|
|
407
411
|
}
|
|
408
412
|
catch {
|