@grainulation/wheat 1.0.2 → 1.0.3
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/compiler/detect-sprints.js +22 -30
- package/compiler/wheat-compiler.js +50 -21
- package/package.json +1 -1
|
@@ -49,7 +49,7 @@ function loadJSON(filePath) {
|
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Batch git queries for all sprint files at once.
|
|
52
|
-
*
|
|
52
|
+
* One git call total instead of 2 per sprint (30x+ faster for 16 sprints).
|
|
53
53
|
* Returns Map<filePath, { date: string|null, count: number }>
|
|
54
54
|
*/
|
|
55
55
|
let _gitCache = null;
|
|
@@ -69,44 +69,36 @@ function batchGitInfo(filePaths) {
|
|
|
69
69
|
|
|
70
70
|
if (filePaths.length === 0) { _gitCache = info; return info; }
|
|
71
71
|
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
// First occurrence of each file
|
|
72
|
+
// Single git call: get dates AND counts from one log traversal.
|
|
73
|
+
// Format: "COMMIT <date>" header per commit, then --name-only lists files.
|
|
74
|
+
// First occurrence of each file gives its last-commit date.
|
|
75
|
+
// Total occurrences per file gives its commit count.
|
|
75
76
|
try {
|
|
76
77
|
const result = execFileSync('git', [
|
|
77
|
-
'log', '--format
|
|
78
|
+
'log', '--format=COMMIT %aI', '--name-only',
|
|
78
79
|
'--', ...relPaths
|
|
79
80
|
], { cwd: ROOT, timeout: 10000, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
80
|
-
const lines = result.toString().
|
|
81
|
-
const
|
|
81
|
+
const lines = result.toString().split('\n');
|
|
82
|
+
const seenForDate = new Set();
|
|
82
83
|
let currentDate = null;
|
|
83
84
|
for (const line of lines) {
|
|
84
85
|
const trimmed = line.trim();
|
|
85
|
-
if (!trimmed) continue;
|
|
86
|
-
if (
|
|
87
|
-
currentDate = trimmed;
|
|
88
|
-
} else
|
|
89
|
-
seen.add(trimmed);
|
|
86
|
+
if (!trimmed) continue;
|
|
87
|
+
if (trimmed.startsWith('COMMIT ')) {
|
|
88
|
+
currentDate = trimmed.slice(7);
|
|
89
|
+
} else {
|
|
90
90
|
const orig = relToOrig.get(trimmed);
|
|
91
|
-
if (orig)
|
|
91
|
+
if (orig) {
|
|
92
|
+
const entry = info.get(orig);
|
|
93
|
+
entry.count++;
|
|
94
|
+
if (!seenForDate.has(trimmed)) {
|
|
95
|
+
seenForDate.add(trimmed);
|
|
96
|
+
entry.date = currentDate;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
92
99
|
}
|
|
93
100
|
}
|
|
94
|
-
} catch { /* git unavailable, dates stay null */ }
|
|
95
|
-
|
|
96
|
-
// Batch 2: commit counts per file (count filename occurrences in log)
|
|
97
|
-
try {
|
|
98
|
-
const result = execFileSync('git', [
|
|
99
|
-
'log', '--format=', '--name-only',
|
|
100
|
-
'--', ...relPaths
|
|
101
|
-
], { cwd: ROOT, timeout: 10000, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
102
|
-
const lines = result.toString().split('\n');
|
|
103
|
-
for (const line of lines) {
|
|
104
|
-
const trimmed = line.trim();
|
|
105
|
-
if (!trimmed) continue;
|
|
106
|
-
const orig = relToOrig.get(trimmed);
|
|
107
|
-
if (orig) info.get(orig).count++;
|
|
108
|
-
}
|
|
109
|
-
} catch { /* counts stay 0 */ }
|
|
101
|
+
} catch { /* git unavailable, dates stay null, counts stay 0 */ }
|
|
110
102
|
|
|
111
103
|
_gitCache = info;
|
|
112
104
|
return info;
|
|
@@ -274,7 +266,7 @@ export function detectSprints(rootDir) {
|
|
|
274
266
|
resetGitCache();
|
|
275
267
|
const roots = findSprintRoots();
|
|
276
268
|
|
|
277
|
-
// Batch all git queries upfront:
|
|
269
|
+
// Batch all git queries upfront: 1 git call instead of 2 per sprint
|
|
278
270
|
batchGitInfo(roots.map(r => r.claimsPath));
|
|
279
271
|
|
|
280
272
|
const sprints = roots.map(analyzeSprint).filter(Boolean);
|
|
@@ -597,7 +597,7 @@ function generateManifest(compilation, dir, sprintsInfo) {
|
|
|
597
597
|
* @param {string|null} outputPath - Path to write compilation.json (null = default from config)
|
|
598
598
|
* @returns {object} The compiled output object
|
|
599
599
|
*/
|
|
600
|
-
function compile(inputPath, outputPath, dir) {
|
|
600
|
+
function compile(inputPath, outputPath, dir, opts = {}) {
|
|
601
601
|
const compilerVersion = '0.2.0';
|
|
602
602
|
const baseDir = dir || TARGET_DIR;
|
|
603
603
|
const claimsPath = inputPath || path.join(baseDir, config.compiler.claims);
|
|
@@ -661,25 +661,28 @@ function compile(inputPath, outputPath, dir) {
|
|
|
661
661
|
|
|
662
662
|
// ── Sprint detection (git-based, non-fatal) ──────────────────────────────
|
|
663
663
|
let sprintsInfo = { active: null, sprints: [] };
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
664
|
+
let sprintSummaries = [];
|
|
665
|
+
if (!opts.skipSprintDetection) {
|
|
666
|
+
try {
|
|
667
|
+
sprintsInfo = detectSprints(baseDir);
|
|
668
|
+
} catch (err) {
|
|
669
|
+
// Non-fatal: sprint detection failure should not block compilation
|
|
670
|
+
console.error(`Warning: sprint detection failed — ${err.message}`);
|
|
671
|
+
}
|
|
670
672
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
673
|
+
// Build sprint summaries: active sprint gets full compilation, others get summary entries
|
|
674
|
+
sprintSummaries = sprintsInfo.sprints.map(s => ({
|
|
675
|
+
name: s.name,
|
|
676
|
+
path: s.path,
|
|
677
|
+
status: s.status,
|
|
678
|
+
phase: s.phase,
|
|
679
|
+
question: s.question,
|
|
680
|
+
claims_count: s.claims_count,
|
|
681
|
+
active_claims: s.active_claims,
|
|
682
|
+
last_git_activity: s.last_git_activity,
|
|
683
|
+
git_commit_count: s.git_commit_count,
|
|
684
|
+
}));
|
|
685
|
+
}
|
|
683
686
|
|
|
684
687
|
const compilation = {
|
|
685
688
|
compiled_at: new Date().toISOString(), // Non-deterministic metadata (excluded from certificate)
|
|
@@ -718,7 +721,9 @@ function compile(inputPath, outputPath, dir) {
|
|
|
718
721
|
|
|
719
722
|
// Generate topic-map manifest (wheat-manifest.json)
|
|
720
723
|
// Pass sprintsInfo to avoid re-running detectSprints in manifest generator
|
|
721
|
-
|
|
724
|
+
if (!opts.skipSprintDetection) {
|
|
725
|
+
generateManifest(compilation, baseDir, sprintsInfo);
|
|
726
|
+
}
|
|
722
727
|
|
|
723
728
|
return compilation;
|
|
724
729
|
}
|
|
@@ -784,6 +789,7 @@ Usage:
|
|
|
784
789
|
|
|
785
790
|
Options:
|
|
786
791
|
--dir <path> Resolve all paths relative to <path> instead of script location
|
|
792
|
+
--quiet, -q One-liner output (for scripts and AI agents)
|
|
787
793
|
--help, -h Show this help message
|
|
788
794
|
--json Output as JSON (works with --summary, --check, --gate, --scan, --next)`);
|
|
789
795
|
process.exit(0);
|
|
@@ -860,8 +866,31 @@ if (outputIdx !== -1 && args[outputIdx + 1]) {
|
|
|
860
866
|
outputPath = path.resolve(args[outputIdx + 1]);
|
|
861
867
|
}
|
|
862
868
|
|
|
863
|
-
const compilation = compile(inputPath, outputPath);
|
|
864
869
|
const jsonFlag = args.includes('--json');
|
|
870
|
+
const quietFlag = args.includes('--quiet') || args.includes('-q');
|
|
871
|
+
const compilation = compile(inputPath, outputPath, undefined, { skipSprintDetection: quietFlag && !args.includes('--summary') });
|
|
872
|
+
|
|
873
|
+
// --quiet / -q: one-liner output for scripts and AI agents (~13 tokens vs ~4,600)
|
|
874
|
+
if (quietFlag && !args.includes('--summary')) {
|
|
875
|
+
const c = compilation;
|
|
876
|
+
const conflicts = c.sprint_meta.conflicted_claims || 0;
|
|
877
|
+
const suffix = conflicts > 0 ? ` (${conflicts} conflicts)` : '';
|
|
878
|
+
const line = `wheat: compiled ${c.sprint_meta.total_claims} claims, ${Object.keys(c.coverage).length} topics${suffix}`;
|
|
879
|
+
if (jsonFlag) {
|
|
880
|
+
console.log(JSON.stringify({
|
|
881
|
+
status: c.status,
|
|
882
|
+
claims: c.sprint_meta.total_claims,
|
|
883
|
+
active: c.sprint_meta.active_claims,
|
|
884
|
+
conflicts,
|
|
885
|
+
topics: Object.keys(c.coverage).length,
|
|
886
|
+
errors: c.errors.length,
|
|
887
|
+
warnings: c.warnings.length,
|
|
888
|
+
}));
|
|
889
|
+
} else {
|
|
890
|
+
console.log(line);
|
|
891
|
+
}
|
|
892
|
+
process.exit(c.status === 'blocked' ? 1 : 0);
|
|
893
|
+
}
|
|
865
894
|
|
|
866
895
|
if (args.includes('--summary')) {
|
|
867
896
|
const c = compilation;
|
package/package.json
CHANGED