UNPKG

@crossplatformai/skills

Version:

Reusable Agent Skills for CrossPlatform.ai projects.

544 lines (491 loc) • 15 kB
#!/usr/bin/env node import { createHash } from 'node:crypto'; import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import process from 'node:process'; import { pathToFileURL } from 'node:url'; const MODES = new Set(['portable', 'managed']); const TIERS = new Set([1, 2, 3]); const SECTION_ALIASES = { objective: /\b(objective|evidence|goal|intent|context|problem|brief|why)\b/i, outcomes: /\b(outcomes?|proof|acceptance|success|done|results?|verification)\b/i, scope: /\b(scope|boundar(?:y|ies)|non-goals?|included|excluded|edges?)\b/i, risks: /\b(risks?|rules?|constraints?|assumptions?|guardrails?)\b/i, execution: /\b(execution|implementation|quality|qa|validation|tests?|commands?|delivery|how|establish)\b/i, }; const MANAGED_IDENTITIES = [ 'userConfirmedIntent', 'repositoryVerifiedFacts', 'primaryPlannerRecommendations', 'unresolvedQuestionsDisposition', 'uxImpact', 'dxImpact', 'experienceEvidence', 'scopeAndNonGoals', 'constraintsAndRepositoryRules', 'risksAndResolvedAssumptions', 'recommendationProvenance', 'noUnresolvedImplementationDecision', 'uxDxClaimToProofMapping', ]; /** @typedef {'portable' | 'managed'} AdmissionMode */ /** @typedef {1 | 2 | 3} RiskTier */ /** @typedef {{ code: string, message: string }} Diagnostic */ /** * Normalize transport-only differences while preserving substantive Markdown. * * @param {string} input */ export function normalizeTransport(input) { const lines = input .replace(/^\uFEFF/, '') .replace(/\r\n?/g, '\n') .split('\n') .map((line) => line.replace(/[\t ]+$/u, '')); while (lines[0] === '') lines.shift(); while (lines.at(-1) === '') lines.pop(); return `${lines.join('\n')}\n`; } /** * @param {string} input */ export function digestInput(input) { return `sha256:${createHash('sha256').update(normalizeTransport(input)).digest('hex')}`; } /** * @param {string} value */ function isPlaceholder(value) { const compact = value .replace(/^[-*+]\s+/gmu, '') .replace(/[`*_#]/gu, '') .trim(); return ( compact.length < 8 || /^(?:n\/?a|none|unknown|tbd|todo|pending|unchanged|not collected)[.!]?$/iu.test( compact, ) ); } /** * @param {string} input */ function collectSections(input) { /** @type {Array<{ heading: string, body: string }>} */ const sections = []; let heading = ''; /** @type {string[]} */ let body = []; const push = () => { if (heading) sections.push({ heading, body: body.join('\n').trim() }); }; for (const line of input.split('\n')) { const markdownHeading = line.match(/^#{1,6}\s+(.+?)\s*#*$/u); const labelHeading = line.match( /^\s*(?:\*\*)?([A-Za-z][A-Za-z /&-]{2,60})(?:\*\*)?\s*:\s*$/u, ); if (markdownHeading || labelHeading) { push(); heading = (markdownHeading?.[1] ?? labelHeading?.[1] ?? '').trim(); body = []; continue; } body.push(line); } push(); return sections; } /** * @param {string} input */ function sectionCoverage(input) { const sections = collectSections(input); /** @type {Record<keyof typeof SECTION_ALIASES, boolean>} */ const coverage = { objective: false, outcomes: false, scope: false, risks: false, execution: false, }; for (const section of sections) { for (const [identity, alias] of Object.entries(SECTION_ALIASES)) { if (alias.test(section.heading) && !isPlaceholder(section.body)) { coverage[/** @type {keyof typeof SECTION_ALIASES} */ (identity)] = true; } } } coverage.objective ||= /^\s*(?:[-*]\s*)?(?:objective|goal|user intent)\s*:\s*\S.{7,}$/imu.test( input, ); coverage.outcomes ||= /^\s*(?:[-*]\s*)?(?:acceptance criteria|outcome|done when|proof|success)\s*:\s*\S.{7,}$/imu.test( input, ); coverage.scope ||= /^\s*(?:[-*]\s*)?(?:in scope|out of scope|scope(?: and non-goals?)?|non-goals?)\s*:\s*\S.{3,}$/imu.test( input, ); coverage.risks ||= /^\s*(?:[-*]\s*)?(?:risks?|rules?|constraints?)\s*:\s*\S.{3,}$/imu.test( input, ); coverage.execution ||= /^\s*(?:[-*]\s*)?(?:execution|qa|validation|commands?|implementation)\s*:\s*\S.{3,}$/imu.test( input, ); return coverage; } /** * @param {string} input * @param {AdmissionMode} mode */ function hasAuthorizingUserAuthority(input, mode) { const explicitAuthority = input.split('\n').some((line) => { if (!/(?:authorizing user|product owner)/iu.test(line)) return false; if ( /(?:authorizing user|product owner).{0,40}\b(?:not approved|unapproved|missing|absent|denied|pending)\b/iu.test( line, ) || /\b(?:not approved|unapproved|missing|absent|denied|pending)\b.{0,40}(?:authorizing user|product owner)/iu.test( line, ) ) { return false; } return /\b(?:approv(?:ed|al)|authoriz(?:ed|ation)|confirm(?:ed|ation)|granted)\b/iu.test( line, ); }); return ( explicitAuthority || (mode === 'managed' && /^\s*(?:[-*]\s*)?(?:approval )?authority source\s*(?::|-)\s*(?:fresh-context user launch|explicit same-window post-review approval|legacy preflight launch with an explicit implementation instruction|approved-plan launch)\b/imu.test( input, )) ); } /** * @param {string} input */ function hasUnresolvedProductDecision(input) { return input .split('\n') .some((line) => /^\s*(?:[-*]\s*)?(?:(?:unresolved|open)\s+(?:product\s+)?(?:decision|question)s?|(?:product\s+)?decision status)\s*(?::|-)\s*(?!none\b|resolved\b|no\b)(?:\S)/iu.test( line, ), ); } /** * @param {string} input * @param {RegExp} label */ function collectBoundaryItems(input, label) { const items = new Set(); for (const line of input.split('\n')) { const match = line.match(label); if (!match?.[1]) continue; for (const item of match[1].split(/[,;|]/u)) { const normalized = item .toLowerCase() .replace(/[`*_]/gu, '') .replace(/^(?:the|a|an)\s+/u, '') .replace(/[^a-z0-9@/_. -]/gu, '') .trim(); if (normalized.length >= 3) items.add(normalized); } } return items; } /** * @param {string} input */ function hasScopeOrRiskContradiction(input) { if ( /^\s*(?:[-*]\s*)?(?:scope|risk|boundary)\s+(?:contradiction|conflict)(?:\s+status)?\s*(?::|-)\s*(?:yes|present|unresolved|open)\b/imu.test( input, ) ) { return true; } const included = collectBoundaryItems( input, /^\s*(?:[-*]\s*)?(?:in scope|included)\s*:\s*(.+)$/iu, ); const excluded = collectBoundaryItems( input, /^\s*(?:[-*]\s*)?(?:out of scope|excluded|non-goals?)\s*:\s*(.+)$/iu, ); return [...included].some((item) => excluded.has(item)); } /** * @param {string} input */ function hasUnmetExternalPrerequisite(input) { return /^\s*(?:[-*]\s*)?(?:external\s+)?prerequisites?\s*(?::|-)\s*(?:unmet|missing|unavailable|blocked|pending)\b/imu.test( input, ); } /** * @param {string} input */ function hasUnavailableRequiredExecution(input) { return ( /^\s*(?:[-*]\s*)?(?:required\s+)?(?:command execution|commands?|execution capability|tooling)\s*(?::|-)\s*(?:unavailable|missing|cannot run|blocked)\b/imu.test( input, ) || /^\s*(?:[-*]\s*)?required\s+(?:command|tool)\b.{0,80}\b(?:unavailable|missing|cannot run)\b/imu.test( input, ) ); } /** * @param {string} input */ function hasFailedRequiredQa(input) { return /^\s*(?:[-*]\s*)?(?:required\s+qa|qa\s+status|required\s+(?:checks?|tests?))\s*(?::|-)\s*(?:failed|failing|unavailable|blocked)\b/imu.test( input, ); } /** * @param {string} input * @param {{ mode?: AdmissionMode, tier?: RiskTier }} [options] */ export function admitPacket(input, options = {}) { const mode = options.mode ?? 'portable'; const tier = options.tier ?? 2; const normalized = normalizeTransport(input); const coverage = sectionCoverage(normalized); /** @type {Diagnostic[]} */ const blockers = []; /** @type {Diagnostic[]} */ const warnings = []; /** @type {Diagnostic[]} */ const defaults = []; if (!hasAuthorizingUserAuthority(normalized, mode)) { blockers.push({ code: 'missing-authorizing-user-authority', message: 'Record Authorizing User approval for this objective before Build.', }); } if (!coverage.objective) { blockers.push({ code: 'missing-objective', message: 'State the accepted objective and enough evidence to implement it.', }); } if (hasUnresolvedProductDecision(normalized)) { blockers.push({ code: 'unresolved-product-decision', message: 'Resolve the open product decision before Build.', }); } if (!coverage.outcomes) { blockers.push({ code: 'missing-acceptance-outcomes', message: 'Add observable acceptance outcomes and the proof that will establish them.', }); } if (!coverage.scope) { blockers.push({ code: 'missing-material-boundaries', message: 'State the material scope and boundaries for the objective.', }); } if (hasScopeOrRiskContradiction(normalized)) { blockers.push({ code: 'contradictory-scope-or-risk', message: 'Resolve contradictory scope, boundary, or risk statements before Build.', }); } if (hasUnmetExternalPrerequisite(normalized)) { blockers.push({ code: 'unmet-external-prerequisite', message: 'Satisfy or explicitly remove the current external prerequisite before Build.', }); } if (hasUnavailableRequiredExecution(normalized)) { blockers.push({ code: 'required-execution-unavailable', message: 'Restore the command capability required to implement or verify this objective.', }); } if (hasFailedRequiredQa(normalized)) { blockers.push({ code: 'required-qa-failed', message: 'Resolve the failed required QA before admission.', }); } const normalizedLabels = Object.entries(coverage) .filter(([, present]) => present) .map(([identity]) => identity); if (normalizedLabels.length > 0 && normalizedLabels.length < 5) { warnings.push({ code: 'compact-section-normalization', message: 'Substantive content was normalized into the compact Post sections despite presentation differences.', }); } if (!coverage.risks) { defaults.push({ code: 'no-additional-risk-rules', message: 'No additional risk rule was declared; repository rules and stated boundaries apply.', }); } if (!coverage.execution) { defaults.push({ code: 'adaptive-execution-and-qa', message: 'Build selects implementation tactics and a proportionate final QA manifest from the completed diff.', }); } if (!/\broadmap\b/iu.test(normalized)) { defaults.push({ code: 'no-roadmap-dependency', message: 'No roadmap dependency is assumed for this objective.', }); } if (mode === 'managed') { defaults.push({ code: 'managed-pbs-v1-identity-map', message: `Normalize the compact packet to Workspace PBS v1 identities: ${MANAGED_IDENTITIES.join(', ')}.`, }); } return { inputDigest: digestInput(normalized), mode, tier, blockers, warnings, defaults, }; } /** * @param {string[]} argv */ function parseArguments(argv) { /** @type {{ file?: string, mode: AdmissionMode, tier: RiskTier, json: boolean, help: boolean }} */ const parsed = { mode: 'portable', tier: 2, json: false, help: false, }; for (let index = 0; index < argv.length; index += 1) { const argument = argv[index]; if (argument === '--json') { parsed.json = true; } else if (argument === '--help' || argument === '-h') { parsed.help = true; } else if (argument === '--file') { const file = argv[index + 1]; if (!file || file.startsWith('--')) throw new Error('--file requires a path'); parsed.file = file; index += 1; } else if (argument === '--mode') { const mode = argv[index + 1]; if (!mode || !MODES.has(mode)) { throw new Error('--mode must be portable or managed'); } parsed.mode = /** @type {AdmissionMode} */ (mode); index += 1; } else if (argument === '--tier') { const tier = Number(argv[index + 1]); if (!TIERS.has(tier)) throw new Error('--tier must be 1, 2, or 3'); parsed.tier = /** @type {RiskTier} */ (tier); index += 1; } else { throw new Error(`unknown argument: ${argument}`); } } return parsed; } const USAGE = `Usage: pbs-admit [--file <path>] [--mode portable|managed] [--tier 1|2|3] [--json] Reads a Post packet from --file or stdin. It performs no writes, Git operations, or network calls.`; /** * @param {ReturnType<typeof admitPacket>} result */ function formatHumanResult(result) { const lines = [ result.blockers.length === 0 ? 'PBS admission: accepted' : 'PBS admission: blocked', `Input digest: ${result.inputDigest}`, `Mode: ${result.mode}`, `Risk tier: ${result.tier}`, ]; for (const [label, diagnostics] of [ ['Blockers', result.blockers], ['Warnings', result.warnings], ['Defaults', result.defaults], ]) { lines.push(`${label}:`); if (diagnostics.length === 0) { lines.push('- none'); } else { lines.push( ...diagnostics.map((item) => `- ${item.code}: ${item.message}`), ); } } return lines.join('\n'); } /** * @param {string[]} argv */ export function run(argv) { let parsed; try { parsed = parseArguments(argv); } catch (error) { const message = error instanceof Error ? error.message : String(error); const wantsJson = argv.includes('--json'); process.stderr.write( wantsJson ? `${JSON.stringify({ error: message })}\n` : `${message}\n${USAGE}\n`, ); return 2; } if (parsed.help) { process.stdout.write(`${USAGE}\n`); return 0; } let input; try { input = parsed.file ? readFileSync(resolve(parsed.file), 'utf8') : readFileSync(0, 'utf8'); } catch (error) { const message = error instanceof Error ? error.message : String(error); process.stderr.write( parsed.json ? `${JSON.stringify({ error: `unable to read input: ${message}` })}\n` : `unable to read input: ${message}\n`, ); return 2; } const result = admitPacket(input, { mode: parsed.mode, tier: parsed.tier }); process.stdout.write( parsed.json ? `${JSON.stringify(result)}\n` : `${formatHumanResult(result)}\n`, ); return result.blockers.length === 0 ? 0 : 1; } const isDirectExecution = process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href; if (isDirectExecution) process.exitCode = run(process.argv.slice(2));