dw-kit 1.3.4 → 1.3.5
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/.claude/hooks/supply-chain-scan.sh +102 -0
- package/.claude/settings.json +4 -0
- package/.dw/security/ioc-namespaces.json +40 -0
- package/CLAUDE.md +3 -1
- package/README.md +14 -2
- package/package.json +2 -1
- package/src/cli.mjs +27 -0
- package/src/commands/doctor.mjs +21 -0
- package/src/commands/init.mjs +45 -2
- package/src/commands/security-scan.mjs +427 -0
- package/src/commands/upgrade.mjs +54 -0
- package/src/lib/gitignore.mjs +86 -0
- package/src/lib/sc-install.mjs +93 -0
- package/src/lib/sc-scanner.mjs +272 -0
- package/src/lib/sc-sync.mjs +198 -0
- package/src/lib/telemetry.mjs +7 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# .claude/hooks/supply-chain-scan.sh
|
|
3
|
+
# Fires after Claude Code Write/Edit. If the file is a lockfile, runs `dw security-scan --quick`
|
|
4
|
+
# to detect known-vulnerable dependency pins (offline IoC + advisory snapshot check).
|
|
5
|
+
# Non-blocking — never aborts the parent tool call. Emits telemetry.
|
|
6
|
+
# Reference: ADR-0005 (AI-Native Supply-Chain Guard). Sunset review 2026-08-12.
|
|
7
|
+
|
|
8
|
+
set -e
|
|
9
|
+
|
|
10
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
11
|
+
TELEMETRY_SCRIPT="$PROJECT_DIR/.claude/hooks/telemetry-log.sh"
|
|
12
|
+
|
|
13
|
+
if [ "${DW_NO_TELEMETRY:-}" != "1" ] && [ -x "$TELEMETRY_SCRIPT" ]; then
|
|
14
|
+
"$TELEMETRY_SCRIPT" hook supply-chain-scan >/dev/null 2>&1 || true
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
if [ "${DW_SC_GUARD_DISABLED:-}" = "1" ]; then
|
|
18
|
+
exit 0
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
INPUT=$(cat)
|
|
22
|
+
|
|
23
|
+
FILE_PATH=$(echo "$INPUT" | node -e "
|
|
24
|
+
let d='';
|
|
25
|
+
process.stdin.on('data',c=>d+=c).on('end',()=>{
|
|
26
|
+
try{
|
|
27
|
+
const data=JSON.parse(d);
|
|
28
|
+
const p=data.file_path||data.path||data.filePath||(data.tool_input&&(data.tool_input.file_path||data.tool_input.path))||'';
|
|
29
|
+
process.stdout.write(p);
|
|
30
|
+
}catch(e){}
|
|
31
|
+
});
|
|
32
|
+
" 2>/dev/null || true)
|
|
33
|
+
|
|
34
|
+
[ -z "$FILE_PATH" ] && exit 0
|
|
35
|
+
|
|
36
|
+
# Match: package-lock.json, npm-shrinkwrap.json (case-insensitive).
|
|
37
|
+
# pnpm-lock.yaml and yarn.lock are out-of-scope per ADR-0005 v1.3.5.
|
|
38
|
+
BASENAME=$(basename "$FILE_PATH")
|
|
39
|
+
case "$BASENAME" in
|
|
40
|
+
package-lock.json|npm-shrinkwrap.json)
|
|
41
|
+
;;
|
|
42
|
+
*)
|
|
43
|
+
exit 0
|
|
44
|
+
;;
|
|
45
|
+
esac
|
|
46
|
+
|
|
47
|
+
# Find the project root that contains the lockfile (in case PROJECT_DIR differs)
|
|
48
|
+
LOCKFILE_DIR=$(dirname "$FILE_PATH")
|
|
49
|
+
[ -d "$LOCKFILE_DIR" ] || exit 0
|
|
50
|
+
|
|
51
|
+
# Locate dw binary — prefer local node_modules, fall back to global PATH
|
|
52
|
+
DW_BIN=""
|
|
53
|
+
if command -v dw >/dev/null 2>&1; then
|
|
54
|
+
DW_BIN="dw"
|
|
55
|
+
elif [ -f "$PROJECT_DIR/bin/dw.mjs" ]; then
|
|
56
|
+
DW_BIN="node $PROJECT_DIR/bin/dw.mjs"
|
|
57
|
+
else
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
START_TS=$(date +%s%N 2>/dev/null || date +%s)
|
|
62
|
+
|
|
63
|
+
# Run scan in lockfile's project dir. Capture exit code without failing parent.
|
|
64
|
+
set +e
|
|
65
|
+
SCAN_OUTPUT=$(cd "$LOCKFILE_DIR" && $DW_BIN security-scan --quick 2>&1)
|
|
66
|
+
SCAN_EXIT=$?
|
|
67
|
+
set -e
|
|
68
|
+
|
|
69
|
+
END_TS=$(date +%s%N 2>/dev/null || date +%s)
|
|
70
|
+
LATENCY_MS=$(( (END_TS - START_TS) / 1000000 ))
|
|
71
|
+
|
|
72
|
+
case "$SCAN_EXIT" in
|
|
73
|
+
0)
|
|
74
|
+
# Clean — silent unless verbose
|
|
75
|
+
if [ "${DW_SC_GUARD_VERBOSE:-}" = "1" ]; then
|
|
76
|
+
echo "✓ supply-chain-scan: clean (no advisory matches in $BASENAME)" >&2
|
|
77
|
+
fi
|
|
78
|
+
;;
|
|
79
|
+
1)
|
|
80
|
+
# Low/medium matches — warn, do not block
|
|
81
|
+
echo "" >&2
|
|
82
|
+
echo "⚠ supply-chain-scan: advisory matches in $BASENAME (low/medium)" >&2
|
|
83
|
+
echo "$SCAN_OUTPUT" | tail -20 >&2
|
|
84
|
+
echo " (advisory — not blocking; run \`dw security-scan\` for full report)" >&2
|
|
85
|
+
;;
|
|
86
|
+
2)
|
|
87
|
+
# HIGH+ matches — loud warning, still non-blocking per ADR-0005 v1.3.5 (advisory-only)
|
|
88
|
+
echo "" >&2
|
|
89
|
+
echo "⚠ supply-chain-scan: HIGH+ severity advisory match in $BASENAME" >&2
|
|
90
|
+
echo "$SCAN_OUTPUT" | tail -25 >&2
|
|
91
|
+
echo " ADVISORY ONLY — threshold matrix in v14-evaluation-protocol.md is authoritative." >&2
|
|
92
|
+
echo " Run \`dw security-scan\` for full report. Public sunset review 2026-08-12 (ADR-0005)." >&2
|
|
93
|
+
;;
|
|
94
|
+
*)
|
|
95
|
+
# Setup error (no snapshot, schema mismatch, etc.) — quiet hint
|
|
96
|
+
if [ "${DW_SC_GUARD_VERBOSE:-}" = "1" ]; then
|
|
97
|
+
echo "supply-chain-scan: setup needed — run \`dw security-scan --update-db\`" >&2
|
|
98
|
+
fi
|
|
99
|
+
;;
|
|
100
|
+
esac
|
|
101
|
+
|
|
102
|
+
exit 0
|
package/.claude/settings.json
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": "1.0",
|
|
3
|
+
"updated": "2026-05-13",
|
|
4
|
+
"purpose": "Curated namespace patterns under ACTIVE incident — fallback for pre-install scan when OSV.dev is unavailable. Auto-expires per active_until date. TL updates when new incident requires fixture-level warning before OSV propagation.",
|
|
5
|
+
"namespaces": [
|
|
6
|
+
{
|
|
7
|
+
"pattern": "@tanstack/",
|
|
8
|
+
"reason": "Active incident 2026-05-11 — Mini Shai-Hulud worm (CVE-2026-45321 / GHSA-g7cv-rxg3-hmpx). 84 malicious versions across 42 @tanstack/* packages published 2026-05-11 19:20-19:26 UTC. Worm self-propagated to 169+ packages.",
|
|
9
|
+
"advisory": "https://github.com/advisories/GHSA-g7cv-rxg3-hmpx",
|
|
10
|
+
"active_until": "2026-11-11",
|
|
11
|
+
"severity": "critical",
|
|
12
|
+
"guidance": "If installing @tanstack/* fresh: verify version is NOT in 1.169.5-1.169.8 range; prefer 1.169.9+. Check SLSA provenance attestation matches expected publisher. If already installed and lockfile pins 1.169.5-8: rotate ALL credentials (npm tokens, GitHub PATs, SSH keys, cloud keys, Claude/AI configs) before continuing."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"pattern": "@uipath/",
|
|
16
|
+
"reason": "Worm spread vector from TanStack incident (2026-05-11) — 70+ @uipath/* packages compromised via stolen npm OIDC tokens.",
|
|
17
|
+
"advisory": "https://github.com/advisories/GHSA-g7cv-rxg3-hmpx",
|
|
18
|
+
"active_until": "2026-11-11",
|
|
19
|
+
"severity": "critical",
|
|
20
|
+
"guidance": "Verify each @uipath/* install against official UiPath release notes. Worm SLSA attestations are VALID — provenance check alone is insufficient. Cross-reference with UiPath security bulletin."
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"pattern": "@mistralai/",
|
|
24
|
+
"reason": "Worm spread vector (2026-05-11) — @mistralai/mistralai 2.2.3-2.2.4 compromised on both npm and PyPI.",
|
|
25
|
+
"advisory": "https://github.com/advisories/GHSA-g7cv-rxg3-hmpx",
|
|
26
|
+
"active_until": "2026-11-11",
|
|
27
|
+
"severity": "critical",
|
|
28
|
+
"guidance": "Avoid @mistralai/mistralai 2.2.3-2.2.4. Pin to 2.2.2 or 2.2.5+ once available."
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"pattern": "@opensearch-project/opensearch",
|
|
32
|
+
"reason": "Worm spread vector (2026-05-11) — @opensearch-project/opensearch 3.6.2 compromised (1.3M weekly downloads).",
|
|
33
|
+
"advisory": "https://github.com/advisories/GHSA-g7cv-rxg3-hmpx",
|
|
34
|
+
"active_until": "2026-11-11",
|
|
35
|
+
"severity": "critical",
|
|
36
|
+
"guidance": "Avoid version 3.6.2. Pin to 3.6.1 or 3.6.3+."
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"maintenance_note": "This fixture is a SHORT-TERM defensive measure. Per ADR-0005 design, OSV.dev/GHSA auto-sync is the primary source; this fixture handles offline + post-incident pre-propagation window. TL prunes entries past active_until on regular release cycles."
|
|
40
|
+
}
|
package/CLAUDE.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Workflow toolkit codebase. Rules live in `.claude/rules/` (auto-loaded).
|
|
4
4
|
|
|
5
5
|
**v2.0 direction:** Context-First SDLC Governance Layer (5 pillars — see `.dw/core/PILLARS.md`)
|
|
6
|
-
**Current:** v1.
|
|
6
|
+
**Current:** v1.3.5 (released 2026-05-12) · ADR-0001 active · ADR-0005 Accepted (Supply-Chain Guard, sunset review 2026-08-12) · v1.4 cuts pending telemetry
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -30,6 +30,8 @@ src/
|
|
|
30
30
|
tasks/ Active + archive/ (Bridges pillar — via tracking.md)
|
|
31
31
|
metrics/ Local telemetry (events.jsonl)
|
|
32
32
|
config/ dw.config.yml
|
|
33
|
+
security/ IoC namespace fixture (Guards pillar — ADR-0005)
|
|
34
|
+
research/ Investigation notes, RFC-style proposals, voter panel outputs
|
|
33
35
|
```
|
|
34
36
|
|
|
35
37
|
## Dev Notes
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> An AI development workflow toolkit for teams using agentic IDEs (Claude Code, Cursor) — from idea to review-ready commits.
|
|
4
4
|
|
|
5
|
-
**v1.3** · `npm install -g dw-kit` · [Docs](docs/README.md) · [Get started](docs/get-started.md) · [Cheatsheet](docs/cheatsheet.md) · [Migration v1.3](MIGRATION-v1.3.md) · [Changelog](CHANGELOG.md)
|
|
5
|
+
**v1.3.5** · `npm install -g dw-kit` · [Docs](docs/README.md) · [Get started](docs/get-started.md) · [Cheatsheet](docs/cheatsheet.md) · [Migration v1.3](MIGRATION-v1.3.md) · [Changelog](CHANGELOG.md)
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -36,10 +36,22 @@ It’s designed for collaboration (Dev / Tech Lead / QA / PM) and keeps work aud
|
|
|
36
36
|
|
|
37
37
|
## Release notes
|
|
38
38
|
|
|
39
|
-
- v1.
|
|
39
|
+
- **v1.3.5** (2026-05-12) — AI-Native Supply-Chain Guard: `dw security-scan` CLI + OSV.dev auto-sync + Edit-lockfile hook + scoped `.gitignore` for end-user projects. See [`CHANGELOG.md#v135--2026-05-12`](CHANGELOG.md#v135--2026-05-12) and [ADR-0005](.dw/decisions/0005-supply-chain-guard.md). Public 90-day sunset review committed for 2026-08-12.
|
|
40
|
+
- v1.3.4 (2026-04-21) — `/dw:plan` Quick Debate (red/blue self-critique), depth-driven activation
|
|
41
|
+
- v1.3.3 (2026-04-21) — Writer skills v1/v2 compatibility fix
|
|
42
|
+
- v1.3.0 (2026-04-21) — 5-pillar governance layer + telemetry foundation + ADRs + v2 task docs ([ADR-0001](.dw/decisions/0001-v2-pragmatic-lean.md))
|
|
43
|
+
- v1.2.0 (2026-04-09) — [`CHANGELOG.md#v120--2026-04-09`](CHANGELOG.md#v120--2026-04-09)
|
|
40
44
|
- Full changelog: `CHANGELOG.md`
|
|
41
45
|
- Latest release notes: [GitHub Releases](https://github.com/dv-workflow/dv-workflow/releases)
|
|
42
46
|
|
|
47
|
+
### What's in v1.3.5 for your team
|
|
48
|
+
|
|
49
|
+
- **`dw security-scan`** — scan for known supply-chain advisories against your project's `package-lock.json` (full match) or `package.json` (pre-install approximate). Uses [OSV.dev](https://osv.dev/) as data source (multi-maintainer upstream feed; no solo-curated bundle to go stale).
|
|
50
|
+
- **AI-aware hook** — fires when Claude Code edits a lockfile. Auto-wired by `dw init --preset team` or `--preset enterprise`; opt-in OFF for `--preset solo`.
|
|
51
|
+
- **Scoped `.gitignore`** — `dw init` and `dw upgrade` write `.dw/.gitignore` and `.claude/.gitignore` managed blocks. Framework files stay out of your repo; tasks/decisions/docs/config stay in.
|
|
52
|
+
- **`dw doctor`** has a new security section that fails loud if advisory snapshot is stale (>7 days) or schema-incompatible.
|
|
53
|
+
- **Sunset rule** — feature retires silently in v1.4.x if 90-day telemetry shows zero real catches OR >5% false-positive rate. Disciplined experiment, not panic ship.
|
|
54
|
+
|
|
43
55
|
---
|
|
44
56
|
|
|
45
57
|
## Install
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dw-kit",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
4
4
|
"description": "AI development workflow toolkit — structured, quality-assured, team-ready. From requirements to dashboard.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
".dw/core/",
|
|
15
15
|
".dw/config/",
|
|
16
16
|
".dw/adapters/",
|
|
17
|
+
".dw/security/",
|
|
17
18
|
".claude/agents/",
|
|
18
19
|
".claude/hooks/",
|
|
19
20
|
".claude/rules/",
|
package/src/cli.mjs
CHANGED
|
@@ -101,6 +101,33 @@ export function run(argv) {
|
|
|
101
101
|
await dashboardCommand(opts);
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
+
program
|
|
105
|
+
.command('security-scan')
|
|
106
|
+
.description('Scan project lockfile against advisory snapshot (OSV.dev). Supply-chain guard (ADR-0005, opt-in).')
|
|
107
|
+
.option('--quick', 'Offline mode — use existing snapshot only (default behavior)')
|
|
108
|
+
.option('--update-db', 'Fetch fresh advisory snapshot from OSV.dev before scanning')
|
|
109
|
+
.option('--pre-install', 'Scan package.json without lockfile (OSV name-only + namespace fixture)')
|
|
110
|
+
.option('--offline', 'Skip network in --pre-install mode (fixture-only)')
|
|
111
|
+
.option('--json', 'Output machine-readable JSON')
|
|
112
|
+
.option('--install-hook', 'Wire supply-chain-scan.sh into .claude/settings.json PostToolUse (idempotent)')
|
|
113
|
+
.option('--uninstall-hook', 'Remove supply-chain-scan.sh entry from .claude/settings.json')
|
|
114
|
+
.action(async (opts) => {
|
|
115
|
+
if (opts.installHook || opts.uninstallHook) {
|
|
116
|
+
const { installHookInProject, uninstallHookFromProject } = await import('./lib/sc-install.mjs');
|
|
117
|
+
const r = opts.uninstallHook ? uninstallHookFromProject() : installHookInProject();
|
|
118
|
+
if (!r.ok) {
|
|
119
|
+
console.error(chalk.red(`✗ ${r.error}`));
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
if (r.action === 'added') console.log(chalk.green(`✓ Hook wired into ${r.path}`));
|
|
123
|
+
else if (r.action === 'removed') console.log(chalk.green(`✓ Removed ${r.count} entry from ${r.path}`));
|
|
124
|
+
else console.log(chalk.dim(` (no-op: ${r.reason})`));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const { securityScanCommand } = await import('./commands/security-scan.mjs');
|
|
128
|
+
await securityScanCommand(opts);
|
|
129
|
+
});
|
|
130
|
+
|
|
104
131
|
program
|
|
105
132
|
.command('claude-vn-fix')
|
|
106
133
|
.description('Patch Claude CLI to fix Vietnamese IME (local, with backup/restore)')
|
package/src/commands/doctor.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
4
4
|
import { header, ok, warn, err, info, log } from '../lib/ui.mjs';
|
|
5
5
|
import { loadConfig, getToolkitVersions } from '../lib/config.mjs';
|
|
6
6
|
import { detectPlatform, platformLabel } from '../lib/platform.mjs';
|
|
7
|
+
import { snapshotInfo } from '../lib/sc-sync.mjs';
|
|
7
8
|
|
|
8
9
|
const TOOLKIT_ROOT = resolve(fileURLToPath(import.meta.url), '..', '..', '..');
|
|
9
10
|
|
|
@@ -150,6 +151,26 @@ export async function doctorCommand() {
|
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
|
|
154
|
+
info('Supply-Chain Guard (ADR-0005, opt-in)');
|
|
155
|
+
const sc = snapshotInfo(projectDir);
|
|
156
|
+
if (!sc.exists) {
|
|
157
|
+
log(' Advisory snapshot — not yet created');
|
|
158
|
+
log(' Run `dw security-scan --update-db` to fetch from OSV.dev (opt-in feature)');
|
|
159
|
+
} else {
|
|
160
|
+
if (!sc.schema_compatible) {
|
|
161
|
+
err(`Advisory snapshot schema mismatch — expected 1.0, got ${sc.schema_version || 'unknown'}`);
|
|
162
|
+
log(' Run `dw security-scan --update-db` to refresh');
|
|
163
|
+
issues++;
|
|
164
|
+
} else if (sc.stale) {
|
|
165
|
+
warn(`Advisory snapshot stale: ${sc.age_days.toFixed(1)} days old (>7d threshold)`);
|
|
166
|
+
log(` Source: ${sc.source} (${sc.ecosystem}), advisories=${sc.advisory_count}`);
|
|
167
|
+
log(' Run `dw security-scan --update-db` to refresh');
|
|
168
|
+
warnings++;
|
|
169
|
+
} else {
|
|
170
|
+
ok(`Advisory snapshot — ${sc.age_days.toFixed(1)}d old, ${sc.advisory_count} advisories (${sc.source})`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
153
174
|
console.log();
|
|
154
175
|
header('Diagnosis');
|
|
155
176
|
if (issues === 0 && warnings === 0) {
|
package/src/commands/init.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { banner, ok, warn, info, log, ask, choose } from '../lib/ui.mjs';
|
|
|
5
5
|
import { buildConfig, writeConfig } from '../lib/config.mjs';
|
|
6
6
|
import { copyDir, copyFile, ensureDir } from '../lib/copy.mjs';
|
|
7
7
|
import { detectPlatform, platformLabel } from '../lib/platform.mjs';
|
|
8
|
+
import { ensureDwGitignore, ensureClaudeGitignore } from '../lib/gitignore.mjs';
|
|
8
9
|
|
|
9
10
|
const TOOLKIT_ROOT = resolve(fileURLToPath(import.meta.url), '..', '..', '..');
|
|
10
11
|
|
|
@@ -82,7 +83,7 @@ export async function initCommand(opts) {
|
|
|
82
83
|
ok(`Platform: ${platformLabel(adapter)}`);
|
|
83
84
|
|
|
84
85
|
info('Setting up project...');
|
|
85
|
-
await setupProject(projectDir, { projectName, depth, roles, language, adapter });
|
|
86
|
+
await setupProject(projectDir, { projectName, depth, roles, language, adapter, presetKey: opts.preset });
|
|
86
87
|
|
|
87
88
|
printSummary({ projectName, depth, roles, language, adapter });
|
|
88
89
|
}
|
|
@@ -135,7 +136,7 @@ function normalizeRolesForDepth(parsedRoles, depth) {
|
|
|
135
136
|
return merged;
|
|
136
137
|
}
|
|
137
138
|
|
|
138
|
-
async function setupProject(projectDir, { projectName, depth, roles, language, adapter }) {
|
|
139
|
+
async function setupProject(projectDir, { projectName, depth, roles, language, adapter, presetKey }) {
|
|
139
140
|
copyCoreDocs(projectDir);
|
|
140
141
|
copyConfig(projectDir, { projectName, depth, roles, language });
|
|
141
142
|
copyAdapterStructure(projectDir);
|
|
@@ -143,6 +144,7 @@ async function setupProject(projectDir, { projectName, depth, roles, language, a
|
|
|
143
144
|
if (adapter === 'claude-cli') {
|
|
144
145
|
copyClaudeFiles(projectDir);
|
|
145
146
|
createMinimalCLAUDEmd(projectDir, projectName);
|
|
147
|
+
await maybeInstallSupplyChainHook(projectDir, presetKey);
|
|
146
148
|
} else if (adapter === 'cursor') {
|
|
147
149
|
copyCursorFiles(projectDir);
|
|
148
150
|
copyGenericAdapter(projectDir);
|
|
@@ -152,6 +154,47 @@ async function setupProject(projectDir, { projectName, depth, roles, language, a
|
|
|
152
154
|
|
|
153
155
|
createRuntimeDirs(projectDir);
|
|
154
156
|
updateGitignore(projectDir);
|
|
157
|
+
writeScopedGitignores(projectDir, adapter);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function writeScopedGitignores(projectDir, adapter) {
|
|
161
|
+
try {
|
|
162
|
+
const dwR = ensureDwGitignore(projectDir);
|
|
163
|
+
if (dwR.action !== 'noop') ok(`.dw/.gitignore ${dwR.action}`);
|
|
164
|
+
if (adapter === 'claude-cli') {
|
|
165
|
+
const cR = ensureClaudeGitignore(projectDir);
|
|
166
|
+
if (cR.action !== 'noop') ok(`.claude/.gitignore ${cR.action}`);
|
|
167
|
+
}
|
|
168
|
+
} catch (e) {
|
|
169
|
+
warn(`Scoped gitignore: ${e.message}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function maybeInstallSupplyChainHook(projectDir, presetKey) {
|
|
174
|
+
const preset = presetKey ? PRESETS[presetKey] : null;
|
|
175
|
+
const { installHookInProject, uninstallHookFromProject } = await import('../lib/sc-install.mjs');
|
|
176
|
+
|
|
177
|
+
if (preset && preset.hooksProfile === 'safety-only') {
|
|
178
|
+
// Solo preset — explicitly uninstall hook even if template provided it (TW5: opt-in OFF)
|
|
179
|
+
const result = uninstallHookFromProject(projectDir);
|
|
180
|
+
if (result.ok && result.action === 'removed') {
|
|
181
|
+
log(' Supply-chain guard: hook removed from settings (solo preset — opt-in OFF per ADR-0005 TW5)');
|
|
182
|
+
} else {
|
|
183
|
+
log(' Supply-chain guard: skipped (solo preset — opt-in OFF per ADR-0005 TW5)');
|
|
184
|
+
}
|
|
185
|
+
log(' Enable later: `dw security-scan --install-hook`');
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const result = installHookInProject(projectDir);
|
|
190
|
+
if (!result.ok) {
|
|
191
|
+
warn(`Supply-chain hook wiring skipped: ${result.error}`);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (result.action === 'added') {
|
|
195
|
+
ok('Supply-chain guard hook wired (ADR-0005 — opt-in flag enabled)');
|
|
196
|
+
log(' First scan: `dw security-scan --update-db`');
|
|
197
|
+
}
|
|
155
198
|
}
|
|
156
199
|
|
|
157
200
|
function copyCoreDocs(projectDir) {
|