Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/agents/.config/eslint/deferred-lint-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ export const deferredLintRules = {
'unicorn/no-duplicate-loops': 'warn',
'unicorn/no-incorrect-template-string-interpolation': 'warn',
'unicorn/no-top-level-assignment-in-function': 'warn',
'unicorn/no-unreadable-for-of-expression': 'warn',
'unicorn/no-unsafe-string-replacement': 'warn',
'unicorn/prefer-await': 'warn',
'unicorn/prefer-iterator-to-array': 'warn',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@
// it is constrained by no vetted-closure rule.
it('keeps every standalone artifact out of the collections’ combined closure', async () => {
const collections = await readExplicitCollections(contentDir);
const closure = await resolveClosure({ collection: [...collections.keys()] }, libraryResolver(contentDir));

Check warning on line 87 in packages/agents/content/__tests__/collection-dispositions.unit.test.ts

View workflow job for this annotation

GitHub Actions / code-quality / Code quality

Prefer `Iterator#toArray()` over a temporary spread array

const defects = findClosureDefects(
'every collection',
listClosureIds(closure),
[...collections.keys()],

Check warning on line 92 in packages/agents/content/__tests__/collection-dispositions.unit.test.ts

View workflow job for this annotation

GitHub Actions / code-quality / Code quality

Prefer `Iterator#toArray()` over a temporary spread array
buildClaimMap(collections, Object.keys(STANDALONE)),
);

Expand Down Expand Up @@ -340,7 +340,8 @@
async function readExplicitCollections(contentDir: string): Promise<ReadonlyMap<string, ArtifactDependencies>> {
const collectionsDir = path.join(contentDir, ARTIFACT_TYPES.collection.contentPath);
const found = new Map<string, ArtifactDependencies>();
for (const file of (await listVisibleMarkdownFiles(collectionsDir)).toSorted()) {
const files = (await listVisibleMarkdownFiles(collectionsDir)).toSorted();
for (const file of files) {
const slug = path.basename(file, '.md');
const members = readMembers(await readFile(path.join(collectionsDir, file), 'utf8'), `collection ${slug}`);
if (members.kind === 'explicit') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ describe('comment-discipline reach', () => {

it('no content file reaches the doctrine by reference', async () => {
const violations: Array<string> = [];
for (const file of await listMarkdownFiles(CONTENT_ROOT)) {
const files = await listMarkdownFiles(CONTENT_ROOT);
for (const file of files) {
const content = await readFile(file, 'utf8');
for (const reference of RETIRED_REFERENCES) {
if (content.includes(reference)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ async function findRulebookRejections(): Promise<ReadonlyArray<string>> {

/** Recursively collects installable host `.md` files, skipping `_partials/` at any depth and dotfiles. */
async function collectHostFiles(dir: string, out: Array<string>): Promise<void> {
for (const entry of await readdir(dir, { withFileTypes: true })) {
const entries = await readdir(dir, { withFileTypes: true });
for (const entry of entries) {
if (entry.name === '_partials' || entry.name.startsWith('.') || isTestDirectory(entry.name)) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ const NBSP = '\u{A0}';
describe('content rendering', () => {
it('no Markdown under content/ carries a non-breaking space', async () => {
const offenders: Array<string> = [];
for (const relativePath of await listContentMarkdown()) {
const relativePaths = await listContentMarkdown();
for (const relativePath of relativePaths) {
const body = await readFile(path.join(CONTENT_ROOT, relativePath), 'utf8');
if (body.includes(NBSP)) {
offenders.push(relativePath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ const levelByPartial = new Map<string, number | undefined>();
describe('injection-point placement', () => {
it('no directive reparents the section following it', async () => {
const violations: Array<string> = [];
for (const relativePath of await listContentMarkdown()) {
const relativePaths = await listContentMarkdown();
for (const relativePath of relativePaths) {
violations.push(...(await findViolations(relativePath)));
}

Expand All @@ -78,7 +79,8 @@ async function findViolations(relativePath: string): Promise<ReadonlyArray<strin
const violations: Array<string> = [];
const open: Array<Injection> = [];

for (const token of await readStructure(relativePath)) {
const tokens = await readStructure(relativePath);
for (const token of tokens) {
if (token.kind === 'injection') {
open.push(token);
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ describe('prose-line-breaks reach', () => {

it('is stated in no content file but the partial', async () => {
const violations: Array<string> = [];
for (const file of await listMarkdownFiles(CONTENT_ROOT)) {
const files = await listMarkdownFiles(CONTENT_ROOT);
for (const file of files) {
const relativePath = path.relative(CONTENT_ROOT, file);
if (relativePath === PARTIAL) continue;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ describe('shared guidance references', () => {
const deployed = new Set(await listDeployedSkillNames());
const violations: Array<string> = [];

for (const file of await listMarkdownFiles(SHARED_GUIDANCE_ROOT)) {
const files = await listMarkdownFiles(SHARED_GUIDANCE_ROOT);
for (const file of files) {
const content = await readFile(file, 'utf8');
for (const slug of collectSkillReferences(content)) {
if (!deployed.has(slug)) {
Expand Down
6 changes: 4 additions & 2 deletions packages/agents/content/__tests__/spec-inlining.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,17 @@ describe('output-shaping spec inlining', () => {

it('inlines each spec exactly once', async () => {
const expanded = await expandSkill(slug);
for (const heading of new Set(specs.map((spec) => spec.heading))) {
const headings = new Set(specs.map((spec) => spec.heading));
for (const heading of headings) {
expect(countOccurrences(expanded, heading), `${slug} repeats "${heading}"`).toBe(1);
}
});
});

it('no skill still links to a relocated spec', async () => {
const violations: Array<string> = [];
for (const entry of await readdir(SKILLS_ROOT, { withFileTypes: true })) {
const entries = await readdir(SKILLS_ROOT, { withFileTypes: true });
for (const entry of entries) {
// `_`-prefixed entries are support directories; a directory with no `SKILL.md` (e.g. a bundled helper) is not
// a skill either. Neither can carry a spec link.
if (!entry.isDirectory() || entry.name.startsWith('_')) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@
/** Reports every argument-position store name across the Markdown of the vetted collection's closure. */
async function findViolations(contentDir: string): Promise<ReadonlyArray<Violation>> {
const violations: Array<Violation> = [];
for (const relativePath of await listClosureFiles(contentDir)) {
const relativePaths = await listClosureFiles(contentDir);
for (const relativePath of relativePaths) {
const lines = (await readFile(path.join(contentDir, relativePath), 'utf8')).split('\n');
let isFenced = false;
for (const [index, line] of lines.entries()) {
Expand Down Expand Up @@ -173,13 +174,14 @@

/** Extracts the content of each backtick-delimited code span on a line. */
function listCodeSpans(text: string): Array<string> {
return [...text.matchAll(CODE_SPAN_PATTERN)].map((match) => match[1] ?? '');

Check warning on line 177 in packages/agents/content/__tests__/vetted-store-conventions.unit.test.ts

View workflow job for this annotation

GitHub Actions / code-quality / Code quality

Prefer `Iterator#toArray()` over a temporary spread array
}

/** Lists every Markdown file under `relativeDir`, recursively, as paths relative to `root`. */
async function listMarkdownFilesUnder(root: string, relativeDir: string): Promise<Array<string>> {
const foundFiles: Array<string> = [];
for (const entry of await readDirEntries(path.join(root, relativeDir))) {
const entries = await readDirEntries(path.join(root, relativeDir));
for (const entry of entries) {
const relativePath = path.join(relativeDir, entry.name);
if (entry.isDirectory()) {
foundFiles.push(...(await listMarkdownFilesUnder(root, relativePath)));
Expand All @@ -192,7 +194,7 @@

/** Reads each value a store flag takes within one span of text, in either the spaced or the `=` form. */
function readFlagValues(text: string): Array<string> {
return [...text.matchAll(STORE_FLAG_PATTERN)].flatMap((match) => (match[1] === undefined ? [] : [match[1]]));

Check warning on line 197 in packages/agents/content/__tests__/vetted-store-conventions.unit.test.ts

View workflow job for this annotation

GitHub Actions / code-quality / Code quality

Prefer `Iterator#toArray()` over a temporary spread array
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,12 @@ describe('install stale-file pruning', () => {
for (const [name, body] of Object.entries(shared)) {
await writeFile(path.join(contentDir, 'guidance', 'shared', name), body, 'utf8');
}
for (const [name, body] of Object.entries(options.scripts ?? {})) {
const scripts = options.scripts ?? {};
for (const [name, body] of Object.entries(scripts)) {
await writeFile(path.join(contentDir, 'scripts', name), body, 'utf8');
}
for (const [dirName, files] of Object.entries(options.supportDirs ?? {})) {
const supportDirs = options.supportDirs ?? {};
for (const [dirName, files] of Object.entries(supportDirs)) {
const supportDir = path.join(contentDir, 'skills', dirName);
await mkdir(supportDir, { recursive: true });
for (const [fileName, body] of Object.entries(files)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ async function collectBareRelativeLinks(baseDir: string): Promise<Array<{ file:

/** Walks `dir`, invoking `visit` for each non-symlinked `.md` file. Symlinks are skipped because readFile follows them. */
async function walkMarkdownFiles(dir: string, visit: (filePath: string) => Promise<void>): Promise<void> {
for (const entry of await readdir(dir)) {
const entries = await readdir(dir);
for (const entry of entries) {
const full = path.join(dir, entry);
const info = await lstat(full);
if (info.isSymbolicLink()) {
Expand Down
3 changes: 2 additions & 1 deletion packages/agents/src/commands/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ async function installSupportDirectories(
// Install non-skill support directories (e.g. `_data`, which skills reference at runtime by absolute path). What
// counts as one is `listSupportEntries`, shared with `validate` so the pass that checks these and the pass that
// deploys them cannot come to disagree about which entries they are.
for (const entry of await listSupportEntries(skillsSrcDir)) {
const supportEntries = await listSupportEntries(skillsSrcDir);
for (const entry of supportEntries) {
const result = await installSkillEntry(
path.join(skillsSrcDir, entry),
path.join(skillsDestDir, entry),
Expand Down
12 changes: 8 additions & 4 deletions packages/agents/src/commands/library-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@
async function listCollections(contentDir: string): Promise<Array<ArtifactEntry>> {
const dir = path.join(contentDir, ARTIFACT_TYPES.collection.contentPath);
const entries: Array<ArtifactEntry> = [];
for (const file of await listVisibleMarkdownFiles(dir)) {
const files = await listVisibleMarkdownFiles(dir);
for (const file of files) {
const content = await readFile(path.join(dir, file), 'utf8');
const entry = buildEntryOrSkip('collection', file, () => {
const meta = readNameAndDescription(content);
Expand All @@ -174,7 +175,8 @@
async function listRulebooks(contentDir: string): Promise<Array<ArtifactEntry>> {
const dir = path.join(contentDir, ARTIFACT_TYPES.rulebook.contentPath);
const entries: Array<ArtifactEntry> = [];
for (const file of await listVisibleMarkdownFiles(dir)) {
const files = await listVisibleMarkdownFiles(dir);
for (const file of files) {
const content = await readFile(path.join(dir, file), 'utf8');
const entry = buildEntryOrSkip('rulebook', file, () => {
const { rulebook } = parseRulebookFile(content, file);
Expand All @@ -195,7 +197,8 @@
async function listSkills(contentDir: string): Promise<Array<ArtifactEntry>> {
const dir = path.join(contentDir, ARTIFACT_TYPES.skill.contentPath);
const entries: Array<ArtifactEntry> = [];
for (const name of await listSkillDirectories(dir)) {
const names = await listSkillDirectories(dir);
for (const name of names) {
const content = await readFile(path.join(dir, name, 'SKILL.md'), 'utf8');
const entry = buildEntryOrSkip('skill', name, () => {
const meta = readNameAndDescription(content);
Expand All @@ -216,7 +219,8 @@
async function listSubagents(contentDir: string): Promise<Array<ArtifactEntry>> {
const dir = path.join(contentDir, ARTIFACT_TYPES.subagent.contentPath);
const entries: Array<ArtifactEntry> = [];
for (const file of await listVisibleMarkdownFiles(dir)) {
const files = await listVisibleMarkdownFiles(dir);
for (const file of files) {
const content = await readFile(path.join(dir, file), 'utf8');
const entry = buildEntryOrSkip('subagent', file, () => {
const meta = readNameAndDescription(content);
Expand Down Expand Up @@ -283,7 +287,7 @@
function wrapText(text: string, width: number): Array<string> {
const lines: Array<string> = [];
let current = '';
for (const word of text.split(/\s+/).filter(Boolean)) {

Check warning on line 290 in packages/agents/src/commands/library-list.ts

View workflow job for this annotation

GitHub Actions / code-quality / Code quality

Do not use `.filter()` directly in a `for…of` loop header. It creates an intermediate array before the loop
if (current === '') {
current = word;
} else if (current.length + 1 + word.length <= width) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ describe('library invocation edges', () => {
const literalRefRe = /(?<![\w./])\/([a-z][a-z0-9-]*)(?![\w/.-])/g;
const offenders: Array<string> = [];

for (const file of await listMarkdownFilesRecursively(contentDir)) {
const files = await listMarkdownFilesRecursively(contentDir);
for (const file of files) {
const body = (await readFile(file, 'utf8')).replace(tokenRe, '');
for (const [, ref] of body.matchAll(literalRefRe)) {
if (ref !== undefined && known.has(ref)) {
Expand All @@ -95,7 +96,8 @@ describe('library invocation edges', () => {
/** Lists every markdown file under `dir` recursively — the full set of deployed content the completeness guard scans. */
async function listMarkdownFilesRecursively(dir: string): Promise<Array<string>> {
const found: Array<string> = [];
for (const entry of await readdir(dir, { withFileTypes: true })) {
const entries = await readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
found.push(...(await listMarkdownFilesRecursively(full)));
Expand Down
6 changes: 4 additions & 2 deletions packages/agents/src/lib/content-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,8 @@ async function renderSupportEntries(
const skillsDir = path.join(root, ARTIFACT_TYPES.skill.contentPath);
const raised: Array<HarnessDefect> = [];

for (const name of await listSupportEntries(skillsDir)) {
const supportEntries = await listSupportEntries(skillsDir);
for (const name of supportEntries) {
const relPath = `${ARTIFACT_TYPES.skill.contentPath}/${name}`;
try {
await renderSupportEntry(path.join(skillsDir, name), name, root, skillContext);
Expand Down Expand Up @@ -420,7 +421,8 @@ async function resolveSeedClosures(
const reached = { rulebook: new Set<string>(), skill: new Set<string>(), subagent: new Set<string>() };

for (const type of ARTIFACT_TYPE_VALUES) {
for (const slug of seeds[type] ?? []) {
const slugs = seeds[type] ?? [];
for (const slug of slugs) {
const seed: DirectArtifacts = { [type]: [slug] };
try {
const closure = await resolveClosure(seed, resolver);
Expand Down
6 changes: 4 additions & 2 deletions packages/agents/src/lib/dependency-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,17 @@ export async function resolveClosure(direct: DirectArtifacts, resolver: SourceRe
onPath.add(id);
const edges = await readArtifactEdges(type, slug, resolver);
for (const edgeType of ARTIFACT_TYPE_VALUES) {
for (const edgeSlug of edges[edgeType] ?? []) {
const edgeSlugs = edges[edgeType] ?? [];
for (const edgeSlug of edgeSlugs) {
await visit(edgeType, edgeSlug, [...trail, id]);
}
}
onPath.delete(id);
}

for (const type of ARTIFACT_TYPE_VALUES) {
for (const slug of direct[type] ?? []) {
const slugs = direct[type] ?? [];
for (const slug of slugs) {
await visit(type, slug, []);
}
}
Expand Down
3 changes: 2 additions & 1 deletion packages/agents/src/lib/library-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ export async function isSkillDirectory(entryDir: string): Promise<boolean> {
*/
export async function listSkillDirectories(skillsDir: string): Promise<Array<string>> {
const names: Array<string> = [];
for (const name of await listVisibleSubdirectories(skillsDir)) {
const subdirectoryNames = await listVisibleSubdirectories(skillsDir);
for (const name of subdirectoryNames) {
if (await isSkillDirectory(path.join(skillsDir, name))) {
names.push(name);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/agents/src/lib/rendered-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ async function copyFileIfChanged(srcPath: string, destPath: string): Promise<voi
* dropped files — and the directories that held them — do not linger across re-deploys.
*/
async function pruneOrphans(destDir: string, relDir: string, expectedFiles: ReadonlySet<string>): Promise<void> {
for (const entry of await readdir(path.join(destDir, relDir), { withFileTypes: true })) {
const entries = await readdir(path.join(destDir, relDir), { withFileTypes: true });
for (const entry of entries) {
const rel = relDir === '' ? entry.name : `${relDir}/${entry.name}`;
const absPath = path.join(destDir, rel);
if (entry.isDirectory()) {
Expand Down
3 changes: 2 additions & 1 deletion packages/agents/src/lib/skill-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ async function collectEntries(
context: SkillDeployContext,
out: Array<RenderedSkillEntry>,
): Promise<void> {
for (const entry of await readdir(dir, { withFileTypes: true })) {
const entries = await readdir(dir, { withFileTypes: true });
for (const entry of entries) {
if (entry.name === '_partials' || isTestDirectory(entry.name) || entry.name.startsWith('.')) {
continue;
}
Expand Down
6 changes: 4 additions & 2 deletions packages/agents/src/lib/support-deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export async function renderSourceSupport(
const skillsDir = path.join(sourceDir, ARTIFACT_TYPES.skill.contentPath);
const rendered: Array<RenderedSkillEntry> = [];

for (const name of await listSupportEntries(skillsDir)) {
const supportEntries = await listSupportEntries(skillsDir);
for (const name of supportEntries) {
const srcPath = path.join(skillsDir, name);
const entry = await renderSupportEntry(srcPath, name, sourceDir, context);
switch (entry.kind) {
Expand Down Expand Up @@ -68,7 +69,8 @@ export async function retractUndeclaredSourceSupport(
sourcesRoot: string,
outcome: SourceSupportOutcome,
): Promise<void> {
for (const target of await listUndeclaredSourceSupport(sourcesRoot, outcome)) {
const targets = await listUndeclaredSourceSupport(sourcesRoot, outcome);
for (const target of targets) {
await rm(target, { recursive: true, force: true });
}
}
Expand Down
Loading