Skip to content

feat(rpgkit): v0.1.3 — global install, home-side runtime store, hook & scripts refactor#56

Open
HuYaSen wants to merge 29 commits into
mainfrom
dev/rpgkit-global-install
Open

feat(rpgkit): v0.1.3 — global install, home-side runtime store, hook & scripts refactor#56
HuYaSen wants to merge 29 commits into
mainfrom
dev/rpgkit-global-install

Conversation

@HuYaSen
Copy link
Copy Markdown
Collaborator

@HuYaSen HuYaSen commented May 22, 2026

Summary

Brings RPG-Kit to the 0.1.3 architecture: a globally-installed CLI with all
runtime artifacts kept outside the user's workspace, plus consolidated
hook/script plumbing and refreshed multilingual docs.

origin/main has already been merged into this branch; PR should be
conflict-free.

Highlights

CLI / packaging (v0.1.3)

  • Bundle-mode rpgkit init ships templates and scripts from the installed
    package; workspace no longer needs a local scripts copy.
  • New home-side runtime store (~/.rpgkit/...) for state, logs and the
    private inner-git snapshot — workspaces stay clean.
  • rpgkit script <stage> dispatcher routes all stage scripts via the global
    CLI; stdout is tee'd into logs/<stage>.log.
  • rpgkit version compares local vs latest and prints upgrade hints.
  • Auto-register rpg-tools in the Copilot CLI global MCP config.
  • Auto-snapshot .rpgkit/ via a private inner git (isolated from user repo).
  • MCP server and git hooks now invoke the global rpgkit command.

Hooks

  • Retired the pre-commit hook; rely on post-commit + post-merge only.
  • Inner-git calls strip inherited GIT_* env to avoid leaking outer state.

Robustness / fixes

  • Tolerate session traces written outside the workspace root.
  • Print only file basenames in script output (avoid path leakage).
  • Sed-safety + workspace config commit fixes in the 0.1.3 plumbing.

CLI cleanup

  • Remove the browser-dependent view-graph command and its references in
    scripts and docs; users should run rpgkit version from inside the
    workspace to see the resolved Data / Logs paths.

Docs

  • Multilingual README sync (README.md, README.zh-CN.md, README.ja-JP.md,
    README.ko-KR.md, README.hi-IN.md) — identical structure and updated
    install / quick-start / update / troubleshooting sections.
  • Refreshed docs/ for the home-side runtime store, retired pre-commit
    hook, bundle mode, --legacy-download / --pull, and
    .rpgkit/config.toml.
  • Mirror markdownlint config inside RPG-Kit/ for local runs.

Templates

  • Drop the deprecated mode: agent field from 3 prompt templates.

Merge resolution

The only conflict with main was RPG-Kit/src/rpgkit_cli/__init__.py
(signature drift around download_and_extract_template). Resolved by
keeping this branch's 0.1.3 implementation; main's --source /
.venv_rpgkit additions were intentionally dropped as they are incompatible
with the new home-side runtime store. All other incoming files from main
(release workflow scripts, root .gitignore, root README.md) merged
cleanly.

Validation

  • python3 -m compileall -q RPG-Kit/src/rpgkit_cli RPG-Kit/scripts — ok
  • markdownlint-cli2 "RPG-Kit/README*.md" — 0 errors
  • git merge-tree main HEAD — no conflicts

HuYaSen added 27 commits May 18, 2026 21:46
Ship scripts/ and templates/commands/ as packaged assets inside the wheel
(rpgkit_cli/core_pack/) via hatch force-include, so 'rpgkit init' works
offline (air-gapped / corporate-proxy / enterprise environments).

Replace the build-time <AI_CLI_CMD> placeholder substitution with a
runtime resolver in scripts/common/llm_client.py that reads .rpgkit/config.toml
per workspace. This decouples scripts from the chosen AI agent and is
forward-compatible with running scripts from a shared installation in
the future.

Key changes:
* pyproject.toml: bump to 0.1.3 + force-include scripts/ and templates/commands/
* src/rpgkit_cli/_assets.py (new): importlib.resources access to core_pack
* src/rpgkit_cli/__init__.py:
  - _AI_TO_CLI_CMD authoritative map (mirrors release-zip CI)
  - _install_from_bundle() + _materialise_commands_for_agent()
  - _write_workspace_config() materialises .rpgkit/config.toml
  - .rpgkit/.source marker so 'rpgkit update' honours the user's channel
  - download_and_extract_template() dispatches: bundle by default, falls
    back to legacy release zip when --legacy-download is passed (or --pre,
    or when the bundle is unavailable / --script ps is requested)
  - 'rpgkit init': new --legacy-download flag
  - 'rpgkit update': new --legacy-download and --pull flags
  - _detect_install_method() + _upgrade_command() power --pull
* scripts/common/llm_client.py:
  - _load_ai_cli_cmd() with P1-P4 priority chain (constructor arg → env var
    → workspace config.toml → release-zip-baked-in fallback)
  - detect_agent_type() now resolves at call time, not module import
  - LLMClient.__init__ tolerates empty tool; generate() raises lazily with
    an actionable error message when the workspace is unconfigured
* scripts/__init__.py (new, empty): marker for hatch force-include
* .gitignore: un-ignore .rpgkit/config.toml so teams can commit the
  workspace default

Behavioural guarantees (7a route):
* Scripts still land under <workspace>/.rpgkit/scripts/
* No slash-command template was modified
* MCP config generation unchanged
* Hook installation logic unchanged
* All 27 pipeline scripts unchanged
* No new tests; the 11 pre-existing test failures on main remain unchanged

Design notes live in plans/01-package-bundle-and-ai-config.md (gitignored;
local reference only).
…/config.toml

* README: add Updating section explaining the three update flavours
  (default offline, --pull, --legacy-download) + note that bundle mode
  is the new default since 0.1.3.
* docs/cli-reference: list --legacy-download (init + update) and --pull
  (update), plus a Provisioning sources table summarising the two
  channels and the .rpgkit/.source marker.
* docs/configuration: new Workspace Configuration section covering
  .rpgkit/config.toml, the P1-P4 resolution priority chain, and the
  authoritative --ai → ai_cli_cmd mapping.
