mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-14 15:16:35 +03:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e8eea2d91 | ||
|
|
aa1b4e4b02 | ||
|
|
6e6b0227b0 | ||
|
|
a075d89bdb | ||
|
|
e76b3424d2 | ||
|
|
e46e0da885 | ||
|
|
b14df98183 | ||
|
|
6886ab8c0e |
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -206,10 +206,10 @@ function parseIgnoreColor(value) {
|
|||||||
if (rgb) {
|
if (rgb) {
|
||||||
const parts = splitColorArgs(rgb[1]);
|
const parts = splitColorArgs(rgb[1]);
|
||||||
if (parts.length < 3 || parts.length > 4) return null;
|
if (parts.length < 3 || parts.length > 4) return null;
|
||||||
const r = parseRgbChannel(parts[0]);
|
const r = parseColorChannel(parts[0], COLOR_CHANNEL_FORMATS.rgb);
|
||||||
const g = parseRgbChannel(parts[1]);
|
const g = parseColorChannel(parts[1], COLOR_CHANNEL_FORMATS.rgb);
|
||||||
const b = parseRgbChannel(parts[2]);
|
const b = parseColorChannel(parts[2], COLOR_CHANNEL_FORMATS.rgb);
|
||||||
const a = parts[3] === undefined ? 1 : parseAlphaChannel(parts[3]);
|
const a = parts[3] === undefined ? 1 : parseColorChannel(parts[3], COLOR_CHANNEL_FORMATS.alpha);
|
||||||
if ([r, g, b, a].some((v) => v === null)) return null;
|
if ([r, g, b, a].some((v) => v === null)) return null;
|
||||||
return { r, g, b, a };
|
return { r, g, b, a };
|
||||||
}
|
}
|
||||||
@@ -218,10 +218,10 @@ function parseIgnoreColor(value) {
|
|||||||
if (hsl) {
|
if (hsl) {
|
||||||
const parts = splitColorArgs(hsl[1]);
|
const parts = splitColorArgs(hsl[1]);
|
||||||
if (parts.length < 3 || parts.length > 4) return null;
|
if (parts.length < 3 || parts.length > 4) return null;
|
||||||
const h = parseHueChannel(parts[0]);
|
const h = parseColorChannel(parts[0], COLOR_CHANNEL_FORMATS.hue);
|
||||||
const s = parsePercentChannel(parts[1]);
|
const s = parseColorChannel(parts[1], COLOR_CHANNEL_FORMATS.percent);
|
||||||
const l = parsePercentChannel(parts[2]);
|
const l = parseColorChannel(parts[2], COLOR_CHANNEL_FORMATS.percent);
|
||||||
const a = parts[3] === undefined ? 1 : parseAlphaChannel(parts[3]);
|
const a = parts[3] === undefined ? 1 : parseColorChannel(parts[3], COLOR_CHANNEL_FORMATS.alpha);
|
||||||
if ([h, s, l, a].some((v) => v === null)) return null;
|
if ([h, s, l, a].some((v) => v === null)) return null;
|
||||||
return hslToRgb(h, s, l, a);
|
return hslToRgb(h, s, l, a);
|
||||||
}
|
}
|
||||||
@@ -230,18 +230,13 @@ function parseIgnoreColor(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parseHexIgnoreColor(hex) {
|
function parseHexIgnoreColor(hex) {
|
||||||
if (hex.length === 3 || hex.length === 4) {
|
const expanded = hex.length <= 4
|
||||||
const r = parseInt(hex[0] + hex[0], 16);
|
? [...hex].map((digit) => digit.repeat(2)).join('')
|
||||||
const g = parseInt(hex[1] + hex[1], 16);
|
: hex;
|
||||||
const b = parseInt(hex[2] + hex[2], 16);
|
const [r, g, b, alpha = 255] = expanded
|
||||||
const a = hex.length === 4 ? parseInt(hex[3] + hex[3], 16) / 255 : 1;
|
.match(/../g)
|
||||||
return { r, g, b, a };
|
.map((channel) => Number.parseInt(channel, 16));
|
||||||
}
|
return { r, g, b, a: alpha / 255 };
|
||||||
const r = parseInt(hex.slice(0, 2), 16);
|
|
||||||
const g = parseInt(hex.slice(2, 4), 16);
|
|
||||||
const b = parseInt(hex.slice(4, 6), 16);
|
|
||||||
const a = hex.length === 8 ? parseInt(hex.slice(6, 8), 16) / 255 : 1;
|
|
||||||
return { r, g, b, a };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function splitColorArgs(body) {
|
function splitColorArgs(body) {
|
||||||
@@ -259,47 +254,34 @@ function splitColorArgs(body) {
|
|||||||
return text.replace(/\s*\/\s*/g, ' / ').split(/\s+/).filter((part) => part && part !== '/');
|
return text.replace(/\s*\/\s*/g, ' / ').split(/\s+/).filter((part) => part && part !== '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseRgbChannel(raw) {
|
const CSS_NUMBER_RE = /^(-?\d*\.?\d+)(%|deg|rad|turn|grad)?$/;
|
||||||
const text = String(raw || '').trim();
|
const identity = (value) => value;
|
||||||
const match = text.match(/^(-?\d*\.?\d+)(%)?$/);
|
const COLOR_CHANNEL_FORMATS = {
|
||||||
if (!match) return null;
|
rgb: { units: { '': identity, '%': (value) => value * 2.55 }, min: 0, max: 255, round: true },
|
||||||
const value = Number.parseFloat(match[1]);
|
alpha: { units: { '': identity, '%': (value) => value / 100 }, min: 0, max: 1 },
|
||||||
if (!Number.isFinite(value)) return null;
|
hue: {
|
||||||
const scaled = match[2] ? value * 2.55 : value;
|
units: {
|
||||||
if (scaled < 0 || scaled > 255) return null;
|
'': identity,
|
||||||
return Math.round(scaled);
|
deg: identity,
|
||||||
}
|
rad: (value) => value * (180 / Math.PI),
|
||||||
|
turn: (value) => value * 360,
|
||||||
|
grad: (value) => value * 0.9,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
percent: { units: { '%': (value) => value / 100 }, min: 0, max: 1 },
|
||||||
|
};
|
||||||
|
|
||||||
function parseAlphaChannel(raw) {
|
function parseColorChannel(raw, { units, min = -Infinity, max = Infinity, round = false }) {
|
||||||
const text = String(raw || '').trim();
|
const text = String(raw || '').trim();
|
||||||
const match = text.match(/^(-?\d*\.?\d+)(%)?$/);
|
const match = text.match(CSS_NUMBER_RE);
|
||||||
if (!match) return null;
|
if (!match) return null;
|
||||||
const value = Number.parseFloat(match[1]);
|
const convert = units[match[2] || ''];
|
||||||
if (!Number.isFinite(value)) return null;
|
if (!convert) return null;
|
||||||
const alpha = match[2] ? value / 100 : value;
|
const number = Number.parseFloat(match[1]);
|
||||||
return alpha >= 0 && alpha <= 1 ? alpha : null;
|
if (!Number.isFinite(number)) return null;
|
||||||
}
|
const value = convert(number);
|
||||||
|
if (value < min || value > max) return null;
|
||||||
function parseHueChannel(raw) {
|
return round ? Math.round(value) : value;
|
||||||
const text = String(raw || '').trim();
|
|
||||||
const match = text.match(/^(-?\d*\.?\d+)(deg|rad|turn|grad)?$/);
|
|
||||||
if (!match) return null;
|
|
||||||
const value = Number.parseFloat(match[1]);
|
|
||||||
if (!Number.isFinite(value)) return null;
|
|
||||||
const unit = match[2] || 'deg';
|
|
||||||
if (unit === 'turn') return value * 360;
|
|
||||||
if (unit === 'rad') return value * (180 / Math.PI);
|
|
||||||
if (unit === 'grad') return value * 0.9;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parsePercentChannel(raw) {
|
|
||||||
const text = String(raw || '').trim();
|
|
||||||
const match = text.match(/^(-?\d*\.?\d+)%$/);
|
|
||||||
if (!match) return null;
|
|
||||||
const value = Number.parseFloat(match[1]);
|
|
||||||
if (!Number.isFinite(value)) return null;
|
|
||||||
return value >= 0 && value <= 100 ? value / 100 : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hslToRgb(hue, saturation, lightness, alpha) {
|
function hslToRgb(hue, saturation, lightness, alpha) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -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')));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,12 @@ import path from 'node:path';
|
|||||||
import { createRequire } from 'node:module';
|
import { createRequire } from 'node:module';
|
||||||
|
|
||||||
const DEFAULT_TIMEOUT_MS = 60_000;
|
const DEFAULT_TIMEOUT_MS = 60_000;
|
||||||
|
const BATCH_OP_TEXT_LIMIT = 240;
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
export function buildCopyEditBatchPrompt(batch, { cwd = process.cwd() } = {}) {
|
export function buildCopyEditBatchPrompt(batch, { cwd = process.cwd() } = {}) {
|
||||||
const repairLines = batch?.repair ? [
|
const compactBatch = compactBatchForPrompt(batch);
|
||||||
|
const repairLines = compactBatch.repair ? [
|
||||||
'',
|
'',
|
||||||
'Repair mode:',
|
'Repair mode:',
|
||||||
'- The previous Apply attempt changed source, but validation failed.',
|
'- The previous Apply attempt changed source, but validation failed.',
|
||||||
@@ -28,7 +30,7 @@ export function buildCopyEditBatchPrompt(batch, { cwd = process.cwd() } = {}) {
|
|||||||
'- If failures or candidates show edited text is also a lookup key, update coupled count, animation, icon, image, asset, style, or metadata keys in the current source, or fail that entry without partial edits.',
|
'- If failures or candidates show edited text is also a lookup key, update coupled count, animation, icon, image, asset, style, or metadata keys in the current source, or fail that entry without partial edits.',
|
||||||
'- Keep failed and notes as arrays.',
|
'- Keep failed and notes as arrays.',
|
||||||
'- Return the same canonical JSON shape after repair.',
|
'- Return the same canonical JSON shape after repair.',
|
||||||
JSON.stringify(batch.repair, null, 2),
|
JSON.stringify(compactBatch.repair, null, 2),
|
||||||
] : [];
|
] : [];
|
||||||
return [
|
return [
|
||||||
'You are the Impeccable staged copy-edit batch applier.',
|
'You are the Impeccable staged copy-edit batch applier.',
|
||||||
@@ -80,7 +82,7 @@ export function buildCopyEditBatchPrompt(batch, { cwd = process.cwd() } = {}) {
|
|||||||
...repairLines,
|
...repairLines,
|
||||||
'',
|
'',
|
||||||
'Staged copy-edit batch:',
|
'Staged copy-edit batch:',
|
||||||
JSON.stringify(compactBatchForPrompt(batch), null, 2),
|
JSON.stringify(compactBatch, null, 2),
|
||||||
].join('\n');
|
].join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,7 +294,7 @@ function readManualEditValidationScript(cwd) {
|
|||||||
function compactBatchForPrompt(batch) {
|
function compactBatchForPrompt(batch) {
|
||||||
return {
|
return {
|
||||||
pageUrl: batch?.pageUrl || null,
|
pageUrl: batch?.pageUrl || null,
|
||||||
repair: batch?.repair || undefined,
|
repair: compactBatchRepair(batch?.repair),
|
||||||
entries: (batch?.entries || []).map((entry) => ({
|
entries: (batch?.entries || []).map((entry) => ({
|
||||||
id: entry.id,
|
id: entry.id,
|
||||||
pageUrl: entry.pageUrl,
|
pageUrl: entry.pageUrl,
|
||||||
@@ -300,7 +302,71 @@ function compactBatchForPrompt(batch) {
|
|||||||
element: compactContextForBatch(entry.element),
|
element: compactContextForBatch(entry.element),
|
||||||
ops: (entry.ops || []).map(compactBatchOp),
|
ops: (entry.ops || []).map(compactBatchOp),
|
||||||
})),
|
})),
|
||||||
candidates: batch?.candidates || [],
|
candidates: compactBatchCandidates(batch?.candidates),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function compactBatchRepair(repair) {
|
||||||
|
if (!repair || typeof repair !== 'object') return undefined;
|
||||||
|
return {
|
||||||
|
status: compactBatchString(repair.status),
|
||||||
|
attempt: normalizeOptionalBatchNumber(repair.attempt),
|
||||||
|
attempts: normalizeOptionalBatchNumber(repair.attempts),
|
||||||
|
maxAttempts: normalizeOptionalBatchNumber(repair.maxAttempts),
|
||||||
|
reason: compactBatchString(repair.reason),
|
||||||
|
transactionId: compactBatchString(repair.transactionId),
|
||||||
|
pageUrl: compactBatchString(repair.pageUrl),
|
||||||
|
failures: compactBatchDiagnostics(repair.failures),
|
||||||
|
files: compactBatchStringList(repair.files, 20),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function compactBatchDiagnostics(items, depth = 0) {
|
||||||
|
if (!Array.isArray(items)) return undefined;
|
||||||
|
return items.slice(0, 12).map((item) => ({
|
||||||
|
entryId: compactBatchString(item?.entryId || item?.id),
|
||||||
|
reason: compactBatchString(item?.reason || item?.kind),
|
||||||
|
detail: compactBatchString(item?.detail),
|
||||||
|
message: compactBatchString(item?.message),
|
||||||
|
file: compactBatchString(item?.file || item?.relativeFile),
|
||||||
|
line: normalizeOptionalBatchNumber(item?.line),
|
||||||
|
ref: compactBatchString(item?.ref),
|
||||||
|
marker: compactBatchString(item?.marker),
|
||||||
|
files: compactBatchStringList(item?.files, 8),
|
||||||
|
candidates: depth < 2 ? compactBatchSourceMatches(item?.candidates, 8) : undefined,
|
||||||
|
failures: depth < 2 ? compactBatchDiagnostics(item?.failures, depth + 1) : undefined,
|
||||||
|
checks: depth < 2 ? compactBatchDiagnostics(item?.checks, depth + 1) : undefined,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function compactBatchCandidates(candidates) {
|
||||||
|
return (Array.isArray(candidates) ? candidates : [])
|
||||||
|
.slice(0, 24)
|
||||||
|
.map((candidate) => ({
|
||||||
|
entryId: compactBatchString(candidate?.entryId),
|
||||||
|
ref: compactBatchString(candidate?.ref),
|
||||||
|
sourceHint: compactBatchSourceMatch(candidate?.sourceHint),
|
||||||
|
textMatches: compactBatchSourceMatches(candidate?.textMatches, 8),
|
||||||
|
objectKeyMatches: compactBatchSourceMatches(candidate?.objectKeyMatches, 8),
|
||||||
|
contextTextMatches: compactBatchSourceMatches(candidate?.contextTextMatches, 8),
|
||||||
|
locatorMatches: compactBatchSourceMatches(candidate?.locatorMatches, 6),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function compactBatchSourceMatches(matches, limit) {
|
||||||
|
if (!Array.isArray(matches)) return undefined;
|
||||||
|
return matches.slice(0, limit).map(compactBatchSourceMatch).filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
function compactBatchSourceMatch(match) {
|
||||||
|
if (!match || typeof match !== 'object') return null;
|
||||||
|
return {
|
||||||
|
file: compactBatchString(match.relativeFile || match.file),
|
||||||
|
line: normalizeBatchNumber(match.line),
|
||||||
|
column: normalizeBatchNumber(match.column),
|
||||||
|
kind: compactBatchString(match.kind),
|
||||||
|
reason: compactBatchString(match.reason || match.kind),
|
||||||
|
status: compactBatchString(match.status),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,25 +377,77 @@ function compactBatchOp(op) {
|
|||||||
contextRef: op.contextRef,
|
contextRef: op.contextRef,
|
||||||
tag: op.tag,
|
tag: op.tag,
|
||||||
elementId: op.elementId,
|
elementId: op.elementId,
|
||||||
classes: op.classes,
|
classes: compactBatchStringList(op.classes, 24),
|
||||||
originalText: op.originalText,
|
originalText: op.originalText,
|
||||||
newText: op.newText,
|
newText: op.newText,
|
||||||
deleted: op.deleted === true || undefined,
|
deleted: op.deleted === true || undefined,
|
||||||
sourceHint: op.sourceHint,
|
sourceHint: normalizeBatchSourceHint(op.sourceHint),
|
||||||
leaf: compactContextForBatch(op.leaf),
|
leaf: compactContextForBatch(op.leaf),
|
||||||
nearbyEditableTexts: Array.isArray(op.nearbyEditableTexts) ? op.nearbyEditableTexts.slice(0, 8) : [],
|
nearbyEditableTexts: compactNearbyBatchTexts(op.nearbyEditableTexts),
|
||||||
container: compactContextForBatch(op.container),
|
container: compactContextForBatch(op.container),
|
||||||
contextHints: Array.isArray(op.contextHints) ? op.contextHints.slice(0, 12) : [],
|
contextHints: compactBatchStringList(op.contextHints, 12),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeBatchSourceHint(hint) {
|
||||||
|
if (!hint || typeof hint !== 'object') return null;
|
||||||
|
let line = normalizeBatchNumber(hint.line);
|
||||||
|
let column = normalizeBatchNumber(hint.column);
|
||||||
|
if ((line === null || column === null) && typeof hint.loc === 'string') {
|
||||||
|
const match = hint.loc.match(/^(\d+)(?::(\d+))?/);
|
||||||
|
if (match) {
|
||||||
|
line = Number(match[1]);
|
||||||
|
if (match[2]) column = Number(match[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
file: compactBatchString(hint.file) || '',
|
||||||
|
loc: compactBatchString(hint.loc) || '',
|
||||||
|
line,
|
||||||
|
column,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeBatchNumber(value) {
|
||||||
|
if (value === null || value === undefined || value === '') return null;
|
||||||
|
const number = Number(value);
|
||||||
|
return Number.isFinite(number) ? number : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeOptionalBatchNumber(value) {
|
||||||
|
const number = normalizeBatchNumber(value);
|
||||||
|
return number === null ? undefined : number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function compactNearbyBatchTexts(items) {
|
||||||
|
return (Array.isArray(items) ? items : [])
|
||||||
|
.slice(0, 8)
|
||||||
|
.map((item) => typeof item === 'string' ? { text: truncate(item, BATCH_OP_TEXT_LIMIT) } : {
|
||||||
|
ref: compactBatchString(item?.ref),
|
||||||
|
tag: compactBatchString(item?.tag),
|
||||||
|
classes: compactBatchStringList(item?.classes, 24),
|
||||||
|
text: compactBatchString(item?.text),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function compactBatchStringList(items, limit) {
|
||||||
|
return (Array.isArray(items) ? items : [])
|
||||||
|
.slice(0, limit)
|
||||||
|
.filter((item) => typeof item === 'string')
|
||||||
|
.map((item) => truncate(item, BATCH_OP_TEXT_LIMIT));
|
||||||
|
}
|
||||||
|
|
||||||
|
function compactBatchString(value) {
|
||||||
|
return typeof value === 'string' ? truncate(value, BATCH_OP_TEXT_LIMIT) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
function compactContextForBatch(value) {
|
function compactContextForBatch(value) {
|
||||||
if (!value || typeof value !== 'object') return value || null;
|
if (!value || typeof value !== 'object') return value || null;
|
||||||
return {
|
return {
|
||||||
ref: value.ref,
|
ref: compactBatchString(value.ref),
|
||||||
tagName: value.tagName,
|
tagName: compactBatchString(value.tagName),
|
||||||
id: value.id,
|
id: compactBatchString(value.id),
|
||||||
classes: value.classes,
|
classes: compactBatchStringList(value.classes, 24),
|
||||||
textContent: truncate(value.textContent, 900),
|
textContent: truncate(value.textContent, 900),
|
||||||
outerHTML: truncate(stripLiveRuntimeHtml(value.outerHTML), 1800),
|
outerHTML: truncate(stripLiveRuntimeHtml(value.outerHTML), 1800),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -93,15 +93,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|||||||
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generatePinnedSkill(command, metadata, commandPrefix) {
|
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
||||||
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
||||||
const hint = metadata[command]?.argumentHint || '[target]';
|
const hint = metadata[command]?.argumentHint || '[target]';
|
||||||
|
const providerFrontmatter = isCodex
|
||||||
|
? `metadata:\n argument-hint: "${hint}"`
|
||||||
|
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
||||||
|
|
||||||
return `---
|
return `---
|
||||||
name: ${command}
|
name: ${command}
|
||||||
description: "${desc}"
|
description: "${desc}"
|
||||||
argument-hint: "${hint}"
|
${providerFrontmatter}
|
||||||
user-invocable: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${PIN_MARKER}
|
${PIN_MARKER}
|
||||||
@@ -128,7 +130,7 @@ function pin(command, projectRoot) {
|
|||||||
|
|
||||||
for (const skillsDir of harnessDirs) {
|
for (const skillsDir of harnessDirs) {
|
||||||
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
||||||
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
||||||
// Check if skill already exists (and isn't a pin)
|
// Check if skill already exists (and isn't a pin)
|
||||||
const skillDir = join(skillsDir, command);
|
const skillDir = join(skillsDir, command);
|
||||||
if (existsSync(skillDir)) {
|
if (existsSync(skillDir)) {
|
||||||
|
|||||||
@@ -76,6 +76,22 @@ describe('gatherSignals', () => {
|
|||||||
assert.equal(s.critique.latest.slug, 'home');
|
assert.equal(s.critique.latest.slug, 'home');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('reads the newest critique snapshot across target slugs', async () => {
|
||||||
|
write('.impeccable/critique/2026-05-01T10-00-00Z__home.md',
|
||||||
|
'---\nslug: home\nscore: 6\np0: 1\np1: 3\ntimestamp: 2026-05-01T10-00-00Z\n---\nbody\n');
|
||||||
|
write('.impeccable/critique/2026-05-02T10-00-00Z__pricing.md',
|
||||||
|
'---\nslug: pricing\nscore: 9\np0: 0\np1: 1\ntimestamp: 2026-05-02T10-00-00Z\n---\nbody\n');
|
||||||
|
write('.impeccable/critique/ignore.md', '# Critique ignores\n');
|
||||||
|
write('.impeccable/critique/9999-not-a-snapshot.md', '# Draft\n');
|
||||||
|
const s = await gatherSignals(scratch);
|
||||||
|
assert.equal(s.critique.latest.slug, 'pricing');
|
||||||
|
assert.equal(s.critique.latest.score, 9);
|
||||||
|
assert.equal(
|
||||||
|
s.critique.latest.file,
|
||||||
|
'.impeccable/critique/2026-05-02T10-00-00Z__pricing.md',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('handles a non-git dir without throwing', async () => {
|
it('handles a non-git dir without throwing', async () => {
|
||||||
const s = await gatherSignals(scratch);
|
const s = await gatherSignals(scratch);
|
||||||
assert.equal(s.git.isRepo, false);
|
assert.equal(s.git.isRepo, false);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { describe, it, beforeEach, afterEach } from 'node:test';
|
import { describe, it, beforeEach, afterEach } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { mkdtempSync, rmSync, symlinkSync } from 'node:fs';
|
import { mkdtempSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import { tmpdir } from 'node:os';
|
import { tmpdir } from 'node:os';
|
||||||
import { spawnSync } from 'node:child_process';
|
import { spawnSync } from 'node:child_process';
|
||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
slugFromTarget,
|
slugFromTarget,
|
||||||
writeSnapshot,
|
writeSnapshot,
|
||||||
readLatestSnapshot,
|
readLatestSnapshot,
|
||||||
|
readLatestSnapshotAcrossTargets,
|
||||||
readTrend,
|
readTrend,
|
||||||
nowFilenameStamp,
|
nowFilenameStamp,
|
||||||
} from '../skill/scripts/critique-storage.mjs';
|
} from '../skill/scripts/critique-storage.mjs';
|
||||||
@@ -114,6 +115,16 @@ describe('writeSnapshot + readLatestSnapshot', () => {
|
|||||||
assert.match(latest.body, /new/);
|
assert.match(latest.body, /new/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('picks the newest snapshot across target slugs', () => {
|
||||||
|
writeSnapshot({ slug: 'home', meta: {}, body: 'old', cwd, now: new Date('2026-05-01T00:00:00Z') });
|
||||||
|
writeSnapshot({ slug: 'pricing', meta: {}, body: 'new', cwd, now: new Date('2026-05-12T00:00:00Z') });
|
||||||
|
writeFileSync(join(cwd, '.impeccable', 'critique', 'ignore.md'), '# Critique ignores\n');
|
||||||
|
writeFileSync(join(cwd, '.impeccable', 'critique', '9999-not-a-snapshot.md'), '# Draft\n');
|
||||||
|
const latest = readLatestSnapshotAcrossTargets({ cwd });
|
||||||
|
assert.equal(latest.meta.slug, 'pricing');
|
||||||
|
assert.match(latest.body, /new/);
|
||||||
|
});
|
||||||
|
|
||||||
it('does not see snapshots for a different slug', () => {
|
it('does not see snapshots for a different slug', () => {
|
||||||
writeSnapshot({ slug: 'pricing-astro', meta: { total_score: 10 }, body: 'b', cwd });
|
writeSnapshot({ slug: 'pricing-astro', meta: { total_score: 10 }, body: 'b', cwd });
|
||||||
assert.equal(readLatestSnapshot('index-astro', { cwd }), null);
|
assert.equal(readLatestSnapshot('index-astro', { cwd }), null);
|
||||||
|
|||||||
@@ -235,6 +235,53 @@ describe('cli/lib/impeccable-config', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('filterDetectionFindings normalizes every supported CSS color unit', () => {
|
||||||
|
const findings = [
|
||||||
|
{ antipattern: 'design-system-color', line: 1, ignoreValue: '#f00' },
|
||||||
|
{ antipattern: 'design-system-color', line: 2, ignoreValue: 'rgb(100% 0% 0%)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 3, ignoreValue: 'hsl(360deg 100% 50%)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 4, ignoreValue: 'hsl(180deg 100% 50%)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 5, ignoreValue: 'hsl(3.141592653589793rad 100% 50%)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 6, ignoreValue: 'hsl(0.5turn 100% 50%)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 7, ignoreValue: 'hsl(200grad 100% 50%)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 8, ignoreValue: 'rgba(255, 0, 0, 0.5)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 9, ignoreValue: 'rgb(100% 0% 0% / 50%)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 10, ignoreValue: 'hsla(0, 100%, 50%, 50%)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 11, ignoreValue: '#f008' },
|
||||||
|
];
|
||||||
|
const filtered = filterDetectionFindings(findings, {
|
||||||
|
ignoreValues: [
|
||||||
|
{ rule: 'design-system-color', value: '#ff0000' },
|
||||||
|
{ rule: 'design-system-color', value: '#00ffff' },
|
||||||
|
{ rule: 'design-system-color', value: '#ff000080' },
|
||||||
|
{ rule: 'design-system-color', value: '#ff000088' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(filtered).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('filterDetectionFindings rejects out-of-range and malformed CSS colors', () => {
|
||||||
|
const findings = [
|
||||||
|
{ antipattern: 'design-system-color', line: 1, ignoreValue: 'rgb(256 0 0)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 2, ignoreValue: 'rgb(100.1% 0% 0%)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 3, ignoreValue: 'rgba(255, 0, 0, 101%)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 4, ignoreValue: 'rgba(255, 0, 0, -0.1)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 5, ignoreValue: 'hsl(0 100 50%)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 6, ignoreValue: 'hsl(0 101% 50%)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 7, ignoreValue: 'hsl(0foo 100% 50%)' },
|
||||||
|
{ antipattern: 'design-system-color', line: 8, ignoreValue: '#ff00000' },
|
||||||
|
];
|
||||||
|
const filtered = filterDetectionFindings(findings, {
|
||||||
|
ignoreValues: [
|
||||||
|
{ rule: 'design-system-color', value: '#ff0000' },
|
||||||
|
{ rule: 'design-system-color', value: '#ff000080' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(filtered.map((finding) => finding.line)).toEqual([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||||
|
});
|
||||||
|
|
||||||
test('extractFindingIgnoreValue handles fonts, Google font URLs, and motion snippets', () => {
|
test('extractFindingIgnoreValue handles fonts, Google font URLs, and motion snippets', () => {
|
||||||
expect(extractFindingIgnoreValue({ antipattern: 'overused-font', snippet: 'Primary font: Avenir Next (80% of text)' })).toBe('avenir next');
|
expect(extractFindingIgnoreValue({ antipattern: 'overused-font', snippet: 'Primary font: Avenir Next (80% of text)' })).toBe('avenir next');
|
||||||
expect(extractFindingIgnoreValue({ antipattern: 'overused-font', snippet: 'https://fonts.googleapis.com/css2?family=Alumni+Sans:wght@700' })).toBe('alumni sans');
|
expect(extractFindingIgnoreValue({ antipattern: 'overused-font', snippet: 'https://fonts.googleapis.com/css2?family=Alumni+Sans:wght@700' })).toBe('alumni sans');
|
||||||
|
|||||||
@@ -51,6 +51,124 @@ describe('live-copy-edit-agent', () => {
|
|||||||
assert.match(prompt, /Return ONLY JSON/);
|
assert.match(prompt, /Return ONLY JSON/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('bounds and whitelists operation context in batch prompts', () => {
|
||||||
|
const huge = 'Z'.repeat(50_000);
|
||||||
|
const prompt = buildCopyEditBatchPrompt({
|
||||||
|
pageUrl: '/',
|
||||||
|
entries: [{
|
||||||
|
id: 'bounded',
|
||||||
|
pageUrl: '/',
|
||||||
|
ops: [{
|
||||||
|
classes: [huge],
|
||||||
|
originalText: 'Old',
|
||||||
|
newText: 'New',
|
||||||
|
sourceHint: {
|
||||||
|
file: 'src/App.jsx',
|
||||||
|
loc: '12:3',
|
||||||
|
nested: { payload: huge },
|
||||||
|
},
|
||||||
|
nearbyEditableTexts: [{
|
||||||
|
ref: 'body>main>span',
|
||||||
|
tag: 'span',
|
||||||
|
classes: ['label'],
|
||||||
|
text: huge,
|
||||||
|
extra: huge,
|
||||||
|
}],
|
||||||
|
contextHints: [huge],
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
const serializedBatch = prompt.split('Staged copy-edit batch:\n').pop();
|
||||||
|
const op = JSON.parse(serializedBatch).entries[0].ops[0];
|
||||||
|
assert.ok(prompt.length < 20_000, `expected compact prompt, got ${prompt.length} characters`);
|
||||||
|
assert.ok(op.classes[0].length < 400);
|
||||||
|
assert.deepEqual(op.sourceHint, {
|
||||||
|
file: 'src/App.jsx',
|
||||||
|
loc: '12:3',
|
||||||
|
line: 12,
|
||||||
|
column: 3,
|
||||||
|
});
|
||||||
|
assert.deepEqual(Object.keys(op.nearbyEditableTexts[0]).sort(), ['classes', 'ref', 'tag', 'text']);
|
||||||
|
assert.ok(op.nearbyEditableTexts[0].text.length < 400);
|
||||||
|
assert.ok(op.contextHints[0].length < 400);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('bounds batch repair, candidate, and element context', () => {
|
||||||
|
const huge = 'Z'.repeat(250_000);
|
||||||
|
const prompt = buildCopyEditBatchPrompt({
|
||||||
|
pageUrl: '/',
|
||||||
|
repair: {
|
||||||
|
status: 'needs_decision',
|
||||||
|
attempt: 2,
|
||||||
|
maxAttempts: 3,
|
||||||
|
reason: 'source_verification_failed',
|
||||||
|
pageUrl: '/pricing',
|
||||||
|
transactionId: huge,
|
||||||
|
failures: [{
|
||||||
|
entryId: 'bounded',
|
||||||
|
message: huge,
|
||||||
|
candidates: [{ file: huge, line: 12, kind: 'text' }],
|
||||||
|
failures: [{ ref: huge, reason: huge }],
|
||||||
|
checks: [{ file: huge, reason: huge }],
|
||||||
|
extra: huge,
|
||||||
|
}],
|
||||||
|
files: [huge],
|
||||||
|
extra: huge,
|
||||||
|
},
|
||||||
|
entries: [{
|
||||||
|
id: 'bounded',
|
||||||
|
element: { ref: huge, tagName: huge, id: huge, classes: [huge], textContent: huge },
|
||||||
|
ops: [{
|
||||||
|
originalText: 'Old',
|
||||||
|
newText: 'New',
|
||||||
|
sourceHint: { file: 'src/App.jsx', line: null, column: null },
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
candidates: [{
|
||||||
|
entryId: 'bounded',
|
||||||
|
ref: huge,
|
||||||
|
sourceHint: { file: huge, line: null, extra: huge },
|
||||||
|
textMatches: [{ file: huge, reason: huge, extra: huge }],
|
||||||
|
extra: huge,
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
const serializedBatch = prompt.split('Staged copy-edit batch:\n').pop();
|
||||||
|
const compact = JSON.parse(serializedBatch);
|
||||||
|
assert.ok(prompt.length < 25_000, `expected compact prompt, got ${prompt.length} characters`);
|
||||||
|
assert.deepEqual(Object.keys(compact.repair).sort(), [
|
||||||
|
'failures',
|
||||||
|
'files',
|
||||||
|
'attempt',
|
||||||
|
'maxAttempts',
|
||||||
|
'pageUrl',
|
||||||
|
'reason',
|
||||||
|
'status',
|
||||||
|
'transactionId',
|
||||||
|
].sort());
|
||||||
|
assert.equal(compact.repair.attempt, 2);
|
||||||
|
assert.equal(compact.repair.reason, 'source_verification_failed');
|
||||||
|
assert.ok(compact.repair.transactionId.length < 400);
|
||||||
|
assert.ok(compact.repair.failures[0].message.length < 400);
|
||||||
|
assert.equal(compact.repair.failures[0].entryId, 'bounded');
|
||||||
|
assert.ok(compact.repair.failures[0].candidates[0].file.length < 400);
|
||||||
|
assert.ok(compact.repair.failures[0].failures[0].ref.length < 400);
|
||||||
|
assert.ok(compact.repair.failures[0].checks[0].file.length < 400);
|
||||||
|
assert.deepEqual(Object.keys(compact.candidates[0]).sort(), [
|
||||||
|
'entryId',
|
||||||
|
'ref',
|
||||||
|
'sourceHint',
|
||||||
|
'textMatches',
|
||||||
|
]);
|
||||||
|
assert.ok(compact.candidates[0].ref.length < 400);
|
||||||
|
assert.ok(compact.candidates[0].sourceHint.file.length < 400);
|
||||||
|
assert.ok(compact.entries[0].element.ref.length < 400);
|
||||||
|
assert.ok(compact.entries[0].element.classes[0].length < 400);
|
||||||
|
assert.equal(compact.entries[0].ops[0].sourceHint.line, null);
|
||||||
|
assert.equal(compact.entries[0].ops[0].sourceHint.column, null);
|
||||||
|
});
|
||||||
|
|
||||||
it('parses partial batch results', () => {
|
it('parses partial batch results', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
parseCopyEditBatchResult('{"status":"partial","appliedEntryIds":["a"],"failed":[{"entryId":"b","reason":"ambiguous"}],"files":["src/page.js"]}'),
|
parseCopyEditBatchResult('{"status":"partial","appliedEntryIds":["a"],"failed":[{"entryId":"b","reason":"ambiguous"}],"files":["src/page.js"]}'),
|
||||||
|
|||||||
@@ -35,12 +35,17 @@ describe('pin command provider syntax', () => {
|
|||||||
const skill = fs.readFileSync(path.join(project, harness, 'skills', 'audit', 'SKILL.md'), 'utf8');
|
const skill = fs.readFileSync(path.join(project, harness, 'skills', 'audit', 'SKILL.md'), 'utf8');
|
||||||
assert.match(skill, /\/impeccable audit/);
|
assert.match(skill, /\/impeccable audit/);
|
||||||
assert.doesNotMatch(skill, /\$impeccable audit/);
|
assert.doesNotMatch(skill, /\$impeccable audit/);
|
||||||
|
assert.match(skill, /^argument-hint:/m);
|
||||||
|
assert.match(skill, /^user-invocable: true$/m);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const harness of ['.agents', '.codex']) {
|
for (const harness of ['.agents', '.codex']) {
|
||||||
const skill = fs.readFileSync(path.join(project, harness, 'skills', 'audit', 'SKILL.md'), 'utf8');
|
const skill = fs.readFileSync(path.join(project, harness, 'skills', 'audit', 'SKILL.md'), 'utf8');
|
||||||
assert.match(skill, /\$impeccable audit/);
|
assert.match(skill, /\$impeccable audit/);
|
||||||
assert.doesNotMatch(skill, /\/impeccable audit/);
|
assert.doesNotMatch(skill, /\/impeccable audit/);
|
||||||
|
assert.doesNotMatch(skill, /^argument-hint:/m);
|
||||||
|
assert.doesNotMatch(skill, /^user-invocable:/m);
|
||||||
|
assert.match(skill, /^metadata:\n argument-hint:/m);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user