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.
- package/bin/specweave.js +30 -0
- package/dist/dashboard/assets/{index-DdI7mc7Y.js → index-DH3l1QBV.js} +6 -6
- package/dist/dashboard/assets/index-vkwF92tD.css +1 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/plugins/specweave/lib/integrations/github/github-access-error.d.ts +48 -0
- package/dist/plugins/specweave/lib/integrations/github/github-access-error.d.ts.map +1 -0
- package/dist/plugins/specweave/lib/integrations/github/github-access-error.js +69 -0
- package/dist/plugins/specweave/lib/integrations/github/github-access-error.js.map +1 -0
- package/dist/plugins/specweave/lib/integrations/github/github-client-v2.d.ts +8 -0
- package/dist/plugins/specweave/lib/integrations/github/github-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave/lib/integrations/github/github-client-v2.js +22 -2
- package/dist/plugins/specweave/lib/integrations/github/github-client-v2.js.map +1 -1
- package/dist/plugins/specweave/lib/utils/fs-native.d.ts +1 -1
- package/dist/plugins/specweave/lib/vendor/generators/spec/task-parser.js +38 -16
- package/dist/plugins/specweave/lib/vendor/generators/spec/task-parser.js.map +1 -1
- package/dist/plugins/specweave/lib/vendor/utils/fs-native.d.ts +1 -1
- package/dist/src/cli/commands/handoff.d.ts +54 -0
- package/dist/src/cli/commands/handoff.d.ts.map +1 -0
- package/dist/src/cli/commands/handoff.js +82 -0
- package/dist/src/cli/commands/handoff.js.map +1 -0
- package/dist/src/cli/helpers/init/external-import.js.map +1 -1
- package/dist/src/cli/helpers/init/gitignore-generator.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/gitignore-generator.js +3 -0
- package/dist/src/cli/helpers/init/gitignore-generator.js.map +1 -1
- package/dist/src/core/hooks/handlers/hook-router.d.ts.map +1 -1
- package/dist/src/core/hooks/handlers/hook-router.js +5 -0
- package/dist/src/core/hooks/handlers/hook-router.js.map +1 -1
- package/dist/src/core/hooks/handlers/pre-compact.d.ts +33 -0
- package/dist/src/core/hooks/handlers/pre-compact.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/pre-compact.js +109 -0
- package/dist/src/core/hooks/handlers/pre-compact.js.map +1 -0
- package/dist/src/core/hooks/handlers/types.d.ts +1 -1
- package/dist/src/core/hooks/handlers/types.d.ts.map +1 -1
- package/dist/src/core/hooks/handlers/types.js +3 -0
- package/dist/src/core/hooks/handlers/types.js.map +1 -1
- package/dist/src/core/session/handoff-doc-format.d.ts +164 -0
- package/dist/src/core/session/handoff-doc-format.d.ts.map +1 -0
- package/dist/src/core/session/handoff-doc-format.js +292 -0
- package/dist/src/core/session/handoff-doc-format.js.map +1 -0
- package/dist/src/core/session/handoff-git-state.d.ts +49 -0
- package/dist/src/core/session/handoff-git-state.d.ts.map +1 -0
- package/dist/src/core/session/handoff-git-state.js +164 -0
- package/dist/src/core/session/handoff-git-state.js.map +1 -0
- package/dist/src/core/session/handoff-secret-scrub.d.ts +59 -0
- package/dist/src/core/session/handoff-secret-scrub.d.ts.map +1 -0
- package/dist/src/core/session/handoff-secret-scrub.js +72 -0
- package/dist/src/core/session/handoff-secret-scrub.js.map +1 -0
- package/dist/src/core/session/{handoff-context.d.ts → install-handoff-context.d.ts} +7 -3
- package/dist/src/core/session/install-handoff-context.d.ts.map +1 -0
- package/dist/src/core/session/{handoff-context.js → install-handoff-context.js} +7 -3
- package/dist/src/core/session/install-handoff-context.js.map +1 -0
- package/dist/src/core/session/work-handoff.d.ts +88 -0
- package/dist/src/core/session/work-handoff.d.ts.map +1 -0
- package/dist/src/core/session/work-handoff.js +412 -0
- package/dist/src/core/session/work-handoff.js.map +1 -0
- package/dist/src/generators/spec/task-parser.d.ts.map +1 -1
- package/dist/src/generators/spec/task-parser.js +38 -16
- package/dist/src/generators/spec/task-parser.js.map +1 -1
- package/dist/src/utils/fs-native.d.ts +1 -1
- package/package.json +2 -2
- package/plugins/specweave/commands/handoff.md +54 -0
- package/plugins/specweave/lib/integrations/github/github-access-error.js +43 -0
- package/plugins/specweave/lib/integrations/github/github-access-error.ts +103 -0
- package/plugins/specweave/lib/integrations/github/github-client-v2.js +24 -4
- package/plugins/specweave/lib/integrations/github/github-client-v2.ts +26 -4
- package/plugins/specweave/lib/vendor/generators/spec/task-parser.js +38 -16
- package/plugins/specweave/lib/vendor/generators/spec/task-parser.js.map +1 -1
- package/plugins/specweave/lib/vendor/utils/fs-native.d.ts +1 -1
- package/plugins/specweave/skills/handoff/SKILL.md +59 -0
- package/dist/dashboard/assets/index-YkogWPHY.css +0 -1
- package/dist/src/core/session/handoff-context.d.ts.map +0 -1
- 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"}
|