Skip to content

feat(cli): support batch skill imports from skill dirs and parent fol…#230

Open
Fascinax wants to merge 16 commits intoPackmindHub:mainfrom
Fascinax:batch-skill-import-parent-directories
Open

feat(cli): support batch skill imports from skill dirs and parent fol…#230
Fascinax wants to merge 16 commits intoPackmindHub:mainfrom
Fascinax:batch-skill-import-parent-directories

Conversation

@Fascinax
Copy link
Copy Markdown

@Fascinax Fascinax commented Mar 25, 2026

Explanation

packmind-cli skills add now accepts multiple paths at once. Each path can be a skill directory (contains SKILL.md), a file inside a skill directory, or a parent directory containing several skill directories.

The CLI discovers all matching skill directories, deduplicates them, uploads them sequentially, and prints a summary. An interactive confirmation prompt is shown before bulk-importing 2+ skills (skipped in non-TTY/CI environments).
I also added support to ignore specific directories in skill path resolution.

Type of Change

  • Bug fix
  • New feature
  • Improvement/Enhancement
  • Refactoring
  • Documentation
  • Breaking change

Affected Components

  • Domain packages affected: packmind-cli
  • Frontend / Backend / Both: Backend (CLI)
  • Breaking changes (if any): None, single path usage remain unchanged

A quick look of the new beahviour :

image

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing completed
  • Test coverage maintained or improved

Test Details:

  • AddSkillCommand.spec.ts — multiple paths, partial failures, batch confirmation rejection, empty resolved paths
  • resolveSkillInputPaths.spec.ts — direct skill dir, parent dir expansion, file-inside-skill resolution, deduplication

TODO List

  • CHANGELOG Updated
  • Documentation Updated

Reviewer Notes

  • jest.config.ts was updated to add strip-ansi and ansi-regex to transformIgnorePatterns (ESM modules not handled by default Jest transform)
  • PackmindCliHexaFactory is mocked in AddSkillCommand.spec.ts to cut the open ESM import chain (consistent with existing test patterns in PackmindCliHexa.spec.ts)
  • Confirmation prompt uses process.stdin.once('data', ...) rather than readline.createInterface for simpler testability
  • 18 pre-existing test suite failures remain (chalk initialization, path separators on Windows, etc.) — unrelated to this PR
  • precommit skipped because I'm not in the "packmind" org :(
  • for the optimization I kept the ignore list focused on low-risk machine/dependency directories (node_modules, .git, package-manager caches, Nx/Turbo caches) to avoid accidentally hiding user skill folders under generic names like build or tmp.

Notes

To remove the mock, maybe use a global moduleNameMapper for open in jest.config.ts

…ders

- Allow multiple positional paths for \skills add\ (skill dir, file inside skill, or parent dir)
- Recursively discover and deduplicate skill directories from parent folders
- Add interactive confirmation prompt when importing 2+ skills (TTY only)
- Show batch summary and improved error handling for empty/invalid paths
- Update documentation and add comprehensive unit tests for new behaviors
Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@cteyton
Copy link
Copy Markdown
Contributor

cteyton commented Mar 25, 2026

Hi @Fascinax, thanks for your contribution :-)
We're gonna take a look at it and will provide a feedback very soon.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 25, 2026

Greptile Summary

This PR extends packmind-cli skills add to accept multiple paths at once, where each path can be a skill directory, a file inside one, or a parent directory containing several skill directories. The CLI now discovers and deduplicates matching skill directories, shows an interactive confirmation prompt for bulk imports (skipped in non-TTY/CI environments), handles per-path permission-error fallback gracefully, and prints a unified upload summary. Documentation and skill files across .claude/, .cursor/, and .github/ are also updated to reflect the new syntax.\n\nKey changes:\n- New resolveSkillInputPaths utility (with resolveSkillDirectoryRoot and findNestedSkillDirectories) centralises all path-resolution logic; well-tested with real-filesystem integration tests.\n- AddSkillCommand is refactored into focused helper functions (resolveSkillPathsForUpload, confirmBatchUploadIfNeeded, uploadResolvedSkillPaths, etc.) with full unit-test coverage.\n- Both issues from the previous review round have been addressed: the exitWhenNoSkillPathsResolved guard is now reachable for empty directories, and confirmBatchUpload correctly resolves to false when stdin closes before a data event.\n- Two remaining concerns: (1) a file path that has no enclosing skill directory is silently queued for upload and will fail with an opaque upload-layer error; (2) when the user cancels a batch with prior resolution failures, the process exits with code 1 but no closing summary line is printed.

