aiwg
Version:
Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo
279 lines (271 loc) • 12.2 kB
JavaScript
/**
* Best-Practices Audit Handler
*
* Launches `/best-practices-audit` — a research-grounded validation of a
* target (file, directory, or topic) against current external best practices
* with citation guardrails. The actual research is performed by the agent
* following the slash command skill in research-complete; this handler is
* a thin shim that parses flags, validates the target, computes the output
* path, and either spawns the configured provider or prints guidance for
* IDE-integrated providers.
*
* Pattern matches src/cli/handlers/sdlc-accelerate.ts — same agent-spawn
* conventions, same provider gating, same --params / --dangerous handling.
*
* @architecture @.aiwg/architecture/decisions/ADR-023-contributor-discovery-convention.md
* @issue #943
*/
import { spawn } from 'child_process';
import { existsSync } from 'fs';
import path from 'path';
import { parseAgentSpawnFlags, buildAgentArgs, getProviderConfig, isSpawnableProvider, dangerousWarning, spawnableProviders, } from '../agent-spawn.js';
/**
* Best-practices audit positional + flag parser. Distinct from
* `parseAgentSpawnFlags` because most of the flags are forwarded to the
* skill. We extract only the ones we need to validate locally; the rest
* survive in `remaining` and get appended to the slash-command prompt.
*/
function parseAuditFlags(args) {
const audit = {};
const rest = [];
for (let i = 0; i < args.length; i++) {
const a = args[i];
const next = () => args[++i];
switch (a) {
case '--focus':
audit.focus = next();
rest.push(a, audit.focus ?? '');
break;
case '--framework':
audit.framework = next();
rest.push(a, audit.framework ?? '');
break;
case '--standard':
audit.standard = next();
rest.push(a, audit.standard ?? '');
break;
case '--recency':
audit.recency = next();
rest.push(a, audit.recency ?? '');
break;
case '--depth': {
const v = next();
if (v && (v === 'quick' || v === 'standard' || v === 'deep')) {
audit.depth = v;
}
rest.push(a, v ?? '');
break;
}
case '--sources':
audit.sources = next();
rest.push(a, audit.sources ?? '');
break;
case '--exclude':
audit.exclude = next();
rest.push(a, audit.exclude ?? '');
break;
case '--cite-threshold': {
const v = next();
const n = v ? Number(v) : NaN;
if (Number.isInteger(n) && n > 0)
audit.citeThreshold = n;
rest.push(a, v ?? '');
break;
}
case '--dissent':
audit.dissent = true;
rest.push(a);
break;
case '--validate':
audit.validateMode = true;
rest.push(a);
break;
case '--output':
audit.output = next();
// Don't forward --output to the slash command; the handler
// computes/normalizes the absolute output path separately.
break;
default:
rest.push(a);
}
}
return { audit, rest };
}
/**
* Compute the default output path. Naming convention from #929 §5:
*
* .aiwg/reports/best-practices-audit-<target-slug>-<YYYY-MM-DD>.md
*
* The slug is derived from a path basename, freeform topic words, or 'audit'
* if neither produces something usable.
*/
function computeOutputPath(target, override, projectRoot) {
if (override) {
return path.isAbsolute(override) ? override : path.resolve(projectRoot, override);
}
const date = new Date().toISOString().slice(0, 10);
const slugSource = existsSync(target) ? path.basename(target).replace(/\.[^.]+$/, '') : target;
const slug = slugSource
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
.slice(0, 60) || 'audit';
return path.join(projectRoot, '.aiwg', 'reports', `best-practices-audit-${slug}-${date}.md`);
}
export class BestPracticesAuditHandler {
id = 'best-practices-audit';
name = 'Best-Practices Audit';
description = 'Research-grounded validation of a target against current external best practices';
category = 'orchestration';
aliases = ['best-practices-audit'];
async execute(ctx) {
if (ctx.args.includes('--help') || ctx.args.includes('-h')) {
return { exitCode: 0, message: this.getHelpText() };
}
// Spawn flags peeled first (--provider, --dangerous, --params).
const { opts: spawnOpts, remaining } = parseAgentSpawnFlags(ctx.args);
// Audit-specific flags peeled next; the rest carries the target +
// forwarded flags into the slash-command prompt.
const { audit, rest } = parseAuditFlags(remaining);
// Target must be the first positional argument. Without one, this is
// a usage error — the audit command requires a target.
const positionals = rest.filter(a => !a.startsWith('-'));
if (positionals.length === 0) {
return {
exitCode: 1,
message: 'best-practices-audit: missing <target>. Pass a file path, directory, or freeform topic.\n' +
'Run: aiwg best-practices-audit --help',
};
}
const target = positionals[0];
const outputPath = computeOutputPath(target, audit.output, ctx.cwd ?? process.cwd());
const provider = spawnOpts.provider ?? 'claude';
const config = getProviderConfig(provider);
const prompt = this.buildPrompt(target, rest, outputPath);
if (!isSpawnableProvider(provider)) {
return {
exitCode: 0,
message: [
`Provider '${config.name}' is not spawnable from the CLI.`,
config.guidanceMessage ?? '',
'',
`Run this in your IDE or agent panel:`,
` ${prompt}`,
'',
`Output will be written to: ${outputPath}`,
].join('\n'),
};
}
const warn = dangerousWarning(provider);
if (spawnOpts.dangerous && warn) {
console.warn(`Warning: ${warn}`);
}
const agentArgs = buildAgentArgs(prompt, spawnOpts);
return this.spawnAgent(config.binary, agentArgs, prompt, outputPath);
}
/**
* Compose the slash-command invocation. The agent skill receives the
* target and forwarded flags verbatim, plus an explicit `--output`
* pointing at the absolute output path so the skill writes to the
* handler-computed location regardless of how it interprets cwd.
*/
buildPrompt(target, rest, outputPath) {
const flagsAfterTarget = rest.slice(rest.indexOf(target) + 1).join(' ');
const targetStr = target.includes(' ') ? `"${target}"` : target;
const flagsPart = flagsAfterTarget ? ` ${flagsAfterTarget}` : '';
return `/best-practices-audit ${targetStr}${flagsPart} --output "${outputPath}"`;
}
spawnAgent(binary, args, prompt, outputPath) {
return new Promise(resolve => {
try {
const child = spawn(binary, args, { stdio: 'inherit', env: process.env });
child.on('close', code => resolve({ exitCode: code ?? 0 }));
child.on('error', err => {
if (err.code === 'ENOENT') {
resolve({
exitCode: 1,
message: this.getNotFoundMessage(binary, prompt, outputPath),
});
}
else {
resolve({
exitCode: 1,
message: `Failed to launch ${binary}: ${err.message}\n\n${this.getNotFoundMessage(binary, prompt, outputPath)}`,
error: err,
});
}
});
}
catch (err) {
resolve({
exitCode: 1,
message: `Failed to start best-practices-audit: ${err instanceof Error ? err.message : String(err)}\n\n${this.getNotFoundMessage(binary, prompt, outputPath)}`,
error: err instanceof Error ? err : new Error(String(err)),
});
}
});
}
getNotFoundMessage(binary, prompt, outputPath) {
return [
`'${binary}' not found. Install the provider CLI and try again.`,
`To run manually, open the agent and run:`,
` ${prompt}`,
``,
`Output will be written to: ${outputPath}`,
].join('\n');
}
getHelpText() {
const spawnable = spawnableProviders().join(', ');
return `
Best-Practices Audit — Research-grounded validation against current practice
USAGE:
aiwg best-practices-audit <target> [options]
aiwg best-practices-audit ".aiwg/architecture/SAD.md" --focus security
aiwg best-practices-audit "FastAPI dependency injection" --depth deep
ARGUMENTS:
<target> File path, directory, freeform topic, or issue ref
FOCUS / SCOPE:
--focus <area> security, performance, accessibility, licensing,
api-design, testing, docs, ops, compliance, ...
--framework <name> Bias toward a named stack (React, Kubernetes, ...)
--standard <name> Align to a standard (OWASP, SOC2, WCAG 2.2, ...)
RESEARCH BUDGET:
--recency <window> Default 18m. Tighten for fast-moving domains.
--depth quick|standard|deep Research effort budget (default: standard)
--sources <list> Restrict to: vendor-docs, standards-bodies,
practitioner-blogs, conference-talks, academic,
github-discussions
--exclude <list> e.g. exclude SEO-spam domains
--cite-threshold <N> Minimum distinct sources before a finding is reported (default: 2)
REPORTING:
--dissent Surface practitioner disagreement, not just consensus
--validate Re-validate existing claims in the target rather than
generating new ones
--output <path> Default: .aiwg/reports/best-practices-audit-<slug>-<date>.md
AGENT OPTIONS:
--provider <name> Agent system to use (default: claude)
Spawnable: ${spawnable}
IDE-integrated (guidance only): copilot, cursor, factory, warp, windsurf
--dangerous Enable unrestricted mode (passes provider's native flag).
--params "<args>" Pass arbitrary args verbatim to the agent binary.
ANTI-HALLUCINATION GUARDRAILS:
- No fabricated citations, DOIs, URLs, or quotes (Citation Policy enforced).
- Findings need >= --cite-threshold distinct, retrievable sources or are
downgraded.
- Conflicting sources surface as labeled disagreements, not silent picks.
- Sparse-signal honesty: confidence is downgraded rather than filled with priors.
EXAMPLES:
aiwg best-practices-audit ".aiwg/architecture/SAD.md"
aiwg best-practices-audit ".aiwg/architecture/SAD.md" --focus security --standard OWASP
aiwg best-practices-audit "src/auth/" --focus security --depth deep --dissent
aiwg best-practices-audit "FastAPI request validation patterns" --recency 6m
aiwg best-practices-audit ".aiwg/architecture/" --validate
`;
}
}
export const bestPracticesAuditHandler = new BestPracticesAuditHandler();
/**
* Internal exports for testing only. Not part of the public CLI surface.
*/
export const __test__ = { parseAuditFlags, computeOutputPath };
//# sourceMappingURL=best-practices-audit.js.map