* docs/project-structure: surface config.toml and .source in the
  workspace tree.
Eight issues surfaced during a second-pass review of the bundle / config
work landed in commit 1c47c1d.  All fixes are local to
src/rpgkit_cli/__init__.py; no scripts, templates, hooks, or MCP logic
were touched.

1. _detect_install_method() reordered editable detection before uv.
   Previously an editable install placed in a uv-managed venv would be
   reported as 'uv' and 'rpgkit update --pull' would run
   'uv tool upgrade' instead of telling the user to git pull.

2. --script ps fallback notice was guarded by
   'if tracker is None and verbose:' which is never True in the actual
   init()/update() call path, so users never saw the message.  Emit it
   through the tracker as a skipped 'ps-fallback' step so it shows up
   in the live status report.

3. --pull self-upgrade is now executed BEFORE constructing rich.Live
   instead of from inside the Live context.  Mixing subprocess output
   (and the subsequent os.execvp) with Live's terminal control left a
   corrupted screen state.

4. --pull no longer continues the update flow with stale in-memory
   code after upgrading the wheel.  On successful upgrade we
   os.execvp() the rpgkit binary (with --pull stripped) so the new
   logic runs against the freshly-installed core_pack.

5. .rpgkit/.source provisioning marker is now written by whichever
   path actually ran (_install_from_bundle for bundle,
   _download_and_extract_release_zip for legacy zip).  The previous
   init/update logic decided based on flag state, which gave wrong
   results in the --script ps fallback case (bundle attempt → ps
   fallback to legacy zip → marker incorrectly left absent).

6. Removed redundant _install_command_templates_from_bundle wrapper.
   _materialise_commands_for_agent now also produces the rpgkit.<name>.md
   filename for unknown agents, matching the supported-agent paths.

7. _install_from_bundle no longer re-adds tracker step keys
   ('download', 'cleanup') that init()/update() had already registered;
   it just transitions them with .skip() to keep the live status report
   coherent.

8. Update()'s 'effective_legacy' boolean was the OR of three terms one
   of which redundantly re-checked another flag, followed by a no-op
   reassignment.  Simplified to a single OR.

No behaviour change for the offline-bundle happy path; smoke tests
continue to show 883 passed / 11 pre-existing failures (no regression).
Three more bugs surfaced in a deeper review of the 0.1.3 plumbing.
All fixes are local; existing test suite still shows the same 11
pre-existing failures from main (no regression).

1. .rpgkit/config.toml was still gitignored in user workspaces.
   1c47c1d but the gitignore content that rpgkit init/update WRITES
   into user workspaces (_GITIGNORE_RPGKIT_COMMON) did not.  As a
   result, the config a team would want to commit was still hidden
   from git.  Fixed by adding the un-ignore line to the embedded block.

2. LLMClient.generate_with_memory() silently swallowed the
   'AI CLI not configured' RuntimeError and returned None.  The caller
   then sees a generic LLM failure with no hint to run rpgkit init.
   Now pre-validates self.tool and re-raises the configuration error;
   genuine LLM-call failures continue to surface as None as before.

3. P4 (release-zip baked-in fallback) was broken by sed.  The new
   resolver introduced two module-level constants:
     _PLACEHOLDER_LITERAL = '<AI_CLI_CMD>'
     _BAKED_IN_VALUE     = _PLACEHOLDER_LITERAL
   The release-zip CI runs 'sed s|<AI_CLI_CMD>|<cmd>|g' on every script
   file, which would have rewritten BOTH constants to the same
   substituted value, making the
       _BAKED_IN_VALUE != _PLACEHOLDER_LITERAL
   guard tautologically false and the P4 fallback effectively dead.
   Now _PLACEHOLDER_LITERAL is built with string concatenation
   ("<" + "AI_CLI_CMD" + ">") so sed leaves it alone and the
   guard does the right thing on legacy-download workspaces.

Plus:
* Tightened --pre help text in both init() and update() to say
  'Implies --legacy-download', matching the new dispatcher behaviour.
* Updated the two gitignore-related tests in tests/test_hooks_install.py
  to count exact lines rather than substrings — the embedded block now
  contains both '.rpgkit/' and '!.rpgkit/config.toml', so the previous
  substring count would always be 2.  This is the only test edit
  introduced by the 0.1.3 work.
… hints

Previously `rpgkit version` listed both the local CLI version and the
latest GitHub release tag but did NOT tell the user which was which or
whether action was needed.  Combined with `uv tool upgrade rpgkit-cli`
silently printing 'Nothing to upgrade' (with no version context), users
had to mentally diff two version strings to figure out their status.

Now the output adds:

* 'Latest Release' (was 'Template Version' — clearer naming).
* 'Status' row with one of:
    - [green]up to date[/]                    local == remote
    - [yellow]outdated → X.Y.Z[/]             local < remote
    - [cyan]ahead of release (X.Y.Z)[/]       local > remote (dev build)
    - [yellow]offline[/]                      GitHub query failed
* When relevant, a second panel ('Upgrade tip') with actionable advice:
    - outdated → list of upgrade commands (uv / pipx / pip) + reminder
      to follow up with `rpgkit update` in each existing workspace.
    - ahead   → reassure the user nothing is broken (dev build).
    - offline → show the underlying error and suggest retrying online.

Version comparison goes through packaging.version.Version so PEP 440
pre-release / dev / post suffixes (eg 0.1.4.dev0, 0.1.3rc1, 0.1.3.post1)
sort correctly.  Falls back to plain comparison when packaging is
unavailable.

No new dependencies (packaging ships with setuptools / pip).
Test baseline unchanged: 883 passed, 11 pre-existing failures.
Plan 02 Batch A: build dispatcher infrastructure and convert MCP + hooks
to use the globally-installed rpgkit CLI rather than workspace-local
script copies.

- Add 'rpgkit script <relpath> [args...]' dispatcher with --list/--where.
- Add 'rpgkit-mcp' console script (rpgkit_cli.entries:mcp_main).
- _assets: add list_scripts() + dev-mode fallback to repo scripts/.
- mcp_server.py: extract main() function for console-script reuse.
- Rewrite MCP config writer: command='rpgkit-mcp', no absolute paths.
- Rewrite pre-commit / post-merge / post-commit / Claude SessionStart
  hooks to invoke 'rpgkit script update_graphs.py ...', with a PATH
  fallback line for GUI-launched commits.
