dw-kit 1.2.0 → 1.3.0
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/post-write.sh +64 -58
- package/.claude/hooks/pre-commit-gate.sh +96 -90
- package/.claude/hooks/privacy-block.sh +99 -94
- package/.claude/hooks/progress-ping.sh +53 -47
- package/.claude/hooks/safety-guard.sh +60 -54
- package/.claude/hooks/scout-block.sh +88 -82
- package/.claude/hooks/session-init.sh +91 -74
- package/.claude/hooks/stop-check.sh +88 -36
- package/.claude/hooks/telemetry-log.sh +34 -0
- package/.claude/rules/code-style.md +37 -37
- package/.claude/rules/commit-standards.md +37 -37
- package/.claude/rules/dw.md +136 -0
- package/.claude/settings.json +120 -99
- package/.claude/skills/dw-arch-review/SKILL.md +119 -119
- package/.claude/skills/dw-archive/SKILL.md +81 -81
- package/.claude/skills/dw-commit/SKILL.md +81 -81
- package/.claude/skills/dw-config-init/SKILL.md +91 -91
- package/.claude/skills/dw-config-validate/SKILL.md +75 -75
- package/.claude/skills/dw-dashboard/SKILL.md +209 -209
- package/.claude/skills/dw-debug/SKILL.md +97 -97
- package/.claude/skills/dw-decision/SKILL.md +116 -0
- package/.claude/skills/dw-docs-update/SKILL.md +125 -125
- package/.claude/skills/dw-estimate/SKILL.md +90 -90
- package/.claude/skills/dw-execute/SKILL.md +98 -98
- package/.claude/skills/dw-flow/SKILL.md +274 -274
- package/.claude/skills/dw-handoff/SKILL.md +81 -81
- package/.claude/skills/dw-kit-report/SKILL.md +152 -152
- package/.claude/skills/dw-log-work/SKILL.md +69 -69
- package/.claude/skills/dw-onboard/SKILL.md +201 -201
- package/.claude/skills/dw-plan/SKILL.md +125 -125
- package/.claude/skills/dw-prompt/SKILL.md +62 -62
- package/.claude/skills/dw-requirements/SKILL.md +98 -98
- package/.claude/skills/dw-research/SKILL.md +114 -114
- package/.claude/skills/dw-retroactive/SKILL.md +311 -311
- package/.claude/skills/dw-review/SKILL.md +66 -66
- package/.claude/skills/dw-rollback/SKILL.md +90 -90
- package/.claude/skills/dw-sprint-review/SKILL.md +99 -99
- package/.claude/skills/dw-task-init/SKILL.md +59 -59
- package/.claude/skills/dw-test-plan/SKILL.md +113 -113
- package/.claude/skills/dw-thinking/SKILL.md +70 -70
- package/.claude/skills/dw-upgrade/SKILL.md +72 -72
- package/.dw/config/dw.config.yml +82 -82
- package/.dw/core/PILLARS.md +122 -0
- package/.dw/core/templates/v2/spec.md +68 -0
- package/.dw/core/templates/v2/tracking.md +62 -0
- package/.dw/core/v14-evaluation-protocol.md +118 -0
- package/CLAUDE.md +42 -39
- package/MIGRATION-v1.3.md +201 -0
- package/README.md +43 -6
- package/package.json +86 -84
- package/src/cli.mjs +45 -9
- package/src/commands/dashboard.mjs +116 -0
- package/src/commands/doctor.mjs +165 -149
- package/src/commands/init.mjs +339 -332
- package/src/commands/metrics.mjs +165 -0
- package/src/commands/upgrade.mjs +297 -262
- package/src/lib/active-index.mjs +87 -0
- package/src/lib/copy.mjs +118 -110
- package/src/lib/cut-analysis.mjs +161 -0
- package/src/lib/telemetry.mjs +80 -0
- package/.claude/rules/dw-core.md +0 -100
- package/.claude/rules/dw-skills.md +0 -53
- package/.claude/rules/workflow-rules.md +0 -77
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { readEvents, summarize } from '../lib/telemetry.mjs';
|
|
2
|
+
import { analyze, THRESHOLDS } from '../lib/cut-analysis.mjs';
|
|
3
|
+
import { banner, log, info, warn, ok, err } from '../lib/ui.mjs';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
|
|
6
|
+
export async function metricsCommand(opts) {
|
|
7
|
+
const sub = opts.sub || 'show';
|
|
8
|
+
|
|
9
|
+
if (sub === 'show') {
|
|
10
|
+
return showMetrics(opts);
|
|
11
|
+
}
|
|
12
|
+
if (sub === 'cut-analysis') {
|
|
13
|
+
return cutAnalysisReport(opts);
|
|
14
|
+
}
|
|
15
|
+
if (sub === 'clear') {
|
|
16
|
+
warn('Use `rm .dw/metrics/events.jsonl` to clear manually. Telemetry is append-only.');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
warn(`Unknown subcommand: ${sub}. Available: show | cut-analysis | clear`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function showMetrics(opts) {
|
|
23
|
+
banner('dw-kit Telemetry — Local Events');
|
|
24
|
+
|
|
25
|
+
if (process.env.DW_NO_TELEMETRY === '1' || process.env.DW_NO_TELEMETRY === 'true') {
|
|
26
|
+
warn('Telemetry is disabled (DW_NO_TELEMETRY=1).');
|
|
27
|
+
log('No events are being collected.');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const events = readEvents(process.cwd(), {
|
|
32
|
+
since: opts.since,
|
|
33
|
+
filterName: opts.skill,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (events.length === 0) {
|
|
37
|
+
info('No telemetry events recorded yet.');
|
|
38
|
+
log('Events will be logged as you use dw-kit skills and hooks.');
|
|
39
|
+
log('Storage: .dw/metrics/events.jsonl (local, append-only, zero network)');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const s = summarize(events);
|
|
44
|
+
log('');
|
|
45
|
+
log(chalk.bold(`Total events: ${s.totalEvents}`));
|
|
46
|
+
if (s.dateRange) {
|
|
47
|
+
log(`Range: ${s.dateRange.from.slice(0, 10)} → ${s.dateRange.to.slice(0, 10)}`);
|
|
48
|
+
}
|
|
49
|
+
log('');
|
|
50
|
+
|
|
51
|
+
if (Object.keys(s.bySkill).length > 0) {
|
|
52
|
+
log(chalk.bold('Skills invoked:'));
|
|
53
|
+
const skillsSorted = Object.entries(s.bySkill).sort((a, b) => b[1] - a[1]);
|
|
54
|
+
for (const [name, count] of skillsSorted) {
|
|
55
|
+
log(` ${count.toString().padStart(4)}× /${name}`);
|
|
56
|
+
}
|
|
57
|
+
log('');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (Object.keys(s.byHook).length > 0) {
|
|
61
|
+
log(chalk.bold('Hooks fired:'));
|
|
62
|
+
const hooksSorted = Object.entries(s.byHook).sort((a, b) => b[1] - a[1]);
|
|
63
|
+
for (const [name, count] of hooksSorted) {
|
|
64
|
+
log(` ${count.toString().padStart(4)}× ${name}`);
|
|
65
|
+
}
|
|
66
|
+
log('');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (Object.keys(s.byTask).length > 0) {
|
|
70
|
+
log(chalk.bold('Task actions:'));
|
|
71
|
+
for (const [action, count] of Object.entries(s.byTask)) {
|
|
72
|
+
log(` ${count.toString().padStart(4)}× ${action}`);
|
|
73
|
+
}
|
|
74
|
+
log('');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
info('Privacy: all data is local. Set DW_NO_TELEMETRY=1 to disable.');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function cutAnalysisReport(opts) {
|
|
81
|
+
banner('dw-kit Cut-Analysis — ADR-0001 Cut Criteria Matrix');
|
|
82
|
+
|
|
83
|
+
if (process.env.DW_NO_TELEMETRY === '1' || process.env.DW_NO_TELEMETRY === 'true') {
|
|
84
|
+
warn('Telemetry disabled — no data to analyze.');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const events = readEvents(process.cwd(), { since: opts.since });
|
|
89
|
+
if (events.length === 0) {
|
|
90
|
+
info('No telemetry events recorded — nothing to analyze.');
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const result = analyze(events);
|
|
95
|
+
const { coverage, skills, hooks, candidates } = result;
|
|
96
|
+
|
|
97
|
+
log('');
|
|
98
|
+
log(chalk.bold('Coverage'));
|
|
99
|
+
const covMsg = ` days=${coverage.days} unique_sessions=${coverage.sessions}`;
|
|
100
|
+
log(covMsg);
|
|
101
|
+
if (!coverage.coverageOk) {
|
|
102
|
+
warn(`coverage_days=${coverage.days} < ${THRESHOLDS.skill.minCoverageDays} — skill cuts NOT recommended yet`);
|
|
103
|
+
} else {
|
|
104
|
+
ok(`coverage_days ≥ ${THRESHOLDS.skill.minCoverageDays} — eligible for skill evaluation`);
|
|
105
|
+
}
|
|
106
|
+
if (!coverage.devsOk) {
|
|
107
|
+
warn(`devs=${coverage.sessions} < ${THRESHOLDS.skill.minDevs} — skill cuts NOT recommended yet (session-hash is a proxy; may undercount)`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
log('');
|
|
111
|
+
log(chalk.bold('Skills (sorted by uses/week/dev, ascending)'));
|
|
112
|
+
if (skills.length === 0) {
|
|
113
|
+
log(' (no skill events)');
|
|
114
|
+
} else {
|
|
115
|
+
for (const r of skills) {
|
|
116
|
+
const rate = r.stats.usesPerWeekPerDev.toFixed(2);
|
|
117
|
+
const tag = r.qualify
|
|
118
|
+
? chalk.red('CUT CANDIDATE')
|
|
119
|
+
: r.critical
|
|
120
|
+
? chalk.cyan('protected')
|
|
121
|
+
: r.perProject
|
|
122
|
+
? chalk.dim('per-project')
|
|
123
|
+
: chalk.green('keep');
|
|
124
|
+
log(` ${tag.padEnd(24)} /${r.name.padEnd(24)} uses/wk/dev=${rate} total=${r.stats.count}`);
|
|
125
|
+
for (const reason of r.reasons) log(chalk.dim(` └─ ${reason}`));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
log('');
|
|
130
|
+
log(chalk.bold('Hooks (sorted by fires/session, descending)'));
|
|
131
|
+
if (hooks.length === 0) {
|
|
132
|
+
log(' (no hook events)');
|
|
133
|
+
} else {
|
|
134
|
+
for (const r of hooks) {
|
|
135
|
+
const fires = r.stats.firesPerSession.toFixed(1);
|
|
136
|
+
const lat = r.stats.avgLatency !== null ? `${r.stats.avgLatency.toFixed(0)}ms` : 'n/a';
|
|
137
|
+
const tag = r.qualify ? chalk.red('CUT CANDIDATE') : chalk.green('keep');
|
|
138
|
+
log(` ${tag.padEnd(24)} ${r.name.padEnd(24)} fires/session=${fires} avg_latency=${lat}`);
|
|
139
|
+
for (const reason of r.reasons) log(chalk.dim(` └─ ${reason}`));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
log('');
|
|
144
|
+
log(chalk.bold('Summary'));
|
|
145
|
+
const skillCount = candidates.skills.length;
|
|
146
|
+
const hookCount = candidates.hooks.length;
|
|
147
|
+
if (skillCount === 0 && hookCount === 0) {
|
|
148
|
+
ok('No cut candidates — keep current surface.');
|
|
149
|
+
} else {
|
|
150
|
+
log(` Skills flagged: ${skillCount}`);
|
|
151
|
+
log(` Hooks flagged: ${hookCount}`);
|
|
152
|
+
log('');
|
|
153
|
+
info('Next steps:');
|
|
154
|
+
log(' 1. Run team survey for qualitative check on flagged items (avoid false-positives).');
|
|
155
|
+
log(' 2. For each confirmed cut → write ADR (`/dw:decision "Remove <name>"`).');
|
|
156
|
+
log(' 3. Remove from .claude/hooks/ or .claude/skills/ + update .claude/settings.json.');
|
|
157
|
+
log(' 4. Document in MIGRATION-v1.4.md with rollback instructions.');
|
|
158
|
+
}
|
|
159
|
+
log('');
|
|
160
|
+
info('Thresholds (from ADR-0001 Cut Criteria Matrix):');
|
|
161
|
+
log(` Skill: uses/wk/dev < ${THRESHOLDS.skill.minUsesPerWeekPerDev} AND devs ≥ ${THRESHOLDS.skill.minDevs} AND coverage ≥ ${THRESHOLDS.skill.minCoverageDays}d`);
|
|
162
|
+
log(` Hook: avg_latency > ${THRESHOLDS.hook.maxAvgLatencyMs}ms OR fires/session > ${THRESHOLDS.hook.maxFiresPerSession}`);
|
|
163
|
+
log('');
|
|
164
|
+
info('Caveat: "devs" proxied by unique session hashes — undercounts real headcount.');
|
|
165
|
+
}
|