Confidence Score: 4/5

Safe to merge; no data-loss or security risk, and both previously flagged issues are resolved. The two new comments are P1 UX/diagnostic improvements rather than crash-blocking bugs.

Both prior review concerns are addressed in this revision. The two new P1 comments describe edge-case UX degradation (confusing error message for a non-skill file path; missing summary line on cancel-with-failures), not crashes or data loss. Core happy-path and failure-path test coverage is solid.

apps/cli/src/application/utils/resolveSkillInputPaths.ts — the fallback return in resolveSkillDirectoryRoot when no enclosing skill directory is found.

Important Files Changed

Filename Overview
apps/cli/src/application/utils/resolveSkillInputPaths.ts New utility: recursively discovers skill directories from an input path (direct skill dir, parent dir, or file inside a skill dir), with deduplication and an ignored-directory allowlist. Edge case: when a file path has no enclosing skill directory, the raw file path is silently passed through to the upload layer.
apps/cli/src/infra/commands/skills/AddSkillCommand.ts Extends the add-skill command to accept multiple paths, adds a batch confirmation prompt, a per-path permission-error fallback, and a upload summary. Both previously flagged issues (unreachable "no skill directories" guard and unresolvable Promise on stdin close) are addressed in this version.
apps/cli/src/application/utils/resolveSkillInputPaths.spec.ts Good integration-style tests using a real tmp filesystem plus targeted mocks for permission errors. Missing a test for a file path that has no enclosing skill directory (no SKILL.md walking up), so the silent passthrough edge case is uncovered.
apps/cli/src/infra/commands/skills/AddSkillCommand.spec.ts Comprehensive unit tests covering multi-path uploads, partial failures, TTY confirmation, permission-error fallback, and early-exit scenarios. Good coverage.
apps/cli/jest.config.ts Adds strip-ansi and ansi-regex to transformIgnorePatterns to handle ESM modules — expected boilerplate for these deps.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[packmind-cli skills add path1 path2...] --> B{skillPaths.length == 0?}
    B -- Yes --> C[logError: Missing skill path\nexit 1]
    B -- No --> D[resolveSkillPathsForUpload]
    D --> E[resolveSkillInputPaths all paths]
    E -- EACCES --> F[Fallback: resolve each path individually]
    F --> G{Per-path EACCES?}
    G -- Yes --> H[Add to resolutionFailures]
    G -- No --> I[Add to resolvedSkillPaths]
    E -- OK --> J[resolvedSkillPaths collected]
    F --> J
    J --> K[logResolutionFailures]
    K --> L{resolvedSkillPaths empty?}
    L -- Yes & failures --> M[exit 1]
    L -- Yes & no failures --> N[logError: No skill dirs found\nexit 1]
    L -- No --> O{count > 1 AND isTTY?}
    O -- No --> Q[Upload skills sequentially]
    O -- Yes --> P[confirmBatchUpload prompt]
    P -- n/no --> R[logConsole: Import cancelled\nif failures: exit 1]
    P -- y/yes --> Q
    Q --> S{Upload each skillPath}
    S -- success --> T[logUploadResult\nsuccessCount++]
    S -- error --> U[logError\nfailureCount++]
    T --> V[Next path]
    U --> V
    V --> W{More paths?}
    W -- Yes --> S
    W -- No --> X{count > 1 OR failureCount > 0?}
    X -- Yes --> Y[logUploadSummary]
    X -- No --> Z[Done]
    Y --> AA{failureCount > 0?}
    AA -- Yes --> BB[exit 1]
    AA -- No --> Z
Loading