- PATH self-check at end of 'rpgkit init' (warns when rpgkit-mcp
  missing from PATH).
- Update test_hooks_install.py assertions for the new contract.
- Batch B (templates + drop workspace scripts copy) to follow.

Refs: plans/02-route-scripts-via-cli.md (local)
…-1/2)

Plan 02 Batch B step 1+2: introduce cmd_for() helper and rewrite all
hardcoded 'python3 .rpgkit/scripts/X.py' references inside the
pipeline scripts.

- common/paths.py: re-anchor SCRIPTS_DIR to Path(__file__).parent.parent
  so it points at the actual filesystem location regardless of whether
  scripts are run from a workspace copy (pre-0.1.3) or the packaged
  rpgkit_cli/core_pack/scripts/ dir (post-0.1.3).
- common/paths.py: add cmd_for(relpath) helper returning the canonical
  'rpgkit script <relpath>' invocation.
- Sweep 12 script files: replace 21 hardcoded invocation strings in
  next_action messages, inter-script spawn commands, and error hints
  with cmd_for() calls. Files: init_codebase.py, smoke_test.py,
  check_skeleton.py, check_code_gen.py, run_batch.py, update_graphs.py,
  code_gen/result_builders.py, rpg_encoder/run_update_rpg.py,
  rpg_encoder/run_encode.py, rpg_edit/{validate,review,code}.py.

Workspace .rpgkit/scripts/ copy still happens (templates not yet
converted); next commit removes both.
…/4/5)

Plan 02 Batch B steps 3-5: complete the migration to globally-installed
scripts.

- Rewrite 13 slash-command templates: 'python3 .rpgkit/scripts/X.py'
  → 'rpgkit script X.py' (64 substitutions via sed).
- _install_from_bundle: stop copying scripts to .rpgkit/scripts/; only
  materialise slash-command templates and the .source marker.
- _download_and_extract_release_zip (legacy --legacy-download): strip
  the extracted .rpgkit/scripts/ after extraction; legacy channel now
  delivers commands only (D7).
- ensure_executable_scripts: collapse to a deprecated no-op stub.
- .gitignore: drop the obsolete .rpgkit/scripts/**/__pycache__/ rule
  (covered by the blanket .rpgkit/ ignore anyway).

After init the workspace contains only data/, logs/, config.toml,
.source — no scripts/ dir.  All pipeline scripts run from the wheel
via 'rpgkit script <name>'.  Test baseline unchanged (11 failed /
883 passed, all pre-existing failures).
Plan 02 Batch B step 8: rewrite documentation to match the new
contract.

- docs/cli-reference.md: add full 'rpgkit script' section (synopsis,
  options --list/--where, examples, rpgkit-mcp companion mention).
- docs/commands.md: rewrite 13 inline references from
  '.rpgkit/scripts/X.py' → 'rpgkit script X.py'.
- docs/project-structure.md: drop the obsolete .rpgkit/scripts/ subtree
  from the workspace layout diagram; add a callout explaining the
  packaged-scripts model.

Plan 02 self-repo agent prompt regeneration (Batch B-6) is gitignored
in this repo so changes there are not part of this commit; the next
'rpgkit init/update' on the RPG-Kit dev workspace picks up the new
template content automatically.
Audit pass on Plan 02 surface; address loose ends.

- templates/commands: sed prose backtick refs (5 files, 5 sites) —
  'Run the script `.rpgkit/scripts/X.py`' was missed by the previous
  python3-prefix sed pass and would mislead AI agents into trying a
  filesystem path that no longer exists.
- scripts/**.py: change all 'cmd_for("X.py")' inside f-strings to
  'cmd_for(\'X.py\')'.  PEP 701 nested same-kind quotes work on
  Python 3.12+ but trip many syntax highlighters / linters and are
  fragile.  Single quotes inside the f-string is the safer style.
- scripts/mcp_server.py: refresh module docstring to mention the
  rpgkit-mcp console-script entry instead of the legacy workspace
  path.
- scripts/feature_spec_to_json.py: usage docstring uses 'rpgkit
  script' form.
- src/rpgkit_cli/__init__.py: drop the deprecated
  'ensure_executable_scripts' no-op (no remaining callers); update
  one stale prose reference in _has_python_files docstring.

E2E + full test suite pass at baseline (11 pre-existing failures,
883 passing).
Two follow-ups from running 'rpgkit init .' on a real workspace:

1. Optional initial-encode kickoff (the 'Run the encoder now?' prompt
   at end of 'rpgkit init') still resolved the encoder via
   '$workspace/.rpgkit/scripts/rpg_encoder/run_encode.py'.  After
   plan 02 the workspace no longer has that subtree, so the kickoff
   always printed 'Encoder script not found' and aborted.  Fall back
   to '_assets.scripts_dir()' (the packaged location) when the
   workspace copy is absent.

2. The VS Code 'folderOpen' task in .vscode/tasks.json was still
   invoking sys.executable against the workspace 'update_graphs.py'
   path.  Rewrite to 'command: rpgkit, args: [script, update_graphs.py,
   status]' so it works against the globally-installed CLI.

Updated test_install_copilot_hooks_writes_folder_open_task assertions
for the new task shape.
…strings)

Full audit pass turned up a few more cases that needed updating after
'rpgkit init .' on a real workspace exposed gaps.

Functional bugs:
- scripts/rpg_edit/review.py: the review-stage prompt embedded
  '$WORKSPACE_ROOT/.rpgkit/scripts/tools/{browser,gui}.py' paths
  into the LLM instructions.  Since the workspace no longer hosts
  the scripts dir, the AI would invoke a non-existent file.  Switch
  to 'cmd_for("tools/browser.py")' / 'cmd_for("tools/gui.py")'
  and drop the leading 'python' from each invocation in the prompt
  template (it's already rooted by 'rpgkit script').
- scripts/code_gen/batch_prompts.py: the sub-agent guard-rail rule
  'You MUST NOT run any .rpgkit/scripts/*.py commands' now reads
  'run any rpgkit script ... or rpgkit-mcp commands' so the rule
  still covers what it intended to prohibit.

Docstring / comment cleanup:
- scripts/rpg_edit/__init__.py: module docstring example.
- scripts/update_graphs.py: post-commit lock comment example.
- Removed unused 'WORKSPACE_ROOT' import from rpg_edit/review.py.

Tests baseline preserved (11 pre-existing failures, 883 passing).
Every successful (or failed) 'rpgkit script <X>' invocation now
commits the current state of .rpgkit/ to a dedicated repo at
.rpgkit/.git/, giving users (and the e2e test runner) a free
'git log' / 'git diff' between any two pipeline stages.

New module rpgkit_cli._inner_git holds all the logic:
  - find_workspace_root()    walk up from cwd for .rpgkit/
  - ensure_inner_git()       create .rpgkit/.git + initial commit
  - auto_commit_after_script() snapshot after a 'rpgkit script' call
  - categorise_script()      derive [<category>] prefix
  - should_skip_script()     skip check_* / *_validation* / mcp_server
  - snapshot_count()         for 'rpgkit version' display

CLI surface:
  - rpgkit init      gains --no-rpgkit-git (default OFF = inner git ON)
  - rpgkit update    gains --no-rpgkit-git; backfills inner git for
                     pre-plan-03 workspaces, leaves pre-existing repos
                     untouched.
  - rpgkit script    auto-commits after the child exits; commit
                     message: '[<category>] <relpath> <args>'  with a
                     ' — FAILED (exit N)' suffix when the child failed.
  - rpgkit version   gains 'Inner git: N snapshots' line when present.

Commit identity uses per-call -c user.email/user.name (rpgkit-snapshot
<rpgkit@local>) — never writes to global git config.  Concurrent locks
(post-commit hook background worker) trigger a 1s retry then silent
skip; the next successful commit folds in any missed changes.

Plan: plans/03-auto-snapshot-inner-git.md (local)
Test baseline preserved (11 pre-existing failures, 883 passing).
The GitHub Copilot CLI (`copilot`) does NOT read workspace-local
`.vscode/mcp.json`; it only reads `~/.copilot/mcp-config.json` (or
accepts inline JSON via `--additional-mcp-config`).

To make `copilot` find `rpg-tools` automatically in any
rpgkit-initialised workspace, we now register the server globally on
`rpgkit init --ai copilot` and `rpgkit update --ai copilot`.  This is
safe because rpgkit-mcp is cwd-aware (walks up for rpg.json) and
stateless across workspaces — one global registration serves every
workspace the user cd-s into.  In workspaces without rpg.json the server
starts in degraded mode and tool calls return a rpg_unavailable hint
instructing the user to run /rpgkit.encode.

Safety rules baked into _register_copilot_cli_global_mcp():

  - No-op when in-sync: if the file already contains exactly our
    desired entry, we don't touch it (no mtime bump, no .bak).
  - Refuse to wipe a malformed config: file exists but isn't valid
    JSON -> abort with a clear error; user fixes it or passes
    --no-copilot-cli-mcp.  Without this a stray comma would let us
    silently drop every non-rpg-tools server.
  - Atomic write: serialise to mcp-config.json.tmp then os.replace()
    into place, so Ctrl-C mid-write can't leave the file half-written.
  - Respect user-customised entries: existing rpg-tools whose
    `command` is not `rpgkit-mcp` is left alone (user has pointed it
    elsewhere, e.g. a dev checkout).
  - One-shot .bak: only created on the first modification we actually
    perform; never on no-op runs, never overwritten.

New flag: --no-copilot-cli-mcp on both `init` and `update` to opt out.
Wired into the StepTracker plan so the tree output shows the step.

Verified against five scenarios (fresh, idempotent no-op, preserve
other-servers + update outdated entry, refuse malformed JSON, respect
user-customised command) — all PASS, no stray .tmp files.
VS Code Copilot's custom-agents schema (the rename of the old
chatmode format) no longer recognises the `mode:` frontmatter field.
Three of our prompt templates still carried `mode: agent` from the
chatmode era, producing this on load:

    Custom Agents — The following agents have warnings:
      • rpgkit.code_gen.agent.md: unknown field ignored: mode
      • rpgkit.design_interfaces.agent.md: unknown field ignored: mode
      • rpgkit.plan_tasks.agent.md: unknown field ignored: mode

Fix: replace `mode: agent` with the canonical `name:` field so
the three files match the frontmatter convention already used by
the other ten prompts (`name:` + `description:`).

Files: code_gen.md, design_interfaces.md, plan_tasks.md.
No behavioural change — `mode:` was already being silently ignored;
this just clears the warnings so the diagnostics view stays clean.

Ref: https://code.visualstudio.com/docs/copilot/customization/custom-agents#_are-custom-agents-different-from-chat-modes
Per-workspace data, logs and the inner-git snapshot repo now live at
~/.rpgkit/workspaces/<hash>/ (hash = sha256(realpath(workspace))[:12])
so the user's repository stays clean. Only the workspace marker
(.rpgkit/config.toml) and small user-facing reports (.rpgkit/reports/)
stay in the workspace; rpg.html lives there too so it can be browsed
next to the code.

Scripts:
- common/paths.py: derive DATA_DIR/LOGS_DIR/RPG_FILE/RPG_HTML_FILE
  from the new home-side helpers.  RPG_HTML_FILE points to REPORTS_DIR.
- feature_spec_to_json.py defaults to FEATURE_SPEC_FILE; the
  encoder, update_graphs.py and rpg_edit/* read and write through the
  new common.paths constants.
- New common/rpg_io.py with atomic_write_rpg and safe_load_rpg.  When
  rpg.json is truncated or invalid, scan the inner-git snapshot repo
  for the most recent valid copy, rewrite atomically and continue.
- New rpg_edit/save_plan.py so prompts can pipe EditPlan JSON into the
  home-side data store via the CLI rather than the Write tool.
- init_codebase.py: drop dead 'scripts = get_scripts_dir()' assignments
  and the now-unused import.

Prompt templates & docs:
- README.md and docs/* describe the new layout and point users at
  'rpgkit view-graph' to open rpg.html without computing the hash.
- templates/commands/*.md drop shell '> .rpgkit/logs/X.log 2>&1'
  redirections (scripts log internally) and remove every reference to
  '~/.rpgkit/workspaces/<hash>/' from agent-facing prompts.  The agent
  only reads stdout, and '<hash>' was often misread as a literal
  placeholder.

Tests:
- New tests/test_storage.py: workspace id, meta read/write, path
  helpers, WorkspaceMetaMismatch guard.
- New tests/test_rpg_io.py: atomic-write, unicode roundtrip, inner-git
  restore on corruption.
- test_e2e.py: assertion no longer hard-codes '.rpgkit/data/'.

Comment-style cleanup:
- Drop internal codename references (Plan B, Plan 03, plan A2/A3,
  plan B3/B4, plan E2/E3, plans/*.md, plan §2.5).
- Drop emphasis adverbs (deliberately, the single most, carve-out,
  Defensive:), version narration (pre-0.1.3, Pre-v4) and caps emphasis
  (MUST NOT, do NOT) from docstrings and inline comments.
src/rpgkit_cli/_storage.py (new):
- workspace_id = sha256(realpath(workspace))[:12]; helpers for home
  dir, data/, logs/, inner-git/, reports/, meta.toml.
- ensure_workspace_storage creates the layout idempotently and raises
  WorkspaceMetaMismatch on a hash collision against a different
  recorded workspace path.
- find_workspace_root_from walks up from cwd, but only accepts a
  candidate when its home-side dir exists AND meta.workspace_path
  matches.  Stale .rpgkit/config.toml markers (workspace deleted,
  moved, or renamed) are skipped instead of being silently adopted.

src/rpgkit_cli/__init__.py:
- Wire init/update through ensure_workspace_storage; logs and the
  inner-git dir are home-side, reports stay workspace-side.
- 'rpgkit version' prints the resolved workspace path, data/logs
  dirs and inner-git snapshot count, tagged '(not created yet)' when
  home-side storage hasn't been bootstrapped.
- New hidden 'rpgkit hook <name>' command.  The on-disk hooks under
  .git/hooks/ are now 3-line stubs that exec into this command, so
  path resolution, logging, locking and the detached background
  worker live in one Python place; upgrading the CLI takes effect
  without reinstalling hooks.
- post-commit: phase-1 'update_graphs.py sync' foreground, phase-2
  'update_graphs.py update-rpg --json' detached via
  Popen(start_new_session=True), with an mkdir-based directory lock
  and 60-min stale-lock recovery.
- pre-commit and post-merge: same dispatcher, sync only.

src/rpgkit_cli/_inner_git.py:
- Set RPGKIT_HOOK / RPGKIT_HOOK_SHA in 'rpgkit hook' so inner-git
  snapshot messages tag automated commits:
    [hook:post-commit @ a1b2c3d] update-rpg --json
    [hook:pre-commit  @ 9f8e7d6] sync --staged-only
    [decoder] feature_build.py --mode step1    (manual)
- _INNER_GIT_IGNORE now only excludes logs/copilot/ (MB-scale LLM
  session traces).  Other per-stage logs are tracked so users can
  'git -C ~/.rpgkit/workspaces/<hash> log -p logs/<stage>.log' to
  inspect how a stage's output evolved across snapshots.
- _ensure_gitignore_current rewrites the inner-git .gitignore before
  each commit if it has drifted from the current policy, so existing
  inner repos upgrade silently on the next snapshot.

src/rpgkit_cli/_assets.py, entries.py:
- Comment-style cleanup (drop emphasis adverbs, internal codenames,
  version narration).
When the outer repo's git commit runs a hook, git exports GIT_INDEX_FILE pointing at the outer .git/index.lock. If we leak that into the inner-git subprocess, the inner-git add -A writes entries (from $HOME/.rpgkit/...) into the outer index.lock, corrupting the outer commit ("error: invalid object ... Error building trees"). Strip GIT_INDEX_FILE / GIT_DIR / GIT_WORK_TREE / GIT_OBJECT_DIRECTORY before invoking the inner-git so the two repos stay isolated.
Several scripts logged absolute paths like '/home/user/proj/data/X.json' on success. When run via 'rpgkit script' under an LLM agent, those paths reveal the workspace layout (and home-side .rpgkit/ paths) to the agent, which then tries to access them and fails. Switch all 'saved to: {path}' messages to use path.name so stdout stays workspace-independent. Files touched: build_data_flow, build_skeleton, design_base_classes, design_interfaces, feature_spec_to_json, plan_tasks, summary_skeleton.
Per-stage script invocations now persist their stdout into the workspace logs directory (resolved via _storage.workspace_logs_dir). If the home-side dir doesn't exist yet (rpgkit init hasn't run), fall back to the previous behaviour (no log, direct passthrough).
Claude writes session traces under $HOME/.claude/projects/<hash>/sessions/ which is outside the rpgkit workspace. The metadata bookkeeping line called Path.relative_to(workspace_root) and raised ValueError, aborting the whole LLM call (and all retries) even after a successful response. Catch ValueError and fall back to the absolute path — session_trace is informational metadata and must never break the LLM call.
The pre-commit hook ran 'update_graphs.py sync --staged-only' and was followed ~1 sec later by the full post-commit sync that overwrote its output. Net effect: extra latency on every git commit and noisy [hook:pre-commit] entries in inner-git history, with no observable benefit (its writes never reach user's commit because all RPG state lives home-side).

Drop the install path; replace with idempotent _uninstall_git_pre_commit_hook so existing workspaces are cleaned on the next 'rpgkit init' / 'rpgkit update'. Keep 'rpgkit hook pre-commit' dispatcher branch as a deliberate no-op for backward compat with old hook files that haven't been stripped yet.

Also update MCP rpg-tools description ("pre-commit hook" -> "post-commit hook").
README.zh-CN.md: switch to 'Coding Agent' / 'workflow' English terminology (avoids translation drift to ja/ko/hi); rewrite '快速开始:已有仓库' to not require copying the repo; rewrite 'rpgkit init 之后会发生什么' with workspace-side vs home-side split, three-bullet rationale, multi-project parallelism callout; add troubleshooting entries for finding the home-side hash, cleaning home-side space, and full reset; split platform tables (Agent x Surface and OS x Shell) with legend.

docs/project-structure.md: fix self-contradiction (reports stay inside workspace, not outside); fix stale '.rpgkit/data/' path reference; add '.git/hooks/' (post-commit + post-merge only) and '.rpgkit/reports/' to the workspace file tree; replace the Python hashlib one-liner with 'rpgkit version' guidance; add 'Quick reference: where does each file live?' table covering workspace-side vs home-side artefacts.
markdownlint-cli2 only walks up from each linted file; running it from RPG-Kit/ never reached the workspace-root config. Add a sibling config that disables MD013 / MD041 / MD060 (line length, first-line H1, double-width column alignment — all false positives on slash-command templates and CJK docs) and ignores .venv/, plans/, workspace/ as non-published trees.
Remove the view-graph CLI command and update docs to direct users to rpgkit version and the generated reports path instead.

This avoids noisy browser-launch failures in headless and WSL environments while keeping the graph report discoverable.
Use the current zh-CN README as the source structure and sync English, Japanese, Korean, and Hindi variants.

Update installation, runtime storage, update workflow, platform support, and troubleshooting sections consistently across the localized READMEs.
…tall

# Conflicts:
#	RPG-Kit/src/rpgkit_cli/__init__.py
@microsoft-github-policy-service
Copy link
Copy Markdown

@HuYaSen please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.

@microsoft-github-policy-service agree [company="{your company}"]

Options:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@microsoft-github-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@microsoft-github-policy-service agree company="Microsoft"
Contributor License Agreement

Contribution License Agreement

This Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
and conveys certain license rights to Microsoft Corporation and its affiliates (“Microsoft”) for Your
contributions to Microsoft open source projects. This Agreement is effective as of the latest signature
date below.

  1. Definitions.
    “Code” means the computer software code, whether in human-readable or machine-executable form,
    that is delivered by You to Microsoft under this Agreement.
    “Project” means any of the projects owned or managed by Microsoft and offered under a license
    approved by the Open Source Initiative (www.opensource.org).
    “Submit” is the act of uploading, submitting, transmitting, or distributing code or other content to any
    Project, including but not limited to communication on electronic mailing lists, source code control
    systems, and issue tracking systems that are managed by, or on behalf of, the Project for the purpose of
    discussing and improving that Project, but excluding communication that is conspicuously marked or
    otherwise designated in writing by You as “Not a Submission.”
    “Submission” means the Code and any other copyrightable material Submitted by You, including any
    associated comments and documentation.
  2. Your Submission. You must agree to the terms of this Agreement before making a Submission to any
    Project. This Agreement covers any and all Submissions that You, now or in the future (except as
    described in Section 4 below), Submit to any Project.
  3. Originality of Work. You represent that each of Your Submissions is entirely Your original work.
    Should You wish to Submit materials that are not Your original work, You may Submit them separately
    to the Project if You (a) retain all copyright and license information that was in the materials as You
    received them, (b) in the description accompanying Your Submission, include the phrase “Submission
    containing materials of a third party:” followed by the names of the third party and any licenses or other
    restrictions of which You are aware, and (c) follow any other instructions in the Project’s written
    guidelines concerning Submissions.
  4. Your Employer. References to “employer” in this Agreement include Your employer or anyone else
    for whom You are acting in making Your Submission, e.g. as a contractor, vendor, or agent. If Your
    Submission is made in the course of Your work for an employer or Your employer has intellectual
    property rights in Your Submission by contract or applicable law, You must secure permission from Your
    employer to make the Submission before signing this Agreement. In that case, the term “You” in this
    Agreement will refer to You and the employer collectively. If You change employers in the future and
    desire to Submit additional Submissions for the new employer, then You agree to sign a new Agreement
    and secure permission from the new employer before Submitting those Submissions.
  5. Licenses.
  • Copyright License. You grant Microsoft, and those who receive the Submission directly or
    indirectly from Microsoft, a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license in the
    Submission to reproduce, prepare derivative works of, publicly display, publicly perform, and distribute
    the Submission and such derivative works, and to sublicense any or all of the foregoing rights to third
    parties.
  • Patent License. You grant Microsoft, and those who receive the Submission directly or
    indirectly from Microsoft, a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license under
    Your patent claims that are necessarily infringed by the Submission or the combination of the
    Submission with the Project to which it was Submitted to make, have made, use, offer to sell, sell and
    import or otherwise dispose of the Submission alone or with the Project.
  • Other Rights Reserved. Each party reserves all rights not expressly granted in this Agreement.
    No additional licenses or rights whatsoever (including, without limitation, any implied licenses) are
    granted by implication, exhaustion, estoppel or otherwise.
  1. Representations and Warranties. You represent that You are legally entitled to grant the above
    licenses. You represent that each of Your Submissions is entirely Your original work (except as You may
    have disclosed under Section 3). You represent that You have secured permission from Your employer to
    make the Submission in cases where Your Submission is made in the course of Your work for Your
    employer or Your employer has intellectual property rights in Your Submission by contract or applicable
    law. If You are signing this Agreement on behalf of Your employer, You represent and warrant that You
    have the necessary authority to bind the listed employer to the obligations contained in this Agreement.
    You are not expected to provide support for Your Submission, unless You choose to do so. UNLESS
    REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, AND EXCEPT FOR THE WARRANTIES
    EXPRESSLY STATED IN SECTIONS 3, 4, AND 6, THE SUBMISSION PROVIDED UNDER THIS AGREEMENT IS
    PROVIDED WITHOUT WARRANTY OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY OF
    NONINFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
  2. Notice to Microsoft. You agree to notify Microsoft in writing of any facts or circumstances of which
    You later become aware that would make Your representations in this Agreement inaccurate in any
    respect.
  3. Information about Submissions. You agree that contributions to Projects and information about
    contributions may be maintained indefinitely and disclosed publicly, including Your name and other
    information that You submit with Your Submission.
  4. Governing Law/Jurisdiction. This Agreement is governed by the laws of the State of Washington, and
    the parties consent to exclusive jurisdiction and venue in the federal courts sitting in King County,
    Washington, unless no federal subject matter jurisdiction exists, in which case the parties consent to
    exclusive jurisdiction and venue in the Superior Court of King County, Washington. The parties waive all
    defenses of lack of personal jurisdiction and forum non-conveniens.
  5. Entire Agreement/Assignment. This Agreement is the entire agreement between the parties, and
    supersedes any and all prior agreements, understandings or communications, written or oral, between
    the parties relating to the subject matter hereof. This Agreement may be assigned by Microsoft.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates RPG-Kit to the v0.1.3 “global CLI + home-side runtime store” architecture, moving runtime artifacts out of the workspace, consolidating script/hook invocation through rpgkit script / rpgkit-mcp, and syncing templates/docs/tests to the new layout.

Changes:

  • Add home-directory workspace storage (~/.rpgkit/workspaces/<hash>/...) plus inner-git snapshotting and MCP console-script entrypoint.
  • Refactor scripts/templates/docs/tests to invoke pipeline stages via the global rpgkit dispatcher and to avoid leaking absolute paths.
  • Add atomic JSON writes + corruption recovery from inner-git history (common.rpg_io), and switch key readers/writers to use it.

Reviewed changes

Copilot reviewed 76 out of 77 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
RPG-Kit/tests/test_workspace_unified_layout.py Docstring alignment tweak.
RPG-Kit/tests/test_storage.py New tests for home-side storage API.
RPG-Kit/tests/test_step3_polish.py Hook script expectation update.
RPG-Kit/tests/test_rpg_io.py New tests for atomic write + recovery.
RPG-Kit/tests/test_hooks_install.py Tests updated for global rpgkit hooks/tasks.
RPG-Kit/tests/test_e2e.py E2E path expectation updated for home store.
RPG-Kit/tests/test_dep_graph_incremental.py Test header text tweak.
RPG-Kit/templates/commands/update_rpg.md Use rpgkit script and new log guidance.
RPG-Kit/templates/commands/rpg_edit.md Update workflow to rpgkit script + saved artifacts.
RPG-Kit/templates/commands/plan_tasks.md Remove deprecated frontmatter; use rpgkit script.
RPG-Kit/templates/commands/feature_spec.md Switch to rpgkit script invocation.
RPG-Kit/templates/commands/feature_refactor.md Switch to rpgkit script invocation/log guidance.
RPG-Kit/templates/commands/feature_edit.md Switch to rpgkit script invocation/log guidance.
RPG-Kit/templates/commands/feature_build.md Switch to rpgkit script; stdout/log instructions.
RPG-Kit/templates/commands/encode.md Switch to rpgkit script; stdout/log instructions.
RPG-Kit/templates/commands/design_interfaces.md Remove deprecated frontmatter; use rpgkit script.
RPG-Kit/templates/commands/design_base_classes.md Switch to rpgkit script invocation.
RPG-Kit/templates/commands/code_gen.md Remove deprecated frontmatter; use rpgkit script.
RPG-Kit/templates/commands/build_skeleton.md Switch to rpgkit script; home-store outputs.
RPG-Kit/templates/commands/build_data_flow.md Switch to rpgkit script; home-store outputs.
RPG-Kit/src/rpgkit_cli/entries.py Add rpgkit-mcp console-script entry.
RPG-Kit/src/rpgkit_cli/_storage.py New home-side workspace storage layout + meta.
RPG-Kit/src/rpgkit_cli/_inner_git.py New inner-git snapshotting for home workspace dir.
RPG-Kit/src/rpgkit_cli/_assets.py Locate bundled scripts/templates inside wheel.
RPG-Kit/scripts/update_graphs.py Use home-store paths, safe load, reports dir HTML.
RPG-Kit/scripts/summary_skeleton.py Avoid printing absolute output paths.
RPG-Kit/scripts/smoke_test.py Use cmd_for() in user guidance.
RPG-Kit/scripts/run_batch.py Use cmd_for() in retry guidance.
RPG-Kit/scripts/rpg/models.py Comment refinement (type inference docs).
RPG-Kit/scripts/rpg/graph_query.py Switch JSON loads to safe_load_rpg.
RPG-Kit/scripts/rpg_encoder/workflow.py Use atomic_write_rpg for rpg.json writes.
RPG-Kit/scripts/rpg_encoder/version_control.py Use atomic_write_rpg during rollback writes.
RPG-Kit/scripts/rpg_encoder/run_update_rpg.py Update usage docs to rpgkit script.
RPG-Kit/scripts/rpg_encoder/run_encode.py Write rpg.html to workspace reports dir.
RPG-Kit/scripts/rpg_edit/validate.py Update guidance to rpgkit script form.
RPG-Kit/scripts/rpg_edit/save_plan.py New helper to save plan into home-store data dir.
RPG-Kit/scripts/rpg_edit/review.py Default plan/impact paths; use cmd_for() tools.
RPG-Kit/scripts/rpg_edit/locate.py Remove plan-doc references in comments.
RPG-Kit/scripts/rpg_edit/impact.py Add --save to persist impact JSON.
RPG-Kit/scripts/rpg_edit/code.py Default plan path; use cmd_for() for smoke test.
RPG-Kit/scripts/rpg_edit/apply.py Default plan path; home-store plan default.
RPG-Kit/scripts/rpg_edit/init.py Update invocation docs to rpgkit script.
RPG-Kit/scripts/plan_tasks.py Avoid printing absolute output paths.
RPG-Kit/scripts/mcp_server.py Make MCP runnable via rpgkit-mcp; doc updates.
RPG-Kit/scripts/init_codebase.py Use cmd_for() in next-action guidance.
RPG-Kit/scripts/feature_spec_to_json.py Output to canonical home-store path; avoid path leaks.
RPG-Kit/scripts/feature_build_validation.py Log message cleanup.
RPG-Kit/scripts/design_interfaces.py Avoid printing absolute output paths.
RPG-Kit/scripts/design_base_classes.py Avoid printing absolute output paths.
RPG-Kit/scripts/common/rpg_io.py New atomic write + inner-git recovery helpers.
RPG-Kit/scripts/common/project_types.py Remove plan-doc references in comments.
RPG-Kit/scripts/common/paths.py New home-store path resolution + cmd_for().
RPG-Kit/scripts/common/llm_client.py Resolve AI CLI cmd via env/config; better trace handling.
RPG-Kit/scripts/common/execution_state.py Comment cleanups (remove plan refs).
RPG-Kit/scripts/code_gen/rpg_updater.py Comment cleanup (remove plan refs).
RPG-Kit/scripts/code_gen/result_builders.py Use cmd_for() in next-action hints.
RPG-Kit/scripts/code_gen/global_review.py Comment cleanup (remove plan refs).
RPG-Kit/scripts/code_gen/batch_prompts.py Update “must not run” guidance to global CLI.
RPG-Kit/scripts/code_gen/init.py Comment cleanup.
RPG-Kit/scripts/check_skeleton.py Update next_action to rpgkit script.
RPG-Kit/scripts/check_code_gen.py Use cmd_for() in next-action hints.
RPG-Kit/scripts/build_skeleton.py Avoid printing absolute output paths.
RPG-Kit/scripts/build_data_flow.py Avoid printing absolute output paths.
RPG-Kit/scripts/init.py Add marker file for hatch bundling.
RPG-Kit/README.zh-CN.md Sync docs for v0.1.3 architecture.
RPG-Kit/README.md Sync docs for v0.1.3 architecture.
RPG-Kit/README.ko-KR.md Sync docs for v0.1.3 architecture.
RPG-Kit/README.ja-JP.md Sync docs for v0.1.3 architecture.
RPG-Kit/README.hi-IN.md Sync docs for v0.1.3 architecture.
RPG-Kit/pyproject.toml Bump to 0.1.3; add bundled assets + rpgkit-mcp.
RPG-Kit/docs/project-structure.md Document new home-side runtime store + reports.
RPG-Kit/docs/configuration.md Document .rpgkit/config.toml + AI CLI resolution.
RPG-Kit/docs/commands.md Update command docs for rpgkit script + paths note.
RPG-Kit/docs/cli-reference.md Document script, legacy-download, auto-upgrade flags.
RPG-Kit/.markdownlint-cli2.jsonc Add local markdownlint config mirror.
RPG-Kit/.gitignore Unignore .rpgkit/config.toml; update patterns.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread RPG-Kit/src/rpgkit_cli/_storage.py Outdated
Comment thread RPG-Kit/scripts/common/rpg_io.py
Comment thread RPG-Kit/scripts/__init__.py Outdated
Comment thread RPG-Kit/src/rpgkit_cli/entries.py
Comment thread RPG-Kit/templates/commands/feature_build.md Outdated
Comment thread RPG-Kit/docs/project-structure.md Outdated
- _storage.find_workspace_root_from: drop home-side dir guard so
  fresh marker-only workspaces are discovered before any state is
  written; rely on .meta.toml workspace_path for stale-marker
  protection (moved/renamed dirs). Add regression test.
- scripts/common/rpg_io: strip GIT_INDEX_FILE / GIT_DIR /
  GIT_WORK_TREE / GIT_OBJECT_DIRECTORY from env before running
  inner-git recovery subprocesses so hook contexts don't bleed the
  outer repo's git state into recovery queries.
- scripts/__init__.py: refresh docstring to describe the
  'rpgkit script <name>' dispatcher path (no longer copied into
  the workspace).
- entries.mcp_main: remove unreachable 'scripts_dir is None' branch
  (_assets.scripts_dir always returns Path).
- templates/commands/feature_build.md: rewrite broken / duplicated
  Step 2 sentence into a single coherent paragraph.
- docs/project-structure.md: drop retired 'pre-commit' from the
  installed-hooks quick-reference row.
@HuYaSen HuYaSen force-pushed the dev/rpgkit-global-install branch from c9cb889 to ea05e7d Compare May 22, 2026 09:04
@HuYaSen HuYaSen requested a review from Copilot May 22, 2026 09:21
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 76 out of 77 changed files in this pull request and generated 3 comments.

Comment thread RPG-Kit/scripts/common/rpg_io.py Outdated
# working when a script ever renames data files in the future.
try:
log = subprocess.run(
["git", "-C", str(git_dir), "log", "--format=%H", "--", relpath],
Comment thread RPG-Kit/src/rpgkit_cli/_inner_git.py Outdated
Comment on lines +234 to +236
When a fresh repo is created we also drop a ``.gitignore`` that
excludes ``logs/`` (too noisy), then commit the current state of
``data/`` + ``.meta.toml`` so ``git log`` has a starting point.
Comment thread RPG-Kit/tests/test_e2e.py Outdated
Comment on lines 731 to 742
def test_cli_encode_helpers(self, sample_repo, tmp_path):
"""RPG_FILE path constant points to correct location."""
"""RPG_FILE path constant points to the home-dir runtime location.

Workspace state lives under ``~/.rpgkit/workspaces/<hash>/``,
so ``RPG_FILE`` resolves to ``<home-state>/data/rpg.json`` rather than the
legacy ``<workspace>/.rpgkit/data/rpg.json``. We just assert the trailing
path components so the test is independent of any specific hash.
"""
from common.paths import RPG_FILE

assert str(RPG_FILE).endswith(os.path.join(".rpgkit", "data", "rpg.json"))
assert str(RPG_FILE).endswith(os.path.join("data", "rpg.json"))

- scripts/common/rpg_io.py: add `--follow` to inner-git `git log` recovery
  call so the rename-tracking promise in the surrounding comment is
  actually delivered (works because the call uses a single path)
- src/rpgkit_cli/_inner_git.py: correct `ensure_inner_git` docstring to
  say the dropped `.gitignore` excludes `logs/copilot/` (not `logs/`),
  matching the actual `_INNER_GIT_IGNORE` content and the file's own
  earlier comment
- tests/test_e2e.py: drop `test_cli_encode_helpers` — it was a misnamed
  path-constant shape check (no encode helpers exercised, fixtures
  unused) whose responsibilities are already covered by tests/test_storage.py
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