Skip to content

feat(workplace): per-project custom working directory - #32

Open
CatJuly wants to merge 3 commits into
HKUDS:mainfrom
CatJuly:feat/project-custom-workplace
Open

feat(workplace): per-project custom working directory#32
CatJuly wants to merge 3 commits into
HKUDS:mainfrom
CatJuly:feat/project-custom-workplace

Conversation

@CatJuly

@CatJuly CatJuly commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Every project's workplace is currently pinned to ../OpenOPC_workplace/<project_id>/ beside the OpenOPC repo, with no way to relocate it. In practice most users want agents to work inside an existing repository (their actual codebase), not in a freshly created empty folder. Today the only workarounds are symlinks or spelling out the path in every prompt.

This PR makes the workplace configurable per project, end to end: the path a user sets is the cwd the execution agent (claude_code / codex / cursor / opencode) is spawned with.

Design

Resolution is three-tiered, most specific first:

  1. Per-project path — stored in a new project_config table inside that project's own tasks.db
  2. $OPC_WORKPLACE_ROOT — global override; each project resolves to $OPC_WORKPLACE_ROOT/<project_id>
  3. Default — the existing ../OpenOPC_workplace/<project_id>/ layout, unchanged

get_project_workplace() gains an optional custom_path parameter; callers without DB access (tests, preflight) keep the old behaviour untouched. The DDL is idempotent and runs on every store initialize(), so existing databases gain the table with no migration step. The engine loads the custom path into a synchronous cache during initialize(), keeping the existing sync resolvers' signatures unchanged.

Sticky session workspace roots that merely equal the project workplace are no longer persisted into execution_defaults — they carry no session-specific intent and previously froze the session to whatever the workplace was at first message, so later set-workplace changes never took effect. Sessions pinned before this rule self-heal on their next message. Roots derived from an explicit output dir keep sticking exactly as before (covered by tests/test_engine_session_defaults.py).

Surfaces

Office UI

  • Create dialog: + opens a centered modal (same conventions as the delete-confirm / org-create dialogs) with project name and an optional workplace path. The name field validates live and previews the derived slug.
  • Settings: a gear button next to the project selector shows the resolved path and its source tier (custom / env / default), pre-fills custom paths for editing, and offers reset-to-default.
  • All dialogs use theme tokens, so the paper theme stays readable.

CLI

opc project set-workplace myapp --path /path/to/existing/repo [--create]
opc project show-workplace myapp
opc project reset-workplace myapp

Docs: README / README.zh-CN document the resolution order and commands.

Testing

  • End to end: created a project pointed at an existing repository and ran a claude_code task — the agent reports cwd inside that repository (verified against a real session, including one that had been pinned to the old default and self-healed).
  • set-workplace → show-workplace → reset-workplace round-trips through the project DB; a legacy DB (pre-existing tables, no project_config) upgrades in place with data intact.
  • pytest failure set is identical to current main baseline (no new failures); tests/test_engine_session_defaults.py passes 7/7.
  • Frontend npm run typecheck and npm run build clean; rebuilt frontend_dist included.

Stacked on #31

This branch is based on #31 (fix/project-create-name-feedback) because both touch ProjectSelector.tsx — the create-dialog rewrite here supersedes the inline validation added there. Until #31 merges, its commit (d7cfb2b) appears in this PR's diff; once #31 lands, this PR reduces to its own two commits. Merging #31 first is recommended.

CatJuly added 3 commits August 8, 2026 14:25
Creating a project does nothing when the typed name contains no ASCII
word characters — for example a fully Chinese name like "知识工程".
handleCreate slugifies the input through [^a-z0-9_-] before submitting,
and when everything is stripped it bails out with a bare `return`, so
the "+" click appears dead with no hint about what went wrong.

Derive the slug live while typing. When the name is non-empty but the
slug is empty, mark the input invalid (red outline, tooltip, and a
small anchored hint that does not shift the topbar layout) and disable
the submit button, so the constraint is visible before the click
instead of being swallowed after it.

Verified with `npm run typecheck` and `npm run build`; manually checked
that Chinese-only input shows the invalid state with the hint, that
mixed input like "My App" still creates as "my-app", and that plain
ASCII names are unaffected.
Projects were pinned to `{repo_parent}/OpenOPC_workplace/{project_id}`
with no way to relocate them, so agents could not work inside an
existing repository or on another drive.

Resolution is now three-tiered, most specific first:
  1. per-project path stored in the new `project_config` table, in that
     project's own tasks.db
  2. `$OPC_WORKPLACE_ROOT` global override
  3. the previous default layout

`get_project_workplace()` gains an optional `custom_path` parameter, so
callers without DB access (tests, preflight) keep the old behaviour
unchanged. The DDL is idempotent and runs on every `initialize()`, so
existing databases gain the table without a migration step.

The engine loads the custom path into a synchronous cache during
initialize() and re-reads it on demand, keeping the existing sync
resolvers (`_resolve_external_workspace`) untouched in signature.
Sticky session workspace roots that merely equal the project workplace
are no longer persisted — they carry no session intent and would pin
the session to a stale path after set-workplace changes; existing
pinned sessions self-heal on their next message.

CLI: `opc project set-workplace/show-workplace/reset-workplace`.

Verified end to end: a claude_code task in a project pointed at an
existing repository runs with cwd inside that repository; set/show/
reset round-trips through the project DB; legacy DBs upgrade in place;
tests/test_engine_session_defaults.py passes.
Surfaces the per-project working directory in the Office UI.

Create dialog: the "+" button now opens a centered modal (matching the
delete-confirm and org-create conventions) with the project name and an
optional workplace path. The name field validates live — input that
slugifies to empty (e.g. fully non-ASCII names) shows an inline hint
and disables submit, and a preview shows the derived id when it differs
from the typed name.

Settings panel: a gear button next to the project selector opens a
dialog showing the currently resolved path and its source tier
(custom / OPC_WORKPLACE_ROOT / default), pre-fills custom paths for
editing, and offers reset-to-default. The `project_workplace` envelope
is routed through wsClient so the dialog receives the echo.

All surfaces use theme tokens (--bg-elevated / --text / --border) so
the paper theme stays readable.

Ships the rebuilt frontend_dist bundle; README and README.zh-CN
document the three-tier resolution order and the CLI commands.
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