Skip to content

feat: add open command for opening directories as projects#70

Open
paschdan wants to merge 4 commits intogrinev:mainfrom
paschdan:feature/add-open-command-for-adding-projects
Open

feat: add open command for opening directories as projects#70
paschdan wants to merge 4 commits intogrinev:mainfrom
paschdan:feature/add-open-command-for-adding-projects

Conversation

@paschdan
Copy link
Copy Markdown

@paschdan paschdan commented Apr 1, 2026

Description of changes

  • New /open command: Adds a directory browser via Telegram inline keyboard, allowing users to navigate the local filesystem and select a folder as a project — without needing to know the exact path upfront.
  • switchToProject extraction: Refactors the project-switching state logic (clear session, reset pinned message, refresh keyboard) out of projects.ts into a shared src/bot/utils/switch-project.ts utility, eliminating duplication between /projects and /open.
  • file-tree utility: Adds src/bot/utils/file-tree.ts with pure functions for directory scanning, pagination (8 entries/page), hidden-directory filtering, tilde-based display paths, and i18n-aware subfolder counts.
  • Callback-data path encoding: Handles Telegram's 64-byte callback_data limit by transparently switching to compact indexed references (#0, #1, …) for long absolute paths.
  • i18n: All new user-facing strings added to all 6 locales (en, de, es, fr, ru, zh).

Closes issue (optional)

How it was tested

  • 73 new unit tests across 3 test files:
    • tests/bot/commands/open.test.ts — command, callback routing, busy guard, error paths, path encoding round-trips, stale index invalidation
    • tests/bot/utils/switch-project.test.ts — all state-clearing branches, keyboard rebuild, pinned message failure resilience
    • tests/bot/utils/file-tree.test.ts — real temp-directory integration tests for scanning, pagination, hidden-dir filtering, error codes
  • npm run build — clean
  • npm run lint — clean
  • npm test — all pass

Checklist

  • PR title follows Conventional Commits: feat: add /open command for browsing and adding project directories
  • This PR contains one logically complete change
  • Branch is rebased on the latest main
  • I ran npm run lint, npm run build, and npm test
  • If this PR is OS-sensitive, behavior/limitations for Linux/macOS/Windows are described

OS note: Directory scanning uses node:fs and node:path — works on all platforms. The pathToDisplayPath function uses path.sep for correct tilde substitution on both Unix and Windows. Hidden-directory filtering (.-prefixed) follows Unix convention; on Windows, system/hidden directories without a dot prefix will still appear in the listing.

paschdan added 4 commits April 1, 2026 13:43
Extract the project-switching state logic (clear session, reset pinned
message, refresh keyboard) from projects.ts into a shared utility so it
can be reused by the upcoming /open command without duplication.
Add file-tree.ts with pure utilities for browsing the local filesystem:
directory scanning with pagination, hidden-dir filtering, tilde-based
display paths, entry labels, and tree headers with i18n-aware counts.
Add a new /open bot command that lets users browse the local filesystem
via inline keyboard navigation and select a directory as a project.
Includes callback-data path encoding to stay within Telegram's 64-byte
limit, pagination, and i18n strings for all 6 supported locales.
@grinev grinev closed this Apr 4, 2026
@grinev grinev reopened this Apr 4, 2026
@grinev
Copy link
Copy Markdown
Owner

grinev commented Apr 4, 2026

@paschdan thanks for the PR! Only today I had time to look at it carefully and think about the feature as a whole.

In general, I see the value of /open. It can be useful in real life, especially when the user does not remember the exact path and wants to add a project quickly from Telegram.

At the same time, in the current form I am a bit uncomfortable with this feature from a security point of view. Right now it allows browsing almost any local directory and selecting it as a project. That means the bot can be used not only for known project folders, but also for system folders or other sensitive locations on the machine. Even if the bot is limited to one Telegram user, this still feels too broad by default.

I think this feature could work much better if we limit it to a configurable allowlist of roots.

My suggestion:

  • Add a new env config, for example PROJECT_BROWSER_ROOTS
  • If it is not set, use only the user's home directory as the default root
  • Parse it as a list of allowed root paths
  • In /open, first show these allowed roots
  • After the user enters one root, allow browsing only inside that root
  • Do not allow going above the selected root
  • Normalize paths before checks (resolve, and ideally realpath)
  • On Windows, compare paths case-insensitively
  • If someone wants more locations, they can add them manually in .env

For me this would be a much safer balance:

  • the feature still exists and stays useful
  • default behavior is conservative
  • broader access requires explicit local configuration

Below are also some technical remarks from the review:

  1. src/bot/index.ts
    The global clearOpenPathIndex() cleanup is called for any handled inline cancel. Because /open uses a shared in-memory path index for long callback data, a stale cancel from another menu can break the active /open menu.
  2. src/project/manager.ts
    getProjectByWorktree() uses exact string equality, but on Windows path handling is effectively case-insensitive in other parts of the code. This can make project selection fail for paths that differ only by letter case.
  3. src/bot/commands/open.ts
    upsertSessionDirectory() is called before the full project switch finishes successfully. If a later step fails, the directory may still stay in the cached project list.
  4. src/bot/commands/open.ts / src/bot/utils/file-tree.ts
    Page numbers are not normalized strongly enough. If directory contents change or callback data is stale/crafted, the UI can end up showing an invalid page like (4/2).

Overall, I think the implementation work is solid, and I especially like the extraction of switchToProject() and the handling of Telegram callback length limits. My main concern is the product/security scope of /open in the current unrestricted form.

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