Skip to content

Fix dashboard contract follow-ups - #14

Merged
cyhhao merged 4 commits into
mainfrom
fix/dashboard-contract-followups
May 30, 2026
Merged

Fix dashboard contract follow-ups#14
cyhhao merged 4 commits into
mainfrom
fix/dashboard-contract-followups

Conversation

@cyhhao

@cyhhao cyhhao commented May 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds registry-sourced version checks/updates for dashboard JSON flows using SKILL.md frontmatter versions.
  • Adds askill add --skill <name> for non-interactive local multi-skill installs.
  • Makes canonical .agents/skills/<name> re-linking idempotent while preserving existing lock source metadata.

Tests

  • npm run build
  • npm test
  • node -e \"JSON.parse(require('fs').readFileSync('docs/json-contracts/askill-cli-json.schema.json','utf8')); console.log('schema ok')\"
  • npm run test:e2e:build
  • docker run --rm askill-test test/e2e/run.sh test_dashboard_json_contracts test_add_skill_selector_local test_add_canonical_path_relinks_agent
  • npm run test:e2e (207 passed, 0 failed, 1 skipped existing production collection fixture)

Follow-up for #11.

@cyhhao

cyhhao commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d2fb612fef

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli.ts Outdated

function compareVersionStrings(localVersion: string, remoteVersion: string): number {
const parse = (version: string): number[] | null => {
const match = version.trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle prerelease suffixes when comparing versions

For registry-installed skills whose versions include prerelease tags, this parser strips the suffix and compares only major/minor/patch. As a result, upgrading from 1.0.0-alpha to 1.0.0 is reported as up_to_date, so askill check --json and askill update --json skip the stable registry release even though SemVer orders prereleases below the corresponding normal version. Preserve and compare prerelease identifiers, or delegate to a SemVer comparator, before using this status to decide whether to update.

Useful? React with 👍 / 👎.

Comment thread src/cli.ts
Comment on lines +2776 to +2778
if (entry.sourceType === 'registry') {
results.push(await checkRegistrySkillVersion(name, entry, scope, base));
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Route interactive updates through registry checks

When a registry-installed skill has an older local version, this new registry check only feeds buildCheckPayload, so it is used by the --json paths only. The normal askill check / askill update -y loops below still gate on GitHub sources and therefore report registry skills as uncheckable or say nothing is available to update. Share this registry version check with the non-JSON update flow so CLI users get the same update behavior as dashboard integrations.

Useful? React with 👍 / 👎.

Comment thread src/cli.ts
const parsed = parseSource(source);

const applyExplicitSkillFilter = (skills: DiscoveredSkill[]): DiscoveredSkill[] => {
return options.skill ? filterSkills(skills, [options.skill]) : skills;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply --skill to registry refs inside collections

When --skill is used with a shared collection whose entries resolve to registry slugs, the recursive resolveSkills() call reaches the registry branch, but this new filter is only applied in the local/Git paths. That means a non-interactive command such as askill add col:owner/tools --skill formatter -y --json still installs every registry-backed collection entry instead of the requested skill. Apply the explicit filter after registry resolution, or filter the collection's resolved skills before returning.

Useful? React with 👍 / 👎.

@cyhhao

cyhhao commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Pushed follow-up commit 389d788.

What changed:

  • Added E2E coverage for --skill against registry-backed collection refs.
  • Added E2E coverage for non-JSON registry check / update -y.
  • Changed registry update E2E to cover prerelease-to-stable SemVer ordering.
  • Routed non-JSON check/update through the same registry-aware check/update path as --json.
  • Applied --skill filters consistently across registry, collection, and GitHub API fallback resolution.

Validation:

  • npm run build
  • npm test (27 passed)
  • targeted Docker E2E (15 passed)
  • full Docker E2E (216 passed, 0 failed, 1 skipped existing production collection fixture)
  • JSON contract schema parse

This addresses the three Codex P2 review comments and solidifies the new PR13/PR14 behavior in E2E.

@cyhhao

cyhhao commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 389d788bc2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli.ts
}

async function getInstalledSkillVersion(skillName: string, globalScope: boolean): Promise<string | null> {
const installedSkills = await listInstalledSkills({ global: globalScope });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Read copy-mode registry versions from agent dirs

When a registry skill was installed with --copy, installSkill writes only to the agent-specific directory and does not create the canonical .agents/skills/<skill> entry that listInstalledSkills() enumerates. In that scenario this returns no installed skill, so checkRegistrySkillVersion() reports no local version recorded and both askill check and askill update can never update a valid copy-mode registry install. The version lookup needs to fall back to the installed agent paths or the lock's selected agents instead of only canonical skills.

Useful? React with 👍 / 👎.

Comment thread src/cli.ts
const parsed = parseSource(source);

const applyExplicitSkillFilter = (skills: DiscoveredSkill[]): DiscoveredSkill[] => {
return options.skill ? filterSkills(skills, [options.skill]) : skills;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fail when --skill matches nothing

If the user passes a typo such as askill add ./skills --skill formater -y --json, this filter silently returns an empty list and the later install path treats it like a successful no-op (ok: true with no skills) rather than an invalid selection. For CI/dashboard callers using the new non-interactive selector, that makes a requested install appear successful even though no skill was installed; the explicit --skill path should produce a non-zero/error when the source had skills but none matched.

Useful? React with 👍 / 👎.

Comment thread src/cli.ts
Comment on lines 345 to +348
options.all = true;
} else if (arg === '--json') {
options.json = true;
} else if (arg === '--skill') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require --skill to have a value

When --skill is present without a following value, for example askill add ./skills --skill -y --json, this branch leaves options.skill unset and the non-interactive install path proceeds as if no selector was requested, installing every discovered skill. Since this option is meant to constrain multi-skill installs, the parser should reject the missing value instead of silently broadening the install.

Useful? React with 👍 / 👎.

Comment thread src/cli.ts Outdated
}

function latestRegistrySlug(source: string): string {
const match = source.match(/^(@[^/]+\/[^@/]+)(?:@[^/]+)?$/);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Respect registry version pins during updates

For a registry install whose lock source includes a pinned version or range, such as @anthropic/memory@^1.0.0, this strips the constraint before checking and updating. If the registry's latest release is 2.0.0, askill check will report an update and askill update will install it even though the original source explicitly constrained the allowed version; preserve the version/range when querying or filter the latest result with semver.

Useful? React with 👍 / 👎.

Comment thread src/cli.ts Outdated
const installErrors: string[] = [];

for (const agent of targetAgents) {
const result = await installSkill(resolvedName, content, agent, { mode: 'symlink', global: lockOptions.global });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep registry updates on the locked skill name

If a registry skill's remote SKILL.md changes its frontmatter name, this installs the update into a new directory while the lock entry is still updated under check.name and the old skill directory is left in place. Subsequent checks continue reading the old installation and report the same update again, so updates never converge for renamed registry skills; either install in place using the locked name or migrate/remove the old lock and directory together.

Useful? React with 👍 / 👎.

@cyhhao

cyhhao commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Pushed follow-up commit f98d7aa addressing the latest Codex P2 review comments.

Fixes:

  • --skill now fails with structured errors when the value is missing or no skill matches.
  • Registry copy-mode installs now read local versions from agent directories, not only canonical .agents/skills.
  • Registry version/range pins are respected during check/update; out-of-range latest releases are skipped.
  • Registry updates install using the locked skill name/path so renamed remote frontmatter converges instead of creating a second directory.

New E2E coverage:

  • test_add_skill_selector_errors
  • test_registry_copy_mode_check_update
  • test_registry_version_pin_blocks_major_update
  • test_registry_update_keeps_locked_skill_name

Validation:

  • npm run build
  • npm test (27 passed)
  • JSON contract schema parse
  • targeted Docker E2E (29 passed)
  • full Docker E2E (230 passed, 0 failed, 1 skipped existing production collection fixture)

@cyhhao

cyhhao commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f98d7aa23b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli.ts
Comment on lines +2755 to +2757
async function getRegistrySkillVersion(source: string): Promise<string | null> {
const content = await api.getSkillRaw(latestRegistrySlug(source));
return stringOrNull(parseSkillMd(content).frontmatter.version);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve registry ranges when fetching update candidates

When a locked registry source includes a semver range such as @scope/skill@^1.0.0, this helper strips the range before calling the registry, so askill check queries the latest overall version rather than the latest version satisfying the locked range. If 1.1.0 exists and 2.0.0 is the overall latest, the later semver.satisfies check marks the skill up_to_date because 2.0.0 is outside the range, causing users to miss the valid 1.x update; the update path also uses the same stripped slug. Query the range-aware source (or otherwise resolve the newest matching version) instead of dropping the range.

Useful? React with 👍 / 👎.

Comment thread src/cli.ts Outdated
Comment on lines +3056 to +3057
for (const agent of targetAgents) {
const result = await installSkill(check.name, content, agent, { mode: 'symlink', global: lockOptions.global });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve copy-mode installs during registry updates

For registry skills originally installed with --copy, askill update now always calls installSkill with mode: 'symlink', which replaces the copied agent directory with a symlink and creates the canonical .agents/skills copy. This changes the install layout for users who explicitly chose copy mode (for example to avoid symlinks or keep per-agent copies) as soon as a registry update is applied; detect the existing layout or recorded install mode before forcing symlink mode.

Useful? React with 👍 / 👎.

Comment thread src/cli.ts Outdated
Comment on lines 442 to 443
if (collectionItems.length > 0 && resolvedSkills.length === 0) {
throw new Error(`Collection ${collectionOwner}/${collectionHandle} does not contain any installable skills`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return a not-found result for unmatched collection selectors

When a collection item lacks skillName, using --skill with a non-existent name still includes that item in collectionItems; after each candidate resolves to an empty filtered result, this throw happens before runInstallJson can emit the intended SKILL_NOT_FOUND response. In that collection metadata shape, askill add col:... --skill missing --json returns UNHANDLED_ERROR instead of the structured selector error, so dashboards cannot reliably handle no-match cases.

Useful? React with 👍 / 👎.

Comment thread src/cli.ts Outdated
Comment on lines +711 to +712
if (options.skill) {
printJsonError('SKILL_NOT_FOUND', `Skill "${options.skill}" not found in source`, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge Clean up cloned repos before exiting on selector misses

When --skill filters a cloned Git/GitHub source down to zero matches, tempDir has already been created, but printJsonError calls process.exit(1) from inside the try block so the finally cleanup below is bypassed. Repeated JSON-mode selector misses against remote repos will leave cloned temp directories behind; emit the error in a way that lets the existing cleanup run first.

Useful? React with 👍 / 👎.

@cyhhao

cyhhao commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

Updated PR #14 with latest Codex review follow-ups.

Changes:

  • Preserve registry semver ranges when fetching check/update candidates.
  • Preserve copy-mode registry installs during update instead of converting to canonical symlink layout.
  • Return structured SKILL_NOT_FOUND for unmatched collection --skill selectors.
  • Let JSON selector misses unwind through cleanup so cloned temp dirs are removed.
  • Added targeted E2E coverage for all four regressions.

Validation:

  • npm run build: passed
  • npm test: 27 passed
  • schema parse: passed
  • targeted Docker E2E: 24 passed, 0 failed
  • full Docker E2E: 237 passed, 0 failed, 1 skipped

@cyhhao

cyhhao commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@cyhhao
cyhhao merged commit 8b9ff81 into main May 30, 2026
1 check passed
@cyhhao
cyhhao deleted the fix/dashboard-contract-followups branch May 30, 2026 09:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant