mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-11 21:57:14 +03:00
Sync generated provider output
This commit is contained in:
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import path from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { execFileSync } from 'node:child_process';
|
import { execFileSync } from 'node:child_process';
|
||||||
import { loadContext, extractPlatform } from './context.mjs';
|
import { loadContext, extractPlatform } from './context.mjs';
|
||||||
import { getCritiqueDir } from './lib/impeccable-paths.mjs';
|
import { readLatestSnapshotAcrossTargets } from './critique-storage.mjs';
|
||||||
|
|
||||||
/** Is there code here at all, or just context files / an empty repo? */
|
/** Is there code here at all, or just context files / an empty repo? */
|
||||||
function hasCode(cwd) {
|
function hasCode(cwd) {
|
||||||
@@ -34,23 +34,13 @@ function hasCode(cwd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent critique snapshot across all targets. Filenames are
|
* Summarize the most recent critique snapshot across all targets.
|
||||||
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
||||||
* Parses the small frontmatter for score + P0/P1 counts.
|
|
||||||
*/
|
*/
|
||||||
function latestCritique(cwd) {
|
function latestCritique(cwd) {
|
||||||
try {
|
try {
|
||||||
const dir = getCritiqueDir(cwd);
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
if (!fs.existsSync(dir)) return null;
|
if (!latest) return null;
|
||||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
const get = (key) => latest.meta[key] ?? null;
|
||||||
if (!files.length) return null;
|
|
||||||
const newest = files[files.length - 1];
|
|
||||||
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
||||||
const front = text.split('---')[1] || '';
|
|
||||||
const get = (k) => {
|
|
||||||
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
||||||
return m ? m[1].trim() : null;
|
|
||||||
};
|
|
||||||
const num = (v) => {
|
const num = (v) => {
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
@@ -61,7 +51,7 @@ function latestCritique(cwd) {
|
|||||||
p0: num(get('p0')),
|
p0: num(get('p0')),
|
||||||
p1: num(get('p1')),
|
p1: num(get('p1')),
|
||||||
timestamp: get('timestamp'),
|
timestamp: get('timestamp'),
|
||||||
file: path.relative(cwd, path.join(dir, newest)),
|
file: path.relative(cwd, latest.path),
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -105,28 +105,37 @@ function parseFrontmatter(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all snapshot files for `slug`, sorted oldest → newest.
|
* Return snapshot files matching `suffix`, sorted oldest → newest.
|
||||||
*/
|
*/
|
||||||
function listSnapshotsForSlug(slug, cwd) {
|
const SNAPSHOT_FILENAME = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}Z__.+\.md$/;
|
||||||
|
|
||||||
|
function listSnapshots(suffix, cwd) {
|
||||||
const dir = getCritiqueDir(cwd);
|
const dir = getCritiqueDir(cwd);
|
||||||
if (!fs.existsSync(dir)) return [];
|
if (!fs.existsSync(dir)) return [];
|
||||||
const suffix = `__${slug}.md`;
|
|
||||||
return fs.readdirSync(dir)
|
return fs.readdirSync(dir)
|
||||||
.filter((f) => f.endsWith(suffix))
|
.filter((f) => SNAPSHOT_FILENAME.test(f) && f.endsWith(suffix))
|
||||||
.sort()
|
.sort()
|
||||||
.map((f) => path.join(dir, f));
|
.map((f) => path.join(dir, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readLatestSnapshotMatching(suffix, cwd) {
|
||||||
|
const filePath = listSnapshots(suffix, cwd).at(-1);
|
||||||
|
if (!filePath) return null;
|
||||||
|
const body = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
return { path: filePath, body, meta: parseFrontmatter(body) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
* Return the most recent snapshot for `slug`, or null. Polish reads this
|
||||||
* to find its fix backlog when the slug matches.
|
* to find its fix backlog when the slug matches.
|
||||||
*/
|
*/
|
||||||
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
return readLatestSnapshotMatching(`__${slug}.md`, cwd);
|
||||||
if (!all.length) return null;
|
}
|
||||||
const latest = all[all.length - 1];
|
|
||||||
const body = fs.readFileSync(latest, 'utf-8');
|
/** Return the most recent snapshot across all targets, or null. */
|
||||||
return { path: latest, body, meta: parseFrontmatter(body) };
|
export function readLatestSnapshotAcrossTargets({ cwd = process.cwd() } = {}) {
|
||||||
|
return readLatestSnapshotMatching('.md', cwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,7 +143,7 @@ export function readLatestSnapshot(slug, { cwd = process.cwd() } = {}) {
|
|||||||
* Critique appends a one-line trend to its output using this.
|
* Critique appends a one-line trend to its output using this.
|
||||||
*/
|
*/
|
||||||
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
export function readTrend(slug, { limit = 5, cwd = process.cwd() } = {}) {
|
||||||
const all = listSnapshotsForSlug(slug, cwd);
|
const all = listSnapshots(`__${slug}.md`, cwd);
|
||||||
const slice = all.slice(-limit);
|
const slice = all.slice(-limit);
|
||||||
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
return slice.map((file) => parseFrontmatter(fs.readFileSync(file, 'utf-8')));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user