@mmnto/totem 1.5.6 → 1.5.7
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/ast-gate.js +2 -2
- package/dist/ast-gate.js.map +1 -1
- package/dist/ast-gate.test.js +2 -1
- package/dist/ast-gate.test.js.map +1 -1
- package/dist/ast-query.d.ts +1 -1
- package/dist/ast-query.d.ts.map +1 -1
- package/dist/ast-query.js +12 -2
- package/dist/ast-query.js.map +1 -1
- package/dist/ast-query.test.js +2 -1
- package/dist/ast-query.test.js.map +1 -1
- package/dist/compile-manifest.d.ts.map +1 -1
- package/dist/compile-manifest.js +18 -21
- package/dist/compile-manifest.js.map +1 -1
- package/dist/compile-manifest.test.js +5 -4
- package/dist/compile-manifest.test.js.map +1 -1
- package/dist/compiler.d.ts +1 -1
- package/dist/compiler.d.ts.map +1 -1
- package/dist/compiler.js +1 -1
- package/dist/compiler.js.map +1 -1
- package/dist/compiler.test.js +2 -1
- package/dist/compiler.test.js.map +1 -1
- package/dist/cursor-adapter.test.js +2 -1
- package/dist/cursor-adapter.test.js.map +1 -1
- package/dist/drift-detector.test.js +2 -1
- package/dist/drift-detector.test.js.map +1 -1
- package/dist/embedders/embedder.test.js +25 -3
- package/dist/embedders/embedder.test.js.map +1 -1
- package/dist/errors.d.ts +7 -7
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +15 -15
- package/dist/errors.js.map +1 -1
- package/dist/errors.test.d.ts +2 -0
- package/dist/errors.test.d.ts.map +1 -0
- package/dist/errors.test.js +28 -0
- package/dist/errors.test.js.map +1 -0
- package/dist/exporter.test.js +2 -1
- package/dist/exporter.test.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/ingest/file-resolver.d.ts.map +1 -1
- package/dist/ingest/file-resolver.js +8 -19
- package/dist/ingest/file-resolver.js.map +1 -1
- package/dist/ingest/pipeline.test.js +2 -1
- package/dist/ingest/pipeline.test.js.map +1 -1
- package/dist/ledger.d.ts +1 -1
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +1 -1
- package/dist/ledger.js.map +1 -1
- package/dist/ledger.test.js +2 -1
- package/dist/ledger.test.js.map +1 -1
- package/dist/lesson-io.test.js +5 -4
- package/dist/lesson-io.test.js.map +1 -1
- package/dist/lock.test.js +2 -1
- package/dist/lock.test.js.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +13 -19
- package/dist/registry.js.map +1 -1
- package/dist/registry.test.js +3 -2
- package/dist/registry.test.js.map +1 -1
- package/dist/rule-engine.d.ts +8 -0
- package/dist/rule-engine.d.ts.map +1 -1
- package/dist/rule-engine.js +57 -14
- package/dist/rule-engine.js.map +1 -1
- package/dist/rule-engine.test.js +83 -4
- package/dist/rule-engine.test.js.map +1 -1
- package/dist/rule-metrics.test.js +2 -1
- package/dist/rule-metrics.test.js.map +1 -1
- package/dist/secrets.test.js +2 -1
- package/dist/secrets.test.js.map +1 -1
- package/dist/store/lance-store.test.js +2 -1
- package/dist/store/lance-store.test.js.map +1 -1
- package/dist/sys/exec.d.ts +28 -0
- package/dist/sys/exec.d.ts.map +1 -0
- package/dist/sys/exec.js +37 -0
- package/dist/sys/exec.js.map +1 -0
- package/dist/sys/exec.test.d.ts +2 -0
- package/dist/sys/exec.test.d.ts.map +1 -0
- package/dist/sys/exec.test.js +51 -0
- package/dist/sys/exec.test.js.map +1 -0
- package/dist/sys/fs.d.ts +8 -0
- package/dist/sys/fs.d.ts.map +1 -0
- package/dist/sys/fs.js +35 -0
- package/dist/sys/fs.js.map +1 -0
- package/dist/sys/fs.test.d.ts +2 -0
- package/dist/sys/fs.test.d.ts.map +1 -0
- package/dist/sys/fs.test.js +72 -0
- package/dist/sys/fs.test.js.map +1 -0
- package/dist/sys/git.d.ts +42 -0
- package/dist/sys/git.d.ts.map +1 -0
- package/dist/sys/git.js +235 -0
- package/dist/sys/git.js.map +1 -0
- package/dist/sys/git.test.d.ts +2 -0
- package/dist/sys/git.test.d.ts.map +1 -0
- package/dist/sys/git.test.js +134 -0
- package/dist/sys/git.test.js.map +1 -0
- package/dist/test-utils.d.ts +6 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +11 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/test-utils.test.d.ts +2 -0
- package/dist/test-utils.test.d.ts.map +1 -0
- package/dist/test-utils.test.js +43 -0
- package/dist/test-utils.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { TotemParseError } from '../errors.js';
|
|
7
|
+
import { cleanTmpDir } from '../test-utils.js';
|
|
8
|
+
import { readJsonSafe } from './fs.js';
|
|
9
|
+
describe('readJsonSafe', () => {
|
|
10
|
+
let tmpDir;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'totem-fs-'));
|
|
13
|
+
});
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
cleanTmpDir(tmpDir);
|
|
16
|
+
});
|
|
17
|
+
it('reads and parses valid JSON without schema', () => {
|
|
18
|
+
const filePath = path.join(tmpDir, 'data.json');
|
|
19
|
+
fs.writeFileSync(filePath, '{"key": "value"}');
|
|
20
|
+
const result = readJsonSafe(filePath);
|
|
21
|
+
expect(result).toEqual({ key: 'value' });
|
|
22
|
+
});
|
|
23
|
+
it('reads and validates with Zod schema', () => {
|
|
24
|
+
const filePath = path.join(tmpDir, 'data.json');
|
|
25
|
+
fs.writeFileSync(filePath, '{"name": "totem", "version": 1}');
|
|
26
|
+
const schema = z.object({ name: z.string(), version: z.number() });
|
|
27
|
+
const result = readJsonSafe(filePath, schema);
|
|
28
|
+
expect(result).toEqual({ name: 'totem', version: 1 });
|
|
29
|
+
});
|
|
30
|
+
it('throws TotemParseError on ENOENT', () => {
|
|
31
|
+
expect(() => readJsonSafe(path.join(tmpDir, 'missing.json'))).toThrow(TotemParseError);
|
|
32
|
+
try {
|
|
33
|
+
readJsonSafe(path.join(tmpDir, 'missing.json'));
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
expect(err.message).toContain('File not found');
|
|
37
|
+
expect(err.cause).toBeDefined();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
it('throws TotemParseError on invalid JSON', () => {
|
|
41
|
+
const filePath = path.join(tmpDir, 'bad.json');
|
|
42
|
+
fs.writeFileSync(filePath, '{invalid json!!!}');
|
|
43
|
+
expect(() => readJsonSafe(filePath)).toThrow(TotemParseError);
|
|
44
|
+
try {
|
|
45
|
+
readJsonSafe(filePath);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
expect(err.message).toContain('Invalid JSON');
|
|
49
|
+
expect(err.cause).toBeDefined();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
it('throws TotemParseError on empty file', () => {
|
|
53
|
+
const filePath = path.join(tmpDir, 'empty.json');
|
|
54
|
+
fs.writeFileSync(filePath, '');
|
|
55
|
+
expect(() => readJsonSafe(filePath)).toThrow(TotemParseError);
|
|
56
|
+
});
|
|
57
|
+
it('throws TotemParseError with detailed paths on schema mismatch', () => {
|
|
58
|
+
const filePath = path.join(tmpDir, 'data.json');
|
|
59
|
+
fs.writeFileSync(filePath, '{"name": 123}');
|
|
60
|
+
const schema = z.object({ name: z.string() });
|
|
61
|
+
try {
|
|
62
|
+
readJsonSafe(filePath, schema);
|
|
63
|
+
expect.unreachable('should have thrown');
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
expect(err).toBeInstanceOf(TotemParseError);
|
|
67
|
+
expect(err.message).toContain('Schema validation failed');
|
|
68
|
+
expect(err.message).toContain('name');
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
//# sourceMappingURL=fs.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.test.js","sourceRoot":"","sources":["../../src/sys/fs.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export declare function getGitBranch(cwd: string): string;
|
|
2
|
+
export declare function getGitStatus(cwd: string): string;
|
|
3
|
+
export declare function getGitDiff(mode: 'staged' | 'all', cwd: string): string;
|
|
4
|
+
export declare function getGitDiffStat(cwd: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Detect the default branch of the remote (e.g. main, master).
|
|
7
|
+
* Falls back to 'main' if detection fails.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getDefaultBranch(cwd: string): string;
|
|
10
|
+
export declare function getGitBranchDiff(cwd: string, base?: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Get the author date of a tag in YYYY-MM-DD format.
|
|
13
|
+
* Returns null if tag doesn't exist or lookup fails.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getTagDate(cwd: string, tag: string): string | null;
|
|
16
|
+
/**
|
|
17
|
+
* Get the most recent semver tag (e.g., "v0.14.0").
|
|
18
|
+
* Returns null if no tags exist.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getLatestTag(cwd: string): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Get git log since a ref (tag or commit), or last N commits as fallback.
|
|
23
|
+
* Returns one-line-per-commit format: "hash subject".
|
|
24
|
+
*/
|
|
25
|
+
export declare function getGitLogSince(cwd: string, since?: string, maxCommits?: number): string;
|
|
26
|
+
/**
|
|
27
|
+
* Check if a specific file has uncommitted changes (staged or unstaged).
|
|
28
|
+
*/
|
|
29
|
+
export declare function isFileDirty(cwd: string, filePath: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Resolve the git repository root from any subdirectory.
|
|
32
|
+
* Returns the normalized absolute path, or null if not in a git repo.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveGitRoot(cwd: string): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Filter a unified diff to exclude files matching ignore patterns.
|
|
37
|
+
* Splits on `diff --git` boundaries and removes sections for ignored files.
|
|
38
|
+
* Uses matchesGlob from core for consistent glob behavior.
|
|
39
|
+
*/
|
|
40
|
+
export declare function filterDiffByPatterns(diff: string, patterns: string[]): string;
|
|
41
|
+
export declare function extractChangedFiles(diff: string): string[];
|
|
42
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/sys/git.ts"],"names":[],"mappings":"AAwBA,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAiBtE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CASlD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA+BpD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CA0BnE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUlE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWvD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,UAAU,SAAK,GAAG,MAAM,CAYnF;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAUlE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWzD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAe7E;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAgB1D"}
|
package/dist/sys/git.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import { matchesGlob } from '../compiler.js';
|
|
3
|
+
import { TotemGitError } from '../errors.js';
|
|
4
|
+
import { safeExec } from './exec.js';
|
|
5
|
+
// ─── Constants ──────────────────────────────────────────
|
|
6
|
+
const GIT_COMMAND_TIMEOUT_MS = 15_000;
|
|
7
|
+
const GIT_DIFF_MAX_BUFFER = 10 * 1024 * 1024; // 10MB — large diffs (e.g., compiled-rules.json)
|
|
8
|
+
function throwIfGitMissing(err) {
|
|
9
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10
|
+
if (msg.includes('ENOENT') || msg.includes('not found')) {
|
|
11
|
+
throw new TotemGitError("'git' command not found.", 'Ensure Git is installed and in your PATH.', err);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
// ─── Git helpers ────────────────────────────────────────
|
|
15
|
+
export function getGitBranch(cwd) {
|
|
16
|
+
try {
|
|
17
|
+
return safeExec('git', ['branch', '--show-current'], { cwd });
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return '(unknown)';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function getGitStatus(cwd) {
|
|
24
|
+
try {
|
|
25
|
+
return safeExec('git', ['status', '--porcelain'], { cwd });
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function getGitDiff(mode, cwd) {
|
|
32
|
+
const args = mode === 'staged' ? ['diff', '--staged'] : ['diff', 'HEAD'];
|
|
33
|
+
try {
|
|
34
|
+
return safeExec('git', args, {
|
|
35
|
+
cwd,
|
|
36
|
+
timeout: GIT_COMMAND_TIMEOUT_MS,
|
|
37
|
+
maxBuffer: GIT_DIFF_MAX_BUFFER,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
throwIfGitMissing(err);
|
|
42
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
43
|
+
throw new TotemGitError(`Failed to get git diff: ${msg}`, 'Check that you are inside a Git repository with at least one commit.', err);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function getGitDiffStat(cwd) {
|
|
47
|
+
try {
|
|
48
|
+
return safeExec('git', ['diff', 'HEAD', '--stat'], {
|
|
49
|
+
cwd,
|
|
50
|
+
timeout: GIT_COMMAND_TIMEOUT_MS,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return '';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Detect the default branch of the remote (e.g. main, master).
|
|
59
|
+
* Falls back to 'main' if detection fails.
|
|
60
|
+
*/
|
|
61
|
+
export function getDefaultBranch(cwd) {
|
|
62
|
+
try {
|
|
63
|
+
const ref = safeExec('git', ['symbolic-ref', 'refs/remotes/origin/HEAD', '--short'], {
|
|
64
|
+
cwd,
|
|
65
|
+
timeout: GIT_COMMAND_TIMEOUT_MS,
|
|
66
|
+
});
|
|
67
|
+
// ref is like "origin/main" — strip the remote prefix
|
|
68
|
+
return ref.replace(/^origin\//, '');
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
throwIfGitMissing(err);
|
|
72
|
+
// Fallback: check local then remote refs for 'main' / 'master'
|
|
73
|
+
for (const branch of ['main', 'master']) {
|
|
74
|
+
for (const ref of [branch, `origin/${branch}`]) {
|
|
75
|
+
try {
|
|
76
|
+
safeExec('git', ['rev-parse', '--verify', ref], {
|
|
77
|
+
cwd,
|
|
78
|
+
timeout: GIT_COMMAND_TIMEOUT_MS,
|
|
79
|
+
});
|
|
80
|
+
return branch;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Try next candidate
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
throw new TotemGitError("Could not determine default branch. Neither 'main' nor 'master' found locally, and 'git symbolic-ref' failed.", "Run 'git remote set-head origin --auto' to configure the default branch, or pass --base explicitly.", err);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export function getGitBranchDiff(cwd, base) {
|
|
91
|
+
const baseBranch = base ?? getDefaultBranch(cwd);
|
|
92
|
+
// Try local ref first, then remote — CI may only have origin/<branch>
|
|
93
|
+
const refs = [baseBranch, `origin/${baseBranch}`];
|
|
94
|
+
for (const ref of refs) {
|
|
95
|
+
try {
|
|
96
|
+
return safeExec('git', ['diff', `${ref}...HEAD`], {
|
|
97
|
+
cwd,
|
|
98
|
+
timeout: GIT_COMMAND_TIMEOUT_MS,
|
|
99
|
+
maxBuffer: GIT_DIFF_MAX_BUFFER,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
throwIfGitMissing(err);
|
|
104
|
+
// If this was the last ref, throw
|
|
105
|
+
if (ref === refs[refs.length - 1]) {
|
|
106
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
107
|
+
throw new TotemGitError(`Failed to get branch diff (${baseBranch}...HEAD): ${msg}`, `Ensure the base branch '${baseBranch}' exists locally or as a remote ref. Try 'git fetch origin ${baseBranch}'.`, err);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Unreachable — loop always returns or throws
|
|
112
|
+
return '';
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get the author date of a tag in YYYY-MM-DD format.
|
|
116
|
+
* Returns null if tag doesn't exist or lookup fails.
|
|
117
|
+
*/
|
|
118
|
+
export function getTagDate(cwd, tag) {
|
|
119
|
+
try {
|
|
120
|
+
const date = safeExec('git', ['log', '-1', '--format=%aI', tag], {
|
|
121
|
+
cwd,
|
|
122
|
+
timeout: GIT_COMMAND_TIMEOUT_MS,
|
|
123
|
+
});
|
|
124
|
+
return date.slice(0, 10) || null;
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get the most recent semver tag (e.g., "v0.14.0").
|
|
132
|
+
* Returns null if no tags exist.
|
|
133
|
+
*/
|
|
134
|
+
export function getLatestTag(cwd) {
|
|
135
|
+
try {
|
|
136
|
+
return (safeExec('git', ['describe', '--tags', '--abbrev=0'], {
|
|
137
|
+
cwd,
|
|
138
|
+
timeout: GIT_COMMAND_TIMEOUT_MS,
|
|
139
|
+
}) || null);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Get git log since a ref (tag or commit), or last N commits as fallback.
|
|
147
|
+
* Returns one-line-per-commit format: "hash subject".
|
|
148
|
+
*/
|
|
149
|
+
export function getGitLogSince(cwd, since, maxCommits = 50) {
|
|
150
|
+
const args = since
|
|
151
|
+
? ['log', `${since}..HEAD`, '--oneline', `--max-count=${maxCommits}`]
|
|
152
|
+
: ['log', '--oneline', `-${maxCommits}`];
|
|
153
|
+
try {
|
|
154
|
+
return safeExec('git', args, {
|
|
155
|
+
cwd,
|
|
156
|
+
timeout: GIT_COMMAND_TIMEOUT_MS,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return '';
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Check if a specific file has uncommitted changes (staged or unstaged).
|
|
165
|
+
*/
|
|
166
|
+
export function isFileDirty(cwd, filePath) {
|
|
167
|
+
try {
|
|
168
|
+
const output = safeExec('git', ['status', '--porcelain', '--', filePath], {
|
|
169
|
+
cwd,
|
|
170
|
+
timeout: GIT_COMMAND_TIMEOUT_MS,
|
|
171
|
+
});
|
|
172
|
+
return output.length > 0;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Resolve the git repository root from any subdirectory.
|
|
180
|
+
* Returns the normalized absolute path, or null if not in a git repo.
|
|
181
|
+
*/
|
|
182
|
+
export function resolveGitRoot(cwd) {
|
|
183
|
+
try {
|
|
184
|
+
const root = safeExec('git', ['rev-parse', '--show-toplevel'], {
|
|
185
|
+
cwd,
|
|
186
|
+
timeout: GIT_COMMAND_TIMEOUT_MS,
|
|
187
|
+
});
|
|
188
|
+
// git returns forward slashes even on Windows — normalize for fs operations
|
|
189
|
+
return path.normalize(root);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Filter a unified diff to exclude files matching ignore patterns.
|
|
197
|
+
* Splits on `diff --git` boundaries and removes sections for ignored files.
|
|
198
|
+
* Uses matchesGlob from core for consistent glob behavior.
|
|
199
|
+
*/
|
|
200
|
+
export function filterDiffByPatterns(diff, patterns) {
|
|
201
|
+
if (patterns.length === 0)
|
|
202
|
+
return diff;
|
|
203
|
+
const sections = diff.split(/^(?=diff --git )/m);
|
|
204
|
+
return sections
|
|
205
|
+
.filter((section) => {
|
|
206
|
+
// Extract destination path (b/) — handles renames correctly
|
|
207
|
+
const firstLine = section.substring(0, section.indexOf('\n'));
|
|
208
|
+
const quoted = firstLine.match(/^diff --git "a\/.*?" "b\/(.*?)"$/);
|
|
209
|
+
const unquoted = firstLine.match(/^diff --git a\/\S+ b\/(.+)$/);
|
|
210
|
+
const filePath = quoted?.[1] ?? unquoted?.[1];
|
|
211
|
+
if (!filePath)
|
|
212
|
+
return true;
|
|
213
|
+
return !patterns.some((p) => matchesGlob(filePath, p));
|
|
214
|
+
})
|
|
215
|
+
.join(''); // totem-ignore (#669) — joining diff sections, not text fragments
|
|
216
|
+
}
|
|
217
|
+
export function extractChangedFiles(diff) {
|
|
218
|
+
const files = [];
|
|
219
|
+
for (const line of diff.split('\n')) {
|
|
220
|
+
if (line.startsWith('diff --git')) {
|
|
221
|
+
// Handle quoted paths (spaces): diff --git "a/my file.ts" "b/my file.ts"
|
|
222
|
+
const quoted = line.match(/^diff --git "a\/.+" "b\/(.+)"$/); // totem-ignore — single line match, not iterating
|
|
223
|
+
if (quoted) {
|
|
224
|
+
files.push(quoted[1]);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
// Standard unquoted paths: diff --git a/file.ts b/file.ts
|
|
228
|
+
const unquoted = line.match(/^diff --git a\/.+ b\/(.+)$/); // totem-ignore — single line match
|
|
229
|
+
if (unquoted)
|
|
230
|
+
files.push(unquoted[1]);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return files;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/sys/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.test.d.ts","sourceRoot":"","sources":["../../src/sys/git.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
vi.mock('node:child_process', () => ({
|
|
4
|
+
execFileSync: vi.fn(),
|
|
5
|
+
execFile: vi.fn(),
|
|
6
|
+
execSync: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
import { extractChangedFiles, filterDiffByPatterns, getGitLogSince, getLatestTag, getTagDate, isFileDirty, } from './git.js';
|
|
9
|
+
describe('getLatestTag', () => {
|
|
10
|
+
beforeEach(() => vi.clearAllMocks());
|
|
11
|
+
it('returns the latest tag', () => {
|
|
12
|
+
vi.mocked(execFileSync).mockReturnValue('v0.14.0\n');
|
|
13
|
+
expect(getLatestTag('/tmp')).toBe('v0.14.0');
|
|
14
|
+
});
|
|
15
|
+
it('returns null when no tags exist', () => {
|
|
16
|
+
vi.mocked(execFileSync).mockImplementation(() => {
|
|
17
|
+
throw new Error('fatal: no tags');
|
|
18
|
+
});
|
|
19
|
+
expect(getLatestTag('/tmp')).toBeNull();
|
|
20
|
+
});
|
|
21
|
+
it('returns null for empty output', () => {
|
|
22
|
+
vi.mocked(execFileSync).mockReturnValue('\n');
|
|
23
|
+
expect(getLatestTag('/tmp')).toBeNull();
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe('getTagDate', () => {
|
|
27
|
+
beforeEach(() => vi.clearAllMocks());
|
|
28
|
+
it('returns YYYY-MM-DD date for a valid tag', () => {
|
|
29
|
+
vi.mocked(execFileSync).mockReturnValue('2026-03-01T12:00:00-05:00\n');
|
|
30
|
+
expect(getTagDate('/tmp', 'v0.14.0')).toBe('2026-03-01');
|
|
31
|
+
});
|
|
32
|
+
it('returns null when tag does not exist', () => {
|
|
33
|
+
vi.mocked(execFileSync).mockImplementation(() => {
|
|
34
|
+
throw new Error('fatal: bad object');
|
|
35
|
+
});
|
|
36
|
+
expect(getTagDate('/tmp', 'v999.0.0')).toBeNull();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe('getGitLogSince', () => {
|
|
40
|
+
beforeEach(() => vi.clearAllMocks());
|
|
41
|
+
it('returns log since a tag', () => {
|
|
42
|
+
vi.mocked(execFileSync).mockReturnValue('abc1234 feat: thing\ndef5678 fix: bug\n');
|
|
43
|
+
const result = getGitLogSince('/tmp', 'v0.14.0');
|
|
44
|
+
expect(result).toContain('abc1234');
|
|
45
|
+
expect(vi.mocked(execFileSync)).toHaveBeenCalledWith('git', ['log', 'v0.14.0..HEAD', '--oneline', '--max-count=50'], expect.any(Object));
|
|
46
|
+
});
|
|
47
|
+
it('returns recent commits when no since ref provided', () => {
|
|
48
|
+
vi.mocked(execFileSync).mockReturnValue('abc1234 feat: thing\n');
|
|
49
|
+
getGitLogSince('/tmp');
|
|
50
|
+
expect(vi.mocked(execFileSync)).toHaveBeenCalledWith('git', ['log', '--oneline', '-50'], expect.any(Object));
|
|
51
|
+
});
|
|
52
|
+
it('returns empty string on error', () => {
|
|
53
|
+
vi.mocked(execFileSync).mockImplementation(() => {
|
|
54
|
+
throw new Error('not a git repo');
|
|
55
|
+
});
|
|
56
|
+
expect(getGitLogSince('/tmp')).toBe('');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('filterDiffByPatterns', () => {
|
|
60
|
+
const DIFF_WITH_STRATEGY = [
|
|
61
|
+
'diff --git a/.strategy b/.strategy',
|
|
62
|
+
'index abc1234..def5678 160000',
|
|
63
|
+
'--- a/.strategy',
|
|
64
|
+
'+++ b/.strategy',
|
|
65
|
+
'@@ -1 +1 @@',
|
|
66
|
+
'-Subproject commit aaa',
|
|
67
|
+
'+Subproject commit bbb',
|
|
68
|
+
].join('\n');
|
|
69
|
+
const DIFF_WITH_CODE = [
|
|
70
|
+
'diff --git a/packages/cli/src/commands/lint.ts b/packages/cli/src/commands/lint.ts',
|
|
71
|
+
'index 1234567..abcdef0 100644',
|
|
72
|
+
'--- a/packages/cli/src/commands/lint.ts',
|
|
73
|
+
'+++ b/packages/cli/src/commands/lint.ts',
|
|
74
|
+
'@@ -1,3 +1,4 @@',
|
|
75
|
+
' import { foo } from "bar";',
|
|
76
|
+
'+const x = 1;',
|
|
77
|
+
].join('\n');
|
|
78
|
+
it('removes sections matching ignore patterns', () => {
|
|
79
|
+
const combined = DIFF_WITH_STRATEGY + '\n' + DIFF_WITH_CODE;
|
|
80
|
+
const result = filterDiffByPatterns(combined, ['.strategy']);
|
|
81
|
+
expect(result).not.toContain('.strategy');
|
|
82
|
+
expect(result).toContain('lint.ts');
|
|
83
|
+
});
|
|
84
|
+
it('returns full diff when no patterns match', () => {
|
|
85
|
+
const result = filterDiffByPatterns(DIFF_WITH_CODE, ['*.md']);
|
|
86
|
+
expect(result).toContain('lint.ts');
|
|
87
|
+
});
|
|
88
|
+
it('returns full diff when patterns array is empty', () => {
|
|
89
|
+
const combined = DIFF_WITH_STRATEGY + '\n' + DIFF_WITH_CODE;
|
|
90
|
+
const result = filterDiffByPatterns(combined, []);
|
|
91
|
+
expect(result).toContain('.strategy');
|
|
92
|
+
expect(result).toContain('lint.ts');
|
|
93
|
+
});
|
|
94
|
+
it('returns empty string when all sections are filtered out', () => {
|
|
95
|
+
const result = filterDiffByPatterns(DIFF_WITH_STRATEGY, ['.strategy']);
|
|
96
|
+
expect(result.trim()).toBe('');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe('isFileDirty', () => {
|
|
100
|
+
beforeEach(() => vi.clearAllMocks());
|
|
101
|
+
it('returns true when file has changes', () => {
|
|
102
|
+
vi.mocked(execFileSync).mockReturnValue(' M README.md\n');
|
|
103
|
+
expect(isFileDirty('/tmp', 'README.md')).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
it('returns false when file is clean', () => {
|
|
106
|
+
vi.mocked(execFileSync).mockReturnValue('');
|
|
107
|
+
expect(isFileDirty('/tmp', 'README.md')).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
it('returns false on error', () => {
|
|
110
|
+
vi.mocked(execFileSync).mockImplementation(() => {
|
|
111
|
+
throw new Error('not a git repo');
|
|
112
|
+
});
|
|
113
|
+
expect(isFileDirty('/tmp', 'README.md')).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
describe('extractChangedFiles', () => {
|
|
117
|
+
it('extracts file paths from unquoted diff headers', () => {
|
|
118
|
+
const diff = [
|
|
119
|
+
'diff --git a/src/foo.ts b/src/foo.ts',
|
|
120
|
+
'+++ b/src/foo.ts',
|
|
121
|
+
'diff --git a/src/bar.ts b/src/bar.ts',
|
|
122
|
+
'+++ b/src/bar.ts',
|
|
123
|
+
].join('\n');
|
|
124
|
+
expect(extractChangedFiles(diff)).toEqual(['src/foo.ts', 'src/bar.ts']);
|
|
125
|
+
});
|
|
126
|
+
it('extracts file paths from quoted diff headers', () => {
|
|
127
|
+
const diff = 'diff --git "a/my file.ts" "b/my file.ts"\n+++ "b/my file.ts"';
|
|
128
|
+
expect(extractChangedFiles(diff)).toEqual(['my file.ts']);
|
|
129
|
+
});
|
|
130
|
+
it('returns empty array for empty diff', () => {
|
|
131
|
+
expect(extractChangedFiles('')).toEqual([]);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
//# sourceMappingURL=git.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.test.js","sourceRoot":"","sources":["../../src/sys/git.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAGzD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
/**
|
|
3
|
+
* Remove a temporary directory with retry semantics for Windows ENOTEMPTY flakes.
|
|
4
|
+
* Safe to call with falsy paths (no-op).
|
|
5
|
+
*/
|
|
6
|
+
export function cleanTmpDir(dir) {
|
|
7
|
+
if (!dir)
|
|
8
|
+
return;
|
|
9
|
+
fs.rmSync(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=test-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,GAAuB;IACjD,IAAI,CAAC,GAAG;QAAE,OAAO;IACjB,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;AACnF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-utils.test.d.ts","sourceRoot":"","sources":["../src/test-utils.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as os from 'node:os';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
// Mock node:fs so rmSync is spyable in ESM
|
|
5
|
+
vi.mock('node:fs', async () => {
|
|
6
|
+
const actual = await vi.importActual('node:fs');
|
|
7
|
+
return { ...actual, default: actual };
|
|
8
|
+
});
|
|
9
|
+
import * as fs from 'node:fs';
|
|
10
|
+
import { cleanTmpDir } from './test-utils.js';
|
|
11
|
+
describe('cleanTmpDir', () => {
|
|
12
|
+
let tmpDir;
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
vi.restoreAllMocks();
|
|
15
|
+
cleanTmpDir(tmpDir);
|
|
16
|
+
});
|
|
17
|
+
it('removes a directory', () => {
|
|
18
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'totem-test-'));
|
|
19
|
+
fs.writeFileSync(path.join(tmpDir, 'file.txt'), 'test');
|
|
20
|
+
cleanTmpDir(tmpDir);
|
|
21
|
+
expect(fs.existsSync(tmpDir)).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
it('no-ops on undefined', () => {
|
|
24
|
+
expect(() => cleanTmpDir(undefined)).not.toThrow();
|
|
25
|
+
});
|
|
26
|
+
it('no-ops on empty string', () => {
|
|
27
|
+
expect(() => cleanTmpDir('')).not.toThrow();
|
|
28
|
+
});
|
|
29
|
+
it('does not throw on nonexistent path', () => {
|
|
30
|
+
expect(() => cleanTmpDir('/tmp/nonexistent-totem-test-12345')).not.toThrow();
|
|
31
|
+
});
|
|
32
|
+
it('passes maxRetries and retryDelay to fs.rmSync', () => {
|
|
33
|
+
const spy = vi.spyOn(fs, 'rmSync').mockImplementation(() => { });
|
|
34
|
+
cleanTmpDir('/fake/path');
|
|
35
|
+
expect(spy).toHaveBeenCalledWith('/fake/path', {
|
|
36
|
+
recursive: true,
|
|
37
|
+
force: true,
|
|
38
|
+
maxRetries: 3,
|
|
39
|
+
retryDelay: 100,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=test-utils.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-utils.test.js","sourceRoot":"","sources":["../src/test-utils.test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE7D,2CAA2C;AAC3C,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;IAC5B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,YAAY,CAA2B,SAAS,CAAC,CAAC;IAC1E,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AACxC,CAAC,CAAC,CAAC;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,IAAI,MAA0B,CAAC;IAE/B,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,eAAe,EAAE,CAAC;QACrB,WAAW,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC;QAC/D,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,CAAC;QACxD,WAAW,CAAC,MAAM,CAAC,CAAC;QACpB,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,mCAAmC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAChE,WAAW,CAAC,YAAY,CAAC,CAAC;QAC1B,MAAM,CAAC,GAAG,CAAC,CAAC,oBAAoB,CAAC,YAAY,EAAE;YAC7C,SAAS,EAAE,IAAI;YACf,KAAK,EAAE,IAAI;YACX,UAAU,EAAE,CAAC;YACb,UAAU,EAAE,GAAG;SAChB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|