@boyingliu01/xp-gate 0.15.5 → 0.18.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/adapter-common.sh +0 -0
- package/adapters/cpp.sh +0 -0
- package/adapters/dart.sh +0 -0
- package/adapters/flutter.sh +0 -0
- package/adapters/go.sh +0 -0
- package/adapters/iac.sh +0 -0
- package/adapters/java.sh +0 -0
- package/adapters/kotlin.sh +0 -0
- package/adapters/objectivec.sh +0 -0
- package/adapters/plugins/book299-20132-python/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-20132-python/scripts/install-maven.sh +0 -0
- package/adapters/plugins/book299-4081-c/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-4081-c/scripts/install-maven.sh +0 -0
- package/adapters/plugins/book299-4083-javascriptes5/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-4083-javascriptes5/scripts/install-maven.sh +0 -0
- package/adapters/powershell.sh +0 -0
- package/adapters/python.sh +0 -0
- package/adapters/shell.sh +0 -0
- package/adapters/swift.sh +0 -0
- package/adapters/typescript.sh +0 -0
- package/bin/xp-gate.js +29 -1
- package/gate-3.sh +0 -0
- package/gate-4.sh +0 -0
- package/gate-7.sh +0 -0
- package/gate-8.sh +0 -0
- package/gate-9.sh +0 -0
- package/hooks/adapter-common.sh +0 -0
- package/hooks/gate-3.sh +0 -0
- package/hooks/gate-4.sh +0 -0
- package/hooks/gate-7.sh +0 -0
- package/hooks/gate-8.sh +0 -0
- package/hooks/gate-9.sh +0 -0
- package/hooks/lib/now-ms.sh +33 -0
- package/hooks/pre-commit +63 -47
- package/hooks/pre-push +0 -0
- package/hooks/sprint-gate.sh +291 -0
- package/lib/__tests__/check-version.test.js +122 -0
- package/lib/__tests__/doctor.test.js +13 -15
- package/lib/__tests__/gate-audit.test.ts +38 -0
- package/lib/__tests__/phase-transition.test.js +512 -272
- package/lib/__tests__/retro.test.js +131 -0
- package/lib/__tests__/sprint-audit.test.js +207 -207
- package/lib/__tests__/sprint-status-rework.test.js +184 -0
- package/lib/check-version.js +9 -0
- package/lib/doctor-tui.js +268 -198
- package/lib/doctor.js +15 -3
- package/lib/gate-audit.ts +18 -7
- package/lib/gate-runner.js +77 -30
- package/lib/init.js +10 -0
- package/lib/language-tools.js +833 -833
- package/lib/mattpocock-license-verdict.json +7 -0
- package/lib/phase-transition.js +499 -249
- package/lib/retro.js +222 -0
- package/lib/shared-utils.js +7 -0
- package/lib/sprint-audit.js +235 -235
- package/lib/sprint-init.js +207 -0
- package/lib/sprint-state-migrator.js +1 -0
- package/lib/sprint-status.js +111 -0
- package/mock-policy/AGENTS.md +3 -3
- package/mutation/AGENTS.md +3 -3
- package/package.json +3 -2
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/bin/delphi-review-guard.sh +0 -0
- package/plugins/claude-code/bin/xp-gate-check +0 -0
- package/plugins/claude-code/skills/batch-grill-me/SKILL.md +15 -0
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/claude-code/skills/delphi-review/SKILL.md +59 -4
- package/plugins/claude-code/skills/delphi-review/references/requirements.md +390 -0
- package/plugins/claude-code/skills/delphi-review/references/round-templates.md +67 -0
- package/plugins/claude-code/skills/domain-modeling/ADR-FORMAT.md +64 -0
- package/plugins/claude-code/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/plugins/claude-code/skills/domain-modeling/SKILL.md +74 -0
- package/plugins/claude-code/skills/grill-with-docs/SKILL.md +7 -0
- package/plugins/claude-code/skills/grilling/SKILL.md +12 -0
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/claude-code/skills/sprint-flow/SKILL.md +168 -43
- package/plugins/claude-code/skills/sprint-flow/references/components/memory.md +1 -1
- package/plugins/claude-code/skills/sprint-flow/references/components/middleware.md +1 -1
- package/plugins/claude-code/skills/sprint-flow/references/components/skill-invocations.md +21 -20
- package/plugins/claude-code/skills/sprint-flow/references/components/tool-descriptions.md +22 -23
- package/plugins/claude-code/skills/sprint-flow/references/orchestration-rules.md +14 -12
- package/plugins/claude-code/skills/sprint-flow/references/phase-2-design.md +145 -133
- package/plugins/claude-code/skills/sprint-flow/references/phase-3-build.md +69 -2
- package/plugins/claude-code/skills/sprint-flow/references/phase-4-verify.md +93 -31
- package/plugins/claude-code/skills/sprint-flow/references/phase-5-ship.md +99 -14
- package/plugins/claude-code/skills/sprint-flow/references/phase-6-close.md +37 -5
- package/plugins/claude-code/skills/sprint-flow/references/phase-overview.md +58 -53
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/claude-code/skills/test-specification-alignment/SKILL.md +8 -3
- package/plugins/claude-code/skills/to-issues/SKILL.md +1 -1
- package/plugins/opencode/index.ts +1 -0
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/batch-grill-me/SKILL.md +15 -0
- package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/opencode/skills/delphi-review/SKILL.md +59 -4
- package/plugins/opencode/skills/delphi-review/references/requirements.md +390 -0
- package/plugins/opencode/skills/delphi-review/references/round-templates.md +67 -0
- package/plugins/opencode/skills/domain-modeling/ADR-FORMAT.md +64 -0
- package/plugins/opencode/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/plugins/opencode/skills/domain-modeling/SKILL.md +74 -0
- package/plugins/opencode/skills/grill-with-docs/SKILL.md +7 -0
- package/plugins/opencode/skills/grilling/SKILL.md +12 -0
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/opencode/skills/sprint-flow/SKILL.md +168 -43
- package/plugins/opencode/skills/sprint-flow/references/components/memory.md +1 -1
- package/plugins/opencode/skills/sprint-flow/references/components/middleware.md +1 -1
- package/plugins/opencode/skills/sprint-flow/references/components/skill-invocations.md +21 -20
- package/plugins/opencode/skills/sprint-flow/references/components/tool-descriptions.md +22 -23
- package/plugins/opencode/skills/sprint-flow/references/orchestration-rules.md +14 -12
- package/plugins/opencode/skills/sprint-flow/references/phase-2-design.md +145 -133
- package/plugins/opencode/skills/sprint-flow/references/phase-3-build.md +69 -2
- package/plugins/opencode/skills/sprint-flow/references/phase-4-verify.md +93 -31
- package/plugins/opencode/skills/sprint-flow/references/phase-5-ship.md +99 -14
- package/plugins/opencode/skills/sprint-flow/references/phase-6-close.md +37 -5
- package/plugins/opencode/skills/sprint-flow/references/phase-overview.md +58 -53
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/opencode/skills/test-specification-alignment/SKILL.md +8 -3
- package/plugins/opencode/skills/to-issues/SKILL.md +1 -1
- package/plugins/qoder/agents/delphi-architecture.md +48 -48
- package/plugins/qoder/agents/delphi-feasibility.md +48 -48
- package/plugins/qoder/agents/delphi-technical.md +48 -48
- package/plugins/qoder/bin/sprint-flow-guard.sh +0 -0
- package/plugins/qoder/bin/xp-gate-check +0 -0
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
- package/principles/AGENTS.md +3 -3
- package/scripts/__tests__/check-mattpocock-license.test.js +229 -0
- package/scripts/__tests__/sync-package-content.test.js +50 -0
- package/scripts/check-mattpocock-license.cjs +302 -0
- package/scripts/delphi-external-review.cjs +432 -432
- package/scripts/migrate-audit-anomalies.mjs +78 -0
- package/scripts/sync-package-content.js +52 -5
- package/skills/batch-grill-me/SKILL.md +15 -0
- package/skills/delphi-review/AGENTS.md +3 -3
- package/skills/delphi-review/SKILL.md +59 -4
- package/skills/delphi-review/references/requirements.md +390 -0
- package/skills/delphi-review/references/round-templates.md +67 -0
- package/skills/domain-modeling/ADR-FORMAT.md +64 -0
- package/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/skills/domain-modeling/SKILL.md +74 -0
- package/skills/grill-with-docs/SKILL.md +7 -0
- package/skills/grilling/SKILL.md +12 -0
- package/skills/sprint-flow/AGENTS.md +3 -3
- package/skills/sprint-flow/SKILL.md +168 -43
- package/skills/sprint-flow/references/components/memory.md +1 -1
- package/skills/sprint-flow/references/components/middleware.md +1 -1
- package/skills/sprint-flow/references/components/skill-invocations.md +21 -20
- package/skills/sprint-flow/references/components/tool-descriptions.md +22 -23
- package/skills/sprint-flow/references/orchestration-rules.md +14 -12
- package/skills/sprint-flow/references/phase-2-design.md +145 -133
- package/skills/sprint-flow/references/phase-3-build.md +69 -2
- package/skills/sprint-flow/references/phase-4-verify.md +93 -31
- package/skills/sprint-flow/references/phase-5-ship.md +99 -14
- package/skills/sprint-flow/references/phase-6-close.md +37 -5
- package/skills/sprint-flow/references/phase-overview.md +58 -53
- package/skills/test-specification-alignment/AGENTS.md +3 -3
- package/skills/test-specification-alignment/SKILL.md +8 -3
- package/skills/to-issues/SKILL.md +1 -1
- package/adapters/gate-3.sh +0 -68
- package/adapters/gate-4.sh +0 -80
- package/adapters/gate-7.sh +0 -45
- package/adapters/gate-8.sh +0 -56
- package/adapters/gate-9.sh +0 -104
package/lib/retro.js
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xp-gate retro — Sprint engineering retrospective CLI (#369, design §8.1)
|
|
3
|
+
*
|
|
4
|
+
* Reads git log + .xp-gate/audit.jsonl + .quality-history.jsonl + .sprint-history/
|
|
5
|
+
* and generates a Markdown retrospective report with rework rate trend (#369)
|
|
6
|
+
* and --skip-evidence exposure (§8.4) sections.
|
|
7
|
+
*
|
|
8
|
+
* Zero npm dependencies (Node built-ins only).
|
|
9
|
+
* @module retro
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { execSync } = require('child_process');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get git commit subjects within a date window.
|
|
20
|
+
* @param {string} dir - Repository root
|
|
21
|
+
* @param {number} days - Lookback window in days
|
|
22
|
+
* @returns {Array<{hash: string, date: string, subject: string}>}
|
|
23
|
+
*/
|
|
24
|
+
function getGitCommits(dir, days) {
|
|
25
|
+
try {
|
|
26
|
+
const out = execSync(
|
|
27
|
+
`git log --since="${days} days ago" --pretty=format:"%H|%ai|%s"`,
|
|
28
|
+
{ cwd: dir, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
|
|
29
|
+
);
|
|
30
|
+
return out.split('\n').filter(Boolean).map(l => {
|
|
31
|
+
const [hash, date, ...rest] = l.split('|');
|
|
32
|
+
return { hash, date, subject: rest.join('|') };
|
|
33
|
+
});
|
|
34
|
+
} catch {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Parse .xp-gate/audit.jsonl entries.
|
|
41
|
+
* @param {string} dir - Repository root
|
|
42
|
+
* @returns {object[]} Parsed audit entries
|
|
43
|
+
*/
|
|
44
|
+
function readAuditEntries(dir) {
|
|
45
|
+
const p = path.join(dir, '.xp-gate', 'audit.jsonl');
|
|
46
|
+
if (!fs.existsSync(p)) return [];
|
|
47
|
+
return fs.readFileSync(p, 'utf8').split('\n')
|
|
48
|
+
.filter(l => l.trim())
|
|
49
|
+
.map(l => { try { return JSON.parse(l); } catch { return null; } })
|
|
50
|
+
.filter(Boolean);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Read last 10 entries from .quality-history.jsonl.
|
|
55
|
+
* @param {string} dir - Repository root
|
|
56
|
+
* @returns {object[]} Recent quality entries
|
|
57
|
+
*/
|
|
58
|
+
function readQualityHistory(dir) {
|
|
59
|
+
const p = path.join(dir, '.quality-history.jsonl');
|
|
60
|
+
if (!fs.existsSync(p)) return [];
|
|
61
|
+
const entries = fs.readFileSync(p, 'utf8').split('\n')
|
|
62
|
+
.filter(l => l.trim())
|
|
63
|
+
.map(l => { try { return JSON.parse(l); } catch { return null; } })
|
|
64
|
+
.filter(Boolean);
|
|
65
|
+
return entries.slice(-10);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Read sprint history JSON files from .sprint-state/sprint-history/.
|
|
70
|
+
* @param {string} dir - Repository root
|
|
71
|
+
* @param {string|null} sprintFilter - Optional sprint ID filter
|
|
72
|
+
* @returns {object[]} Sprint state objects
|
|
73
|
+
*/
|
|
74
|
+
function readSprintHistory(dir, sprintFilter) {
|
|
75
|
+
const histDir = path.join(dir, '.sprint-state', 'sprint-history');
|
|
76
|
+
const results = [];
|
|
77
|
+
if (fs.existsSync(histDir)) {
|
|
78
|
+
for (const f of fs.readdirSync(histDir).filter(f => f.endsWith('.json'))) {
|
|
79
|
+
try {
|
|
80
|
+
const s = JSON.parse(fs.readFileSync(path.join(histDir, f), 'utf8'));
|
|
81
|
+
if (!sprintFilter || s.id === sprintFilter) results.push(s);
|
|
82
|
+
} catch { /* skip */ }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return results;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Build the Markdown retrospective report.
|
|
90
|
+
* @param {object} opts - Report data
|
|
91
|
+
* @returns {string} Markdown report
|
|
92
|
+
*/
|
|
93
|
+
function buildReport(opts) {
|
|
94
|
+
const { days, commits, audit, quality, sprints } = opts;
|
|
95
|
+
const now = new Date();
|
|
96
|
+
const start = new Date(now.getTime() - days * 86400000);
|
|
97
|
+
const L = [];
|
|
98
|
+
|
|
99
|
+
L.push('# Sprint Engineering Retrospective');
|
|
100
|
+
L.push('');
|
|
101
|
+
L.push(`Period: ${start.toISOString().slice(0, 10)} to ${now.toISOString().slice(0, 10)}`);
|
|
102
|
+
L.push('');
|
|
103
|
+
|
|
104
|
+
// Activity Summary
|
|
105
|
+
L.push('## Activity Summary');
|
|
106
|
+
const gateEntries = audit.filter(e => e.gate_id);
|
|
107
|
+
const passed = gateEntries.filter(e => e.passed).length;
|
|
108
|
+
const normal = gateEntries.filter(e => !e.duration_anomaly);
|
|
109
|
+
const anomalous = gateEntries.length - normal.length;
|
|
110
|
+
const avgMs = normal.length > 0
|
|
111
|
+
? Math.round(normal.reduce((s, e) => s + (e.duration_ms || 0), 0) / normal.length)
|
|
112
|
+
: 0;
|
|
113
|
+
L.push(`- Total commits in window: ${commits.length}`);
|
|
114
|
+
L.push(`- Sprint transitions recorded: ${sprints.length}`);
|
|
115
|
+
L.push(`- Gate pass rate: ${gateEntries.length > 0 ? ((passed / gateEntries.length) * 100).toFixed(1) : '0.0'}% (${gateEntries.length} gates total, ${gateEntries.length - passed} failed)`);
|
|
116
|
+
if (gateEntries.length > 0) {
|
|
117
|
+
L.push(`- Avg gate duration: ${avgMs}ms (${anomalous} anomal${anomalous === 1 ? 'y' : 'ies'} excluded)`);
|
|
118
|
+
}
|
|
119
|
+
L.push('');
|
|
120
|
+
|
|
121
|
+
// Rework Rate Trend (#369)
|
|
122
|
+
L.push('## Rework Rate Trend (#369)');
|
|
123
|
+
const withRate = sprints.filter(s => s.metrics && s.metrics.rework_rate != null);
|
|
124
|
+
if (withRate.length === 0) {
|
|
125
|
+
L.push('No data');
|
|
126
|
+
} else {
|
|
127
|
+
L.push('| Sprint | Rework Rate | Fix Commits | Total Commits |');
|
|
128
|
+
L.push('|--------|-------------|-------------|---------------|');
|
|
129
|
+
for (const s of withRate) {
|
|
130
|
+
const pct = (s.metrics.rework_rate * 100).toFixed(1);
|
|
131
|
+
const fix = s.metrics.total_sprint_commits != null
|
|
132
|
+
? Math.round(s.metrics.rework_rate * s.metrics.total_sprint_commits)
|
|
133
|
+
: '?';
|
|
134
|
+
L.push(`| ${s.id || 'unknown'} | ${pct}% | ${fix} | ${s.metrics.total_sprint_commits ?? '?'} |`);
|
|
135
|
+
if (s.metrics.rework_rate > 0.30) {
|
|
136
|
+
L.push(`⚠️ Sprint ${s.id || 'unknown'}: ${pct}% rework — exceeds 30% threshold`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
L.push('');
|
|
141
|
+
|
|
142
|
+
// Evidence Skip Exposure (§8.4)
|
|
143
|
+
L.push('## Evidence Skip Exposure (§8.4)');
|
|
144
|
+
const skipped = audit.filter(e => e.event === 'evidence_skipped');
|
|
145
|
+
if (skipped.length === 0) {
|
|
146
|
+
L.push('No data');
|
|
147
|
+
} else {
|
|
148
|
+
L.push('| Phase | Reason | Timestamp |');
|
|
149
|
+
L.push('|-------|--------|-----------|');
|
|
150
|
+
for (const e of skipped) {
|
|
151
|
+
L.push(`| ${e.phase || '?'} | ${e.reason || '?'} | ${(e.timestamp || '').slice(0, 19)} |`);
|
|
152
|
+
}
|
|
153
|
+
const bySprint = {};
|
|
154
|
+
for (const e of skipped) {
|
|
155
|
+
const k = e.sprint_id || 'unknown';
|
|
156
|
+
bySprint[k] = (bySprint[k] || 0) + 1;
|
|
157
|
+
}
|
|
158
|
+
for (const [sid, count] of Object.entries(bySprint)) {
|
|
159
|
+
if (count > 2) {
|
|
160
|
+
L.push(`⚠️ Sprint ${sid} used skip ${count} times`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
L.push('');
|
|
165
|
+
|
|
166
|
+
// Quality Trend
|
|
167
|
+
L.push('## Quality Trend');
|
|
168
|
+
if (quality.length === 0) {
|
|
169
|
+
L.push('No data');
|
|
170
|
+
} else {
|
|
171
|
+
for (const q of quality) {
|
|
172
|
+
L.push(`- ${(q.timestamp || '').slice(0, 10)}: score ${q.score ?? '?'}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return L.join('\n');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* CLI entry point for xp-gate retro.
|
|
181
|
+
* @param {string[]} args - CLI subargs (without 'retro')
|
|
182
|
+
* @returns {Promise<number>} Exit code
|
|
183
|
+
*/
|
|
184
|
+
async function handleRetro(args = []) {
|
|
185
|
+
const allFlag = args.includes('--all');
|
|
186
|
+
const jsonFlag = args.includes('--json');
|
|
187
|
+
const daysIdx = args.indexOf('--days');
|
|
188
|
+
const sprintIdx = args.indexOf('--sprint');
|
|
189
|
+
|
|
190
|
+
let days = allFlag ? 30 : 7;
|
|
191
|
+
if (daysIdx >= 0 && args[daysIdx + 1]) {
|
|
192
|
+
days = parseInt(args[daysIdx + 1], 10);
|
|
193
|
+
if (isNaN(days) || days < 1) days = 7;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const sprintFilter = (sprintIdx >= 0 && args[sprintIdx + 1]) ? args[sprintIdx + 1] : null;
|
|
197
|
+
const dir = process.cwd();
|
|
198
|
+
|
|
199
|
+
const commits = getGitCommits(dir, days);
|
|
200
|
+
const audit = readAuditEntries(dir);
|
|
201
|
+
const quality = readQualityHistory(dir);
|
|
202
|
+
const sprints = readSprintHistory(dir, sprintFilter);
|
|
203
|
+
|
|
204
|
+
if (jsonFlag) {
|
|
205
|
+
const gateEntries = audit.filter(e => e.gate_id);
|
|
206
|
+
const passed = gateEntries.filter(e => e.passed).length;
|
|
207
|
+
console.log(JSON.stringify({
|
|
208
|
+
period: { days, from: new Date(Date.now() - days * 86400000).toISOString(), to: new Date().toISOString() },
|
|
209
|
+
total_commits: commits.length,
|
|
210
|
+
gate_pass_rate: gateEntries.length > 0 ? passed / gateEntries.length : null,
|
|
211
|
+
rework_rates: sprints.filter(s => s.metrics?.rework_rate != null).map(s => ({ id: s.id, rate: s.metrics.rework_rate })),
|
|
212
|
+
evidence_skipped: audit.filter(e => e.event === 'evidence_skipped'),
|
|
213
|
+
quality: quality.map(q => ({ timestamp: q.timestamp, score: q.score })),
|
|
214
|
+
}, null, 2));
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
console.log(buildReport({ days, commits, audit, quality, sprints }));
|
|
219
|
+
return 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = { handleRetro };
|
package/lib/shared-utils.js
CHANGED
|
@@ -42,6 +42,13 @@ function copyHooks(srcDir, destDir) {
|
|
|
42
42
|
fs.chmodSync(dest, 0o755);
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
|
+
|
|
46
|
+
// Copy lib/ directory (e.g. now-ms.sh) — sourced by pre-commit at runtime
|
|
47
|
+
const libSrc = path.join(srcDir, 'hooks', 'lib');
|
|
48
|
+
const libDest = path.join(destDir, 'lib');
|
|
49
|
+
if (fs.existsSync(libSrc)) {
|
|
50
|
+
copyDirRecursive(libSrc, libDest);
|
|
51
|
+
}
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
function copyAdapters(srcDir, destDir) {
|