specweave 1.0.586 → 1.0.588

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/bin/specweave.js +30 -0
  2. package/dist/dashboard/assets/{index-DdI7mc7Y.js → index-DH3l1QBV.js} +6 -6
  3. package/dist/dashboard/assets/index-vkwF92tD.css +1 -0
  4. package/dist/dashboard/index.html +2 -2
  5. package/dist/plugins/specweave/lib/integrations/github/github-access-error.d.ts +48 -0
  6. package/dist/plugins/specweave/lib/integrations/github/github-access-error.d.ts.map +1 -0
  7. package/dist/plugins/specweave/lib/integrations/github/github-access-error.js +69 -0
  8. package/dist/plugins/specweave/lib/integrations/github/github-access-error.js.map +1 -0
  9. package/dist/plugins/specweave/lib/integrations/github/github-client-v2.d.ts +8 -0
  10. package/dist/plugins/specweave/lib/integrations/github/github-client-v2.d.ts.map +1 -1
  11. package/dist/plugins/specweave/lib/integrations/github/github-client-v2.js +22 -2
  12. package/dist/plugins/specweave/lib/integrations/github/github-client-v2.js.map +1 -1
  13. package/dist/plugins/specweave/lib/utils/fs-native.d.ts +1 -1
  14. package/dist/plugins/specweave/lib/vendor/generators/spec/task-parser.js +38 -16
  15. package/dist/plugins/specweave/lib/vendor/generators/spec/task-parser.js.map +1 -1
  16. package/dist/plugins/specweave/lib/vendor/utils/fs-native.d.ts +1 -1
  17. package/dist/src/cli/commands/handoff.d.ts +54 -0
  18. package/dist/src/cli/commands/handoff.d.ts.map +1 -0
  19. package/dist/src/cli/commands/handoff.js +82 -0
  20. package/dist/src/cli/commands/handoff.js.map +1 -0
  21. package/dist/src/cli/helpers/init/external-import.js.map +1 -1
  22. package/dist/src/cli/helpers/init/gitignore-generator.d.ts.map +1 -1
  23. package/dist/src/cli/helpers/init/gitignore-generator.js +3 -0
  24. package/dist/src/cli/helpers/init/gitignore-generator.js.map +1 -1
  25. package/dist/src/core/hooks/handlers/hook-router.d.ts.map +1 -1
  26. package/dist/src/core/hooks/handlers/hook-router.js +5 -0
  27. package/dist/src/core/hooks/handlers/hook-router.js.map +1 -1
  28. package/dist/src/core/hooks/handlers/pre-compact.d.ts +33 -0
  29. package/dist/src/core/hooks/handlers/pre-compact.d.ts.map +1 -0
  30. package/dist/src/core/hooks/handlers/pre-compact.js +109 -0
  31. package/dist/src/core/hooks/handlers/pre-compact.js.map +1 -0
  32. package/dist/src/core/hooks/handlers/types.d.ts +1 -1
  33. package/dist/src/core/hooks/handlers/types.d.ts.map +1 -1
  34. package/dist/src/core/hooks/handlers/types.js +3 -0
  35. package/dist/src/core/hooks/handlers/types.js.map +1 -1
  36. package/dist/src/core/session/handoff-doc-format.d.ts +164 -0
  37. package/dist/src/core/session/handoff-doc-format.d.ts.map +1 -0
  38. package/dist/src/core/session/handoff-doc-format.js +292 -0
  39. package/dist/src/core/session/handoff-doc-format.js.map +1 -0
  40. package/dist/src/core/session/handoff-git-state.d.ts +49 -0
  41. package/dist/src/core/session/handoff-git-state.d.ts.map +1 -0
  42. package/dist/src/core/session/handoff-git-state.js +164 -0
  43. package/dist/src/core/session/handoff-git-state.js.map +1 -0
  44. package/dist/src/core/session/handoff-secret-scrub.d.ts +59 -0
  45. package/dist/src/core/session/handoff-secret-scrub.d.ts.map +1 -0
  46. package/dist/src/core/session/handoff-secret-scrub.js +72 -0
  47. package/dist/src/core/session/handoff-secret-scrub.js.map +1 -0
  48. package/dist/src/core/session/{handoff-context.d.ts → install-handoff-context.d.ts} +7 -3
  49. package/dist/src/core/session/install-handoff-context.d.ts.map +1 -0
  50. package/dist/src/core/session/{handoff-context.js → install-handoff-context.js} +7 -3
  51. package/dist/src/core/session/install-handoff-context.js.map +1 -0
  52. package/dist/src/core/session/work-handoff.d.ts +88 -0
  53. package/dist/src/core/session/work-handoff.d.ts.map +1 -0
  54. package/dist/src/core/session/work-handoff.js +412 -0
  55. package/dist/src/core/session/work-handoff.js.map +1 -0
  56. package/dist/src/generators/spec/task-parser.d.ts.map +1 -1
  57. package/dist/src/generators/spec/task-parser.js +38 -16
  58. package/dist/src/generators/spec/task-parser.js.map +1 -1
  59. package/dist/src/utils/fs-native.d.ts +1 -1
  60. package/package.json +2 -2
  61. package/plugins/specweave/commands/handoff.md +54 -0
  62. package/plugins/specweave/lib/integrations/github/github-access-error.js +43 -0
  63. package/plugins/specweave/lib/integrations/github/github-access-error.ts +103 -0
  64. package/plugins/specweave/lib/integrations/github/github-client-v2.js +24 -4
  65. package/plugins/specweave/lib/integrations/github/github-client-v2.ts +26 -4
  66. package/plugins/specweave/lib/vendor/generators/spec/task-parser.js +38 -16
  67. package/plugins/specweave/lib/vendor/generators/spec/task-parser.js.map +1 -1
  68. package/plugins/specweave/lib/vendor/utils/fs-native.d.ts +1 -1
  69. package/plugins/specweave/skills/handoff/SKILL.md +59 -0
  70. package/dist/dashboard/assets/index-YkogWPHY.css +0 -1
  71. package/dist/src/core/session/handoff-context.d.ts.map +0 -1
  72. package/dist/src/core/session/handoff-context.js.map +0 -1
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Handoff Git State Capture
3
+ *
4
+ * Captures the cheap, deterministic git facts the handoff doc needs (branch,
5
+ * short sha, porcelain status, diff --stat) AND dumps the FULL working-tree +
6
+ * staged diff to a sibling `.diff` file. The full diff is captured for free via
7
+ * git — no LLM tokens are spent — and is the key in-flight-fidelity artifact:
8
+ * it lets the resuming agent see exactly what was changed but not yet committed.
9
+ *
10
+ * In a non-git directory every field degrades to empty and no error is thrown,
11
+ * so the non-SpecWeave fallback path still works on plain folders.
12
+ *
13
+ * Part of increment 0867: Cross-Tool Work Handoff (AC-US4-01..04).
14
+ *
15
+ * @module core/session/handoff-git-state
16
+ */
17
+ /**
18
+ * Captured git state for the handoff doc.
19
+ */
20
+ export interface GitState {
21
+ /** Whether `repoRoot` is inside a git work tree at all. */
22
+ isGitRepo: boolean;
23
+ /** Current branch name (empty if detached or non-git). */
24
+ branch: string;
25
+ /** Short commit SHA of HEAD (empty if no commits / non-git). */
26
+ shortSha: string;
27
+ /** `git status --porcelain` output (empty when clean or non-git). */
28
+ statusPorcelain: string;
29
+ /** Combined `git diff --stat` (working) + `git diff --cached --stat` (staged). */
30
+ diffStat: string;
31
+ /** True when there are working-tree or staged changes. */
32
+ hasUncommittedChanges: boolean;
33
+ }
34
+ /**
35
+ * Capture git state for `repoRoot` and dump the full uncommitted diff to
36
+ * `diffOutputPath`.
37
+ *
38
+ * The diff file always contains `git diff HEAD` (working-tree vs last commit)
39
+ * concatenated with `git diff --cached` (staged vs HEAD). When the repo is
40
+ * clean — or before the first commit — the file is written empty so callers can
41
+ * unconditionally link to it.
42
+ *
43
+ * @param repoRoot - Absolute path to the candidate repository root.
44
+ * @param diffOutputPath - Absolute path where the full diff is written.
45
+ * @returns The captured {@link GitState}; all-empty + `isGitRepo:false` when
46
+ * `repoRoot` is not a git work tree (no throw).
47
+ */
48
+ export declare function captureGitState(repoRoot: string, diffOutputPath: string): GitState;
49
+ //# sourceMappingURL=handoff-git-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff-git-state.d.ts","sourceRoot":"","sources":["../../../../src/core/session/handoff-git-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,2DAA2D;IAC3D,SAAS,EAAE,OAAO,CAAC;IACnB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,eAAe,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,qBAAqB,EAAE,OAAO,CAAC;CAChC;AAqBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,QAAQ,CA6DlF"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Handoff Git State Capture
3
+ *
4
+ * Captures the cheap, deterministic git facts the handoff doc needs (branch,
5
+ * short sha, porcelain status, diff --stat) AND dumps the FULL working-tree +
6
+ * staged diff to a sibling `.diff` file. The full diff is captured for free via
7
+ * git — no LLM tokens are spent — and is the key in-flight-fidelity artifact:
8
+ * it lets the resuming agent see exactly what was changed but not yet committed.
9
+ *
10
+ * In a non-git directory every field degrades to empty and no error is thrown,
11
+ * so the non-SpecWeave fallback path still works on plain folders.
12
+ *
13
+ * Part of increment 0867: Cross-Tool Work Handoff (AC-US4-01..04).
14
+ *
15
+ * @module core/session/handoff-git-state
16
+ */
17
+ import { execFileSync } from 'child_process';
18
+ import * as fs from 'fs';
19
+ import * as path from 'path';
20
+ /**
21
+ * Run a git command, returning trimmed stdout or `null` on any failure.
22
+ *
23
+ * Uses execFileSync with an argument array (no shell) so paths and refs are
24
+ * never re-interpreted by a shell.
25
+ */
26
+ function git(repoRoot, args) {
27
+ try {
28
+ return execFileSync('git', args, {
29
+ cwd: repoRoot,
30
+ encoding: 'utf-8',
31
+ stdio: ['ignore', 'pipe', 'ignore'],
32
+ maxBuffer: 64 * 1024 * 1024, // diffs can be large
33
+ }).trim();
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ /**
40
+ * Capture git state for `repoRoot` and dump the full uncommitted diff to
41
+ * `diffOutputPath`.
42
+ *
43
+ * The diff file always contains `git diff HEAD` (working-tree vs last commit)
44
+ * concatenated with `git diff --cached` (staged vs HEAD). When the repo is
45
+ * clean — or before the first commit — the file is written empty so callers can
46
+ * unconditionally link to it.
47
+ *
48
+ * @param repoRoot - Absolute path to the candidate repository root.
49
+ * @param diffOutputPath - Absolute path where the full diff is written.
50
+ * @returns The captured {@link GitState}; all-empty + `isGitRepo:false` when
51
+ * `repoRoot` is not a git work tree (no throw).
52
+ */
53
+ export function captureGitState(repoRoot, diffOutputPath) {
54
+ const empty = {
55
+ isGitRepo: false,
56
+ branch: '',
57
+ shortSha: '',
58
+ statusPorcelain: '',
59
+ diffStat: '',
60
+ hasUncommittedChanges: false,
61
+ };
62
+ const insideWorkTree = git(repoRoot, ['rev-parse', '--is-inside-work-tree']);
63
+ if (insideWorkTree !== 'true') {
64
+ // Still write an empty diff file so the doc's link target exists.
65
+ safeWriteDiff(diffOutputPath, '');
66
+ return empty;
67
+ }
68
+ const branch = git(repoRoot, ['rev-parse', '--abbrev-ref', 'HEAD']) ?? '';
69
+ const shortSha = git(repoRoot, ['rev-parse', '--short', 'HEAD']) ?? '';
70
+ const statusPorcelain = git(repoRoot, ['status', '--porcelain']) ?? '';
71
+ // Intent-to-add every untracked file so `git diff` includes its FULL body,
72
+ // not just a `??` porcelain line. Without this, brand-new uncommitted files —
73
+ // the most common in-flight-work case — would be captured as a filename only,
74
+ // breaking the spec's "exact uncommitted edits" promise (AC-US4-01/02).
75
+ // `add -N` records intent only (no content staged) but it DOES add an index
76
+ // entry, so we MUST revert it afterward to leave the user's real index exactly
77
+ // as we found it — a handoff is a read-only capture and must not silently
78
+ // change `git status`, `git stash`, or `git commit -a` behavior.
79
+ const intentAdded = stageIntentToAddUntracked(repoRoot, statusPorcelain);
80
+ // `git diff HEAD` covers working-tree vs HEAD; if there is no HEAD yet
81
+ // (no commits), fall back to plain `git diff` so brand-new repos still work.
82
+ const hasHead = shortSha !== '';
83
+ const workingDiff = (hasHead ? git(repoRoot, ['diff', 'HEAD']) : git(repoRoot, ['diff'])) ?? '';
84
+ const stagedDiff = (hasHead ? git(repoRoot, ['diff', '--cached']) : '') ?? '';
85
+ const workingStat = (hasHead ? git(repoRoot, ['diff', '--stat', 'HEAD']) : git(repoRoot, ['diff', '--stat'])) ?? '';
86
+ const stagedStat = (hasHead ? git(repoRoot, ['diff', '--cached', '--stat']) : '') ?? '';
87
+ // Restore the index: drop the intent-to-add entries we created so the user's
88
+ // staging area is byte-identical to its pre-handoff state.
89
+ unstageIntentToAdd(repoRoot, hasHead, intentAdded);
90
+ const diffStat = [workingStat, stagedStat].filter(Boolean).join('\n');
91
+ const fullDiff = [workingDiff, stagedDiff].filter(Boolean).join('\n');
92
+ safeWriteDiff(diffOutputPath, fullDiff);
93
+ const hasUncommittedChanges = statusPorcelain.length > 0;
94
+ return {
95
+ isGitRepo: true,
96
+ branch,
97
+ shortSha,
98
+ statusPorcelain,
99
+ diffStat,
100
+ hasUncommittedChanges,
101
+ };
102
+ }
103
+ /**
104
+ * Intent-to-add every untracked file from a porcelain status so its content
105
+ * shows up in `git diff`. Parses `??`-prefixed porcelain lines and runs a
106
+ * single `git add -N -- <paths...>`. Never throws — a failure here only means
107
+ * untracked bodies are omitted, which must not abort the handoff.
108
+ *
109
+ * `add -N` (intent-to-add) records that a path will be tracked but stages NO
110
+ * content. It DOES create an index entry, so the caller MUST pass the returned
111
+ * paths to {@link unstageIntentToAdd} once the diff has been captured to leave
112
+ * the user's real index untouched.
113
+ *
114
+ * @returns The untracked paths that were intent-to-added (empty if none).
115
+ */
116
+ function stageIntentToAddUntracked(repoRoot, statusPorcelain) {
117
+ if (!statusPorcelain)
118
+ return [];
119
+ const untracked = statusPorcelain
120
+ .split('\n')
121
+ .filter((line) => line.startsWith('??'))
122
+ // Porcelain format is `XY <path>`; for untracked the path starts at col 3.
123
+ .map((line) => line.slice(3).trim())
124
+ .filter(Boolean);
125
+ if (untracked.length === 0)
126
+ return [];
127
+ // `--` guards against any path that looks like a flag.
128
+ git(repoRoot, ['add', '-N', '--', ...untracked]);
129
+ return untracked;
130
+ }
131
+ /**
132
+ * Revert the intent-to-add entries created by {@link stageIntentToAddUntracked}
133
+ * so the user's index is restored to its pre-handoff state. After this the
134
+ * affected files are untracked (`??`) again, exactly as before the capture.
135
+ *
136
+ * `git reset -- <paths>` clears the index entries when a HEAD exists. Before the
137
+ * first commit there is no HEAD, so `git reset` would fail; `git rm --cached
138
+ * --force -- <paths>` removes the intent entries in that case. Never throws — a
139
+ * failed revert must not abort the handoff (worst case: the index keeps the
140
+ * intent entries, which is the pre-fix behavior).
141
+ */
142
+ function unstageIntentToAdd(repoRoot, hasHead, paths) {
143
+ if (paths.length === 0)
144
+ return;
145
+ if (hasHead) {
146
+ git(repoRoot, ['reset', '--quiet', '--', ...paths]);
147
+ }
148
+ else {
149
+ git(repoRoot, ['rm', '--cached', '--force', '--quiet', '--', ...paths]);
150
+ }
151
+ }
152
+ /**
153
+ * Write the diff file, creating the parent dir; never throws.
154
+ */
155
+ function safeWriteDiff(diffOutputPath, content) {
156
+ try {
157
+ fs.mkdirSync(path.dirname(diffOutputPath), { recursive: true });
158
+ fs.writeFileSync(diffOutputPath, content, 'utf-8');
159
+ }
160
+ catch {
161
+ // Best-effort: a failed diff dump must not abort the handoff.
162
+ }
163
+ }
164
+ //# sourceMappingURL=handoff-git-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff-git-state.js","sourceRoot":"","sources":["../../../../src/core/session/handoff-git-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Handoff Secret Scrub
3
+ *
4
+ * Regex-based redaction over both the handoff doc's free-text fields AND the
5
+ * captured git diff. This is a HEURISTIC baseline (regex only) — an empty
6
+ * redaction list is NOT a guarantee the content is clean. Opportunistic
7
+ * scanner support (gitleaks/trufflehog) is a deferred enhancement.
8
+ *
9
+ * Each match is replaced with a `[REDACTED-<type>]` marker, and the function
10
+ * returns a per-pattern counts map so the doc's "Redaction" section can report
11
+ * how many token-like strings were masked.
12
+ *
13
+ * Part of increment 0867: Cross-Tool Work Handoff (AC-US6-01, AC-US6-02).
14
+ *
15
+ * @module core/session/handoff-secret-scrub
16
+ */
17
+ /**
18
+ * A single secret-detection pattern.
19
+ */
20
+ export interface SecretPattern {
21
+ /** Stable type slug used in the `[REDACTED-<type>]` marker + counts map. */
22
+ type: string;
23
+ /** Regex matching the secret. MUST use the global flag for replace + count. */
24
+ regex: RegExp;
25
+ }
26
+ /**
27
+ * The 12 redaction patterns (AC-US6-01).
28
+ *
29
+ * Patterns are intentionally conservative: they match the well-known token
30
+ * prefixes plus enough trailing characters to avoid masking the prefix alone.
31
+ * Assignment-style secrets (`password=`, `api_key=`) capture the value up to
32
+ * whitespace.
33
+ */
34
+ export declare const SECRET_PATTERNS: readonly SecretPattern[];
35
+ /**
36
+ * Result of a scrub: the redacted text plus per-pattern occurrence counts.
37
+ */
38
+ export interface ScrubResult {
39
+ /** Text with every matched secret replaced by `[REDACTED-<type>]`. */
40
+ scrubbed: string;
41
+ /** Map of pattern `type` → number of redactions (only non-zero entries). */
42
+ counts: Record<string, number>;
43
+ }
44
+ /**
45
+ * Scrub secrets from a block of text.
46
+ *
47
+ * Runs each declared pattern in order. Patterns are applied sequentially over
48
+ * the progressively-scrubbed text, so an already-redacted span cannot be
49
+ * matched a second time by a later pattern.
50
+ *
51
+ * @param text - Free-text or diff content to scrub.
52
+ * @returns Scrubbed text + per-pattern counts (only patterns that fired).
53
+ */
54
+ export declare function scrubSecrets(text: string): ScrubResult;
55
+ /**
56
+ * Total number of redactions across all patterns.
57
+ */
58
+ export declare function totalRedactions(counts: Record<string, number>): number;
59
+ //# sourceMappingURL=handoff-secret-scrub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff-secret-scrub.d.ts","sourceRoot":"","sources":["../../../../src/core/session/handoff-secret-scrub.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,aAAa,EAa1C,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAkBtD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAEtE"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Handoff Secret Scrub
3
+ *
4
+ * Regex-based redaction over both the handoff doc's free-text fields AND the
5
+ * captured git diff. This is a HEURISTIC baseline (regex only) — an empty
6
+ * redaction list is NOT a guarantee the content is clean. Opportunistic
7
+ * scanner support (gitleaks/trufflehog) is a deferred enhancement.
8
+ *
9
+ * Each match is replaced with a `[REDACTED-<type>]` marker, and the function
10
+ * returns a per-pattern counts map so the doc's "Redaction" section can report
11
+ * how many token-like strings were masked.
12
+ *
13
+ * Part of increment 0867: Cross-Tool Work Handoff (AC-US6-01, AC-US6-02).
14
+ *
15
+ * @module core/session/handoff-secret-scrub
16
+ */
17
+ /**
18
+ * The 12 redaction patterns (AC-US6-01).
19
+ *
20
+ * Patterns are intentionally conservative: they match the well-known token
21
+ * prefixes plus enough trailing characters to avoid masking the prefix alone.
22
+ * Assignment-style secrets (`password=`, `api_key=`) capture the value up to
23
+ * whitespace.
24
+ */
25
+ export const SECRET_PATTERNS = [
26
+ { type: 'openai-key', regex: /sk-[A-Za-z0-9_-]{16,}/g },
27
+ { type: 'github-token', regex: /ghp_[A-Za-z0-9]{20,}/g },
28
+ { type: 'github-oauth', regex: /gho_[A-Za-z0-9]{20,}/g },
29
+ { type: 'github-server', regex: /ghs_[A-Za-z0-9]{20,}/g },
30
+ { type: 'aws-key', regex: /AKIA[0-9A-Z]{12,}/g },
31
+ { type: 'aws-temp-key', regex: /ASIA[0-9A-Z]{12,}/g },
32
+ { type: 'private-key', regex: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----/g },
33
+ { type: 'vskill-token', regex: /vsk_[A-Za-z0-9]{16,}/g },
34
+ { type: 'slack-token', regex: /xox[bap]-[A-Za-z0-9-]{10,}/g },
35
+ { type: 'bearer', regex: /Bearer\s+[A-Za-z0-9._~+/=-]{8,}/g },
36
+ { type: 'password', regex: /password=\S+/gi },
37
+ { type: 'api-key', regex: /api_key=\S+/gi },
38
+ ];
39
+ /**
40
+ * Scrub secrets from a block of text.
41
+ *
42
+ * Runs each declared pattern in order. Patterns are applied sequentially over
43
+ * the progressively-scrubbed text, so an already-redacted span cannot be
44
+ * matched a second time by a later pattern.
45
+ *
46
+ * @param text - Free-text or diff content to scrub.
47
+ * @returns Scrubbed text + per-pattern counts (only patterns that fired).
48
+ */
49
+ export function scrubSecrets(text) {
50
+ const counts = {};
51
+ let scrubbed = text ?? '';
52
+ for (const { type, regex } of SECRET_PATTERNS) {
53
+ // Fresh lastIndex per call (regex literals are module-scoped + global).
54
+ regex.lastIndex = 0;
55
+ let matched = 0;
56
+ scrubbed = scrubbed.replace(regex, () => {
57
+ matched += 1;
58
+ return `[REDACTED-${type}]`;
59
+ });
60
+ if (matched > 0) {
61
+ counts[type] = matched;
62
+ }
63
+ }
64
+ return { scrubbed, counts };
65
+ }
66
+ /**
67
+ * Total number of redactions across all patterns.
68
+ */
69
+ export function totalRedactions(counts) {
70
+ return Object.values(counts).reduce((sum, n) => sum + n, 0);
71
+ }
72
+ //# sourceMappingURL=handoff-secret-scrub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff-secret-scrub.js","sourceRoot":"","sources":["../../../../src/core/session/handoff-secret-scrub.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAYH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAA6B;IACvD,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,wBAAwB,EAAE;IACvD,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,uBAAuB,EAAE;IACxD,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,uBAAuB,EAAE;IACxD,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,uBAAuB,EAAE;IACzD,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,oBAAoB,EAAE;IAChD,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,oBAAoB,EAAE;IACrD,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,wCAAwC,EAAE;IACxE,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,uBAAuB,EAAE;IACxD,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,6BAA6B,EAAE;IAC7D,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,kCAAkC,EAAE;IAC7D,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,gBAAgB,EAAE;IAC7C,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE;CACnC,CAAC;AAYX;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,IAAI,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC;IAE1B,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,eAAe,EAAE,CAAC;QAC9C,wEAAwE;QACxE,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;QACpB,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE;YACtC,OAAO,IAAI,CAAC,CAAC;YACb,OAAO,aAAa,IAAI,GAAG,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,MAA8B;IAC5D,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AAC9D,CAAC"}
@@ -1,5 +1,9 @@
1
1
  /**
2
- * Handoff Context Generator
2
+ * Plugin-Install Handoff Context Generator
3
+ *
4
+ * NOTE: This is the legacy plugin-INSTALL handoff (renamed from
5
+ * `handoff-context.ts` in 0867 to free that name for the cross-tool work
6
+ * handoff). It is unrelated to `work-handoff.ts` / `handoff-doc-format.ts`.
3
7
  *
4
8
  * Generates context information for session handoff, enabling users
5
9
  * to continue their work in a new session after plugin installation.
@@ -11,7 +15,7 @@
11
15
  * - Available skills from those plugins
12
16
  * - Suggested continuation prompt
13
17
  *
14
- * @module core/session/handoff-context
18
+ * @module core/session/install-handoff-context
15
19
  */
16
20
  /**
17
21
  * A skill/command available from an installed plugin
@@ -96,4 +100,4 @@ export declare function generateHandoffContext(options: HandoffContextOptions):
96
100
  * ```
97
101
  */
98
102
  export declare function formatHandoffText(options: HandoffContextOptions): string;
99
- //# sourceMappingURL=handoff-context.d.ts.map
103
+ //# sourceMappingURL=install-handoff-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-handoff-context.d.ts","sourceRoot":"","sources":["../../../../src/core/session/install-handoff-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH;;GAEG;AACH,MAAM,WAAW,KAAK;IACpB,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,gCAAgC;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gCAAgC;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,wBAAwB;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,eAAe,EAAE,KAAK,EAAE,CAAC;IACzB,kDAAkD;IAClD,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAgRD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,qBAAqB,GAC7B,cAAc,CAwBhB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAiExE"}
@@ -1,5 +1,9 @@
1
1
  /**
2
- * Handoff Context Generator
2
+ * Plugin-Install Handoff Context Generator
3
+ *
4
+ * NOTE: This is the legacy plugin-INSTALL handoff (renamed from
5
+ * `handoff-context.ts` in 0867 to free that name for the cross-tool work
6
+ * handoff). It is unrelated to `work-handoff.ts` / `handoff-doc-format.ts`.
3
7
  *
4
8
  * Generates context information for session handoff, enabling users
5
9
  * to continue their work in a new session after plugin installation.
@@ -11,7 +15,7 @@
11
15
  * - Available skills from those plugins
12
16
  * - Suggested continuation prompt
13
17
  *
14
- * @module core/session/handoff-context
18
+ * @module core/session/install-handoff-context
15
19
  */
16
20
  // ============================================================================
17
21
  // Constants - Formatting
@@ -371,4 +375,4 @@ export function formatHandoffText(options) {
371
375
  lines.push(HEAVY_SEPARATOR);
372
376
  return lines.join('\n');
373
377
  }
374
- //# sourceMappingURL=handoff-context.js.map
378
+ //# sourceMappingURL=install-handoff-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-handoff-context.js","sourceRoot":"","sources":["../../../../src/core/session/install-handoff-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Work Handoff Builder
3
+ *
4
+ * Assembles a portable, secret-scrubbed handoff document (+ a full diff of
5
+ * uncommitted edits) from durable on-disk state, so a developer can stop work
6
+ * in one AI tool and resume in another. This is the single deterministic engine
7
+ * behind the `specweave handoff` CLI, the `/sw:handoff` command, and the
8
+ * PreCompact hook — all of which call {@link buildWorkHandoff}.
9
+ *
10
+ * Workspace detection is intentionally NOT a raw `.specweave/` directory test:
11
+ * a stale child-repo `.specweave/` (no real state) would misclassify. We resolve
12
+ * the effective root then require an `active-increment.json` that actually
13
+ * lists increments. Metadata reads are gated with `MetadataManager.exists()`
14
+ * because `MetadataManager.read()` LAZILY CREATES default metadata — a side
15
+ * effect a read-only handoff must never trigger.
16
+ *
17
+ * All increment/task/AC/workspace logic is REUSED from existing modules (DRY):
18
+ * `parseTasksWithUSLinks`, `calculateProgressFromTasksFile`,
19
+ * `ActiveIncrementManager.getActive()`, `MetadataManager`, `resolveEffectiveRoot`.
20
+ *
21
+ * Part of increment 0867: Cross-Tool Work Handoff
22
+ * (AC-US1-03..07, AC-US3-01..05, AC-US4-*, AC-US6-01/02/05).
23
+ *
24
+ * @module core/session/work-handoff
25
+ */
26
+ /**
27
+ * Options controlling a handoff build. All fields are optional; the agent /
28
+ * CLI supplies only the short free-text strings + flags.
29
+ */
30
+ export interface WorkHandoffOptions {
31
+ /** Required disambiguator when 2+ increments are active. */
32
+ incrementId?: string;
33
+ /** "Why I'm handing off" (e.g. "out of tokens"). */
34
+ reason?: string;
35
+ /** Where things stand. */
36
+ summary?: string;
37
+ /** The exact next step. */
38
+ next?: string;
39
+ /** A gotcha / warning for the next agent. */
40
+ gotcha?: string;
41
+ /** Agent-supplied decisions; merged OVER plan.md decisions. */
42
+ decisions?: string[];
43
+ /** Embed the full body in the paste-prompt (cross-machine). */
44
+ inline?: boolean;
45
+ /** Override the doc output path. */
46
+ out?: string;
47
+ /** Force the non-SpecWeave `.handoff/` fallback even in a workspace. */
48
+ nonSpecweave?: boolean;
49
+ }
50
+ /**
51
+ * Result of a handoff build.
52
+ */
53
+ export interface WorkHandoffResult {
54
+ /** Absolute path of the written doc (the CLI prints this FIRST). */
55
+ docPath: string;
56
+ /** Absolute path of the sibling full-diff file. */
57
+ diffPath: string;
58
+ /** The full rendered + scrubbed doc markdown. */
59
+ docMarkdown: string;
60
+ /** The copy-paste resume prompt. */
61
+ pastePrompt: string;
62
+ /** Whether the high-fidelity SpecWeave path was taken. */
63
+ isSpecWeave: boolean;
64
+ }
65
+ /**
66
+ * Thrown when 2+ increments are active and no explicit id was supplied.
67
+ * Carries the candidate ids so the CLI can list them.
68
+ */
69
+ export declare class AmbiguousActiveIncrementError extends Error {
70
+ readonly candidates: string[];
71
+ constructor(candidates: string[]);
72
+ }
73
+ /**
74
+ * Build a handoff for `repoRoot`.
75
+ *
76
+ * @param repoRoot - Where to start workspace resolution from (usually cwd).
77
+ * @param opts - {@link WorkHandoffOptions}.
78
+ * @returns {@link WorkHandoffResult}.
79
+ * @throws {AmbiguousActiveIncrementError} when 2+ active increments + no id.
80
+ */
81
+ export declare function buildWorkHandoff(repoRoot: string, opts?: WorkHandoffOptions): Promise<WorkHandoffResult>;
82
+ /**
83
+ * Ownership sentinel: is a root `./HANDOFF.md` a foreign file (lacks the
84
+ * `Doc format v1` marker)? Exposed for the builder + tests. When foreign, the
85
+ * caller must NOT overwrite it.
86
+ */
87
+ export declare function isForeignHandoffFile(handoffPath: string): boolean;
88
+ //# sourceMappingURL=work-handoff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"work-handoff.d.ts","sourceRoot":"","sources":["../../../../src/core/session/work-handoff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAoBH;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,oCAAoC;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;aAC1B,UAAU,EAAE,MAAM,EAAE;gBAApB,UAAU,EAAE,MAAM,EAAE;CAMjD;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CAmG5B;AAwOD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAIjE"}