@codluv/versionguard 0.4.0 → 0.5.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/dist/calver.d.ts +1 -0
- package/dist/calver.d.ts.map +1 -1
- package/dist/changelog.d.ts +52 -0
- package/dist/changelog.d.ts.map +1 -1
- package/dist/chunks/{index-B3R60bYJ.js → index-CwOyEn5L.js} +467 -39
- package/dist/chunks/index-CwOyEn5L.js.map +1 -0
- package/dist/ckm/engine.d.ts +92 -0
- package/dist/ckm/engine.d.ts.map +1 -0
- package/dist/ckm/index.d.ts +32 -0
- package/dist/ckm/index.d.ts.map +1 -0
- package/dist/ckm/types.d.ts +168 -0
- package/dist/ckm/types.d.ts.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1090 -7
- package/dist/cli.js.map +1 -1
- package/dist/fix/index.d.ts.map +1 -1
- package/dist/hooks.d.ts +14 -7
- package/dist/hooks.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -33
- package/dist/init-wizard.d.ts +19 -0
- package/dist/init-wizard.d.ts.map +1 -1
- package/dist/project-root.d.ts +74 -0
- package/dist/project-root.d.ts.map +1 -0
- package/package.json +2 -2
- package/dist/chunks/index-B3R60bYJ.js.map +0 -1
package/dist/calver.d.ts
CHANGED
|
@@ -154,6 +154,7 @@ export declare function parse(version: string, calverFormat: CalVerFormat): CalV
|
|
|
154
154
|
* @param version - Version string to validate.
|
|
155
155
|
* @param calverFormat - Format expected for the version string.
|
|
156
156
|
* @param preventFutureDates - Whether future dates should be reported as errors.
|
|
157
|
+
* @param schemeRules - Optional scheme rules for modifier validation and segment count warnings.
|
|
157
158
|
* @returns A validation result containing any discovered errors and the parsed version on success.
|
|
158
159
|
*
|
|
159
160
|
* @example
|
package/dist/calver.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calver.d.ts","sourceRoot":"","sources":["../src/calver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EAEZ,WAAW,EAEX,gBAAgB,EACjB,MAAM,SAAS,CAAC;AA8BjB;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,IAAI,YAAY,CAoBhF;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAE3B;;;;OAIG;IACH,KAAK,CAAC,EAAE,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC;IAE1B;;;;OAIG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAEnB;;;;OAIG;IACH,GAAG,CAAC,EAAE,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC;IAExB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,WAAW,CAAC,YAAY,EAAE,YAAY,GAAG,kBAAkB,CAoB1E;AAyCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM,CAKpE;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAqDhF;AAED
|
|
1
|
+
{"version":3,"file":"calver.d.ts","sourceRoot":"","sources":["../src/calver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EAEZ,WAAW,EAEX,gBAAgB,EACjB,MAAM,SAAS,CAAC;AA8BjB;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,IAAI,YAAY,CAoBhF;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAE3B;;;;OAIG;IACH,KAAK,CAAC,EAAE,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC;IAE1B;;;;OAIG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAEnB;;;;OAIG;IACH,GAAG,CAAC,EAAE,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC;IAExB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,WAAW,CAAC,YAAY,EAAE,YAAY,GAAG,kBAAkB,CAoB1E;AAyCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM,CAKpE;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAqDhF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,kBAAkB,GAAE,OAAc,EAClC,WAAW,CAAC,EAAE,WAAW,GACxB,gBAAgB,CA6GlB;AAgBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAsB9C;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,YAAY,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAY5F;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,GAAG,MAAM,CAiBhF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,GAAG,MAAM,CAqB7E;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,GAAG,MAAM,EAAE,CAE5F"}
|
package/dist/changelog.d.ts
CHANGED
|
@@ -80,4 +80,56 @@ export declare function getLatestVersion(changelogPath: string): string | null;
|
|
|
80
80
|
* ```
|
|
81
81
|
*/
|
|
82
82
|
export declare function addVersionEntry(changelogPath: string, version: string, date?: string): void;
|
|
83
|
+
/**
|
|
84
|
+
* Detects whether a changelog has been mangled by Changesets.
|
|
85
|
+
*
|
|
86
|
+
* @remarks
|
|
87
|
+
* Changesets prepends version content above the Keep a Changelog preamble,
|
|
88
|
+
* producing `## 0.4.0` (no brackets, no date) before the "All notable changes"
|
|
89
|
+
* paragraph. This function detects that pattern.
|
|
90
|
+
*
|
|
91
|
+
* @param changelogPath - Path to the changelog file.
|
|
92
|
+
* @returns `true` when the changelog appears to be mangled by Changesets.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* import { isChangesetMangled } from 'versionguard';
|
|
97
|
+
*
|
|
98
|
+
* if (isChangesetMangled('CHANGELOG.md')) {
|
|
99
|
+
* fixChangesetMangling('CHANGELOG.md');
|
|
100
|
+
* }
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* @public
|
|
104
|
+
* @since 0.4.0
|
|
105
|
+
*/
|
|
106
|
+
export declare function isChangesetMangled(changelogPath: string): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Fixes a Changesets-mangled changelog into proper Keep a Changelog format.
|
|
109
|
+
*
|
|
110
|
+
* @remarks
|
|
111
|
+
* This function:
|
|
112
|
+
* 1. Extracts the version number and content prepended by Changesets
|
|
113
|
+
* 2. Converts Changesets section names (Minor Changes, Patch Changes) to
|
|
114
|
+
* Keep a Changelog names (Added, Fixed)
|
|
115
|
+
* 3. Strips commit hashes from entry lines
|
|
116
|
+
* 4. Adds the date and brackets to the version header
|
|
117
|
+
* 5. Inserts the entry after `## [Unreleased]` in the correct position
|
|
118
|
+
* 6. Restores the preamble to its proper location
|
|
119
|
+
*
|
|
120
|
+
* @param changelogPath - Path to the changelog file to fix.
|
|
121
|
+
* @param date - Release date in `YYYY-MM-DD` format.
|
|
122
|
+
* @returns `true` when the file was modified, `false` when no fix was needed.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* import { fixChangesetMangling } from 'versionguard';
|
|
127
|
+
*
|
|
128
|
+
* const fixed = fixChangesetMangling('CHANGELOG.md');
|
|
129
|
+
* ```
|
|
130
|
+
*
|
|
131
|
+
* @public
|
|
132
|
+
* @since 0.4.0
|
|
133
|
+
*/
|
|
134
|
+
export declare function fixChangesetMangling(changelogPath: string, date?: string): boolean;
|
|
83
135
|
//# sourceMappingURL=changelog.d.ts.map
|
package/dist/changelog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../src/changelog.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB;IACxC;;OAEG;IACH,KAAK,EAAE,OAAO,CAAC;IACf;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;OAEG;IACH,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAID;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,OAAc,EACtB,YAAY,GAAE,OAAc,GAC3B,yBAAyB,CAgD3B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQrE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,MAA8C,GACnD,IAAI,CAmBN"}
|
|
1
|
+
{"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../src/changelog.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB;IACxC;;OAEG;IACH,KAAK,EAAE,OAAO,CAAC;IACf;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;OAEG;IACH,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAID;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,OAAc,EACtB,YAAY,GAAE,OAAc,GAC3B,yBAAyB,CAgD3B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQrE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,MAA8C,GACnD,IAAI,CAmBN;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAMjE;AASD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,MAAM,EACrB,IAAI,GAAE,MAA8C,GACnD,OAAO,CAoET"}
|
|
@@ -381,10 +381,109 @@ function addVersionEntry(changelogPath, version, date = (/* @__PURE__ */ new Dat
|
|
|
381
381
|
const updated = `${content.slice(0, insertIndex)}${block}${content.slice(insertIndex)}`;
|
|
382
382
|
fs.writeFileSync(changelogPath, updated, "utf-8");
|
|
383
383
|
}
|
|
384
|
+
function isChangesetMangled(changelogPath) {
|
|
385
|
+
if (!fs.existsSync(changelogPath)) return false;
|
|
386
|
+
const content = fs.readFileSync(changelogPath, "utf-8");
|
|
387
|
+
return /^## \d+\.\d+/m.test(content) && content.includes("## [Unreleased]");
|
|
388
|
+
}
|
|
389
|
+
const SECTION_MAP = {
|
|
390
|
+
"Major Changes": "Changed",
|
|
391
|
+
"Minor Changes": "Added",
|
|
392
|
+
"Patch Changes": "Fixed"
|
|
393
|
+
};
|
|
394
|
+
function fixChangesetMangling(changelogPath, date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)) {
|
|
395
|
+
if (!fs.existsSync(changelogPath)) return false;
|
|
396
|
+
const content = fs.readFileSync(changelogPath, "utf-8");
|
|
397
|
+
const versionMatch = content.match(/^## (\d+\.\d+\.\d+[^\n]*)\n/m);
|
|
398
|
+
if (!versionMatch || versionMatch.index === void 0) return false;
|
|
399
|
+
const fullHeader = versionMatch[0];
|
|
400
|
+
if (fullHeader.includes("[")) return false;
|
|
401
|
+
const version = versionMatch[1].trim();
|
|
402
|
+
if (content.includes(`## [${version}]`)) return false;
|
|
403
|
+
const startIndex = versionMatch.index;
|
|
404
|
+
const preambleMatch = content.indexOf("All notable changes", startIndex);
|
|
405
|
+
const unreleasedMatch = content.indexOf("## [Unreleased]", startIndex);
|
|
406
|
+
let endIndex;
|
|
407
|
+
if (preambleMatch !== -1 && preambleMatch < unreleasedMatch) {
|
|
408
|
+
endIndex = preambleMatch;
|
|
409
|
+
} else if (unreleasedMatch !== -1) {
|
|
410
|
+
endIndex = unreleasedMatch;
|
|
411
|
+
} else {
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
const changesetsBlock = content.slice(startIndex + fullHeader.length, endIndex).trim();
|
|
415
|
+
const transformedSections = transformChangesetsContent(changesetsBlock);
|
|
416
|
+
const newEntry = `## [${version}] - ${date}
|
|
417
|
+
|
|
418
|
+
${transformedSections}
|
|
419
|
+
|
|
420
|
+
`;
|
|
421
|
+
const beforeChangesets = content.slice(0, startIndex);
|
|
422
|
+
const afterChangesets = content.slice(endIndex);
|
|
423
|
+
const unreleasedInAfter = afterChangesets.indexOf("## [Unreleased]");
|
|
424
|
+
if (unreleasedInAfter === -1) {
|
|
425
|
+
const rebuilt2 = `${beforeChangesets}${newEntry}${afterChangesets}`;
|
|
426
|
+
fs.writeFileSync(changelogPath, rebuilt2, "utf-8");
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
const unreleasedLineEnd = afterChangesets.indexOf("\n", unreleasedInAfter);
|
|
430
|
+
const afterUnreleased = unreleasedLineEnd !== -1 ? afterChangesets.slice(0, unreleasedLineEnd + 1) : afterChangesets;
|
|
431
|
+
const rest = unreleasedLineEnd !== -1 ? afterChangesets.slice(unreleasedLineEnd + 1) : "";
|
|
432
|
+
const rebuilt = `${beforeChangesets}${afterUnreleased}
|
|
433
|
+
${newEntry}${rest}`;
|
|
434
|
+
const withLinks = updateCompareLinks(rebuilt, version);
|
|
435
|
+
fs.writeFileSync(changelogPath, withLinks, "utf-8");
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
function transformChangesetsContent(block) {
|
|
439
|
+
const lines = block.split("\n");
|
|
440
|
+
const result = [];
|
|
441
|
+
for (const line of lines) {
|
|
442
|
+
const sectionMatch = line.match(/^### (.+)/);
|
|
443
|
+
if (sectionMatch) {
|
|
444
|
+
const mapped = SECTION_MAP[sectionMatch[1]] ?? sectionMatch[1];
|
|
445
|
+
result.push(`### ${mapped}`);
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
const entryMatch = line.match(
|
|
449
|
+
/^(\s*-\s+)[a-f0-9]{7,}: (?:feat|fix|chore|docs|refactor|perf|test|ci|build|style)(?:\([^)]*\))?: (.+)/
|
|
450
|
+
);
|
|
451
|
+
if (entryMatch) {
|
|
452
|
+
result.push(`${entryMatch[1]}${entryMatch[2]}`);
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
const simpleHashMatch = line.match(/^(\s*-\s+)[a-f0-9]{7,}: (.+)/);
|
|
456
|
+
if (simpleHashMatch) {
|
|
457
|
+
result.push(`${simpleHashMatch[1]}${simpleHashMatch[2]}`);
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
result.push(line);
|
|
461
|
+
}
|
|
462
|
+
return result.join("\n");
|
|
463
|
+
}
|
|
464
|
+
function updateCompareLinks(content, version) {
|
|
465
|
+
const unreleasedLinkRegex = /\[Unreleased\]: (https:\/\/[^\s]+\/compare\/v)([\d.]+)(\.\.\.HEAD)/;
|
|
466
|
+
const match = content.match(unreleasedLinkRegex);
|
|
467
|
+
if (match) {
|
|
468
|
+
const baseUrl = match[1].replace(/v$/, "");
|
|
469
|
+
const previousVersion = match[2];
|
|
470
|
+
const newUnreleasedLink = `[Unreleased]: ${baseUrl}v${version}...HEAD`;
|
|
471
|
+
const newVersionLink = `[${version}]: ${baseUrl}v${previousVersion}...v${version}`;
|
|
472
|
+
let updated = content.replace(unreleasedLinkRegex, newUnreleasedLink);
|
|
473
|
+
if (!updated.includes(`[${version}]:`)) {
|
|
474
|
+
updated = updated.replace(newUnreleasedLink, `${newUnreleasedLink}
|
|
475
|
+
${newVersionLink}`);
|
|
476
|
+
}
|
|
477
|
+
return updated;
|
|
478
|
+
}
|
|
479
|
+
return content;
|
|
480
|
+
}
|
|
384
481
|
function escapeRegExp$1(value) {
|
|
385
482
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
386
483
|
}
|
|
387
484
|
const HOOK_NAMES$1 = ["pre-commit", "pre-push", "post-tag"];
|
|
485
|
+
const VG_BLOCK_START = "# >>> versionguard >>>";
|
|
486
|
+
const VG_BLOCK_END = "# <<< versionguard <<<";
|
|
388
487
|
function installHooks(config, cwd = process.cwd()) {
|
|
389
488
|
const gitDir = findGitDir(cwd);
|
|
390
489
|
if (!gitDir) {
|
|
@@ -395,7 +494,36 @@ function installHooks(config, cwd = process.cwd()) {
|
|
|
395
494
|
for (const hookName of HOOK_NAMES$1) {
|
|
396
495
|
if (config.hooks[hookName]) {
|
|
397
496
|
const hookPath = path.join(hooksDir, hookName);
|
|
398
|
-
|
|
497
|
+
const vgBlock = generateHookBlock(hookName);
|
|
498
|
+
if (fs.existsSync(hookPath)) {
|
|
499
|
+
const existing = fs.readFileSync(hookPath, "utf-8");
|
|
500
|
+
if (existing.includes(VG_BLOCK_START)) {
|
|
501
|
+
const updated = replaceVgBlock(existing, vgBlock);
|
|
502
|
+
fs.writeFileSync(hookPath, updated, { encoding: "utf-8", mode: 493 });
|
|
503
|
+
} else if (isLegacyVgHook(existing)) {
|
|
504
|
+
fs.writeFileSync(hookPath, `#!/bin/sh
|
|
505
|
+
|
|
506
|
+
${vgBlock}
|
|
507
|
+
`, {
|
|
508
|
+
encoding: "utf-8",
|
|
509
|
+
mode: 493
|
|
510
|
+
});
|
|
511
|
+
} else {
|
|
512
|
+
const appended = `${existing.trimEnd()}
|
|
513
|
+
|
|
514
|
+
${vgBlock}
|
|
515
|
+
`;
|
|
516
|
+
fs.writeFileSync(hookPath, appended, { encoding: "utf-8", mode: 493 });
|
|
517
|
+
}
|
|
518
|
+
} else {
|
|
519
|
+
fs.writeFileSync(hookPath, `#!/bin/sh
|
|
520
|
+
|
|
521
|
+
${vgBlock}
|
|
522
|
+
`, {
|
|
523
|
+
encoding: "utf-8",
|
|
524
|
+
mode: 493
|
|
525
|
+
});
|
|
526
|
+
}
|
|
399
527
|
}
|
|
400
528
|
}
|
|
401
529
|
}
|
|
@@ -407,7 +535,21 @@ function uninstallHooks(cwd = process.cwd()) {
|
|
|
407
535
|
const hooksDir = path.join(gitDir, "hooks");
|
|
408
536
|
for (const hookName of HOOK_NAMES$1) {
|
|
409
537
|
const hookPath = path.join(hooksDir, hookName);
|
|
410
|
-
if (fs.existsSync(hookPath)
|
|
538
|
+
if (!fs.existsSync(hookPath)) continue;
|
|
539
|
+
const content = fs.readFileSync(hookPath, "utf-8");
|
|
540
|
+
if (!content.includes("versionguard")) continue;
|
|
541
|
+
if (content.includes(VG_BLOCK_START)) {
|
|
542
|
+
const cleaned = removeVgBlock(content);
|
|
543
|
+
const trimmed = cleaned.trim();
|
|
544
|
+
if (!trimmed || trimmed === "#!/bin/sh") {
|
|
545
|
+
fs.unlinkSync(hookPath);
|
|
546
|
+
} else if (isLegacyVgHook(trimmed)) {
|
|
547
|
+
fs.unlinkSync(hookPath);
|
|
548
|
+
} else {
|
|
549
|
+
fs.writeFileSync(hookPath, `${trimmed}
|
|
550
|
+
`, { encoding: "utf-8", mode: 493 });
|
|
551
|
+
}
|
|
552
|
+
} else if (isLegacyVgHook(content)) {
|
|
411
553
|
fs.unlinkSync(hookPath);
|
|
412
554
|
}
|
|
413
555
|
}
|
|
@@ -436,9 +578,8 @@ function areHooksInstalled(cwd = process.cwd()) {
|
|
|
436
578
|
return fs.existsSync(hookPath) && fs.readFileSync(hookPath, "utf-8").includes("versionguard");
|
|
437
579
|
});
|
|
438
580
|
}
|
|
439
|
-
function
|
|
440
|
-
return
|
|
441
|
-
# versionguard
|
|
581
|
+
function generateHookBlock(hookName) {
|
|
582
|
+
return `${VG_BLOCK_START}
|
|
442
583
|
# VersionGuard ${hookName} hook
|
|
443
584
|
# --no-install prevents accidentally downloading an unscoped package
|
|
444
585
|
# if @codluv/versionguard is not installed locally
|
|
@@ -448,8 +589,36 @@ if [ $status -ne 0 ]; then
|
|
|
448
589
|
echo "VersionGuard validation failed."
|
|
449
590
|
exit $status
|
|
450
591
|
fi
|
|
592
|
+
${VG_BLOCK_END}`;
|
|
593
|
+
}
|
|
594
|
+
function generateHookScript(hookName) {
|
|
595
|
+
return `#!/bin/sh
|
|
596
|
+
|
|
597
|
+
${generateHookBlock(hookName)}
|
|
451
598
|
`;
|
|
452
599
|
}
|
|
600
|
+
function isLegacyVgHook(content) {
|
|
601
|
+
if (!content.includes("versionguard validate")) return false;
|
|
602
|
+
if (content.includes(VG_BLOCK_START)) return false;
|
|
603
|
+
if (content.includes("husky")) return false;
|
|
604
|
+
if (content.includes("lefthook")) return false;
|
|
605
|
+
if (content.includes("pre-commit run")) return false;
|
|
606
|
+
return true;
|
|
607
|
+
}
|
|
608
|
+
function replaceVgBlock(content, newBlock) {
|
|
609
|
+
const startIdx = content.indexOf(VG_BLOCK_START);
|
|
610
|
+
const endIdx = content.indexOf(VG_BLOCK_END);
|
|
611
|
+
if (startIdx === -1 || endIdx === -1) return content;
|
|
612
|
+
return content.slice(0, startIdx) + newBlock + content.slice(endIdx + VG_BLOCK_END.length);
|
|
613
|
+
}
|
|
614
|
+
function removeVgBlock(content) {
|
|
615
|
+
const startIdx = content.indexOf(VG_BLOCK_START);
|
|
616
|
+
const endIdx = content.indexOf(VG_BLOCK_END);
|
|
617
|
+
if (startIdx === -1 || endIdx === -1) return content;
|
|
618
|
+
const before = content.slice(0, startIdx).replace(/\n\n$/, "\n");
|
|
619
|
+
const after = content.slice(endIdx + VG_BLOCK_END.length).replace(/^\n\n/, "\n");
|
|
620
|
+
return before + after;
|
|
621
|
+
}
|
|
453
622
|
class GitTagSource {
|
|
454
623
|
/** Human-readable provider name. */
|
|
455
624
|
name = "git-tag";
|
|
@@ -1364,6 +1533,163 @@ function getCalVerConfig(config) {
|
|
|
1364
1533
|
}
|
|
1365
1534
|
return config.versioning.calver;
|
|
1366
1535
|
}
|
|
1536
|
+
function deriveTopicSlug(conceptName) {
|
|
1537
|
+
return conceptName.replace(/Config$/, "").replace(/Result$/, "").replace(/Options$/, "").toLowerCase();
|
|
1538
|
+
}
|
|
1539
|
+
function isTopicConcept(name) {
|
|
1540
|
+
return name.endsWith("Config") && name !== "VersionGuardConfig";
|
|
1541
|
+
}
|
|
1542
|
+
function operationMatchesTopic(op, topicSlug, conceptNames) {
|
|
1543
|
+
const haystack = `${op.name} ${op.what}`.toLowerCase();
|
|
1544
|
+
if (haystack.includes(topicSlug)) return true;
|
|
1545
|
+
return conceptNames.some((n) => haystack.includes(n.toLowerCase()));
|
|
1546
|
+
}
|
|
1547
|
+
function createCkmEngine(manifest) {
|
|
1548
|
+
const topics = deriveTopics(manifest);
|
|
1549
|
+
return {
|
|
1550
|
+
topics,
|
|
1551
|
+
getTopicIndex: (toolName = "tool") => formatTopicIndex(topics, toolName),
|
|
1552
|
+
getTopicContent: (name) => formatTopicContent(topics, name),
|
|
1553
|
+
getTopicJson: (name) => buildTopicJson(topics, manifest, name),
|
|
1554
|
+
getManifest: () => manifest
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
function deriveTopics(manifest) {
|
|
1558
|
+
const topics = [];
|
|
1559
|
+
for (const concept of manifest.concepts) {
|
|
1560
|
+
if (!isTopicConcept(concept.name)) continue;
|
|
1561
|
+
const slug = deriveTopicSlug(concept.name);
|
|
1562
|
+
const conceptNames = [concept.name];
|
|
1563
|
+
const relatedConcepts = manifest.concepts.filter(
|
|
1564
|
+
(c) => c.name !== concept.name && (c.name.toLowerCase().includes(slug) || slug.includes(deriveTopicSlug(c.name)))
|
|
1565
|
+
);
|
|
1566
|
+
conceptNames.push(...relatedConcepts.map((c) => c.name));
|
|
1567
|
+
const operations = manifest.operations.filter(
|
|
1568
|
+
(op) => operationMatchesTopic(op, slug, conceptNames)
|
|
1569
|
+
);
|
|
1570
|
+
const configSchema = manifest.configSchema.filter(
|
|
1571
|
+
(c) => conceptNames.some((n) => c.key?.startsWith(n))
|
|
1572
|
+
);
|
|
1573
|
+
const constraints = manifest.constraints.filter(
|
|
1574
|
+
(c) => conceptNames.some((n) => c.enforcedBy?.includes(n)) || operations.some((o) => c.enforcedBy?.includes(o.name))
|
|
1575
|
+
);
|
|
1576
|
+
topics.push({
|
|
1577
|
+
name: slug,
|
|
1578
|
+
summary: concept.what,
|
|
1579
|
+
concepts: [concept, ...relatedConcepts],
|
|
1580
|
+
operations,
|
|
1581
|
+
configSchema,
|
|
1582
|
+
constraints
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1585
|
+
return topics;
|
|
1586
|
+
}
|
|
1587
|
+
function formatTopicIndex(topics, toolName) {
|
|
1588
|
+
const lines = [
|
|
1589
|
+
`${toolName} CKM — Codebase Knowledge Manifest`,
|
|
1590
|
+
"",
|
|
1591
|
+
`Usage: ${toolName} ckm [topic] [--json] [--llm]`,
|
|
1592
|
+
"",
|
|
1593
|
+
"Topics:"
|
|
1594
|
+
];
|
|
1595
|
+
const maxName = Math.max(...topics.map((t) => t.name.length));
|
|
1596
|
+
for (const topic of topics) {
|
|
1597
|
+
lines.push(` ${topic.name.padEnd(maxName + 2)}${topic.summary}`);
|
|
1598
|
+
}
|
|
1599
|
+
lines.push("");
|
|
1600
|
+
lines.push("Flags:");
|
|
1601
|
+
lines.push(" --json Machine-readable CKM output (concepts, operations, config schema)");
|
|
1602
|
+
lines.push(" --llm Full API context for LLM agents (forge-ts llms.txt)");
|
|
1603
|
+
return lines.join("\n");
|
|
1604
|
+
}
|
|
1605
|
+
function formatTopicContent(topics, topicName) {
|
|
1606
|
+
const topic = topics.find((t) => t.name === topicName);
|
|
1607
|
+
if (!topic) return null;
|
|
1608
|
+
const lines = [`# ${topic.summary}`, ""];
|
|
1609
|
+
if (topic.concepts.length > 0) {
|
|
1610
|
+
lines.push("## Concepts", "");
|
|
1611
|
+
for (const c of topic.concepts) {
|
|
1612
|
+
lines.push(` ${c.name} — ${c.what}`);
|
|
1613
|
+
if (c.properties) {
|
|
1614
|
+
for (const p of c.properties) {
|
|
1615
|
+
const def = findDefault(topic.configSchema, c.name, p.name);
|
|
1616
|
+
lines.push(` ${p.name}: ${p.type}${def ? ` = ${def}` : ""}`);
|
|
1617
|
+
if (p.description) {
|
|
1618
|
+
lines.push(` ${p.description}`);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
lines.push("");
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
if (topic.operations.length > 0) {
|
|
1626
|
+
lines.push("## Operations", "");
|
|
1627
|
+
for (const o of topic.operations) {
|
|
1628
|
+
lines.push(` ${o.name}() — ${o.what}`);
|
|
1629
|
+
if (o.inputs) {
|
|
1630
|
+
for (const i of o.inputs) {
|
|
1631
|
+
lines.push(` @param ${i.name}: ${i.description}`);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
lines.push("");
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
if (topic.configSchema.length > 0) {
|
|
1638
|
+
lines.push("## Config Fields", "");
|
|
1639
|
+
for (const c of topic.configSchema) {
|
|
1640
|
+
lines.push(` ${c.key}: ${c.type}${c.default ? ` = ${c.default}` : ""}`);
|
|
1641
|
+
if (c.description) {
|
|
1642
|
+
lines.push(` ${c.description}`);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
lines.push("");
|
|
1646
|
+
}
|
|
1647
|
+
if (topic.constraints.length > 0) {
|
|
1648
|
+
lines.push("## Constraints", "");
|
|
1649
|
+
for (const c of topic.constraints) {
|
|
1650
|
+
lines.push(` [${c.id}] ${c.rule}`);
|
|
1651
|
+
lines.push(` Enforced by: ${c.enforcedBy}`);
|
|
1652
|
+
}
|
|
1653
|
+
lines.push("");
|
|
1654
|
+
}
|
|
1655
|
+
return lines.join("\n");
|
|
1656
|
+
}
|
|
1657
|
+
function findDefault(schema, conceptName, propName) {
|
|
1658
|
+
return schema.find((c) => c.key === `${conceptName}.${propName}`)?.default;
|
|
1659
|
+
}
|
|
1660
|
+
function buildTopicJson(topics, manifest, topicName) {
|
|
1661
|
+
if (!topicName) {
|
|
1662
|
+
return {
|
|
1663
|
+
topics: topics.map((t) => ({
|
|
1664
|
+
name: t.name,
|
|
1665
|
+
summary: t.summary,
|
|
1666
|
+
concepts: t.concepts.length,
|
|
1667
|
+
operations: t.operations.length,
|
|
1668
|
+
configFields: t.configSchema.length,
|
|
1669
|
+
constraints: t.constraints.length
|
|
1670
|
+
})),
|
|
1671
|
+
ckm: {
|
|
1672
|
+
concepts: manifest.concepts.length,
|
|
1673
|
+
operations: manifest.operations.length,
|
|
1674
|
+
constraints: manifest.constraints.length,
|
|
1675
|
+
workflows: manifest.workflows.length,
|
|
1676
|
+
configSchema: manifest.configSchema.length
|
|
1677
|
+
}
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
const topic = topics.find((t) => t.name === topicName);
|
|
1681
|
+
if (!topic) {
|
|
1682
|
+
return { error: `Unknown topic: ${topicName}`, topics: topics.map((t) => t.name) };
|
|
1683
|
+
}
|
|
1684
|
+
return {
|
|
1685
|
+
topic: topic.name,
|
|
1686
|
+
summary: topic.summary,
|
|
1687
|
+
concepts: topic.concepts,
|
|
1688
|
+
operations: topic.operations,
|
|
1689
|
+
configSchema: topic.configSchema,
|
|
1690
|
+
constraints: topic.constraints
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1367
1693
|
const CONFIG_FILE_NAMES = [
|
|
1368
1694
|
".versionguard.yml",
|
|
1369
1695
|
".versionguard.yaml",
|
|
@@ -1870,6 +2196,17 @@ function fixAll(config, targetVersion, cwd = process.cwd()) {
|
|
|
1870
2196
|
const syncResults = fixSyncIssues(config, cwd);
|
|
1871
2197
|
results.push(...syncResults);
|
|
1872
2198
|
if (config.changelog.enabled) {
|
|
2199
|
+
const changelogPath = path.join(cwd, config.changelog.file);
|
|
2200
|
+
if (isChangesetMangled(changelogPath)) {
|
|
2201
|
+
const fixed = fixChangesetMangling(changelogPath);
|
|
2202
|
+
if (fixed) {
|
|
2203
|
+
results.push({
|
|
2204
|
+
fixed: true,
|
|
2205
|
+
message: `Restructured ${config.changelog.file} from Changesets format to Keep a Changelog`,
|
|
2206
|
+
file: changelogPath
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
1873
2210
|
const changelogResult = fixChangelog(version, config, cwd);
|
|
1874
2211
|
if (changelogResult.fixed) {
|
|
1875
2212
|
results.push(changelogResult);
|
|
@@ -2028,6 +2365,92 @@ function runGuardChecks(config, cwd) {
|
|
|
2028
2365
|
warnings
|
|
2029
2366
|
};
|
|
2030
2367
|
}
|
|
2368
|
+
const PROJECT_MARKERS = [
|
|
2369
|
+
".versionguard.yml",
|
|
2370
|
+
".versionguard.yaml",
|
|
2371
|
+
"versionguard.yml",
|
|
2372
|
+
"versionguard.yaml",
|
|
2373
|
+
".git",
|
|
2374
|
+
"package.json",
|
|
2375
|
+
"Cargo.toml",
|
|
2376
|
+
"pyproject.toml",
|
|
2377
|
+
"pubspec.yaml",
|
|
2378
|
+
"composer.json",
|
|
2379
|
+
"pom.xml",
|
|
2380
|
+
"go.mod",
|
|
2381
|
+
"mix.exs",
|
|
2382
|
+
"Gemfile",
|
|
2383
|
+
".csproj"
|
|
2384
|
+
];
|
|
2385
|
+
function findProjectRoot(startDir) {
|
|
2386
|
+
let current = path.resolve(startDir);
|
|
2387
|
+
while (true) {
|
|
2388
|
+
for (const marker of PROJECT_MARKERS) {
|
|
2389
|
+
if (marker.startsWith(".") && marker !== ".git" && !marker.startsWith(".version")) {
|
|
2390
|
+
try {
|
|
2391
|
+
const files = fs.readdirSync(current);
|
|
2392
|
+
if (files.some((f) => f.endsWith(marker))) {
|
|
2393
|
+
return buildResult(current, marker);
|
|
2394
|
+
}
|
|
2395
|
+
} catch {
|
|
2396
|
+
}
|
|
2397
|
+
} else if (fs.existsSync(path.join(current, marker))) {
|
|
2398
|
+
return buildResult(current, marker);
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
const parent = path.dirname(current);
|
|
2402
|
+
if (parent === current) {
|
|
2403
|
+
return {
|
|
2404
|
+
found: false,
|
|
2405
|
+
root: path.resolve(startDir),
|
|
2406
|
+
hasConfig: false,
|
|
2407
|
+
hasGit: false,
|
|
2408
|
+
hasManifest: false
|
|
2409
|
+
};
|
|
2410
|
+
}
|
|
2411
|
+
current = parent;
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
function buildResult(root, marker) {
|
|
2415
|
+
const configNames = [
|
|
2416
|
+
".versionguard.yml",
|
|
2417
|
+
".versionguard.yaml",
|
|
2418
|
+
"versionguard.yml",
|
|
2419
|
+
"versionguard.yaml"
|
|
2420
|
+
];
|
|
2421
|
+
return {
|
|
2422
|
+
found: true,
|
|
2423
|
+
root,
|
|
2424
|
+
marker,
|
|
2425
|
+
hasConfig: configNames.some((c) => fs.existsSync(path.join(root, c))),
|
|
2426
|
+
hasGit: fs.existsSync(path.join(root, ".git")),
|
|
2427
|
+
hasManifest: [
|
|
2428
|
+
"package.json",
|
|
2429
|
+
"Cargo.toml",
|
|
2430
|
+
"pyproject.toml",
|
|
2431
|
+
"pubspec.yaml",
|
|
2432
|
+
"composer.json",
|
|
2433
|
+
"pom.xml",
|
|
2434
|
+
"VERSION"
|
|
2435
|
+
].some((m) => fs.existsSync(path.join(root, m)))
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
2438
|
+
function formatNotProjectError(cwd, command) {
|
|
2439
|
+
const dir = path.basename(cwd) || cwd;
|
|
2440
|
+
const lines = [
|
|
2441
|
+
`Not a VersionGuard project: ${dir}`,
|
|
2442
|
+
"",
|
|
2443
|
+
"No .versionguard.yml, .git directory, or manifest file found.",
|
|
2444
|
+
"",
|
|
2445
|
+
"To get started:",
|
|
2446
|
+
" versionguard init Set up a new project interactively",
|
|
2447
|
+
" versionguard init --yes Set up with defaults",
|
|
2448
|
+
"",
|
|
2449
|
+
"Or run from a project root directory:",
|
|
2450
|
+
` cd /path/to/project && versionguard ${command}`
|
|
2451
|
+
];
|
|
2452
|
+
return lines.join("\n");
|
|
2453
|
+
}
|
|
2031
2454
|
function runGit(cwd, args, encoding) {
|
|
2032
2455
|
return childProcess.execFileSync("git", args, {
|
|
2033
2456
|
cwd,
|
|
@@ -2405,53 +2828,58 @@ function isWorktreeClean(cwd) {
|
|
|
2405
2828
|
}
|
|
2406
2829
|
}
|
|
2407
2830
|
export {
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2831
|
+
checkHardcodedVersions as A,
|
|
2832
|
+
checkHookIntegrity as B,
|
|
2833
|
+
checkHooksPathOverride as C,
|
|
2834
|
+
checkHuskyBypass as D,
|
|
2835
|
+
detectManifests as E,
|
|
2836
|
+
fixChangelog as F,
|
|
2414
2837
|
GitTagSource as G,
|
|
2415
|
-
|
|
2416
|
-
|
|
2838
|
+
fixPackageVersion as H,
|
|
2839
|
+
getAllTags as I,
|
|
2417
2840
|
JsonVersionSource as J,
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2841
|
+
getCalVerConfig as K,
|
|
2842
|
+
getLatestTag as L,
|
|
2843
|
+
getTagFeedback as M,
|
|
2844
|
+
getVersionSource as N,
|
|
2845
|
+
initConfig as O,
|
|
2846
|
+
resolveVersionSource as P,
|
|
2847
|
+
semver as Q,
|
|
2425
2848
|
RegexVersionSource as R,
|
|
2426
|
-
|
|
2849
|
+
suggestTagMessage as S,
|
|
2427
2850
|
TomlVersionSource as T,
|
|
2851
|
+
sync as U,
|
|
2428
2852
|
VersionFileSource as V,
|
|
2853
|
+
syncVersion as W,
|
|
2854
|
+
validateChangelog as X,
|
|
2429
2855
|
YamlVersionSource as Y,
|
|
2856
|
+
validateTagForPush as Z,
|
|
2857
|
+
validateVersion as _,
|
|
2430
2858
|
installHooks as a,
|
|
2431
2859
|
getPackageVersion as b,
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2860
|
+
createCkmEngine as c,
|
|
2861
|
+
getVersionFeedback as d,
|
|
2862
|
+
getSyncFeedback as e,
|
|
2863
|
+
getChangelogFeedback as f,
|
|
2436
2864
|
getConfig as g,
|
|
2437
2865
|
handlePostTag as h,
|
|
2438
2866
|
isValidCalVerFormat as i,
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2867
|
+
doctor as j,
|
|
2868
|
+
fixAll as k,
|
|
2869
|
+
isChangesetMangled as l,
|
|
2870
|
+
fixChangesetMangling as m,
|
|
2871
|
+
fixSyncIssues as n,
|
|
2872
|
+
setPackageVersion as o,
|
|
2873
|
+
createTag as p,
|
|
2874
|
+
areHooksInstalled as q,
|
|
2447
2875
|
runGuardChecks as r,
|
|
2448
2876
|
suggestNextVersion as s,
|
|
2449
|
-
|
|
2877
|
+
findProjectRoot as t,
|
|
2450
2878
|
uninstallHooks as u,
|
|
2451
2879
|
validate as v,
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2880
|
+
formatNotProjectError as w,
|
|
2881
|
+
calver as x,
|
|
2882
|
+
canBump as y,
|
|
2883
|
+
checkEnforceHooksPolicy as z
|
|
2456
2884
|
};
|
|
2457
|
-
//# sourceMappingURL=index-
|
|
2885
|
+
//# sourceMappingURL=index-CwOyEn5L.js.map
|