@phuc1403/musketeer 0.8.0 → 0.10.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/INSTALLATION.md +52 -52
- package/README.md +49 -49
- package/bin/musketeer.js +168 -168
- package/manifest.json +333 -301
- package/package.json +48 -48
- package/src/dotnet-scaffold-copier.js +79 -79
- package/src/provisioner/detect.js +93 -93
- package/src/self-update.js +77 -77
- package/template/.claude/agents/code-reviewer.md +182 -166
- package/template/.claude/agents/git-manager.md +18 -18
- package/template/.claude/agents/hallmark-auditor.md +78 -78
- package/template/.claude/agents/researcher.md +33 -33
- package/template/.claude/hooks/block-unsafe-adr-title.cjs +85 -85
- package/template/.claude/hooks/git-skill-reminder.cjs +53 -0
- package/template/.claude/hooks/init-adr-dir.cjs +173 -173
- package/template/.claude/hooks/inject-adr-flags.cjs +94 -94
- package/template/.claude/hooks/lib/adr/command-scan.cjs +115 -115
- package/template/.claude/hooks/lib/characteristics/checker.cjs +357 -357
- package/template/.claude/hooks/lib/colors.cjs +180 -122
- package/template/.claude/hooks/lib/git-info-cache.cjs +191 -191
- package/template/.claude/hooks/lib/transcript-parser.cjs +300 -277
- package/template/.claude/hooks/sync-adr-toc.cjs +146 -146
- package/template/.claude/hooks/{usage-context-awareness.cjs → usage-quota-cache-refresh.cjs} +166 -166
- package/template/.claude/hooks/validate-characteristics-hook.cjs +66 -66
- package/template/.claude/hooks/validate-cml-hook.js +145 -145
- package/template/.claude/skills/adr-writer/SKILL.md +48 -48
- package/template/.claude/skills/adr-writer/references/adr-example.md +35 -35
- package/template/.claude/skills/architecture-characteristic-writer/SKILL.md +215 -215
- package/template/.claude/skills/architecture-characteristic-writer/assets/worksheet-template.md +29 -29
- package/template/.claude/skills/architecture-characteristic-writer/references/characteristics-catalog.md +40 -40
- package/template/.claude/skills/architecture-characteristic-writer/scripts/ranking-table.cjs +171 -171
- package/template/.claude/skills/code-review/SKILL.md +201 -54
- package/template/.claude/skills/code-review/references/checklist-workflow.md +96 -0
- package/template/.claude/skills/code-review/references/checklists/api.md +52 -52
- package/template/.claude/skills/code-review/references/checklists/base.md +100 -100
- package/template/.claude/skills/code-review/references/checklists/web-app.md +54 -54
- package/template/.claude/skills/code-review/references/code-review-reception.md +113 -0
- package/template/.claude/skills/code-review/references/codebase-scan-workflow.md +30 -0
- package/template/.claude/skills/code-review/references/edge-case-scouting.md +119 -0
- package/template/.claude/skills/code-review/references/input-mode-resolution.md +135 -0
- package/template/.claude/skills/code-review/references/parallel-review-workflow.md +76 -0
- package/template/.claude/skills/code-review/references/requesting-code-review.md +116 -0
- package/template/.claude/skills/code-review/references/spec-compliance-review.md +43 -0
- package/template/.claude/skills/code-review/references/task-management-reviews.md +140 -0
- package/template/.claude/skills/code-review/references/verification-before-completion.md +139 -0
- package/template/.claude/skills/context-map/SKILL.md +80 -80
- package/template/.claude/skills/context-map/example.cml +106 -106
- package/template/.claude/skills/context-map/reference/Bounded Context/Bounded Context.md +40 -40
- package/template/.claude/skills/context-map/reference/Bounded Context/businessModel.md +5 -5
- package/template/.claude/skills/context-map/reference/Bounded Context/domainVisionStatement.md +2 -2
- package/template/.claude/skills/context-map/reference/Bounded Context/evolution.md +5 -5
- package/template/.claude/skills/context-map/reference/Bounded Context/implementationTechnology.md +1 -1
- package/template/.claude/skills/context-map/reference/Bounded Context/implements.md +1 -1
- package/template/.claude/skills/context-map/reference/Bounded Context/knowledgeLevel.md +4 -4
- package/template/.claude/skills/context-map/reference/Bounded Context/realizes.md +9 -9
- package/template/.claude/skills/context-map/reference/Bounded Context/refines.md +10 -10
- package/template/.claude/skills/context-map/reference/Bounded Context/responsibilities.md +26 -26
- package/template/.claude/skills/context-map/reference/Bounded Context/type.md +23 -23
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Anticorruption Layer.md +5 -5
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Bounded Context Relationship.md +12 -12
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Conformist.md +5 -5
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Customer-Supplier (C-S).md +22 -22
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Open Host Service.md +4 -4
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Partnership (P).md +13 -13
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Published Language.md +4 -4
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Shared Kernel (SK).md +12 -12
- package/template/.claude/skills/context-map/reference/Context Map.md +62 -62
- package/template/.claude/skills/context-map/reference/Domain/Domain.md +30 -30
- package/template/.claude/skills/context-map/reference/Domain/supports.md +33 -33
- package/template/.claude/skills/context-map/reference/Domain/type.md +3 -3
- package/template/.claude/skills/context-map/reference/Semantic Rules.md +32 -32
- package/template/.claude/skills/git/SKILL.md +131 -115
- package/template/.claude/skills/git/references/branch-management.md +88 -88
- package/template/.claude/skills/git/references/commit-standards.md +46 -46
- package/template/.claude/skills/git/references/context-efficiency.md +54 -0
- package/template/.claude/skills/git/references/gh-cli-guide.md +109 -109
- package/template/.claude/skills/git/references/safety-protocols.md +69 -69
- package/template/.claude/skills/git/references/workflow-commit.md +58 -58
- package/template/.claude/skills/git/references/workflow-merge-pr.md +136 -0
- package/template/.claude/skills/git/references/workflow-merge.md +48 -48
- package/template/.claude/skills/git/references/workflow-pr.md +58 -58
- package/template/.claude/skills/git/references/workflow-push.md +52 -52
- package/template/.claude/skills/hallmark/SKILL.md +552 -552
- package/template/.claude/skills/hallmark/references/anti-patterns.md +412 -412
- package/template/.claude/skills/hallmark/references/assets.md +406 -406
- package/template/.claude/skills/hallmark/references/color.md +95 -95
- package/template/.claude/skills/hallmark/references/component-cookbook.md +256 -256
- package/template/.claude/skills/hallmark/references/components/c1-outlined-chip.md +12 -12
- package/template/.claude/skills/hallmark/references/components/c2-inline-form-as-cta.md +16 -16
- package/template/.claude/skills/hallmark/references/components/c3-typographic-link.md +8 -8
- package/template/.claude/skills/hallmark/references/components/c4-sticky-bottom-bar.md +16 -16
- package/template/.claude/skills/hallmark/references/components/f1-bento-grid.md +20 -20
- package/template/.claude/skills/hallmark/references/components/f2-sticky-scroll-stack.md +20 -20
- package/template/.claude/skills/hallmark/references/components/f3-tabular-spec-sheet.md +11 -11
- package/template/.claude/skills/hallmark/references/components/f4-step-sequence.md +11 -11
- package/template/.claude/skills/hallmark/references/components/f5-annotated-screenshot.md +11 -11
- package/template/.claude/skills/hallmark/references/components/f6-product-card-grid.md +41 -41
- package/template/.claude/skills/hallmark/references/components/ft1-mast-headed.md +13 -13
- package/template/.claude/skills/hallmark/references/components/ft2-inline-rule-single-line.md +10 -10
- package/template/.claude/skills/hallmark/references/components/ft3-index-style-category-list.md +12 -12
- package/template/.claude/skills/hallmark/references/components/ft4-dense-typographic.md +10 -10
- package/template/.claude/skills/hallmark/references/components/ft5-statement.md +21 -21
- package/template/.claude/skills/hallmark/references/components/ft6-letter-close.md +19 -19
- package/template/.claude/skills/hallmark/references/components/ft7-newsletter-first.md +27 -27
- package/template/.claude/skills/hallmark/references/components/ft8-marquee-scroll.md +25 -25
- package/template/.claude/skills/hallmark/references/components/h1-marquee.md +15 -15
- package/template/.claude/skills/hallmark/references/components/h2-split-diptych.md +15 -15
- package/template/.claude/skills/hallmark/references/components/h3-quote-led.md +11 -11
- package/template/.claude/skills/hallmark/references/components/h4-stat-led.md +14 -14
- package/template/.claude/skills/hallmark/references/components/h5-letter-hero.md +11 -11
- package/template/.claude/skills/hallmark/references/components/h6-photographic-fold.md +16 -16
- package/template/.claude/skills/hallmark/references/components/h7-demo-video-clipped-by-viewport-edge.md +27 -27
- package/template/.claude/skills/hallmark/references/components/h8-mockup-split-browser-framed.md +23 -23
- package/template/.claude/skills/hallmark/references/components/h9-custom-illustration-centerpiece.md +27 -27
- package/template/.claude/skills/hallmark/references/components/n1-wordmark-2-links.md +12 -12
- package/template/.claude/skills/hallmark/references/components/n10-floating-on-scroll-morph.md +19 -19
- package/template/.claude/skills/hallmark/references/components/n2-floating-chip.md +14 -14
- package/template/.claude/skills/hallmark/references/components/n3-side-rail.md +14 -14
- package/template/.claude/skills/hallmark/references/components/n4-hidden-behind-k.md +9 -9
- package/template/.claude/skills/hallmark/references/components/n5-floating-pill.md +28 -28
- package/template/.claude/skills/hallmark/references/components/n6-newspaper-masthead.md +24 -24
- package/template/.claude/skills/hallmark/references/components/n7-brutal-slab.md +22 -22
- package/template/.claude/skills/hallmark/references/components/n8-terminal-command.md +21 -21
- package/template/.claude/skills/hallmark/references/components/n9-edge-aligned-minimal.md +17 -17
- package/template/.claude/skills/hallmark/references/components/s1-left-margin-numbered.md +15 -15
- package/template/.claude/skills/hallmark/references/components/s2-hanging.md +13 -13
- package/template/.claude/skills/hallmark/references/components/s3-sticky-pinned.md +19 -19
- package/template/.claude/skills/hallmark/references/components/s4-inline-no-break.md +11 -11
- package/template/.claude/skills/hallmark/references/components/s5-bottom-anchored.md +13 -13
- package/template/.claude/skills/hallmark/references/components/t1-pull-quote-with-marginalia.md +12 -12
- package/template/.claude/skills/hallmark/references/components/t2-logo-wall-hairline.md +19 -19
- package/template/.claude/skills/hallmark/references/components/t3-single-huge-quote.md +11 -11
- package/template/.claude/skills/hallmark/references/components/t4-numbered-stat-strip.md +14 -14
- package/template/.claude/skills/hallmark/references/contract.md +24 -24
- package/template/.claude/skills/hallmark/references/copy.md +182 -182
- package/template/.claude/skills/hallmark/references/custom-craft.md +626 -626
- package/template/.claude/skills/hallmark/references/custom-theme.md +329 -329
- package/template/.claude/skills/hallmark/references/design-md.md +116 -116
- package/template/.claude/skills/hallmark/references/export-formats.md +328 -328
- package/template/.claude/skills/hallmark/references/floating-nav.md +89 -89
- package/template/.claude/skills/hallmark/references/genres/atmospheric.md +65 -65
- package/template/.claude/skills/hallmark/references/genres/editorial.md +70 -70
- package/template/.claude/skills/hallmark/references/genres/modern-minimal.md +67 -67
- package/template/.claude/skills/hallmark/references/genres/playful.md +65 -65
- package/template/.claude/skills/hallmark/references/hero-enrichment.md +474 -474
- package/template/.claude/skills/hallmark/references/imagery-kit.md +170 -170
- package/template/.claude/skills/hallmark/references/interaction-and-states.md +207 -207
- package/template/.claude/skills/hallmark/references/layout-and-space.md +111 -111
- package/template/.claude/skills/hallmark/references/macrostructures/01-bento-grid.md +35 -35
- package/template/.claude/skills/hallmark/references/macrostructures/02-long-document.md +34 -34
- package/template/.claude/skills/hallmark/references/macrostructures/03-marquee-hero.md +31 -31
- package/template/.claude/skills/hallmark/references/macrostructures/04-stat-led.md +32 -32
- package/template/.claude/skills/hallmark/references/macrostructures/05-workbench.md +32 -32
- package/template/.claude/skills/hallmark/references/macrostructures/06-conversational-faq.md +33 -33
- package/template/.claude/skills/hallmark/references/macrostructures/07-manifesto.md +32 -32
- package/template/.claude/skills/hallmark/references/macrostructures/08-photographic.md +34 -34
- package/template/.claude/skills/hallmark/references/macrostructures/09-quote-led.md +32 -32
- package/template/.claude/skills/hallmark/references/macrostructures/10-specimen.md +32 -32
- package/template/.claude/skills/hallmark/references/macrostructures/11-catalogue.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/12-letter.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/13-index-first.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/14-narrative-workflow.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/15-split-studio.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/16-feature-stack.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/17-type-specimen.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/18-portfolio-grid.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/19-map-diagram.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/20-ecosystem-index.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures/21-component-playground.md +23 -23
- package/template/.claude/skills/hallmark/references/macrostructures.md +89 -89
- package/template/.claude/skills/hallmark/references/microinteractions.md +260 -260
- package/template/.claude/skills/hallmark/references/motion.md +109 -109
- package/template/.claude/skills/hallmark/references/preview-examples.md +49 -49
- package/template/.claude/skills/hallmark/references/responsive.md +138 -138
- package/template/.claude/skills/hallmark/references/slop-test.md +205 -205
- package/template/.claude/skills/hallmark/references/structure.md +164 -164
- package/template/.claude/skills/hallmark/references/study.md +511 -511
- package/template/.claude/skills/hallmark/references/typography.md +243 -243
- package/template/.claude/skills/hallmark/references/verbs/audit.md +25 -25
- package/template/.claude/skills/hallmark/references/verbs/redesign.md +269 -269
- package/template/.claude/skills/hallmark-loop/SKILL.md +105 -105
- package/template/.claude/skills/hallmark-loop/references/auditor-call.md +60 -60
- package/template/.claude/skills/hallmark-loop/references/capture.md +78 -78
- package/template/.claude/skills/hallmark-loop/references/loop-control.md +79 -79
- package/template/.claude/skills/handoff/SKILL.md +15 -15
- package/template/.claude/skills/knowledge-crunching/SKILL.md +94 -94
- package/template/.claude/skills/research/SKILL.md +69 -69
- package/template/.claude/skills/skill-creator/LICENSE.txt +201 -201
- package/template/.claude/skills/skill-creator/SKILL.md +154 -149
- package/template/.claude/skills/skill-creator/agents/analyzer.md +274 -274
- package/template/.claude/skills/skill-creator/agents/comparator.md +202 -202
- package/template/.claude/skills/skill-creator/agents/grader.md +223 -223
- package/template/.claude/skills/skill-creator/assets/eval_review.html +146 -146
- package/template/.claude/skills/skill-creator/eval-viewer/generate_review.py +471 -471
- package/template/.claude/skills/skill-creator/eval-viewer/viewer.html +1325 -1325
- package/template/.claude/skills/skill-creator/references/benchmark-optimization-guide.md +86 -86
- package/template/.claude/skills/skill-creator/references/distribution-guide.md +79 -79
- package/template/.claude/skills/skill-creator/references/eval-infrastructure-guide.md +129 -129
- package/template/.claude/skills/skill-creator/references/eval-schemas.md +121 -121
- package/template/.claude/skills/skill-creator/references/mcp-skills-integration.md +71 -71
- package/template/.claude/skills/skill-creator/references/metadata-quality-criteria.md +94 -94
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-hosting.md +104 -104
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-overview.md +89 -89
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-schema.md +93 -93
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-sources.md +103 -103
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-troubleshooting.md +76 -76
- package/template/.claude/skills/skill-creator/references/script-quality-criteria.md +106 -106
- package/template/.claude/skills/skill-creator/references/skill-anatomy-and-requirements.md +77 -77
- package/template/.claude/skills/skill-creator/references/skill-creation-workflow.md +152 -151
- package/template/.claude/skills/skill-creator/references/skill-design-patterns.md +75 -75
- package/template/.claude/skills/skill-creator/references/skillmark-benchmark-criteria.md +102 -102
- package/template/.claude/skills/skill-creator/references/structure-organization-criteria.md +114 -114
- package/template/.claude/skills/skill-creator/references/testing-and-iteration.md +78 -78
- package/template/.claude/skills/skill-creator/references/token-efficiency-criteria.md +74 -74
- package/template/.claude/skills/skill-creator/references/troubleshooting-guide.md +81 -81
- package/template/.claude/skills/skill-creator/references/validation-checklist.md +83 -83
- package/template/.claude/skills/skill-creator/references/writing-effective-instructions.md +88 -88
- package/template/.claude/skills/skill-creator/references/yaml-frontmatter-reference.md +92 -92
- package/template/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +401 -401
- package/template/.claude/skills/skill-creator/scripts/encoding_utils.py +36 -36
- package/template/.claude/skills/skill-creator/scripts/generate_report.py +326 -326
- package/template/.claude/skills/skill-creator/scripts/improve_description.py +248 -248
- package/template/.claude/skills/skill-creator/scripts/init_skill.py +360 -360
- package/template/.claude/skills/skill-creator/scripts/package_skill.py +143 -143
- package/template/.claude/skills/skill-creator/scripts/quick_validate.py +110 -110
- package/template/.claude/skills/skill-creator/scripts/run_eval.py +310 -310
- package/template/.claude/skills/skill-creator/scripts/run_loop.py +332 -332
- package/template/.claude/skills/skill-creator/scripts/utils.py +47 -47
- package/template/.claude/skills/tdd/SKILL.md +142 -142
- package/template/.claude/skills/tdd/deep-modules.md +15 -15
- package/template/.claude/skills/tdd/interface-design.md +31 -31
- package/template/.claude/skills/tdd/mocking.md +59 -59
- package/template/.claude/skills/tdd/refactoring.md +10 -10
- package/template/.claude/skills/tdd/tests.md +61 -61
- package/template/.claude/statusline.cjs +0 -0
- package/template/.claude/skills/code-review/references/adversarial-review.md +0 -223
|
@@ -1,191 +1,191 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Git Info Cache - Cross-platform git information batching
|
|
6
|
-
*
|
|
7
|
-
* Problem: 5-6 git process spawns per statusline render are slow on Windows (CreateProcess overhead)
|
|
8
|
-
* Solution: Cache git query results for 3 seconds — subsequent renders read cache (zero processes)
|
|
9
|
-
*
|
|
10
|
-
* Performance: 5 spawns per render → event-driven refresh + 30s TTL fallback
|
|
11
|
-
* Cross-platform: No bash-only syntax (no 2>/dev/null), windowsHide on all exec calls
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const { execSync } = require('child_process');
|
|
15
|
-
const fs = require('fs');
|
|
16
|
-
const path = require('path');
|
|
17
|
-
const os = require('os');
|
|
18
|
-
|
|
19
|
-
// Cache TTL — long fallback for external changes (git checkout outside Claude)
|
|
20
|
-
// Active invalidation happens via PostToolUse hooks after Edit/Write/Bash
|
|
21
|
-
const CACHE_TTL = 30000;
|
|
22
|
-
const CACHE_MISS = Symbol('cache_miss');
|
|
23
|
-
const CACHE_SKIP = Symbol('cache_skip');
|
|
24
|
-
|
|
25
|
-
function isTimeoutError(error) {
|
|
26
|
-
if (!error) return false;
|
|
27
|
-
if (error.killed) return true;
|
|
28
|
-
if (error.signal === 'SIGTERM') return true;
|
|
29
|
-
return /timed out|etimedout/i.test(String(error.message || ''));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function getExecTimeoutMs() {
|
|
33
|
-
const parsed = Number.parseInt(process.env.CK_GIT_TIMEOUT_MS || '', 10);
|
|
34
|
-
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
35
|
-
return 3000;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Safe command execution wrapper with optional cwd
|
|
40
|
-
* Timeout prevents hangs on slow/network-mounted repos
|
|
41
|
-
*/
|
|
42
|
-
function execIn(cmd, cwd) {
|
|
43
|
-
try {
|
|
44
|
-
return {
|
|
45
|
-
output: execSync(cmd, {
|
|
46
|
-
encoding: 'utf8',
|
|
47
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
48
|
-
windowsHide: true,
|
|
49
|
-
cwd: cwd || undefined,
|
|
50
|
-
timeout: getExecTimeoutMs()
|
|
51
|
-
}).trim(),
|
|
52
|
-
timedOut: false
|
|
53
|
-
};
|
|
54
|
-
} catch (error) {
|
|
55
|
-
return {
|
|
56
|
-
output: '',
|
|
57
|
-
timedOut: isTimeoutError(error)
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Get cache file path for current working directory
|
|
64
|
-
*/
|
|
65
|
-
function getCachePath(cwd) {
|
|
66
|
-
const hash = require('crypto')
|
|
67
|
-
.createHash('md5')
|
|
68
|
-
.update(cwd)
|
|
69
|
-
.digest('hex')
|
|
70
|
-
.slice(0, 8);
|
|
71
|
-
return path.join(os.tmpdir(), `ck-git-cache-${hash}.json`);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Read cache if valid (not expired). Returns CACHE_MISS on miss.
|
|
76
|
-
* No existsSync check (TOCTOU race) — just try read and catch.
|
|
77
|
-
*/
|
|
78
|
-
function readCache(cachePath, options = {}) {
|
|
79
|
-
const { allowStale = false } = options;
|
|
80
|
-
try {
|
|
81
|
-
const cache = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
|
|
82
|
-
if (Date.now() - cache.timestamp < CACHE_TTL || allowStale) {
|
|
83
|
-
return cache.data; // Can be null (non-git dir) or object (git info)
|
|
84
|
-
}
|
|
85
|
-
} catch {
|
|
86
|
-
// File missing, corrupted, or expired — all treated as cache miss
|
|
87
|
-
}
|
|
88
|
-
return CACHE_MISS;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Write cache atomically (temp file + rename to avoid partial reads on Windows)
|
|
93
|
-
*/
|
|
94
|
-
function writeCache(cachePath, data) {
|
|
95
|
-
const tmpPath = cachePath + '.tmp';
|
|
96
|
-
try {
|
|
97
|
-
fs.writeFileSync(tmpPath, JSON.stringify({ timestamp: Date.now(), data }));
|
|
98
|
-
fs.renameSync(tmpPath, cachePath);
|
|
99
|
-
} catch {
|
|
100
|
-
try { fs.unlinkSync(tmpPath); } catch {}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Count non-empty lines in a newline-delimited string
|
|
106
|
-
*/
|
|
107
|
-
function countLines(str) {
|
|
108
|
-
if (!str) return 0;
|
|
109
|
-
return str.split('\n').filter(l => l.trim()).length;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Fetch git info directly in-process
|
|
114
|
-
* The cache is what eliminates redundant spawns — not subprocess wrapping
|
|
115
|
-
* @param {string} cwd - Directory to run git commands in
|
|
116
|
-
* Returns: { branch, unstaged, staged, ahead, behind } or null if not git repo
|
|
117
|
-
*/
|
|
118
|
-
function fetchGitInfo(cwd) {
|
|
119
|
-
// Check if git repo (fast check) — run in target cwd, not process.cwd()
|
|
120
|
-
const repoCheck = execIn('git rev-parse --git-dir', cwd);
|
|
121
|
-
if (repoCheck.timedOut) return CACHE_SKIP;
|
|
122
|
-
if (!repoCheck.output) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const branchPrimary = execIn('git branch --show-current', cwd);
|
|
127
|
-
const branchFallback = execIn('git rev-parse --short HEAD', cwd);
|
|
128
|
-
const unstagedResult = execIn('git diff --name-only', cwd);
|
|
129
|
-
const stagedResult = execIn('git diff --cached --name-only', cwd);
|
|
130
|
-
const aheadBehindResult = execIn('git rev-list --left-right --count @{u}...HEAD', cwd);
|
|
131
|
-
|
|
132
|
-
if (
|
|
133
|
-
branchPrimary.timedOut ||
|
|
134
|
-
branchFallback.timedOut ||
|
|
135
|
-
unstagedResult.timedOut ||
|
|
136
|
-
stagedResult.timedOut ||
|
|
137
|
-
aheadBehindResult.timedOut
|
|
138
|
-
) {
|
|
139
|
-
return CACHE_SKIP;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const branch = branchPrimary.output || branchFallback.output;
|
|
143
|
-
const unstaged = countLines(unstagedResult.output);
|
|
144
|
-
const staged = countLines(stagedResult.output);
|
|
145
|
-
|
|
146
|
-
// Ahead/behind — no 2>/dev/null (invalid on Windows cmd.exe)
|
|
147
|
-
let ahead = 0;
|
|
148
|
-
let behind = 0;
|
|
149
|
-
if (aheadBehindResult.output) {
|
|
150
|
-
const parts = aheadBehindResult.output.split(/\s+/);
|
|
151
|
-
behind = parseInt(parts[0], 10) || 0;
|
|
152
|
-
ahead = parseInt(parts[1], 10) || 0;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return { branch, unstaged, staged, ahead, behind };
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Get git info with caching
|
|
160
|
-
* Main export function used by statusline
|
|
161
|
-
*/
|
|
162
|
-
function getGitInfo(cwd = process.cwd()) {
|
|
163
|
-
const cachePath = getCachePath(cwd);
|
|
164
|
-
|
|
165
|
-
// Try cache first (includes cached null for non-git dirs)
|
|
166
|
-
const cached = readCache(cachePath);
|
|
167
|
-
if (cached !== CACHE_MISS) return cached;
|
|
168
|
-
|
|
169
|
-
// Cache miss or expired, fetch fresh data in target cwd
|
|
170
|
-
const data = fetchGitInfo(cwd);
|
|
171
|
-
if (data === CACHE_SKIP) {
|
|
172
|
-
// Timeout/transient failures should not poison cache as non-git.
|
|
173
|
-
// Prefer last known value (even stale) if available.
|
|
174
|
-
const stale = readCache(cachePath, { allowStale: true });
|
|
175
|
-
return stale === CACHE_MISS ? null : stale;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Cache both positive and null results (avoids re-spawning git in non-git dirs)
|
|
179
|
-
writeCache(cachePath, data);
|
|
180
|
-
|
|
181
|
-
return data;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Invalidate cache for a directory (call after file changes to trigger fresh git query)
|
|
186
|
-
*/
|
|
187
|
-
function invalidateCache(cwd = process.cwd()) {
|
|
188
|
-
try { fs.unlinkSync(getCachePath(cwd)); } catch {}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
module.exports = { getGitInfo, invalidateCache };
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Git Info Cache - Cross-platform git information batching
|
|
6
|
+
*
|
|
7
|
+
* Problem: 5-6 git process spawns per statusline render are slow on Windows (CreateProcess overhead)
|
|
8
|
+
* Solution: Cache git query results for 3 seconds — subsequent renders read cache (zero processes)
|
|
9
|
+
*
|
|
10
|
+
* Performance: 5 spawns per render → event-driven refresh + 30s TTL fallback
|
|
11
|
+
* Cross-platform: No bash-only syntax (no 2>/dev/null), windowsHide on all exec calls
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { execSync } = require('child_process');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const os = require('os');
|
|
18
|
+
|
|
19
|
+
// Cache TTL — long fallback for external changes (git checkout outside Claude)
|
|
20
|
+
// Active invalidation happens via PostToolUse hooks after Edit/Write/Bash
|
|
21
|
+
const CACHE_TTL = 30000;
|
|
22
|
+
const CACHE_MISS = Symbol('cache_miss');
|
|
23
|
+
const CACHE_SKIP = Symbol('cache_skip');
|
|
24
|
+
|
|
25
|
+
function isTimeoutError(error) {
|
|
26
|
+
if (!error) return false;
|
|
27
|
+
if (error.killed) return true;
|
|
28
|
+
if (error.signal === 'SIGTERM') return true;
|
|
29
|
+
return /timed out|etimedout/i.test(String(error.message || ''));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getExecTimeoutMs() {
|
|
33
|
+
const parsed = Number.parseInt(process.env.CK_GIT_TIMEOUT_MS || '', 10);
|
|
34
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
35
|
+
return 3000;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Safe command execution wrapper with optional cwd
|
|
40
|
+
* Timeout prevents hangs on slow/network-mounted repos
|
|
41
|
+
*/
|
|
42
|
+
function execIn(cmd, cwd) {
|
|
43
|
+
try {
|
|
44
|
+
return {
|
|
45
|
+
output: execSync(cmd, {
|
|
46
|
+
encoding: 'utf8',
|
|
47
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
48
|
+
windowsHide: true,
|
|
49
|
+
cwd: cwd || undefined,
|
|
50
|
+
timeout: getExecTimeoutMs()
|
|
51
|
+
}).trim(),
|
|
52
|
+
timedOut: false
|
|
53
|
+
};
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return {
|
|
56
|
+
output: '',
|
|
57
|
+
timedOut: isTimeoutError(error)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get cache file path for current working directory
|
|
64
|
+
*/
|
|
65
|
+
function getCachePath(cwd) {
|
|
66
|
+
const hash = require('crypto')
|
|
67
|
+
.createHash('md5')
|
|
68
|
+
.update(cwd)
|
|
69
|
+
.digest('hex')
|
|
70
|
+
.slice(0, 8);
|
|
71
|
+
return path.join(os.tmpdir(), `ck-git-cache-${hash}.json`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Read cache if valid (not expired). Returns CACHE_MISS on miss.
|
|
76
|
+
* No existsSync check (TOCTOU race) — just try read and catch.
|
|
77
|
+
*/
|
|
78
|
+
function readCache(cachePath, options = {}) {
|
|
79
|
+
const { allowStale = false } = options;
|
|
80
|
+
try {
|
|
81
|
+
const cache = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
|
|
82
|
+
if (Date.now() - cache.timestamp < CACHE_TTL || allowStale) {
|
|
83
|
+
return cache.data; // Can be null (non-git dir) or object (git info)
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
// File missing, corrupted, or expired — all treated as cache miss
|
|
87
|
+
}
|
|
88
|
+
return CACHE_MISS;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Write cache atomically (temp file + rename to avoid partial reads on Windows)
|
|
93
|
+
*/
|
|
94
|
+
function writeCache(cachePath, data) {
|
|
95
|
+
const tmpPath = cachePath + '.tmp';
|
|
96
|
+
try {
|
|
97
|
+
fs.writeFileSync(tmpPath, JSON.stringify({ timestamp: Date.now(), data }));
|
|
98
|
+
fs.renameSync(tmpPath, cachePath);
|
|
99
|
+
} catch {
|
|
100
|
+
try { fs.unlinkSync(tmpPath); } catch {}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Count non-empty lines in a newline-delimited string
|
|
106
|
+
*/
|
|
107
|
+
function countLines(str) {
|
|
108
|
+
if (!str) return 0;
|
|
109
|
+
return str.split('\n').filter(l => l.trim()).length;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Fetch git info directly in-process
|
|
114
|
+
* The cache is what eliminates redundant spawns — not subprocess wrapping
|
|
115
|
+
* @param {string} cwd - Directory to run git commands in
|
|
116
|
+
* Returns: { branch, unstaged, staged, ahead, behind } or null if not git repo
|
|
117
|
+
*/
|
|
118
|
+
function fetchGitInfo(cwd) {
|
|
119
|
+
// Check if git repo (fast check) — run in target cwd, not process.cwd()
|
|
120
|
+
const repoCheck = execIn('git rev-parse --git-dir', cwd);
|
|
121
|
+
if (repoCheck.timedOut) return CACHE_SKIP;
|
|
122
|
+
if (!repoCheck.output) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const branchPrimary = execIn('git branch --show-current', cwd);
|
|
127
|
+
const branchFallback = execIn('git rev-parse --short HEAD', cwd);
|
|
128
|
+
const unstagedResult = execIn('git diff --name-only', cwd);
|
|
129
|
+
const stagedResult = execIn('git diff --cached --name-only', cwd);
|
|
130
|
+
const aheadBehindResult = execIn('git rev-list --left-right --count @{u}...HEAD', cwd);
|
|
131
|
+
|
|
132
|
+
if (
|
|
133
|
+
branchPrimary.timedOut ||
|
|
134
|
+
branchFallback.timedOut ||
|
|
135
|
+
unstagedResult.timedOut ||
|
|
136
|
+
stagedResult.timedOut ||
|
|
137
|
+
aheadBehindResult.timedOut
|
|
138
|
+
) {
|
|
139
|
+
return CACHE_SKIP;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const branch = branchPrimary.output || branchFallback.output;
|
|
143
|
+
const unstaged = countLines(unstagedResult.output);
|
|
144
|
+
const staged = countLines(stagedResult.output);
|
|
145
|
+
|
|
146
|
+
// Ahead/behind — no 2>/dev/null (invalid on Windows cmd.exe)
|
|
147
|
+
let ahead = 0;
|
|
148
|
+
let behind = 0;
|
|
149
|
+
if (aheadBehindResult.output) {
|
|
150
|
+
const parts = aheadBehindResult.output.split(/\s+/);
|
|
151
|
+
behind = parseInt(parts[0], 10) || 0;
|
|
152
|
+
ahead = parseInt(parts[1], 10) || 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return { branch, unstaged, staged, ahead, behind };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get git info with caching
|
|
160
|
+
* Main export function used by statusline
|
|
161
|
+
*/
|
|
162
|
+
function getGitInfo(cwd = process.cwd()) {
|
|
163
|
+
const cachePath = getCachePath(cwd);
|
|
164
|
+
|
|
165
|
+
// Try cache first (includes cached null for non-git dirs)
|
|
166
|
+
const cached = readCache(cachePath);
|
|
167
|
+
if (cached !== CACHE_MISS) return cached;
|
|
168
|
+
|
|
169
|
+
// Cache miss or expired, fetch fresh data in target cwd
|
|
170
|
+
const data = fetchGitInfo(cwd);
|
|
171
|
+
if (data === CACHE_SKIP) {
|
|
172
|
+
// Timeout/transient failures should not poison cache as non-git.
|
|
173
|
+
// Prefer last known value (even stale) if available.
|
|
174
|
+
const stale = readCache(cachePath, { allowStale: true });
|
|
175
|
+
return stale === CACHE_MISS ? null : stale;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Cache both positive and null results (avoids re-spawning git in non-git dirs)
|
|
179
|
+
writeCache(cachePath, data);
|
|
180
|
+
|
|
181
|
+
return data;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Invalidate cache for a directory (call after file changes to trigger fresh git query)
|
|
186
|
+
*/
|
|
187
|
+
function invalidateCache(cwd = process.cwd()) {
|
|
188
|
+
try { fs.unlinkSync(getCachePath(cwd)); } catch {}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
module.exports = { getGitInfo, invalidateCache };
|