Skip to content
← Public packages

@hypercubed/aksk

AKSK setup planner for coding agents: tailored install plans, live peer-tool versions, managed templates, paste-in validators.

src/resolve-versions.ts

91 lines · 3.5 KB · TypeScript
export interface VersionPins {
	'@fission-ai/openspec': string
	openwiki: string
	'openspec-skills-source': string
}

export interface ResolveInput {
	/** When true, query the npm registry for latest versions (outbound HTTPS). On failure, falls back to bundled pins with a note. Defaults to false. */
	refresh?: boolean
	/** Scope the skills install command to one host via -a (mirrors plan-setup). Omit for -a '*' (every agent). Never bare --all: it expands to --skill '*' and overrides the -s filter. */
	hosts?: string[]
}

/** Consumer openspec workflow skills. The `fission-ai/openspec` package also ships repo-maintainer skills (release-openspec, *-openspec-docs) for the openspec project itself — installing with bare `--all` pulls those in too. Filter installs to this list with `-s` (check `npx skills add --help` for repeat syntax). */
export const CONSUMER_OPENSPEC_SKILLS = [
	'openspec-propose',
	'openspec-explore',
	'openspec-new-change',
	'openspec-ff-change',
	'openspec-continue-change',
	'openspec-apply-change',
	'openspec-update-change',
	'openspec-sync-specs',
	'openspec-verify-change',
	'openspec-archive-change',
	'openspec-bulk-archive-change',
	'openspec-onboard',
]

/** Bundled pins, mirrored from the kit's references/versions.json. Safe offline default. */
export const PINS: VersionPins = {
	'@fission-ai/openspec': '^1.11.0',
	openwiki: '^0.4.3',
	'openspec-skills-source': 'fission-ai/openspec',
}

async function fetchLatest(pkg: string): Promise<string | null> {
	try {
		const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(pkg).replace('%40', '@').replace('%2F', '/')}/latest`)
		if (!res.ok) return null
		const data = (await res.json()) as { version?: string }
		return typeof data.version === 'string' ? data.version : null
	} catch {
		return null
	}
}

/**
 * Resolve AKSK peer-tool versions and the exact install commands.
 * Use when setup instructions need versions newer than a stale doc pin — call with refresh:true for live npm data, or omit it for the deterministic offline default.
 *
 * @param input - Optional { refresh } flag for live registry lookup
 * @returns Pinned ranges, ready-to-run install commands, and live versions when refresh succeeds
 *
 * @example
 * import resolveVersions from 'kody:@hypercubed/aksk/resolve-versions'
 *
 * const v = await resolveVersions({ refresh: true })
 */
export default async function resolveVersions(input: ResolveInput = {}): Promise<{
	pins: VersionPins
	consumerSkills: string[]
	installCommands: string[]
	live: Record<string, string | null>
	note: string
}> {
	const live: Record<string, string | null> = { '@fission-ai/openspec': null, openwiki: null }
	let note = 'Bundled pins (offline default, mirrored from the kit references/versions.json).'

	if (input.refresh === true) {
		const [spec, wiki] = await Promise.all([fetchLatest('@fission-ai/openspec'), fetchLatest('openwiki')])
		live['@fission-ai/openspec'] = spec
		live['openwiki'] = wiki
		note =
			spec && wiki
				? `Live from npm registry at call time; pins remain the minimum supported range.`
				: `Registry lookup failed; fell back to bundled pins.`
	}

	const scope = input.hosts?.length === 1 && input.hosts[0] !== '*' ? `-a ${input.hosts[0]}` : "-a '*'";
	return {
		pins: PINS,
		consumerSkills: CONSUMER_OPENSPEC_SKILLS,
		installCommands: [
			`npm i -g @fission-ai/openspec@${PINS['@fission-ai/openspec']} openwiki@${PINS.openwiki}`,
			`npx skills add ${PINS['openspec-skills-source']} -g ${scope} ${CONSUMER_OPENSPEC_SKILLS.map((s) => `-s ${s}`).join(' ')} -y`,
		],
		live,
		note,
	}
}