Comments Outside Diff (1)

  1. apps/cli/src/application/utils/resolveSkillInputPaths.ts, line 69-81 (link)

    File path not inside a skill directory silently passes through to upload

    When a user provides a path to a file that is not SKILL.md and whose parent chain contains no SKILL.md file, resolveSkillDirectoryRoot exhausts the while loop and falls back to returning absolutePath — a raw file path. Because addNestedSkillDirectories then calls stat on it, finds isDirectory() === false, and returns false, the fallback addResolvedSkillPath(resolvedSkillPaths, seenPaths, skillDirectoryRoot) at the bottom of resolveSkillInputPaths queues the file path. The upload layer will then fail with an opaque message ("SKILL.md not found in skill directory" or "not a directory") rather than a clear, early diagnostic.

    Consider returning null (or an explicit "not found" sentinel) from resolveSkillDirectoryRoot when the walk exhausts without finding a skill directory, and surfacing a distinct error message in the caller before the path ever reaches uploadSkill.

    The spec also has no test for this case (file path with no enclosing skill directory), so the silent-passthrough behaviour is currently undetected by tests.

Reviews (9): Last reviewed commit: "fix resolve merge" | Re-trigger Greptile

Fascinax added 14 commits March 25, 2026 14:37
…uard

The 'No skill directories found' guard in addSkillHandler was unreachable
because resolveSkillInputPaths always returned at least one path - it fell
back to [skillDirectoryRoot] even for directories with no nested skills.

Now resolveSkillInputPaths returns an empty array when no skill directories
are discovered, making the guard reachable and the error message visible to
users instead of a generic API error.
confirmBatchUpload was leaving the Promise unresolvable if stdin emitted
'close' or 'end' before a 'data' event (e.g., Ctrl+D). Now both listeners
are registered and cleaned up properly: onData resolves true/false based
on user input, while onClose resolves false when stdin closes, ensuring
the Promise always settles.
Change nested describe blocks in AddSkillCommand.spec.ts from 'and...'
to 'with...' prefix to match the project's testing standard which
requires 'when...', 'with...' context blocks for better readability
and consistency with other test files in the codebase.
merge conflict didnt saved the first time
@cteyton
Copy link
Copy Markdown
Contributor

cteyton commented Mar 27, 2026

Hello @Fascinax,
First of all, thanks again for your contribution.

It turns out we're working actively on the CLI and are introducing a new CLI playbook add command to submit new artifacts to packmind (not only skills, but also commands and standards).

With that in mind, two things come up:

This mean we can't unfortunately accept your contribution right now, sorry. But we'll definitively integrate your initial idea very soon!

@Fascinax
Copy link
Copy Markdown
Author

Hello @Fascinax, First of all, thanks again for your contribution.

It turns out we're working actively on the CLI and are introducing a new CLI playbook add command to submit new artifacts to packmind (not only skills, but also commands and standards).

With that in mind, two things come up:

This mean we can't unfortunately accept your contribution right now, sorry. But we'll definitively integrate your initial idea very soon!

Alright then, I understand ! Is it still possible to contribute by answering the issue then ?

@cteyton
Copy link
Copy Markdown
Contributor

cteyton commented Mar 27, 2026

Hello @Fascinax, First of all, thanks again for your contribution.
It turns out we're working actively on the CLI and are introducing a new CLI playbook add command to submit new artifacts to packmind (not only skills, but also commands and standards).
With that in mind, two things come up:

This mean we can't unfortunately accept your contribution right now, sorry. But we'll definitively integrate your initial idea very soon!

Alright then, I understand ! Is it still possible to contribute by answering the issue then ?

I'd recommand to wait few days, let's middle of next week, so that we stabilize the playbook add behavior, we're working actively right now on that :-)

@Fascinax
Copy link
Copy Markdown
Author

Hello @Fascinax, First of all, thanks again for your contribution.
It turns out we're working actively on the CLI and are introducing a new CLI playbook add command to submit new artifacts to packmind (not only skills, but also commands and standards).
With that in mind, two things come up:

This mean we can't unfortunately accept your contribution right now, sorry. But we'll definitively integrate your initial idea very soon!

Alright then, I understand ! Is it still possible to contribute by answering the issue then ?

I'd recommand to wait few days, let's middle of next week, so that we stabilize the playbook add behavior, we're working actively right now on that :-)

Okay ! I will wait till next week then. Thanks for your answer :)

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.

2 participants