feat(workplace): per-project custom working directory - #32
Open
CatJuly wants to merge 3 commits into
Open
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
cwdthe execution agent (claude_code / codex / cursor / opencode) is spawned with.Design
Resolution is three-tiered, most specific first:
project_configtable inside that project's owntasks.db$OPC_WORKPLACE_ROOT— global override; each project resolves to$OPC_WORKPLACE_ROOT/<project_id>../OpenOPC_workplace/<project_id>/layout, unchangedget_project_workplace()gains an optionalcustom_pathparameter; callers without DB access (tests, preflight) keep the old behaviour untouched. The DDL is idempotent and runs on every storeinitialize(), so existing databases gain the table with no migration step. The engine loads the custom path into a synchronous cache duringinitialize(), 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 laterset-workplacechanges 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 bytests/test_engine_session_defaults.py).Surfaces
Office UI
+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.CLI
Docs: README / README.zh-CN document the resolution order and commands.
Testing
cwdinside 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-workplaceround-trips through the project DB; a legacy DB (pre-existing tables, noproject_config) upgrades in place with data intact.pytestfailure set is identical to currentmainbaseline (no new failures);tests/test_engine_session_defaults.pypasses 7/7.npm run typecheckandnpm run buildclean; rebuiltfrontend_distincluded.Stacked on #31
This branch is based on #31 (
fix/project-create-name-feedback) because both touchProjectSelector.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.